diff --git a/app/assets/javascripts/admin/controllers/admin-customize-themes-edit.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-themes-edit.js.es6
index 075eba3e943..aab84d2ab92 100644
--- a/app/assets/javascripts/admin/controllers/admin-customize-themes-edit.js.es6
+++ b/app/assets/javascripts/admin/controllers/admin-customize-themes-edit.js.es6
@@ -81,7 +81,7 @@ export default Ember.Controller.extend({
});
},
- previewUrl: url('model.key', '/?preview-style=%@'),
+ previewUrl: url('model.id', '/admin/themes/%@/preview'),
maximizeIcon: function() {
return this.get('maximized') ? 'compress' : 'expand';
@@ -95,9 +95,6 @@ export default Ember.Controller.extend({
return !this.get('model.changed') || this.get('model.isSaving');
}.property('model.changed', 'model.isSaving'),
- undoPreviewUrl: url('/?preview-style='),
- defaultStyleUrl: url('/?preview-style=default'),
-
actions: {
save() {
this.get('model').saveChanges("theme_fields");
diff --git a/app/assets/javascripts/admin/controllers/admin-customize-themes-show.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-themes-show.js.es6
index 43464954584..7b9c81b7391 100644
--- a/app/assets/javascripts/admin/controllers/admin-customize-themes-show.js.es6
+++ b/app/assets/javascripts/admin/controllers/admin-customize-themes-show.js.es6
@@ -25,6 +25,8 @@ export default Ember.Controller.extend({
return descriptions.reject(d=>Em.isBlank(d));
},
+ previewUrl: url('model.id', '/admin/themes/%@/preview'),
+
@computed("colorSchemeId", "model.color_scheme_id")
colorSchemeChanged(colorSchemeId, existingId) {
colorSchemeId = colorSchemeId === null ? null : parseInt(colorSchemeId);
diff --git a/app/assets/javascripts/admin/templates/customize-themes-show.hbs b/app/assets/javascripts/admin/templates/customize-themes-show.hbs
index 5bbd9cb2ee1..ae06bedc082 100644
--- a/app/assets/javascripts/admin/templates/customize-themes-show.hbs
+++ b/app/assets/javascripts/admin/templates/customize-themes-show.hbs
@@ -106,6 +106,7 @@
{{/if}}
{{/if}}
+ {{fa-icon 'desktop'}}{{i18n 'admin.customize.theme.preview'}}
{{fa-icon "download"}} {{i18n 'admin.export_json.button_text'}}
{{d-button action="destroy" label="admin.customize.delete" icon="trash" class="btn-danger"}}
diff --git a/app/assets/javascripts/discourse/lib/theme-selector.js.es6 b/app/assets/javascripts/discourse/lib/theme-selector.js.es6
index 40cb9eabe57..269d2d26e11 100644
--- a/app/assets/javascripts/discourse/lib/theme-selector.js.es6
+++ b/app/assets/javascripts/discourse/lib/theme-selector.js.es6
@@ -13,9 +13,9 @@ export function currentThemeKey() {
export function selectDefaultTheme(key) {
if (key) {
- $.cookie('preview_style', key, {path: '/', expires: 9999});
+ $.cookie('theme_key', key, {path: '/', expires: 9999});
} else {
- $.cookie('preview_style', null, {path: '/', expires: 1});
+ $.cookie('theme_key', null, {path: '/', expires: 1});
}
}
diff --git a/app/assets/javascripts/discourse/routes/preferences.js.es6 b/app/assets/javascripts/discourse/routes/preferences.js.es6
index 9bb0d645b15..3ff42ca52ea 100644
--- a/app/assets/javascripts/discourse/routes/preferences.js.es6
+++ b/app/assets/javascripts/discourse/routes/preferences.js.es6
@@ -13,7 +13,7 @@ export default RestrictedUserRoute.extend({
controller.setProperties({
model: user,
newNameInput: user.get('name'),
- selectedTheme: $.cookie('preview_style') || currentThemeKey()
+ selectedTheme: $.cookie('theme_key') || currentThemeKey()
});
},
diff --git a/app/controllers/admin/themes_controller.rb b/app/controllers/admin/themes_controller.rb
index 083cc61359f..63f160f1ba3 100644
--- a/app/controllers/admin/themes_controller.rb
+++ b/app/controllers/admin/themes_controller.rb
@@ -1,6 +1,12 @@
class Admin::ThemesController < Admin::AdminController
- skip_before_filter :check_xhr, only: [:show]
+ skip_before_filter :check_xhr, only: [:show, :preview]
+
+ def preview
+ @theme = Theme.find(params[:id])
+
+ redirect_to path("/"), flash: {preview_theme_key: @theme.key}
+ end
def import
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index f0856406b4f..4b3c1196e25 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -33,12 +33,11 @@ class ApplicationController < ActionController::Base
end
end
+ before_filter :handle_theme
before_filter :set_current_user_for_logs
before_filter :clear_notifications
before_filter :set_locale
before_filter :set_mobile_view
- before_filter :inject_preview_style
- before_filter :disable_customization
before_filter :block_if_readonly_mode
before_filter :authorize_mini_profiler
before_filter :preload_json
@@ -243,28 +242,40 @@ class ApplicationController < ActionController::Base
session[:mobile_view] = params[:mobile_view] if params.has_key?(:mobile_view)
end
- def inject_preview_style
- style = request['preview-style']
+ NO_CUSTOM = "no_custom".freeze
+ NO_PLUGINS = "no_plugins".freeze
+ ONLY_OFFICIAL = "only_official".freeze
+ SAFE_MODE = "safe_mode".freeze
- if style.nil?
- session[:preview_style] = cookies[:preview_style]
- else
- cookies.delete(:preview_style)
-
- if style.blank? || style == 'default'
- session[:preview_style] = nil
- else
- session[:preview_style] = style
- if request['sticky']
- cookies[:preview_style] = style
- end
- end
+ def resolve_safe_mode
+ safe_mode = params[SAFE_MODE]
+ if safe_mode
+ request.env[NO_CUSTOM] = true if safe_mode.include?(NO_CUSTOM)
+ request.env[NO_PLUGINS] = true if safe_mode.include?(NO_PLUGINS)
+ request.env[ONLY_OFFICIAL] = true if safe_mode.include?(ONLY_OFFICIAL)
end
-
end
- def disable_customization
- session[:disable_customization] = params[:customization] == "0" if params.has_key?(:customization)
+ def handle_theme
+
+ return if request.xhr? || request.format.json?
+ return if request.method != "GET"
+
+ resolve_safe_mode
+ return if request.env[NO_CUSTOM]
+
+ theme_key = flash[:preview_theme_key] || cookies[:theme_key] || session[:theme_key]
+
+ if theme_key && !guardian.allow_theme?(theme_key)
+ theme_key = nil
+ cookies[:theme_key] = nil
+ session[:theme_key] = nil
+ end
+
+ theme_key ||= SiteSetting.default_theme_key
+ theme_key = nil if theme_key.blank?
+
+ @theme_key = request.env[:resolved_theme_key] = theme_key
end
def guardian
@@ -410,10 +421,14 @@ class ApplicationController < ActionController::Base
def custom_html_json
target = view_context.mobile_view? ? :mobile : :desktop
- data = {
- top: Theme.lookup_field(session[:preview_style], target, "after_header"),
- footer: Theme.lookup_field(session[:preview_style], target, "footer")
- }
+ data = if @theme_key
+ {
+ top: Theme.lookup_field(@theme_key, target, "after_header"),
+ footer: Theme.lookup_field(@theme_key, target, "footer")
+ }
+ else
+ {}
+ end
if DiscoursePluginRegistry.custom_html
data.merge! DiscoursePluginRegistry.custom_html
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 74ad085e700..67651cc0a55 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -248,32 +248,23 @@ module ApplicationHelper
MobileDetection.mobile_device?(request.user_agent)
end
- NO_CUSTOM = "no_custom".freeze
- NO_PLUGINS = "no_plugins".freeze
- ONLY_OFFICIAL = "only_official".freeze
- SAFE_MODE = "safe_mode".freeze
-
def customization_disabled?
- safe_mode = params[SAFE_MODE]
- session[:disable_customization] || (safe_mode && safe_mode.include?(NO_CUSTOM))
+ request.env[ApplicationController::NO_CUSTOM]
end
def allow_plugins?
- safe_mode = params[SAFE_MODE]
- !(safe_mode && safe_mode.include?(NO_PLUGINS))
+ !request.env[ApplicationController::NO_PLUGINS]
end
def allow_third_party_plugins?
- safe_mode = params[SAFE_MODE]
- !(safe_mode && (safe_mode.include?(NO_PLUGINS) || safe_mode.include?(ONLY_OFFICIAL)))
+ allow_plugins? && !request.env[ApplicationController::ONLY_OFFICIAL]
end
def normalized_safe_mode
- mode_string = params["safe_mode"]
safe_mode = nil
- (safe_mode ||= []) << NO_CUSTOM if mode_string.include?(NO_CUSTOM)
- (safe_mode ||= []) << NO_PLUGINS if mode_string.include?(NO_PLUGINS)
- (safe_mode ||= []) << ONLY_OFFICIAL if mode_string.include?(ONLY_OFFICIAL)
+ (safe_mode ||= []) << ApplicationController::NO_CUSTOM if customization_disabled?
+ (safe_mode ||= []) << ApplicationController::NO_PLUGINS if !allow_plugins?
+ (safe_mode ||= []) << ApplicationController::ONLY_OFFICIAL if !allow_third_party_plugins?
if safe_mode
safe_mode.join(",").html_safe
end
@@ -321,7 +312,7 @@ module ApplicationHelper
if customization_disabled?
nil
else
- session[:preview_style] || SiteSetting.default_theme_key
+ request.env[:resolved_theme_key]
end
end
diff --git a/app/models/theme.rb b/app/models/theme.rb
index 28c7b09cb71..06a8bb40fe2 100644
--- a/app/models/theme.rb
+++ b/app/models/theme.rb
@@ -45,6 +45,24 @@ class Theme < ActiveRecord::Base
theme.notify_theme_change
end, on: :update
+ def self.theme_keys
+ if keys = @cache["theme_keys"]
+ return keys
+ end
+ @cache["theme_keys"] = Set.new(Theme.pluck(:key))
+ end
+
+ def self.user_theme_keys
+ if keys = @cache["user_theme_keys"]
+ return keys
+ end
+ @cache["theme_keys"] = Set.new(
+ Theme
+ .where('user_selectable OR key = ?', SiteSetting.default_theme_key)
+ .pluck(:key)
+ )
+ end
+
def self.expire_site_cache!
Site.clear_anon_cache!
ApplicationSerializer.expire_cache_fragment!("user_themes")
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 95c82c25645..7e19ed74f18 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -2785,7 +2785,7 @@ en:
title: "Customize"
long_title: "Site Customizations"
preview: "preview"
- explain_preview: "See the site with this custom stylesheet"
+ explain_preview: "See the site with this theme enabled"
save: "Save"
new: "New"
new_style: "New Style"
@@ -2815,6 +2815,7 @@ en:
common: "Common"
desktop: "Desktop"
mobile: "Mobile"
+ preview: "Preview"
is_default: "Theme is enabled by default"
user_selectable: "Theme can be selected by users"
color_scheme: "Color Scheme"
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 1ff8f33edbf..8cf969d5b7b 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -3361,7 +3361,7 @@ en:
safe_mode:
title: "Enter safe mode"
description: "Safe mode allows you to test your site without loading plugins or site customizations."
- no_customizations: "Disable all site customizations"
+ no_customizations: "Disable current theme"
only_official: "Disable unofficial plugins"
no_plugins: "Disable all plugins"
enter: "Enter Safe Mode"
diff --git a/config/routes.rb b/config/routes.rb
index c534d2d41ec..2f0c9ef3584 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -188,6 +188,7 @@ Discourse::Application.routes.draw do
resources :themes, constraints: AdminConstraint.new
post "themes/import" => "themes#import"
+ get "themes/:id/preview" => "themes#preview"
scope "/customize", constraints: AdminConstraint.new do
resources :user_fields, constraints: AdminConstraint.new
diff --git a/lib/guardian.rb b/lib/guardian.rb
index 4a369938849..1f00960b9bd 100644
--- a/lib/guardian.rb
+++ b/lib/guardian.rb
@@ -297,6 +297,14 @@ class Guardian
UserExport.where(user_id: @user.id, created_at: (Time.zone.now.beginning_of_day..Time.zone.now.end_of_day)).count == 0
end
+ def allow_theme?(theme_key)
+ if is_staff?
+ Theme.theme_keys.include?(theme_key)
+ else
+ Theme.theme_user_keys.include?(theme_key)
+ end
+ end
+
private
diff --git a/spec/models/theme_spec.rb b/spec/models/theme_spec.rb
index 6e11d7a5a5d..e75bdb5d8a9 100644
--- a/spec/models/theme_spec.rb
+++ b/spec/models/theme_spec.rb
@@ -137,5 +137,28 @@ HTML
end
end
+ it 'correctly caches theme keys' do
+ theme = Theme.create!(name: "bob", user_id: -1)
+
+ expect(Theme.theme_keys).to eq(Set.new([theme.key]))
+ expect(Theme.user_theme_keys).to eq(Set.new([]))
+
+ theme.user_selectable = true
+ theme.save
+
+ expect(Theme.user_theme_keys).to eq(Set.new([theme.key]))
+
+ theme.user_selectable = false
+ theme.save
+
+ theme.set_default!
+ expect(Theme.user_theme_keys).to eq(Set.new([theme.key]))
+
+ theme.destroy
+
+ expect(Theme.theme_keys).to eq(Set.new([]))
+ expect(Theme.user_theme_keys).to eq(Set.new([]))
+ end
+
end