From 2d96a0785d02db69413db31b061d3fcee5bcabd6 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 12 May 2017 12:41:26 -0400 Subject: [PATCH] FEATURE: theme selection is now global per-user --- .../controllers/preferences/interface.js.es6 | 10 ++++---- .../discourse/lib/theme-selector.js.es6 | 8 ------- .../javascripts/discourse/models/user.js.es6 | 3 ++- .../routes/preferences-interface.js.es6 | 4 +--- .../templates/preferences/interface.hbs | 2 +- app/controllers/application_controller.rb | 11 ++++++--- app/serializers/user_option_serializer.rb | 7 +++++- app/services/user_updater.rb | 3 ++- ...512153318_add_theme_key_to_user_options.rb | 5 ++++ .../application_controller_spec.rb | 23 +++++++++++++++---- spec/controllers/users_controller_spec.rb | 6 ++++- 11 files changed, 53 insertions(+), 29 deletions(-) create mode 100644 db/migrate/20170512153318_add_theme_key_to_user_options.rb diff --git a/app/assets/javascripts/discourse/controllers/preferences/interface.js.es6 b/app/assets/javascripts/discourse/controllers/preferences/interface.js.es6 index 324c3da7d2b..401d757bfa8 100644 --- a/app/assets/javascripts/discourse/controllers/preferences/interface.js.es6 +++ b/app/assets/javascripts/discourse/controllers/preferences/interface.js.es6 @@ -2,7 +2,6 @@ import PreferencesTabController from "discourse/mixins/preferences-tab-controlle import { default as computed, observes } from "ember-addons/ember-computed-decorators"; import { listThemes, previewTheme } from 'discourse/lib/theme-selector'; import { popupAjaxError } from 'discourse/lib/ajax-error'; -import { selectDefaultTheme } from 'discourse/lib/theme-selector'; export default Ember.Controller.extend(PreferencesTabController, { @@ -12,7 +11,8 @@ export default Ember.Controller.extend(PreferencesTabController, { 'dynamic_favicon', 'enable_quoting', 'disable_jump_reply', - 'automatically_unpin_topics' + 'automatically_unpin_topics', + 'theme_key' ], preferencesController: Ember.inject.controller('preferences'), @@ -26,10 +26,9 @@ export default Ember.Controller.extend(PreferencesTabController, { return listThemes(this.site); }.property(), - @observes("selectedTheme") + @observes("model.user_option.theme_key") themeKeyChanged() { - let key = this.get("selectedTheme"); - this.get('preferencesController').set('selectedTheme', key); + let key = this.get("model.user_option.theme_key"); previewTheme(key); }, @@ -38,7 +37,6 @@ export default Ember.Controller.extend(PreferencesTabController, { this.set('saved', false); return this.get('model').save(this.get('saveAttrNames')).then(() => { this.set('saved', true); - selectDefaultTheme(this.get('selectedTheme')); }).catch(popupAjaxError); } } diff --git a/app/assets/javascripts/discourse/lib/theme-selector.js.es6 b/app/assets/javascripts/discourse/lib/theme-selector.js.es6 index 597fc6569f9..af0f8da3903 100644 --- a/app/assets/javascripts/discourse/lib/theme-selector.js.es6 +++ b/app/assets/javascripts/discourse/lib/theme-selector.js.es6 @@ -13,14 +13,6 @@ export function currentThemeKey() { return themeKey; } -export function selectDefaultTheme(key) { - if (key) { - $.cookie('theme_key', key, {path: '/', expires: 9999}); - } else { - $.cookie('theme_key', null, {path: '/', expires: 1}); - } -} - export function refreshCSS(node, hash, newHref, options) { let $orig = $(node); diff --git a/app/assets/javascripts/discourse/models/user.js.es6 b/app/assets/javascripts/discourse/models/user.js.es6 index 17fb983eaa7..93dce420151 100644 --- a/app/assets/javascripts/discourse/models/user.js.es6 +++ b/app/assets/javascripts/discourse/models/user.js.es6 @@ -242,7 +242,8 @@ const User = RestModel.extend({ 'auto_track_topics_after_msecs', 'notification_level_when_replying', 'like_notification_frequency', - 'include_tl0_in_digests' + 'include_tl0_in_digests', + 'theme_key' ]; if (fields) { diff --git a/app/assets/javascripts/discourse/routes/preferences-interface.js.es6 b/app/assets/javascripts/discourse/routes/preferences-interface.js.es6 index 16f0ba0ca23..f58658956fc 100644 --- a/app/assets/javascripts/discourse/routes/preferences-interface.js.es6 +++ b/app/assets/javascripts/discourse/routes/preferences-interface.js.es6 @@ -1,11 +1,9 @@ import RestrictedUserRoute from "discourse/routes/restricted-user"; -import { currentThemeKey } from 'discourse/lib/theme-selector'; export default RestrictedUserRoute.extend({ setupController(controller, user) { controller.setProperties({ - model: user, - selectedTheme: $.cookie('theme_key') || currentThemeKey() + model: user }); } }); diff --git a/app/assets/javascripts/discourse/templates/preferences/interface.hbs b/app/assets/javascripts/discourse/templates/preferences/interface.hbs index 17db5f1ca32..c6e822b2cea 100644 --- a/app/assets/javascripts/discourse/templates/preferences/interface.hbs +++ b/app/assets/javascripts/discourse/templates/preferences/interface.hbs @@ -2,7 +2,7 @@
- {{combo-box content=userSelectableThemes value=selectedTheme}} + {{combo-box content=userSelectableThemes value=model.user_option.theme_key}}
{{/if}} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index feacb151095..0cd39cd3a57 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -18,6 +18,8 @@ class ApplicationController < ActionController::Base include JsonError include GlobalPath + attr_reader :theme_key + serialization_scope :guardian protect_from_forgery @@ -265,12 +267,15 @@ class ApplicationController < ActionController::Base resolve_safe_mode return if request.env[NO_CUSTOM] - theme_key = flash[:preview_theme_key] || cookies[:theme_key] || session[:theme_key] + theme_key = flash[:preview_theme_key] || current_user&.user_option&.theme_key + + # TODO 2018: delete this, old cookie cleanup code + if cookies[:theme_key] + cookies.delete(:theme_key) + end 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 diff --git a/app/serializers/user_option_serializer.rb b/app/serializers/user_option_serializer.rb index 33cc3f4cc8a..534913323b6 100644 --- a/app/serializers/user_option_serializer.rb +++ b/app/serializers/user_option_serializer.rb @@ -18,7 +18,8 @@ class UserOptionSerializer < ApplicationSerializer :email_previous_replies, :email_in_reply_to, :like_notification_frequency, - :include_tl0_in_digests + :include_tl0_in_digests, + :theme_key def auto_track_topics_after_msecs @@ -33,4 +34,8 @@ class UserOptionSerializer < ApplicationSerializer object.new_topic_duration_minutes || SiteSetting.default_other_new_topic_duration_minutes end + def theme_key + object.theme_key || SiteSetting.default_theme_key + end + end diff --git a/app/services/user_updater.rb b/app/services/user_updater.rb index cbb3c479c49..30bed45d60a 100644 --- a/app/services/user_updater.rb +++ b/app/services/user_updater.rb @@ -33,7 +33,8 @@ class UserUpdater :email_previous_replies, :email_in_reply_to, :like_notification_frequency, - :include_tl0_in_digests + :include_tl0_in_digests, + :theme_key ] def initialize(actor, user) diff --git a/db/migrate/20170512153318_add_theme_key_to_user_options.rb b/db/migrate/20170512153318_add_theme_key_to_user_options.rb new file mode 100644 index 00000000000..8f8fc0e99d5 --- /dev/null +++ b/db/migrate/20170512153318_add_theme_key_to_user_options.rb @@ -0,0 +1,5 @@ +class AddThemeKeyToUserOptions < ActiveRecord::Migration + def change + add_column :user_options, :theme_key, :string + end +end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 62b9bb5afd8..c59fb9120ca 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -17,6 +17,22 @@ describe TopicsController do request.env['HTTP_ACCEPT_LANGUAGE'] = locale end + describe "themes" do + it "selects the theme the user has selected" do + theme = Theme.create!(user_id: -1, name: 'bob', user_selectable: true) + user = log_in + user.user_option.update_columns(theme_key: theme.key) + + get :show, id: 666 + expect(controller.theme_key).to eq(theme.key) + + theme.update_columns(user_selectable: false) + + get :show, id: 666 + expect(controller.theme_key).not_to eq(theme.key) + end + end + it "doesn't store an incoming link when there's no referer" do expect { get :show, id: topic.id @@ -32,20 +48,19 @@ describe TopicsController do render_views context "when the SiteSetting is disabled" do - before do - SiteSetting.stubs(:enable_escaped_fragments?).returns(false) - end it "uses the application layout even with an escaped fragment param" do + SiteSetting.enable_escaped_fragments = false get :show, {'topic_id' => topic.id, 'slug' => topic.slug, '_escaped_fragment_' => 'true'} expect(response).to render_template(layout: 'application') assert_select "meta[name=fragment]", false, "it doesn't have the meta tag" end + end context "when the SiteSetting is enabled" do before do - SiteSetting.stubs(:enable_escaped_fragments?).returns(true) + SiteSetting.enable_escaped_fragments = true end it "uses the application layout when there's no param" do diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index fab6193a921..e5ca94d734c 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -1221,13 +1221,17 @@ describe UsersController do expect(user.custom_fields['test']).to eq 'it' expect(user.muted_users.pluck(:username).sort).to eq [user2.username,user3.username].sort + theme = Theme.create(name: "test", user_selectable: true, user_id: -1) + put :update, username: user.username, - muted_usernames: "" + muted_usernames: "", + theme_key: theme.key user.reload expect(user.muted_users.pluck(:username).sort).to be_empty + expect(user.user_option.theme_key).to eq(theme.key) end