diff --git a/app/assets/javascripts/admin/models/site_customization.js b/app/assets/javascripts/admin/models/site_customization.js index 8e90d13cef7..71143b89683 100644 --- a/app/assets/javascripts/admin/models/site_customization.js +++ b/app/assets/javascripts/admin/models/site_customization.js @@ -7,7 +7,7 @@ @module Discourse **/ Discourse.SiteCustomization = Discourse.Model.extend({ - trackedProperties: ['enabled', 'name', 'stylesheet', 'header', 'override_default_style'], + trackedProperties: ['enabled', 'name', 'stylesheet', 'header', 'mobile_stylesheet', 'mobile_header', 'override_default_style'], init: function() { this._super(); @@ -33,7 +33,7 @@ Discourse.SiteCustomization = Discourse.Model.extend({ return changed; - }.property('override_default_style', 'enabled', 'name', 'stylesheet', 'header', 'originals'), + }.property('override_default_style', 'enabled', 'name', 'stylesheet', 'header', 'mobile_stylesheet', 'mobile_header', 'originals'), startTrackingChanges: function() { var _this = this; @@ -62,6 +62,8 @@ Discourse.SiteCustomization = Discourse.Model.extend({ enabled: this.enabled, stylesheet: this.stylesheet, header: this.header, + mobile_stylesheet: this.mobile_stylesheet, + mobile_header: this.mobile_header, override_default_style: this.override_default_style }; diff --git a/app/assets/javascripts/admin/templates/customize.js.handlebars b/app/assets/javascripts/admin/templates/customize.js.handlebars index 6a32ccee663..44b1307b9b4 100644 --- a/app/assets/javascripts/admin/templates/customize.js.handlebars +++ b/app/assets/javascripts/admin/templates/customize.js.handlebars @@ -12,25 +12,38 @@ {{#if selectedItem}}
-
- -
- {{#with selectedItem}} {{textField class="style-name" value=name}} + +
+ +
+ {{#if view.headerActive}} {{aceEditor content=header mode="html"}} {{/if}} {{#if view.stylesheetActive}} {{aceEditor content=stylesheet mode="scss"}} {{/if}} + {{#if view.mobileHeaderActive}} + {{aceEditor content=mobile_header mode="html"}} + {{/if}} + {{#if view.mobileStylesheetActive}} + {{aceEditor content=mobile_stylesheet mode="scss"}} + {{/if}} {{/with}}
diff --git a/app/assets/javascripts/admin/views/admin_customize_view.js b/app/assets/javascripts/admin/views/admin_customize_view.js index a030d43244b..31761233661 100644 --- a/app/assets/javascripts/admin/views/admin_customize_view.js +++ b/app/assets/javascripts/admin/views/admin_customize_view.js @@ -11,20 +11,16 @@ Discourse.AdminCustomizeView = Discourse.View.extend({ templateName: 'admin/templates/customize', classNames: ['customize'], + headerActive: Ember.computed.equal('selected', 'header'), + stylesheetActive: Ember.computed.equal('selected', 'stylesheet'), + mobileHeaderActive: Ember.computed.equal('selected', 'mobileHeader'), + mobileStylesheetActive: Ember.computed.equal('selected', 'mobileStylesheet'), init: function() { this._super(); this.set('selected', 'stylesheet'); }, - headerActive: (function() { - return this.get('selected') === 'header'; - }).property('selected'), - - stylesheetActive: (function() { - return this.get('selected') === 'stylesheet'; - }).property('selected'), - selectHeader: function() { this.set('selected', 'header'); }, @@ -33,6 +29,14 @@ Discourse.AdminCustomizeView = Discourse.View.extend({ this.set('selected', 'stylesheet'); }, + selectMobileHeader: function() { + this.set('selected', 'mobileHeader'); + }, + + selectMobileStylesheet: function() { + this.set('selected', 'mobileStylesheet'); + }, + didInsertElement: function() { var controller = this.get('controller'); return Mousetrap.bindGlobal(['meta+s', 'ctrl+s'], function() { diff --git a/app/controllers/admin/site_customizations_controller.rb b/app/controllers/admin/site_customizations_controller.rb index 855b947bd9b..d2595a3f11c 100644 --- a/app/controllers/admin/site_customizations_controller.rb +++ b/app/controllers/admin/site_customizations_controller.rb @@ -49,7 +49,7 @@ class Admin::SiteCustomizationsController < Admin::AdminController private def site_customization_params - params.require(:site_customization).permit(:name, :stylesheet, :header, :position, :enabled, :key, :override_default_style, :stylesheet_baked) + params.require(:site_customization).permit(:name, :stylesheet, :header, :mobile_stylesheet, :mobile_header, :position, :enabled, :key, :override_default_style, :stylesheet_baked) end def log_site_customization_change(old_record, new_params) diff --git a/app/models/site_customization.rb b/app/models/site_customization.rb index 40814ea7917..c52eb4841e9 100644 --- a/app/models/site_customization.rb +++ b/app/models/site_customization.rb @@ -12,31 +12,35 @@ class SiteCustomization < ActiveRecord::Base end before_save do - if stylesheet_changed? - begin - self.stylesheet_baked = Sass.compile stylesheet - rescue Sass::SyntaxError => e - error = e.sass_backtrace_str("custom stylesheet") - error.gsub!("\n", '\A ') - error.gsub!("'", '\27 ') + ['stylesheet', 'mobile_stylesheet'].each do |stylesheet_attr| + if self.send("#{stylesheet_attr}_changed?") + begin + self.send("#{stylesheet_attr}_baked=", Sass.compile(self.send(stylesheet_attr))) + rescue Sass::SyntaxError => e + error = e.sass_backtrace_str("custom stylesheet") + error.gsub!("\n", '\A ') + error.gsub!("'", '\27 ') - self.stylesheet_baked = -"#main {display: none;} -footer {white-space: pre; margin-left: 100px;} -footer:after{ content: '#{error}' }" + self.send("#{stylesheet_attr}_baked=", + "#main {display: none;} + footer {white-space: pre; margin-left: 100px;} + footer:after{ content: '#{error}' }") + end end end end after_save do if stylesheet_changed? - if File.exists?(stylesheet_fullpath) - File.delete stylesheet_fullpath - end + File.delete(stylesheet_fullpath) if File.exists?(stylesheet_fullpath) + end + if mobile_stylesheet_changed? + File.delete(stylesheet_fullpath(:mobile)) if File.exists?(stylesheet_fullpath(:mobile)) end remove_from_cache! - if stylesheet_changed? - ensure_stylesheet_on_disk! + if stylesheet_changed? or mobile_stylesheet_changed? + ensure_stylesheets_on_disk! + # TODO: this is broken now because there's mobile stuff too MessageBus.publish "/file-change/#{key}", stylesheet_hash end MessageBus.publish "/header-change/#{key}", header if header_changed? @@ -47,6 +51,9 @@ footer:after{ content: '#{error}' }" if File.exists?(stylesheet_fullpath) File.delete stylesheet_fullpath end + if File.exists?(stylesheet_fullpath(:mobile)) + File.delete stylesheet_fullpath(:mobile) + end self.remove_from_cache! end @@ -71,17 +78,17 @@ footer:after{ content: '#{error}' }" end end - def self.custom_stylesheet(preview_style) + def self.custom_stylesheet(preview_style, target=:desktop) preview_style ||= enabled_style_key style = lookup_style(preview_style) - style.stylesheet_link_tag.html_safe if style + style.stylesheet_link_tag(target).html_safe if style end - def self.custom_header(preview_style) + def self.custom_header(preview_style, target=:desktop) preview_style ||= enabled_style_key style = lookup_style(preview_style) - if style && style.header - style.header.html_safe + if style && ((target == :mobile && style.mobile_header) || style.header) + target == :mobile ? style.mobile_header.html_safe : style.header.html_safe else "" end @@ -104,7 +111,7 @@ footer:after{ content: '#{error}' }" @lock.synchronize do style = where(key: key).first - style.ensure_stylesheet_on_disk! if style + style.ensure_stylesheets_on_disk! if style @cache[key] = style end end @@ -135,39 +142,49 @@ footer:after{ content: '#{error}' }" self.class.remove_from_cache!(key) end - def stylesheet_hash - Digest::MD5.hexdigest(stylesheet) + def stylesheet_hash(target=:desktop) + Digest::MD5.hexdigest( target == :mobile ? mobile_stylesheet : stylesheet ) end def cache_fullpath "#{Rails.root}/public/#{CACHE_PATH}" end - def ensure_stylesheet_on_disk! - path = stylesheet_fullpath - dir = cache_fullpath - FileUtils.mkdir_p(dir) - unless File.exists?(path) - File.open(path, "w") do |f| - f.puts stylesheet_baked + def ensure_stylesheets_on_disk! + [[:desktop, 'stylesheet_baked'], [:mobile, 'mobile_stylesheet_baked']].each do |target, baked_attr| + path = stylesheet_fullpath(target) + dir = cache_fullpath + FileUtils.mkdir_p(dir) + unless File.exists?(path) + File.open(path, "w") do |f| + f.puts self.send(baked_attr) + end end end end - def stylesheet_filename - "/#{self.key}.css" + def stylesheet_filename(target=:desktop) + target == :desktop ? "/#{self.key}.css" : "/#{target}_#{self.key}.css" end - def stylesheet_fullpath - "#{cache_fullpath}#{stylesheet_filename}" + def stylesheet_fullpath(target=:desktop) + "#{cache_fullpath}#{stylesheet_filename(target)}" end - def stylesheet_link_tag + def stylesheet_link_tag(target=:desktop) + return mobile_stylesheet_link_tag if target == :mobile return "" unless stylesheet.present? return @stylesheet_link_tag if @stylesheet_link_tag - ensure_stylesheet_on_disk! + ensure_stylesheets_on_disk! @stylesheet_link_tag = "" end + + def mobile_stylesheet_link_tag + return "" unless mobile_stylesheet.present? + return @mobile_stylesheet_link_tag if @mobile_stylesheet_link_tag + ensure_stylesheets_on_disk! + @mobile_stylesheet_link_tag = "" + end end # == Schema Information diff --git a/app/views/common/_discourse_stylesheet.html.erb b/app/views/common/_discourse_stylesheet.html.erb index 168617f6ae5..460ef7a2436 100644 --- a/app/views/common/_discourse_stylesheet.html.erb +++ b/app/views/common/_discourse_stylesheet.html.erb @@ -10,6 +10,4 @@ <%= stylesheet_link_tag "admin"%> <%-end%> -<% unless mobile_view? %> - <%= SiteCustomization.custom_stylesheet(session[:preview_style]) %> -<% end %> +<%= SiteCustomization.custom_stylesheet(session[:preview_style], mobile_view? ? :mobile : :desktop) %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index d47df7d539b..473ac8ab129 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -30,9 +30,7 @@ - <% unless mobile_view? %> - <%= SiteCustomization.custom_header(session[:preview_style]) %> - <% end %> + <%= SiteCustomization.custom_header(session[:preview_style], mobile_view? ? :mobile : :desktop) %>
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index a255634273e..47a5ac557ff 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1166,6 +1166,8 @@ en: long_title: "Site Customizations" header: "Header" css: "Stylesheet" + mobile_header: "Mobile Header" + mobile_css: "Mobile Stylesheet" override_default: "Do not include standard style sheet" enabled: "Enabled?" preview: "preview" diff --git a/db/migrate/20130913210454_add_mobile_to_site_customizations.rb b/db/migrate/20130913210454_add_mobile_to_site_customizations.rb new file mode 100644 index 00000000000..681788ce6e1 --- /dev/null +++ b/db/migrate/20130913210454_add_mobile_to_site_customizations.rb @@ -0,0 +1,7 @@ +class AddMobileToSiteCustomizations < ActiveRecord::Migration + def change + add_column :site_customizations, :mobile_stylesheet, :text + add_column :site_customizations, :mobile_header, :text + add_column :site_customizations, :mobile_stylesheet_baked, :text + end +end diff --git a/spec/models/site_customization_spec.rb b/spec/models/site_customization_spec.rb index f602b1c7621..e40dc9d7e22 100644 --- a/spec/models/site_customization_spec.rb +++ b/spec/models/site_customization_spec.rb @@ -6,8 +6,16 @@ describe SiteCustomization do Fabricate(:user) end + let :customization_params do + {name: 'my name', user_id: user.id, header: "my awesome header", stylesheet: "my awesome css", mobile_stylesheet: '', mobile_header: ''} + end + let :customization do - SiteCustomization.create!(name: 'my name', user_id: user.id, header: "my awesome header", stylesheet: "my awesome css") + SiteCustomization.create!(customization_params) + end + + let :customization_with_mobile do + SiteCustomization.create!(customization_params.merge(mobile_stylesheet: ".mobile {better: true;}", mobile_header: "fancy mobile stuff")) end it 'should set default key when creating a new customization' do @@ -50,15 +58,46 @@ describe SiteCustomization do c = customization c.remove_from_cache! File.delete(c.stylesheet_fullpath) + File.delete(c.stylesheet_fullpath(:mobile)) SiteCustomization.custom_stylesheet(c.key) File.exists?(c.stylesheet_fullpath).should == true - + File.exists?(c.stylesheet_fullpath(:mobile)).should == true end - it 'should allow me to lookup a filename containing my preview stylesheet' do - SiteCustomization.custom_stylesheet(customization.key).should == - "" + context '#custom_stylesheet' do + it 'should allow me to lookup a filename containing my preview stylesheet' do + SiteCustomization.custom_stylesheet(customization.key).should == + "" + end + + it "should return blank link tag for mobile if mobile_stylesheet is blank" do + SiteCustomization.custom_stylesheet(customization.key, :mobile).should == "" + end + + it "should return link tag for mobile custom stylesheet" do + SiteCustomization.custom_stylesheet(customization_with_mobile.key, :mobile).should == + "" + end + end + + context '#custom_header' do + it "returns empty string when there is no custom header" do + c = SiteCustomization.create!(customization_params.merge(header: '')) + SiteCustomization.custom_header(c.key).should == '' + end + + it "can return the custom header html" do + SiteCustomization.custom_header(customization.key).should == customization_params[:header] + end + + it "returns empty string for mobile header when there's no custom mobile header" do + SiteCustomization.custom_header(customization.key, :mobile).should == '' + end + + it "can return the custom mobile header html" do + SiteCustomization.custom_header(customization_with_mobile.key, :mobile).should == customization_with_mobile.mobile_header + end end it 'should fix stylesheet files after changing the stylesheet' do @@ -72,13 +111,31 @@ describe SiteCustomization do SiteCustomization.custom_stylesheet(customization.key).should_not == original end + it 'should fix mobile stylesheet files after changing the mobile_stylesheet' do + old_file = customization_with_mobile.stylesheet_fullpath(:mobile) + original = SiteCustomization.custom_stylesheet(customization_with_mobile.key, :mobile) + + File.exists?(old_file).should == true + customization_with_mobile.mobile_stylesheet = "div { clear:both; }" + customization_with_mobile.save + + SiteCustomization.custom_stylesheet(customization_with_mobile.key).should_not == original + end + it 'should delete old stylesheet files after deleting' do old_file = customization.stylesheet_fullpath - customization.ensure_stylesheet_on_disk! + customization.ensure_stylesheets_on_disk! customization.destroy File.exists?(old_file).should == false end + it 'should delete old mobile stylesheet files after deleting' do + old_file = customization_with_mobile.stylesheet_fullpath(:mobile) + customization_with_mobile.ensure_stylesheets_on_disk! + customization_with_mobile.destroy + File.exists?(old_file).should == false + end + it 'should nuke old revs out of the cache' do old_style = SiteCustomization.custom_stylesheet(customization.key) @@ -87,16 +144,35 @@ describe SiteCustomization do SiteCustomization.custom_stylesheet(customization.key).should_not == old_style end + it 'should nuke old revs out of the cache for mobile too' do + old_style = SiteCustomization.custom_stylesheet(customization_with_mobile.key) + + customization_with_mobile.mobile_stylesheet = "hello worldz" + customization_with_mobile.save + SiteCustomization.custom_stylesheet(customization.key, :mobile).should_not == old_style + end + it 'should compile scss' do c = SiteCustomization.create!(user_id: user.id, name: "test", stylesheet: '$black: #000; #a { color: $black; }', header: '') c.stylesheet_baked.should == "#a {\n color: black; }\n" end + it 'should compile mobile scss' do + c = SiteCustomization.create!(user_id: user.id, name: "test", stylesheet: '', header: '', mobile_stylesheet: '$black: #000; #a { color: $black; }', mobile_header: '') + c.mobile_stylesheet_baked.should == "#a {\n color: black; }\n" + end + it 'should provide an awesome error on failure' do c = SiteCustomization.create!(user_id: user.id, name: "test", stylesheet: "$black: #000; #a { color: $black; }\n\n\nboom", header: '') - c.stylesheet_baked.should =~ /Syntax error/ + c.mobile_stylesheet_baked.should_not be_present + end + + it 'should provide an awesome error on failure for mobile too' do + c = SiteCustomization.create!(user_id: user.id, name: "test", stylesheet: '', header: '', mobile_stylesheet: "$black: #000; #a { color: $black; }\n\n\nboom", mobile_header: '') + c.mobile_stylesheet_baked.should =~ /Syntax error/ + c.stylesheet_baked.should_not be_present end end