Renaming site contents to site text

This commit is contained in:
Robin Ward 2014-09-24 14:45:35 -04:00
parent 84cf402482
commit bc53d48bd7
45 changed files with 349 additions and 350 deletions

View File

@ -4,9 +4,9 @@ export default Ember.ObjectController.extend({
saveDisabled: function() {
if (this.get('saving')) { return true; }
if ((!this.get('content.allow_blank')) && Ember.empty(this.get('content.content'))) { return true; }
if ((!this.get('allow_blank')) && Ember.empty(this.get('value'))) { return true; }
return false;
}.property('saving', 'content.content'),
}.property('saving', 'value'),
actions: {
saveChanges: function() {

View File

@ -1,25 +0,0 @@
Discourse.SiteContent = Discourse.Model.extend({
markdown: Em.computed.equal('format', 'markdown'),
plainText: Em.computed.equal('format', 'plain'),
html: Em.computed.equal('format', 'html'),
css: Em.computed.equal('format', 'css'),
save: function() {
return Discourse.ajax("/admin/customize/site_contents/" + this.get('content_type'), {
type: 'PUT',
data: {content: this.get('content')}
});
}
});
Discourse.SiteContent.reopenClass({
find: function(type) {
return Discourse.ajax("/admin/customize/site_contents/" + type).then(function (data) {
return Discourse.SiteContent.create(data.site_content);
});
}
});

View File

@ -1,11 +0,0 @@
Discourse.SiteContentType = Discourse.Model.extend();
Discourse.SiteContentType.reopenClass({
findAll: function() {
return Discourse.ajax("/admin/customize/site_content_types").then(function(data) {
return data.map(function(ct) {
return Discourse.SiteContentType.create(ct);
});
});
}
});

View File

@ -0,0 +1,21 @@
Discourse.SiteText = Discourse.Model.extend({
markdown: Em.computed.equal('format', 'markdown'),
plainText: Em.computed.equal('format', 'plain'),
html: Em.computed.equal('format', 'html'),
css: Em.computed.equal('format', 'css'),
save: function() {
return Discourse.ajax("/admin/customize/site_text/" + this.get('text_type'), {
type: 'PUT',
data: {value: this.get('value')}
});
}
});
Discourse.SiteText.reopenClass({
find: function(type) {
return Discourse.ajax("/admin/customize/site_text/" + type).then(function (data) {
return Discourse.SiteText.create(data.site_text);
});
}
});

View File

@ -0,0 +1,11 @@
Discourse.SiteTextType = Discourse.Model.extend();
Discourse.SiteTextType.reopenClass({
findAll: function() {
return Discourse.ajax("/admin/customize/site_text_types").then(function(data) {
return data.map(function(ct) {
return Discourse.SiteTextType.create(ct);
});
});
}
});

View File

@ -1,11 +0,0 @@
export default Ember.Route.extend({
serialize: function(model) {
return {content_type: model.get('content_type')};
},
model: function(params) {
return Discourse.SiteContent.find(params.content_type);
}
});

View File

@ -0,0 +1,5 @@
export default Ember.Route.extend({
model: function(params) {
return Discourse.SiteText.find(params.text_type);
}
});

View File

@ -1,5 +1,5 @@
export default Ember.Route.extend({
model: function() {
return Discourse.SiteContentType.findAll();
return Discourse.SiteTextType.findAll();
}
});

View File

@ -15,8 +15,8 @@ Discourse.Route.buildRoutes(function() {
this.resource('adminCustomize', { path: '/customize' } ,function() {
this.route('colors');
this.route('css_html');
this.resource('adminSiteContents', { path: '/site_contents' }, function() {
this.route('edit', {path: '/:content_type'});
this.resource('adminSiteText', { path: '/site_text' }, function() {
this.route('edit', {path: '/:text_type'});
});
});
this.route('api');

View File

@ -3,7 +3,7 @@
<ul class="nav nav-pills">
<li>{{#link-to 'adminCustomize.colors'}}{{i18n admin.customize.colors.title}}{{/link-to}}</li>
<li>{{#link-to 'adminCustomize.css_html'}}{{i18n admin.customize.css_html.title}}{{/link-to}}</li>
<li>{{#link-to 'adminSiteContents'}}{{i18n admin.site_content.title}}{{/link-to}}</li>
<li>{{#link-to 'adminSiteText'}}{{i18n admin.site_content.title}}{{/link-to}}</li>
</ul>
</div>
</div>

View File

@ -3,7 +3,7 @@
<ul>
{{#each model}}
<li>
{{#link-to 'adminSiteContents.edit' content_type}}{{title}}{{/link-to}}
{{#link-to 'adminSiteText.edit' text_type}}{{title}}{{/link-to}}
</li>
{{/each}}
</ul>

View File

@ -2,16 +2,16 @@
<p class='description'>{{description}}</p>
{{#if markdown}}
{{pagedown-editor value=model.content}}
{{pagedown-editor value=value}}
{{/if}}
{{#if plainText}}
{{textarea value=model.content class="plain"}}
{{textarea value=value class="plain"}}
{{/if}}
{{#if html}}
{{aceEditor content=model.content mode="html"}}
{{aceEditor content=value mode="html"}}
{{/if}}
{{#if css}}
{{aceEditor content=model.content mode="css"}}
{{aceEditor content=value mode="css"}}
{{/if}}
<div class='controls'>

View File

@ -1,7 +0,0 @@
class Admin::SiteContentTypesController < Admin::AdminController
def index
render_serialized(SiteContent.content_types, SiteContentTypeSerializer)
end
end

View File

@ -1,21 +0,0 @@
class Admin::SiteContentsController < Admin::AdminController
def show
site_content = SiteContent.find_or_new(params[:id].to_s)
render_serialized(site_content, SiteContentSerializer)
end
def update
site_content = SiteContent.find_or_new(params[:id].to_s)
# Updating to nothing is the same as removing it
if params[:content].present?
site_content.content = params[:content]
site_content.save!
else
site_content.destroy
end
render nothing: true
end
end

View File

@ -0,0 +1,21 @@
class Admin::SiteTextController < Admin::AdminController
def show
site_text = SiteText.find_or_new(params[:id].to_s)
render_serialized(site_text, SiteTextSerializer)
end
def update
site_text = SiteText.find_or_new(params[:id].to_s)
# Updating to nothing is the same as removing it
if params[:value].present?
site_text.value = params[:value]
site_text.save!
else
site_text.destroy
end
render nothing: true
end
end

View File

@ -0,0 +1,7 @@
class Admin::SiteTextTypesController < Admin::AdminController
def index
render_serialized(SiteText.text_types, SiteTextTypeSerializer)
end
end

View File

@ -256,12 +256,12 @@ class ApplicationController < ActionController::Base
def custom_html_json
data = {
top: SiteContent.content_for(:top),
bottom: SiteContent.content_for(:bottom)
top: SiteText.text_for(:top),
bottom: SiteText.text_for(:bottom)
}
if SiteSetting.tos_accept_required && !current_user
data[:tos_signup_form_message] = SiteContent.content_for(:tos_signup_form_message)
data[:tos_signup_form_message] = SiteText.text_for(:tos_signup_form_message)
end
if DiscoursePluginRegistry.custom_html

View File

@ -114,7 +114,7 @@ module ApplicationHelper
# Look up site content for a key. If the key is blank, you can supply a block and that
# will be rendered instead.
def markdown_content(key, replacements=nil)
result = PrettyText.cook(SiteContent.content_for(key, replacements || {})).html_safe
result = PrettyText.cook(SiteText.text_for(key, replacements || {})).html_safe
if result.blank? && block_given?
yield
nil

View File

@ -17,7 +17,7 @@ class UserNotifications < ActionMailer::Base
build_email(user.email,
template: 'user_notifications.signup_after_approval',
email_token: opts[:email_token],
new_user_tips: SiteContent.content_for(:usage_tips))
new_user_tips: SiteText.text_for(:usage_tips))
end
def authorize_email(user, opts={})
@ -197,7 +197,7 @@ class UserNotifications < ActionMailer::Base
end
end
top = SiteContent.content_for(:notification_email_top)
top = SiteText.text_for(:notification_email_top)
html = UserNotificationRenderer.new(Rails.configuration.paths["app/views"]).render(
template: 'email/notification',

View File

@ -1,45 +0,0 @@
require_dependency 'site_content_type'
require_dependency 'site_content_class_methods'
class SiteContent < ActiveRecord::Base
extend SiteContentClassMethods
self.primary_key = 'content_type'
validates_presence_of :content
def self.formats
@formats ||= Enum.new(:plain, :markdown, :html, :css)
end
add_content_type :usage_tips, default_18n_key: 'system_messages.usage_tips.text_body_template'
add_content_type :education_new_topic, default_18n_key: 'education.new-topic'
add_content_type :education_new_reply, default_18n_key: 'education.new-reply'
add_content_type :tos_user_content_license, default_18n_key: 'terms_of_service.user_content_license'
add_content_type :tos_miscellaneous, default_18n_key: 'terms_of_service.miscellaneous'
add_content_type :login_required_welcome_message, default_18n_key: 'login_required.welcome_message'
add_content_type :tos_signup_form_message, default_18n_key: 'terms_of_service.signup_form_message', format: :html
add_content_type :top, allow_blank: true, format: :html
add_content_type :bottom, allow_blank: true, format: :html
add_content_type :head, allow_blank: true, format: :html
add_content_type :notification_email_top, allow_blank: true, format: :markdown
def site_content_type
@site_content_type ||= SiteContent.content_types.find {|t| t.content_type == content_type.to_sym}
end
end
# == Schema Information
#
# Table name: site_contents
#
# content_type :string(255) not null, primary key
# content :text not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_site_contents_on_content_type (content_type) UNIQUE
#

View File

@ -1,27 +0,0 @@
class SiteContentType
attr_accessor :content_type, :format
def initialize(content_type, format, opts=nil)
@opts = opts || {}
@content_type = content_type
@format = format
end
def title
I18n.t("content_types.#{content_type}.title")
end
def description
I18n.t("content_types.#{content_type}.description")
end
def allow_blank?
!!@opts[:allow_blank]
end
def default_content
@opts[:default_18n_key].present? ? I18n.t(@opts[:default_18n_key]) : ""
end
end

44
app/models/site_text.rb Normal file
View File

@ -0,0 +1,44 @@
require_dependency 'site_text_type'
require_dependency 'site_text_class_methods'
class SiteText < ActiveRecord::Base
extend SiteTextClassMethods
self.primary_key = 'text_type'
validates_presence_of :value
def self.formats
@formats ||= Enum.new(:plain, :markdown, :html, :css)
end
add_text_type :usage_tips, default_18n_key: 'system_messages.usage_tips.text_body_template'
add_text_type :education_new_topic, default_18n_key: 'education.new-topic'
add_text_type :education_new_reply, default_18n_key: 'education.new-reply'
add_text_type :tos_user_content_license, default_18n_key: 'terms_of_service.user_content_license'
add_text_type :tos_miscellaneous, default_18n_key: 'terms_of_service.miscellaneous'
add_text_type :login_required_welcome_message, default_18n_key: 'login_required.welcome_message'
add_text_type :tos_signup_form_message, default_18n_key: 'terms_of_service.signup_form_message', format: :html
add_text_type :top, allow_blank: true, format: :html
add_text_type :bottom, allow_blank: true, format: :html
add_text_type :head, allow_blank: true, format: :html
add_text_type :notification_email_top, allow_blank: true, format: :markdown
def site_text_type
@site_text_type ||= SiteText.find_text_type(text_type)
end
end
# == Schema Information
#
# Table name: site_text
#
# text_type :string(255) not null, primary key
# value :text not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_site_text_on_text_type (text_type) UNIQUE
#

View File

@ -0,0 +1,27 @@
class SiteTextType
attr_accessor :text_type, :format
def initialize(text_type, format, opts=nil)
@opts = opts || {}
@text_type = text_type
@format = format
end
def title
I18n.t("content_types.#{text_type}.title")
end
def description
I18n.t("content_types.#{text_type}.description")
end
def allow_blank?
!!@opts[:allow_blank]
end
def default_text
@opts[:default_18n_key].present? ? I18n.t(@opts[:default_18n_key]) : ""
end
end

View File

@ -1,30 +0,0 @@
class SiteContentSerializer < ApplicationSerializer
attributes :content_type,
:title,
:description,
:content,
:format,
:allow_blank?
def title
object.site_content_type.title
end
def description
object.site_content_type.description
end
def format
object.site_content_type.format
end
def content
return object.content if object.content.present?
object.site_content_type.default_content
end
def allow_blank?
object.site_content_type.allow_blank?
end
end

View File

@ -1,13 +0,0 @@
class SiteContentTypeSerializer < ApplicationSerializer
attributes :content_type, :title
def content_type
object.content_type
end
def title
object.title
end
end

View File

@ -0,0 +1,34 @@
class SiteTextSerializer < ApplicationSerializer
attributes :text_type,
:title,
:description,
:value,
:format,
:allow_blank?
def title
object.site_text_type.title
end
def text_type
object.text_type
end
def description
object.site_text_type.description
end
def format
object.site_text_type.format
end
def value
return object.value if object.value.present?
object.site_text_type.default_text
end
def allow_blank?
object.site_text_type.allow_blank?
end
end

View File

@ -0,0 +1,13 @@
class SiteTextTypeSerializer < ApplicationSerializer
attributes :text_type, :title
def text_type
object.text_type
end
def title
object.title
end
end

View File

@ -18,7 +18,7 @@
<%= script "admin"%>
<%- end %>
<%= raw SiteContent.content_for(:head) %>
<%= raw SiteText.text_for(:head) %>
<%= render_google_universal_analytics_code %>
@ -76,7 +76,7 @@
<%= yield :data %>
<footer id='bottom'><%= raw SiteContent.content_for(:bottom) %></footer>
<footer id='bottom'><%= raw SiteText.text_for(:bottom) %></footer>
<%= render :partial => "common/discourse_javascript" %>

View File

@ -5,7 +5,7 @@
<title><%= content_for?(:title) ? yield(:title) + ' - ' + SiteSetting.title : SiteSetting.title %></title>
<meta name="description" content="<%= @description_meta || SiteSetting.site_description %>">
<%= render partial: "layouts/head" %>
<%= raw SiteContent.content_for(:head) %>
<%= raw SiteText.text_for(:head) %>
<%= yield :head %>
</head>
<body>

View File

@ -5,7 +5,7 @@
<title><%=SiteSetting.title%></title>
<meta name="description" content="">
<%= render partial: "layouts/head" %>
<%= raw SiteContent.content_for(:head) %>
<%= raw SiteText.text_for(:head) %>
<%= yield(:no_js_head) %>
</head>
<body>

View File

@ -110,8 +110,8 @@ Discourse::Application.routes.draw do
post "flags/defer/:id" => "flags#defer"
resources :site_customizations, constraints: AdminConstraint.new
scope "/customize" do
resources :site_contents, constraints: AdminConstraint.new
resources :site_content_types, constraints: AdminConstraint.new
resources :site_text, constraints: AdminConstraint.new
resources :site_text_types, constraints: AdminConstraint.new
end
resources :color_schemes, constraints: AdminConstraint.new

View File

@ -33,10 +33,10 @@ unless Rails.env.test?
})
create_static_page_topic('guidelines_topic_id', 'guidelines_topic.title', "guidelines_topic.body",
(SiteContent.content_for(:faq) rescue nil), staff, "guidelines")
(SiteText.text_for(:faq) rescue nil), staff, "guidelines")
create_static_page_topic('privacy_topic_id', 'privacy_topic.title', "privacy_topic.body",
(SiteContent.content_for(:privacy_policy) rescue nil), staff, "privacy policy")
(SiteText.text_for(:privacy_policy) rescue nil), staff, "privacy policy")
end
if seed_welcome_topics

View File

@ -0,0 +1,7 @@
class RenameContentType < ActiveRecord::Migration
def change
rename_column :site_contents, :content_type, :text_type
rename_column :site_contents, :content, :value
rename_table :site_contents, :site_texts
end
end

View File

@ -28,7 +28,7 @@ class ComposerMessagesFinder
education_posts_text = I18n.t('education.until_posts', count: SiteSetting.educate_until_posts)
return {templateName: 'composer/education',
wait_for_typing: true,
body: PrettyText.cook(SiteContent.content_for(education_key, education_posts_text: education_posts_text)) }
body: PrettyText.cook(SiteText.text_for(education_key, education_posts_text: education_posts_text)) }
end
nil

View File

@ -1,50 +0,0 @@
module SiteContentClassMethods
def content_types
@types || []
end
def find_content_type(ct)
SiteContent.content_types.find {|t| t.content_type == ct.to_sym}
end
def add_content_type(content_type, opts=nil)
opts ||= {}
@types ||= []
format = opts[:format] || :markdown
@types << SiteContentType.new(content_type, format, opts)
end
def content_for(content_type, replacements=nil)
replacements ||= {}
replacements = {site_name: SiteSetting.title}.merge!(replacements)
replacements = SiteSetting.settings_hash.merge!(replacements)
site_content = SiteContent.select(:content).find_by(content_type: content_type)
result = ""
if site_content.blank?
ct = find_content_type(content_type)
result = ct.default_content.dup if ct.present?
else
result = site_content.content.dup
end
result.gsub!(/\%\{[^}]+\}/) do |m|
replacements[m[2..-2].to_sym] || m
end
result
end
def find_or_new(content_type)
site_content = SiteContent.find_by(content_type: content_type)
return site_content if site_content.present?
site_content = SiteContent.new
site_content.content_type = content_type
site_content
end
end

View File

@ -0,0 +1,49 @@
module SiteTextClassMethods
def text_types
@types || []
end
def find_text_type(ct)
SiteText.text_types.find {|t| t.text_type == ct.to_sym}
end
def add_text_type(text_type, opts=nil)
opts ||= {}
@types ||= []
format = opts[:format] || :markdown
@types << SiteTextType.new(text_type, format, opts)
end
def text_for(text_type, replacements=nil)
replacements ||= {}
replacements = {site_name: SiteSetting.title}.merge!(replacements)
replacements = SiteSetting.settings_hash.merge!(replacements)
site_text = SiteText.select(:value).find_by(text_type: text_type)
result = ""
if site_text.blank?
ct = find_text_type(text_type)
result = ct.default_text.dup if ct.present?
else
result = site_text.value.dup
end
result.gsub!(/\%\{[^}]+\}/) do |m|
replacements[m[2..-2].to_sym] || m
end
result
end
def find_or_new(text_type)
site_text = SiteText.find_by(text_type: text_type)
return site_text if site_text.present?
site_text = SiteText.new
site_text.text_type = text_type
site_text
end
end

View File

@ -50,7 +50,7 @@ class SystemMessage
site_name: SiteSetting.title,
username: @recipient.username,
user_preferences_url: "#{Discourse.base_url}/users/#{@recipient.username_lower}/preferences",
new_user_tips: SiteContent.content_for(:usage_tips),
new_user_tips: SiteText.text_for(:usage_tips),
site_password: "",
base_url: Discourse.base_url,
}

View File

@ -1,9 +1,9 @@
require 'spec_helper'
describe Admin::SiteContentsController do
describe Admin::SiteTextController do
it "is a subclass of AdminController" do
(Admin::SiteContentsController < Admin::AdminController).should be_true
(Admin::SiteTextController < Admin::AdminController).should be_true
end
context 'while logged in as an admin' do
@ -12,15 +12,15 @@ describe Admin::SiteContentsController do
end
context '.show' do
let(:content_type) { SiteContent.content_types.first.content_type }
let(:text_type) { SiteText.text_types.first.text_type }
it 'returns success' do
xhr :get, :show, id: content_type
xhr :get, :show, id: text_type
response.should be_success
end
it 'returns JSON' do
xhr :get, :show, id: content_type
xhr :get, :show, id: text_type
::JSON.parse(response.body).should be_present
end
end

View File

@ -1,9 +1,9 @@
require 'spec_helper'
describe Admin::SiteContentTypesController do
describe Admin::SiteTextTypesController do
it "is a subclass of AdminController" do
(Admin::SiteContentTypesController < Admin::AdminController).should be_true
(Admin::SiteTextTypesController < Admin::AdminController).should be_true
end
context 'while logged in as an admin' do

View File

@ -1,14 +0,0 @@
Fabricator(:site_content) do
content "%{flower} are red. %{food} are blue."
content_type "great.poem"
end
Fabricator(:site_content_basic, from: :site_content) do
content_type "breaking.bad"
content "best show ever"
end
Fabricator(:site_content_site_setting, from: :site_content) do
content_type "site.replacement"
content "%{title} is evil."
end

View File

@ -0,0 +1,14 @@
Fabricator(:site_text) do
text_type "great.poem"
value "%{flower} are red. %{food} are blue."
end
Fabricator(:site_text_basic, from: :site_text) do
text_type "breaking.bad"
value "best show ever"
end
Fabricator(:site_text_site_setting, from: :site_text) do
text_type "site.replacement"
value "%{title} is evil."
end

View File

@ -1,62 +0,0 @@
require 'spec_helper'
describe SiteContent do
it { should validate_presence_of :content }
describe "#content_for" do
it "returns an empty string for a missing content_type" do
SiteContent.content_for('breaking.bad').should == ""
end
it "returns the default value for a content type with a default" do
SiteContent.content_for("usage_tips").should be_present
end
context "without replacements" do
let!(:site_content) { Fabricate(:site_content_basic) }
it "returns the simple string" do
SiteContent.content_for('breaking.bad').should == "best show ever"
end
end
context "with replacements" do
let!(:site_content) { Fabricate(:site_content) }
let(:replacements) { {flower: 'roses', food: 'grapes'} }
it "returns the correct string with replacements" do
SiteContent.content_for('great.poem', replacements).should == "roses are red. grapes are blue."
end
it "doesn't mind extra keys in the replacements" do
SiteContent.content_for('great.poem', replacements.merge(extra: 'key')).should == "roses are red. grapes are blue."
end
it "ignores missing keys" do
SiteContent.content_for('great.poem', flower: 'roses').should == "roses are red. %{food} are blue."
end
end
context "replacing site_settings" do
let!(:site_content) { Fabricate(:site_content_site_setting) }
it "replaces site_settings by default" do
SiteSetting.stubs(:title).returns("Evil Trout")
SiteContent.content_for('site.replacement').should == "Evil Trout is evil."
end
it "allows us to override the default site settings" do
SiteSetting.stubs(:title).returns("Evil Trout")
SiteContent.content_for('site.replacement', title: 'Good Tuna').should == "Good Tuna is evil."
end
end
end
end

View File

@ -0,0 +1,62 @@
require 'spec_helper'
describe SiteText do
it { should validate_presence_of :value }
describe "#text_for" do
it "returns an empty string for a missing text_type" do
SiteText.text_for('breaking.bad').should == ""
end
it "returns the default value for a text` type with a default" do
SiteText.text_for("usage_tips").should be_present
end
context "without replacements" do
let!(:site_text) { Fabricate(:site_text_basic) }
it "returns the simple string" do
SiteText.text_for('breaking.bad').should == "best show ever"
end
end
context "with replacements" do
let!(:site_text) { Fabricate(:site_text) }
let(:replacements) { {flower: 'roses', food: 'grapes'} }
it "returns the correct string with replacements" do
SiteText.text_for('great.poem', replacements).should == "roses are red. grapes are blue."
end
it "doesn't mind extra keys in the replacements" do
SiteText.text_for('great.poem', replacements.merge(extra: 'key')).should == "roses are red. grapes are blue."
end
it "ignores missing keys" do
SiteText.text_for('great.poem', flower: 'roses').should == "roses are red. %{food} are blue."
end
end
context "replacing site_settings" do
let!(:site_text) { Fabricate(:site_text_site_setting) }
it "replaces site_settings by default" do
SiteSetting.stubs(:title).returns("Evil Trout")
SiteText.text_for('site.replacement').should == "Evil Trout is evil."
end
it "allows us to override the default site settings" do
SiteSetting.stubs(:title).returns("Evil Trout")
SiteText.text_for('site.replacement', title: 'Good Tuna').should == "Good Tuna is evil."
end
end
end
end