diff --git a/app/assets/javascripts/discourse/lib/plugin-api.js.es6 b/app/assets/javascripts/discourse/lib/plugin-api.js.es6 index c429d6b4a..789b35aa3 100644 --- a/app/assets/javascripts/discourse/lib/plugin-api.js.es6 +++ b/app/assets/javascripts/discourse/lib/plugin-api.js.es6 @@ -93,3 +93,30 @@ export function withPluginApi(version, apiCodeCallback, opts) { return apiCodeCallback(api); } } + +// This is backported so plugins in the new format will not raise errors +// +// To upgrade your plugin for backwards compatibility, you can add code in this +// form: +// +// function newApiCode(api) { +// // api.xyz(); +// } +// +// function oldCode() { +// // your pre-PluginAPI code goes here. You will be able to delete this +// // code once the `PluginAPI` has been rolled out to all versions of +// // Discourse you want to support. +// } +// +// // `newApiCode` will use API version 0.1, if no API support then +// // `oldCode` will be called +// withPluginApi('0.1', newApiCode, { noApi: oldCode }); +// +export function withPluginApi(version, apiCodeCallback, opts) { + console.warn(`Plugin API v${version} is not supported`); + + if (opts && opts.noApi) { + return opts.noApi(); + } +} diff --git a/app/assets/javascripts/discourse/models/user.js.es6 b/app/assets/javascripts/discourse/models/user.js.es6 index fd702adff..a26f5e10e 100644 --- a/app/assets/javascripts/discourse/models/user.js.es6 +++ b/app/assets/javascripts/discourse/models/user.js.es6 @@ -147,25 +147,29 @@ const User = RestModel.extend({ 'location', 'name', 'locale', - 'email_digests', - 'email_direct', - 'email_always', - 'email_private_messages', - 'dynamic_favicon', - 'digest_after_days', 'new_topic_duration_minutes', - 'external_links_in_new_tab', - 'mailing_list_mode', - 'enable_quoting', - 'disable_jump_reply', 'custom_fields', 'user_fields', 'muted_usernames', 'profile_background', - 'card_background', - 'automatically_unpin_topics' + 'card_background' ); + [ 'email_always', + 'mailing_list_mode', + 'external_links_in_new_tab', + 'email_digests', + 'email_direct', + 'email_private_messages', + 'dynamic_favicon', + 'enable_quoting', + 'disable_jump_reply', + 'automatically_unpin_topics', + 'digest_after_days' + ].forEach(s => { + data[s] = this.get(`user_option.${s}`); + }); + ['muted','watched','tracked'].forEach(s => { let cats = this.get(s + 'Categories').map(c => c.get('id')); // HACK: denote lack of categories @@ -174,7 +178,7 @@ const User = RestModel.extend({ }); if (!Discourse.SiteSettings.edit_history_visible_to_public) { - data['edit_history_public'] = this.get('edit_history_public'); + data['edit_history_public'] = this.get('user_option.edit_history_public'); } // TODO: We can remove this when migrated fully to rest model. @@ -184,7 +188,7 @@ const User = RestModel.extend({ type: 'PUT' }).then(result => { this.set('bio_excerpt', result.user.bio_excerpt); - const userProps = this.getProperties('enable_quoting', 'external_links_in_new_tab', 'dynamic_favicon'); + const userProps = Em.getProperties(this.get('user-option'),'enable_quoting', 'external_links_in_new_tab', 'dynamic_favicon'); Discourse.User.current().setProperties(userProps); }).finally(() => { this.set('isSaving', false); diff --git a/app/assets/javascripts/discourse/templates/user/preferences.hbs b/app/assets/javascripts/discourse/templates/user/preferences.hbs index 636227b75..4c65524e8 100644 --- a/app/assets/javascripts/discourse/templates/user/preferences.hbs +++ b/app/assets/javascripts/discourse/templates/user/preferences.hbs @@ -169,17 +169,17 @@ <div class="control-group pref-email-settings"> <label class="control-label">{{i18n 'user.email_settings'}}</label> {{#if canReceiveDigest}} - {{preference-checkbox labelKey="user.email_digests.title" checked=model.email_digests}} - {{#if model.email_digests}} + {{preference-checkbox labelKey="user.email_digests.title" checked=model.user_option.email_digests}} + {{#if model.user_option.email_digests}} <div class='controls controls-dropdown'> - {{combo-box valueAttribute="value" content=digestFrequencies value=model.digest_after_days}} + {{combo-box valueAttribute="value" content=digestFrequencies value=model.user_option.digest_after_days}} </div> {{/if}} {{/if}} - {{preference-checkbox labelKey="user.email_private_messages" checked=model.email_private_messages}} - {{preference-checkbox labelKey="user.email_direct" checked=model.email_direct}} - <span class="pref-mailing-list-mode">{{preference-checkbox labelKey="user.mailing_list_mode" checked=model.mailing_list_mode}}</span> - {{preference-checkbox labelKey="user.email_always" checked=model.email_always}} + {{preference-checkbox labelKey="user.email_private_messages" checked=model.user_option.email_private_messages}} + {{preference-checkbox labelKey="user.email_direct" checked=model.user_option.email_direct}} + <span class="pref-mailing-list-mode">{{preference-checkbox labelKey="user.mailing_list_mode" checked=model.user_option.mailing_list_mode}}</span> + {{preference-checkbox labelKey="user.email_always" checked=model.user_option.email_always}} <div class='instructions'> {{#if siteSettings.email_time_window_mins}} @@ -209,12 +209,12 @@ {{combo-box valueAttribute="value" content=autoTrackDurations value=model.auto_track_topics_after_msecs}} </div> - {{preference-checkbox labelKey="user.external_links_in_new_tab" checked=model.external_links_in_new_tab}} - {{preference-checkbox labelKey="user.enable_quoting" checked=model.enable_quoting}} - {{preference-checkbox labelKey="user.dynamic_favicon" checked=model.dynamic_favicon}} - {{preference-checkbox labelKey="user.disable_jump_reply" checked=model.disable_jump_reply}} + {{preference-checkbox labelKey="user.external_links_in_new_tab" checked=model.user_option.external_links_in_new_tab}} + {{preference-checkbox labelKey="user.enable_quoting" checked=model.user_option.enable_quoting}} + {{preference-checkbox labelKey="user.dynamic_favicon" checked=model.user_option.dynamic_favicon}} + {{preference-checkbox labelKey="user.disable_jump_reply" checked=model.user_option.disable_jump_reply}} {{#unless siteSettings.edit_history_visible_to_public}} - {{preference-checkbox labelKey="user.edit_history_public" checked=model.edit_history_public}} + {{preference-checkbox labelKey="user.edit_history_public" checked=model.user_option.edit_history_public}} {{/unless}} {{plugin-outlet "user-custom-preferences"}} @@ -254,7 +254,7 @@ {{#if siteSettings.automatically_unpin_topics}} <div class="control-group topics"> <label class="control-label">{{i18n 'categories.topics'}}</label> - {{preference-checkbox labelKey="user.automatically_unpin_topics" checked=model.automatically_unpin_topics}} + {{preference-checkbox labelKey="user.automatically_unpin_topics" checked=model.user_option.automatically_unpin_topics}} </div> {{/if}} diff --git a/app/controllers/admin/email_templates_controller.rb b/app/controllers/admin/email_templates_controller.rb index 9d872c998..ccbb3eaf6 100644 --- a/app/controllers/admin/email_templates_controller.rb +++ b/app/controllers/admin/email_templates_controller.rb @@ -26,7 +26,8 @@ class Admin::EmailTemplatesController < Admin::AdminController "user_notifications.user_invited_to_private_message_pm", "user_notifications.user_invited_to_topic", "user_notifications.user_mentioned", "user_notifications.user_posted", "user_notifications.user_posted_pm", - "user_notifications.user_quoted", "user_notifications.user_replied"] + "user_notifications.user_quoted", "user_notifications.user_replied", + "user_notifications.user_linked"] end def show diff --git a/app/controllers/email_controller.rb b/app/controllers/email_controller.rb index 4e0ff301a..22bbec051 100644 --- a/app/controllers/email_controller.rb +++ b/app/controllers/email_controller.rb @@ -25,9 +25,12 @@ class EmailController < ApplicationController end if params[:from_all] - @user.update_columns(email_digests: false, email_direct: false, email_private_messages: false, email_always: false) + @user.user_option.update_columns(email_always: false, + email_digests: false, + email_direct: false, + email_private_messages: false) else - @user.update_column(:email_digests, false) + @user.user_option.update_column(:email_digests, false) end @success = true @@ -36,7 +39,7 @@ class EmailController < ApplicationController def resubscribe @user = DigestUnsubscribeKey.user_for_key(params[:key]) raise Discourse::NotFound unless @user.present? - @user.update_column(:email_digests, true) + @user.user_option.update_column(:email_digests, true) end end diff --git a/app/jobs/regular/notify_mailing_list_subscribers.rb b/app/jobs/regular/notify_mailing_list_subscribers.rb index c78e78f3e..29436e423 100644 --- a/app/jobs/regular/notify_mailing_list_subscribers.rb +++ b/app/jobs/regular/notify_mailing_list_subscribers.rb @@ -11,7 +11,8 @@ module Jobs users = User.activated.not_blocked.not_suspended.real - .where(mailing_list_mode: true) + .joins(:user_option) + .where(user_options: {mailing_list_mode: true}) .where('NOT EXISTS( SELECT 1 FROM topic_users tu diff --git a/app/jobs/regular/user_email.rb b/app/jobs/regular/user_email.rb index 6830ec2e6..ff38409c1 100644 --- a/app/jobs/regular/user_email.rb +++ b/app/jobs/regular/user_email.rb @@ -62,7 +62,7 @@ module Jobs return if user.staged && type == :digest seen_recently = (user.last_seen_at.present? && user.last_seen_at > SiteSetting.email_time_window_mins.minutes.ago) - seen_recently = false if user.email_always || user.staged + seen_recently = false if user.user_option.email_always || user.staged email_args = {} @@ -85,14 +85,14 @@ module Jobs email_args[:notification_type] = email_args[:notification_type].to_s end - if user.mailing_list_mode? && + if user.user_option.mailing_list_mode? && !post.topic.private_message? && NOTIFICATIONS_SENT_BY_MAILING_LIST.include?(email_args[:notification_type]) # no need to log a reason when the mail was already sent via the mailing list job return [nil, nil] end - unless user.email_always? + unless user.user_option.email_always? if (notification && notification.read?) || (post && post.seen?(user)) return skip_message(I18n.t('email_log.notification_already_read')) end diff --git a/app/jobs/scheduled/enqueue_digest_emails.rb b/app/jobs/scheduled/enqueue_digest_emails.rb index 40cf0976d..ea74f98ed 100644 --- a/app/jobs/scheduled/enqueue_digest_emails.rb +++ b/app/jobs/scheduled/enqueue_digest_emails.rb @@ -15,8 +15,10 @@ module Jobs def target_user_ids # Users who want to receive emails and haven't been emailed in the last day query = User.real - .where(email_digests: true, active: true, staged: false) + .where(active: true, staged: false) + .joins(:user_option) .not_suspended + .where(user_options: {email_digests: true}) .where("COALESCE(last_emailed_at, '2010-01-01') <= CURRENT_TIMESTAMP - ('1 DAY'::INTERVAL * digest_after_days)") .where("COALESCE(last_seen_at, '2010-01-01') <= CURRENT_TIMESTAMP - ('1 DAY'::INTERVAL * digest_after_days)") .where("COALESCE(last_seen_at, '2010-01-01') >= CURRENT_TIMESTAMP - ('1 DAY'::INTERVAL * #{SiteSetting.delete_digest_email_after_days})") diff --git a/app/mailers/user_notifications.rb b/app/mailers/user_notifications.rb index 517294278..52db55f8a 100644 --- a/app/mailers/user_notifications.rb +++ b/app/mailers/user_notifications.rb @@ -110,6 +110,13 @@ class UserNotifications < ActionMailer::Base notification_email(user, opts) end + def user_linked(user, opts) + opts[:allow_reply_by_email] = true + opts[:use_site_subject] = true + opts[:show_category_in_subject] = true + notification_email(user, opts) + end + def user_mentioned(user, opts) opts[:allow_reply_by_email] = true opts[:use_site_subject] = true @@ -314,7 +321,7 @@ class UserNotifications < ActionMailer::Base context: context, username: username, add_unsubscribe_link: !user.staged, - add_unsubscribe_via_email_link: user.mailing_list_mode, + add_unsubscribe_via_email_link: user.user_option.mailing_list_mode, unsubscribe_url: post.topic.unsubscribe_url, allow_reply_by_email: allow_reply_by_email, use_site_subject: use_site_subject, diff --git a/app/models/user.rb b/app/models/user.rb index a1be4c16b..5634cbeaf 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -38,6 +38,7 @@ class User < ActiveRecord::Base has_many :user_archived_messages, dependent: :destroy + has_one :user_option, dependent: :destroy has_one :user_avatar, dependent: :destroy has_one :facebook_user_info, dependent: :destroy has_one :twitter_user_info, dependent: :destroy @@ -80,6 +81,7 @@ class User < ActiveRecord::Base after_create :create_email_token after_create :create_user_stat + after_create :create_user_option after_create :create_user_profile after_create :ensure_in_trust_level_group after_create :automatic_group_membership @@ -123,7 +125,7 @@ class User < ActiveRecord::Base # TODO-PERF: There is no indexes on any of these # and NotifyMailingListSubscribers does a select-all-and-loop - # may want to create an index on (active, blocked, suspended_till, mailing_list_mode)? + # may want to create an index on (active, blocked, suspended_till)? scope :blocked, -> { where(blocked: true) } scope :not_blocked, -> { where(blocked: false) } scope :suspended, -> { where('suspended_till IS NOT NULL AND suspended_till > ?', Time.zone.now) } @@ -911,6 +913,10 @@ class User < ActiveRecord::Base stat.save! end + def create_user_option + UserOption.create(user_id: id) + end + def create_email_token email_tokens.create(email: email) end @@ -965,21 +971,8 @@ class User < ActiveRecord::Base end def set_default_user_preferences - set_default_email_digest_frequency - set_default_email_private_messages - set_default_email_direct - set_default_email_mailing_list_mode - set_default_email_always - set_default_other_new_topic_duration_minutes set_default_other_auto_track_topics_after_msecs - set_default_other_external_links_in_new_tab - set_default_other_enable_quoting - set_default_other_dynamic_favicon - set_default_other_disable_jump_reply - set_default_other_edit_history_public - - set_default_topics_automatic_unpin # needed, otherwise the callback chain is broken... true @@ -1031,26 +1024,6 @@ class User < ActiveRecord::Base end end - def set_default_email_digest_frequency - if has_attribute?(:email_digests) - if SiteSetting.default_email_digest_frequency.to_i <= 0 - self.email_digests = false - else - self.email_digests = true - self.digest_after_days ||= SiteSetting.default_email_digest_frequency.to_i if has_attribute?(:digest_after_days) - end - end - end - - def set_default_email_mailing_list_mode - self.mailing_list_mode = SiteSetting.default_email_mailing_list_mode if has_attribute?(:mailing_list_mode) - end - - %w{private_messages direct always}.each do |s| - define_method("set_default_email_#{s}") do - self.send("email_#{s}=", SiteSetting.send("default_email_#{s}")) if has_attribute?("email_#{s}") - end - end %w{new_topic_duration_minutes auto_track_topics_after_msecs}.each do |s| define_method("set_default_other_#{s}") do @@ -1058,16 +1031,6 @@ class User < ActiveRecord::Base end end - %w{external_links_in_new_tab enable_quoting dynamic_favicon disable_jump_reply edit_history_public}.each do |s| - define_method("set_default_other_#{s}") do - self.send("#{s}=", SiteSetting.send("default_other_#{s}")) if has_attribute?(s) - end - end - - def set_default_topics_automatic_unpin - self.automatically_unpin_topics = SiteSetting.default_topics_automatic_unpin - end - end # == Schema Information @@ -1090,14 +1053,11 @@ end # last_seen_at :datetime # admin :boolean default(FALSE), not null # last_emailed_at :datetime -# email_digests :boolean not null # trust_level :integer not null # email_private_messages :boolean default(TRUE) -# email_direct :boolean default(TRUE), not null # approved :boolean default(FALSE), not null # approved_by_id :integer # approved_at :datetime -# digest_after_days :integer # previous_visit_at :datetime # suspended_at :datetime # suspended_till :datetime @@ -1107,24 +1067,16 @@ end # flag_level :integer default(0), not null # ip_address :inet # new_topic_duration_minutes :integer -# external_links_in_new_tab :boolean not null -# enable_quoting :boolean default(TRUE), not null # moderator :boolean default(FALSE) # blocked :boolean default(FALSE) -# dynamic_favicon :boolean default(FALSE), not null # title :string(255) # uploaded_avatar_id :integer -# email_always :boolean default(FALSE), not null -# mailing_list_mode :boolean default(FALSE), not null # primary_group_id :integer # locale :string(10) # registration_ip_address :inet # last_redirected_to_top_at :datetime -# disable_jump_reply :boolean default(FALSE), not null -# edit_history_public :boolean default(FALSE), not null # trust_level_locked :boolean default(FALSE), not null # staged :boolean default(FALSE), not null -# automatically_unpin_topics :boolean default(TRUE) # # Indexes # diff --git a/app/models/user_email_observer.rb b/app/models/user_email_observer.rb index db9dbdc73..d153d01b5 100644 --- a/app/models/user_email_observer.rb +++ b/app/models/user_email_observer.rb @@ -28,6 +28,10 @@ class UserEmailObserver < ActiveRecord::Observer enqueue :user_replied end + def linked + enqueue :user_linked + end + def private_message enqueue_private(:user_private_message) end @@ -60,12 +64,12 @@ class UserEmailObserver < ActiveRecord::Observer EMAILABLE_POST_TYPES ||= Set.new [Post.types[:regular], Post.types[:whisper]] def enqueue(type, delay=default_delay) - return unless notification.user.email_direct? + return unless notification.user.user_option.email_direct? perform_enqueue(type, delay) end def enqueue_private(type, delay=private_delay) - return unless notification.user.email_private_messages? + return unless notification.user.user_option.email_private_messages? perform_enqueue(type, delay) end diff --git a/app/models/user_option.rb b/app/models/user_option.rb new file mode 100644 index 000000000..3dac2ded9 --- /dev/null +++ b/app/models/user_option.rb @@ -0,0 +1,29 @@ +class UserOption < ActiveRecord::Base + self.primary_key = :user_id + belongs_to :user + before_create :set_defaults + + def set_defaults + self.email_always = SiteSetting.default_email_always + self.mailing_list_mode = SiteSetting.default_email_mailing_list_mode + self.email_direct = SiteSetting.default_email_direct + self.automatically_unpin_topics = SiteSetting.default_topics_automatic_unpin + self.email_private_messages = SiteSetting.default_email_private_messages + + self.enable_quoting = SiteSetting.default_other_enable_quoting + self.external_links_in_new_tab = SiteSetting.default_other_external_links_in_new_tab + self.dynamic_favicon = SiteSetting.default_other_dynamic_favicon + self.disable_jump_reply = SiteSetting.default_other_disable_jump_reply + self.edit_history_public = SiteSetting.default_other_edit_history_public + + + if SiteSetting.default_email_digest_frequency.to_i <= 0 + self.email_digests = false + else + self.email_digests = true + self.digest_after_days ||= SiteSetting.default_email_digest_frequency.to_i + end + + true + end +end diff --git a/app/serializers/current_user_serializer.rb b/app/serializers/current_user_serializer.rb index c576025cd..5cf912d47 100644 --- a/app/serializers/current_user_serializer.rb +++ b/app/serializers/current_user_serializer.rb @@ -31,7 +31,8 @@ class CurrentUserSerializer < BasicUserSerializer :is_anonymous, :post_queue_new_count, :show_queued_posts, - :read_faq + :read_faq, + :automatically_unpin_topics def include_site_flagged_posts_count? object.staff? @@ -49,6 +50,26 @@ class CurrentUserSerializer < BasicUserSerializer object.user_stat.topic_reply_count end + def enable_quoting + object.user_option.enable_quoting + end + + def disable_jump_reply + object.user_option.disable_jump_reply + end + + def external_links_in_new_tab + object.user_option.external_links_in_new_tab + end + + def dynamic_favicon + object.user_option.dynamic_favicon + end + + def automatically_unpin_topics + object.user_option.automatically_unpin_topics + end + def site_flagged_posts_count PostAction.flagged_posts_count end diff --git a/app/serializers/user_option_serializer.rb b/app/serializers/user_option_serializer.rb new file mode 100644 index 000000000..77c9d6854 --- /dev/null +++ b/app/serializers/user_option_serializer.rb @@ -0,0 +1,20 @@ +class UserOptionSerializer < ApplicationSerializer + attributes :user_id, + :email_always, + :mailing_list_mode, + :email_digests, + :email_private_messages, + :email_direct, + :external_links_in_new_tab, + :dynamic_favicon, + :enable_quoting, + :disable_jump_reply, + :digest_after_days, + :automatically_unpin_topics, + :edit_history_public + + + def include_edit_history_public? + !SiteSetting.edit_history_visible_to_public + end +end diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index 480fbd9c6..1ad96ce24 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -61,40 +61,33 @@ class UserSerializer < BasicUserSerializer :uploaded_avatar_id, :badge_count, :has_title_badges, - :edit_history_public, :custom_fields, :user_fields, :topic_post_count, :pending_count, - :profile_view_count, - :automatically_unpin_topics + :profile_view_count has_one :invited_by, embed: :object, serializer: BasicUserSerializer has_many :groups, embed: :object, serializer: BasicGroupSerializer has_many :featured_user_badges, embed: :ids, serializer: UserBadgeSerializer, root: :user_badges has_one :card_badge, embed: :object, serializer: BadgeSerializer + has_one :user_option, embed: :object, serializer: UserOptionSerializer + + def include_user_option? + can_edit + end staff_attributes :post_count, :can_be_deleted, :can_delete_all_posts private_attributes :locale, - :email_digests, - :email_private_messages, - :email_direct, - :email_always, - :digest_after_days, - :mailing_list_mode, :auto_track_topics_after_msecs, :new_topic_duration_minutes, - :external_links_in_new_tab, - :dynamic_favicon, - :enable_quoting, :muted_category_ids, :tracked_category_ids, :watched_category_ids, :private_messages_stats, - :disable_jump_reply, :system_avatar_upload_id, :system_avatar_template, :gravatar_avatar_upload_id, @@ -322,10 +315,6 @@ class UserSerializer < BasicUserSerializer object.badges.where(allow_title: true).count > 0 end - def include_edit_history_public? - can_edit && !SiteSetting.edit_history_visible_to_public - end - def user_fields object.user_fields end diff --git a/app/services/anonymous_shadow_creator.rb b/app/services/anonymous_shadow_creator.rb index 18b7eccea..08c1d6a33 100644 --- a/app/services/anonymous_shadow_creator.rb +++ b/app/services/anonymous_shadow_creator.rb @@ -40,11 +40,14 @@ class AnonymousShadowCreator active: true, trust_level: 1, trust_level_locked: true, - email_private_messages: false, - email_digests: false, created_at: 1.day.ago # bypass new user restrictions ) + shadow.user_option.update_columns( + email_private_messages: false, + email_digests: false + ) + shadow.email_tokens.update_all(confirmed: true) shadow.activate diff --git a/app/services/user_anonymizer.rb b/app/services/user_anonymizer.rb index 48699b176..e9ff162db 100644 --- a/app/services/user_anonymizer.rb +++ b/app/services/user_anonymizer.rb @@ -23,14 +23,17 @@ class UserAnonymizer @user.name = nil @user.date_of_birth = nil @user.title = nil - @user.email_digests = false - @user.email_private_messages = false - @user.email_direct = false - @user.email_always = false - @user.mailing_list_mode = false @user.uploaded_avatar_id = nil @user.save + options = @user.user_option + options.email_always = false + options.mailing_list_mode = false + options.email_digests = false + options.email_private_messages = false + options.email_direct = false + options.save + profile = @user.user_profile profile.destroy if profile @user.create_user_profile diff --git a/app/services/user_updater.rb b/app/services/user_updater.rb index c6f236d2b..e028a0d14 100644 --- a/app/services/user_updater.rb +++ b/app/services/user_updater.rb @@ -6,18 +6,19 @@ class UserUpdater muted_category_ids: :muted } - USER_ATTR = [ - :email_digests, + OPTION_ATTR = [ :email_always, + :mailing_list_mode, + :email_digests, :email_direct, :email_private_messages, :external_links_in_new_tab, :enable_quoting, :dynamic_favicon, - :mailing_list_mode, :disable_jump_reply, :edit_history_public, :automatically_unpin_topics, + :digest_after_days ] def initialize(actor, user) @@ -36,7 +37,6 @@ class UserUpdater user.name = attributes.fetch(:name) { user.name } user.locale = attributes.fetch(:locale) { user.locale } - user.digest_after_days = attributes.fetch(:digest_after_days) { user.digest_after_days } if attributes[:auto_track_topics_after_msecs] user.auto_track_topics_after_msecs = attributes[:auto_track_topics_after_msecs].to_i @@ -56,9 +56,19 @@ class UserUpdater end end - USER_ATTR.each do |attribute| + + save_options = false + OPTION_ATTR.each do |attribute| if attributes[attribute].present? - user.send("#{attribute}=", attributes[attribute] == 'true') + save_options = true + + + if [true,false].include?(user.user_option.send(attribute)) + val = attributes[attribute].to_s == 'true' + user.user_option.send("#{attribute}=", val) + else + user.user_option.send("#{attribute}=", attributes[attribute]) + end end end @@ -72,7 +82,7 @@ class UserUpdater update_muted_users(attributes[:muted_usernames]) end - user_profile.save && user.save + (!save_options || user.user_option.save) && user_profile.save && user.save end end diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 78d65d384..5a4011301 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -2092,6 +2092,18 @@ en: --- %{respond_instructions} + user_linked: + subject_template: "[%{site_name}] %{topic_title}" + text_body_template: | + %{header_instructions} + + %{message} + + %{context} + + --- + %{respond_instructions} + user_mentioned: subject_template: "[%{site_name}] %{topic_title}" text_body_template: | diff --git a/db/fixtures/009_users.rb b/db/fixtures/009_users.rb index 7effb7377..4c5f11875 100644 --- a/db/fixtures/009_users.rb +++ b/db/fixtures/009_users.rb @@ -16,14 +16,51 @@ User.seed do |u| u.active = true u.admin = true u.moderator = true - u.email_direct = false u.approved = true - u.email_private_messages = false u.trust_level = TrustLevel[4] end +UserOption.where(user_id: -1).update_all( + email_private_messages: false, + email_direct: false +) + Group.user_trust_level_change!(-1, TrustLevel[4]) +# 60 minutes after our migration runs we need to exectue this code... +duration = Rails.env.production? ? 60 : 0 +if User.exec_sql("SELECT 1 FROM schema_migration_details + WHERE EXISTS( + SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'users' AND column_name = 'enable_quoting' + ) AND + name = 'AllowDefaultsOnUsersTable' AND + created_at < (current_timestamp at time zone 'UTC' - interval '#{duration} minutes') + ").to_a.length > 0 + + + User.transaction do + STDERR.puts "Removing superflous user columns!" + %w[ + email_always + mailing_list_mode + email_digests + email_direct + email_private_messages + external_links_in_new_tab + enable_quoting + dynamic_favicon + disable_jump_reply + edit_history_public + automatically_unpin_topics + digest_after_days + ].each do |column| + User.exec_sql("ALTER TABLE users DROP column #{column}") + end + + end +end + # User for the smoke tests if ENV["SMOKE"] == "1" smoke_user = User.seed do |u| @@ -49,3 +86,4 @@ if ENV["SMOKE"] == "1" et.confirmed = true end end + diff --git a/db/migrate/20000225050318_add_schema_migration_details.rb b/db/migrate/20000225050318_add_schema_migration_details.rb new file mode 100644 index 000000000..dbf4959b1 --- /dev/null +++ b/db/migrate/20000225050318_add_schema_migration_details.rb @@ -0,0 +1,30 @@ +class AddSchemaMigrationDetails < ActiveRecord::Migration + def up + # schema_migrations table is way too thin, does not give info about + # duration of migration or the date it happened, this migration together with the + # monkey patch adds a lot of information to the migration table + + create_table :schema_migration_details do |t| + t.string :version, null: false + t.string :name + t.string :hostname + t.string :git_version + t.string :rails_version + t.integer :duration + t.string :direction # this really should be a pg enum type but annoying to wire up for little gain + t.datetime :created_at, null: false + end + + add_index :schema_migration_details, [:version] + + execute("INSERT INTO schema_migration_details(version, created_at) + SELECT version, current_timestamp + FROM schema_migrations + ORDER BY version + ") + end + + def down + drop_table :schema_migration_details + end +end diff --git a/db/migrate/20160225050317_add_user_options.rb b/db/migrate/20160225050317_add_user_options.rb new file mode 100644 index 000000000..13df057cd --- /dev/null +++ b/db/migrate/20160225050317_add_user_options.rb @@ -0,0 +1,76 @@ +class AddUserOptions < ActiveRecord::Migration + def up + + create_table :user_options, id: false do |t| + t.integer :user_id, null: false + t.boolean :email_always, null: false, default: false + t.boolean :mailing_list_mode, null: false, default: false + t.boolean :email_digests + t.boolean :email_direct, null: false, default: true + t.boolean :email_private_messages, null: false, default: true + t.boolean :external_links_in_new_tab, null: false, default: false + t.boolean :enable_quoting, null: false, default: true + t.boolean :dynamic_favicon, null: false, default: false + t.boolean :disable_jump_reply, null: false, default: false + t.boolean :edit_history_public, null: false, default: false + t.boolean :automatically_unpin_topics, null: false, default: true + t.integer :digest_after_days + end + + add_index :user_options, [:user_id], unique: true + + execute <<SQL + INSERT INTO user_options ( + user_id, + email_always, + mailing_list_mode, + email_digests, + email_direct, + email_private_messages, + external_links_in_new_tab, + enable_quoting, + dynamic_favicon, + disable_jump_reply, + edit_history_public, + automatically_unpin_topics, + digest_after_days + ) + SELECT id, + email_always, + mailing_list_mode, + email_digests, + email_direct, + email_private_messages, + external_links_in_new_tab, + enable_quoting, + dynamic_favicon, + disable_jump_reply, + edit_history_public, + automatically_unpin_topics, + digest_after_days + FROM users +SQL + + # these can not be removed until a bit later + # if we remove them now all currently running unicorns will start erroring out + # + # remove_column :users, :email_always + # remove_column :users, :mailing_list_mode + # remove_column :users, :email_digests + # remove_column :users, :email_direct + # remove_column :users, :email_private_messages + # remove_column :users, :external_links_in_new_tab + # remove_column :users, :enable_quoting + # remove_column :users, :dynamic_favicon + # remove_column :users, :disable_jump_reply + # remove_column :users, :edit_history_public + # remove_column :users, :automatically_unpin_topics + # remove_column :users, :digest_after_days + end + + def down + # we can not move backwards here cause columns + # get removed an hour after the migration + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/migrate/20160225050318_allow_defaults_on_users_table.rb b/db/migrate/20160225050318_allow_defaults_on_users_table.rb new file mode 100644 index 000000000..999e275fd --- /dev/null +++ b/db/migrate/20160225050318_allow_defaults_on_users_table.rb @@ -0,0 +1,10 @@ +class AllowDefaultsOnUsersTable < ActiveRecord::Migration + def up + # we need to temporarily change table a bit to ensure we can insert new records + change_column :users, :email_digests, :boolean, null: false, default: true + change_column :users, :external_links_in_new_tab, :boolean, null: false, default: false + end + + def down + end +end diff --git a/lib/freedom_patches/schema_migration_details.rb b/lib/freedom_patches/schema_migration_details.rb new file mode 100644 index 000000000..bbae956c1 --- /dev/null +++ b/lib/freedom_patches/schema_migration_details.rb @@ -0,0 +1,54 @@ +module FreedomPatches + module SchemaMigrationDetails + def exec_migration(conn, direction) + rval = nil + + time = Benchmark.measure do + rval=super + end + + sql = <<SQL + INSERT INTO schema_migration_details( + version, + hostname, + name, + git_version, + duration, + direction, + rails_version, + created_at + ) values ( + :version, + :hostname, + :name, + :git_version, + :duration, + :direction, + :rails_version, + :created_at + ) +SQL + + hostname = `hostname` rescue "" + sql = ActiveRecord::Base.send(:sanitize_sql_array, [sql, { + version: version || "", + duration: (time.real * 1000).to_i, + hostname: hostname, + name: name, + git_version: Discourse.git_version, + created_at: Time.zone.now, + direction: direction.to_s, + rails_version: Rails.version + }]) + + conn.execute(sql) + + rval + end + + end +end + +class ActiveRecord::Migration + prepend FreedomPatches::SchemaMigrationDetails +end diff --git a/lib/guardian/post_guardian.rb b/lib/guardian/post_guardian.rb index 722c01748..8635bc6c1 100644 --- a/lib/guardian/post_guardian.rb +++ b/lib/guardian/post_guardian.rb @@ -157,7 +157,7 @@ module PostGuardian return false unless post if !post.hidden - return true if post.wiki || SiteSetting.edit_history_visible_to_public || post.user.try(:edit_history_public) + return true if post.wiki || SiteSetting.edit_history_visible_to_public || (post.user && post.user.user_option.edit_history_public) end authenticated? && diff --git a/spec/components/freedom_patches/schema_migration_details_spec.rb b/spec/components/freedom_patches/schema_migration_details_spec.rb new file mode 100644 index 000000000..86e960edb --- /dev/null +++ b/spec/components/freedom_patches/schema_migration_details_spec.rb @@ -0,0 +1,32 @@ +require 'rails_helper' +require_dependency "freedom_patches/schema_migration_details" + +describe FreedomPatches::SchemaMigrationDetails do + + # we usually don't really need this model so lets not clutter up with it + class SchemaMigrationDetail < ActiveRecord::Base + end + + class TestMigration < ActiveRecord::Migration + def up + sleep 0.001 + end + end + + it "logs information on migration" do + migration = TestMigration.new("awesome_migration","20160225050318") + + ActiveRecord::Base.connection_pool.with_connection do |conn| + migration.exec_migration(conn, :up) + end + + info = SchemaMigrationDetail.find_by(version: "20160225050318") + + expect(info.duration).to be > 0 + expect(info.git_version).to eq Discourse.git_version + expect(info.direction).to eq "up" + expect(info.rails_version).to eq Rails.version + expect(info.filename).to eq migration.filename + expect(info.name).to eq "awesome_migration" + end +end diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb index e71c1550a..14db91210 100644 --- a/spec/components/guardian_spec.rb +++ b/spec/components/guardian_spec.rb @@ -510,7 +510,7 @@ describe Guardian do it 'is true if the author has public edit history' do public_post_revision = Fabricate(:post_revision) - public_post_revision.post.user.edit_history_public = true + public_post_revision.post.user.user_option.edit_history_public = true expect(Guardian.new.can_see?(public_post_revision)).to be_truthy end end @@ -533,7 +533,7 @@ describe Guardian do it 'is true if the author has public edit history' do public_post_revision = Fabricate(:post_revision) - public_post_revision.post.user.edit_history_public = true + public_post_revision.post.user.user_option.edit_history_public = true expect(Guardian.new.can_see?(public_post_revision)).to be_truthy end end diff --git a/spec/controllers/email_controller_spec.rb b/spec/controllers/email_controller_spec.rb index f5c6e9180..0376076b9 100644 --- a/spec/controllers/email_controller_spec.rb +++ b/spec/controllers/email_controller_spec.rb @@ -21,7 +21,11 @@ describe EmailController do context '.resubscribe' do - let(:user) { Fabricate(:user, email_digests: false) } + let(:user) { + user = Fabricate(:user) + user.user_option.update_columns(email_digests: false) + user + } let(:key) { DigestUnsubscribeKey.create_key_for(user) } context 'with a valid key' do @@ -31,7 +35,7 @@ describe EmailController do end it 'subscribes the user' do - expect(user.email_digests).to eq(true) + expect(user.user_option.email_digests).to eq(true) end end @@ -39,7 +43,11 @@ describe EmailController do context '.unsubscribe' do - let(:user) { Fabricate(:user, email_digests: true, email_direct: true, email_private_messages: true, email_always: true) } + let(:user) { + user = Fabricate(:user) + user.user_option.update_columns(email_always: true, email_digests: true, email_direct: true, email_private_messages: true) + user + } let(:key) { DigestUnsubscribeKey.create_key_for(user) } context 'from confirm unsubscribe email' do @@ -49,10 +57,11 @@ describe EmailController do end it 'unsubscribes from all emails' do - expect(user.email_digests).to eq false - expect(user.email_direct).to eq false - expect(user.email_private_messages).to eq false - expect(user.email_always).to eq false + options = user.user_option + expect(options.email_digests).to eq false + expect(options.email_direct).to eq false + expect(options.email_private_messages).to eq false + expect(options.email_always).to eq false end end @@ -63,7 +72,7 @@ describe EmailController do end it 'unsubscribes the user' do - expect(user.email_digests).to eq(false) + expect(user.user_option.email_digests).to eq(false) end it "sets the appropriate instance variables" do @@ -90,7 +99,7 @@ describe EmailController do end it 'does not unsubscribe the user' do - expect(user.email_digests).to eq(true) + expect(user.user_option.email_digests).to eq(true) end it 'sets the appropriate instance variables' do @@ -108,7 +117,7 @@ describe EmailController do end it 'unsubscribes the user' do - expect(user.email_digests).to eq(false) + expect(user.user_option.email_digests).to eq(false) end it 'sets the appropriate instance variables' do diff --git a/spec/jobs/user_email_spec.rb b/spec/jobs/user_email_spec.rb index c7ab345a1..5cce202fe 100644 --- a/spec/jobs/user_email_spec.rb +++ b/spec/jobs/user_email_spec.rb @@ -55,7 +55,8 @@ describe Jobs::UserEmail do end it "does send an email to a user that's been recently seen but has email_always set" do - user.update_attributes(last_seen_at: 9.minutes.ago, email_always: true) + user.update_attributes(last_seen_at: 9.minutes.ago) + user.user_option.update_attributes(email_always: true) Email::Sender.any_instance.expects(:send) Jobs::UserEmail.new.execute(type: :user_replied, user_id: user.id, post_id: post.id) end @@ -188,13 +189,13 @@ describe Jobs::UserEmail do it "does send the email if the notification has been seen but the user is set for email_always" do Email::Sender.any_instance.expects(:send) notification.update_column(:read, true) - user.update_column(:email_always, true) + user.user_option.update_column(:email_always, true) Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_id: notification.id) end it "doesn't send the mail if the user is using mailing list mode" do Email::Sender.any_instance.expects(:send).never - user.update_column(:mailing_list_mode, true) + user.user_option.update_column(:mailing_list_mode, true) # sometimes, we pass the notification_id Jobs::UserEmail.new.execute(type: :user_mentioned, user_id: user.id, notification_id: notification.id, post_id: post.id) # other times, we only pass the type of notification diff --git a/spec/models/user_email_observer_spec.rb b/spec/models/user_email_observer_spec.rb index 0f1150c4d..2bd23dd8e 100644 --- a/spec/models/user_email_observer_spec.rb +++ b/spec/models/user_email_observer_spec.rb @@ -51,7 +51,7 @@ describe UserEmailObserver do include_examples "enqueue" it "doesn't enqueue a job if the user has mention emails disabled" do - notification.user.expects(:email_direct?).returns(false) + notification.user.user_option.update_columns(email_direct: false) Jobs.expects(:enqueue_in).with(delay, :user_email, has_entry(type: type)).never UserEmailObserver.process_notification(notification) end @@ -61,7 +61,7 @@ describe UserEmailObserver do include_examples "enqueue" it "doesn't enqueue a job if the user has private message emails disabled" do - notification.user.expects(:email_private_messages?).returns(false) + notification.user.user_option.update_columns(email_private_messages: false) Jobs.expects(:enqueue_in).with(delay, :user_email, has_entry(type: type)).never UserEmailObserver.process_notification(notification) end @@ -99,6 +99,14 @@ describe UserEmailObserver do include_examples "enqueue_public" end + context 'user_linked' do + let(:type) { :user_linked } + let(:delay) { SiteSetting.email_time_window_mins.minutes } + let!(:notification) { create_notification(11) } + + include_examples "enqueue_public" + end + context 'user_posted' do let(:type) { :user_posted } let(:delay) { SiteSetting.email_time_window_mins.minutes } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 792c9ef19..5acf8bfb6 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -152,15 +152,15 @@ describe User do it "is properly initialized" do expect(subject.approved_at).to be_blank expect(subject.approved_by_id).to be_blank - expect(subject.email_private_messages).to eq(true) - expect(subject.email_direct).to eq(true) end context 'after_save' do before { subject.save } - it "has an email token" do + it "has correct settings" do expect(subject.email_tokens).to be_present + expect(subject.user_option.email_private_messages).to eq(true) + expect(subject.user_option.email_direct).to eq(true) end end @@ -1246,50 +1246,62 @@ describe User do context "when user preferences are overriden" do before do - SiteSetting.stubs(:default_email_digest_frequency).returns(1) # daily - SiteSetting.stubs(:default_email_private_messages).returns(false) - SiteSetting.stubs(:default_email_direct).returns(false) - SiteSetting.stubs(:default_email_mailing_list_mode).returns(true) - SiteSetting.stubs(:default_email_always).returns(true) + SiteSetting.default_email_digest_frequency = 1 # daily + SiteSetting.default_email_private_messages = false + SiteSetting.default_email_direct = false + SiteSetting.default_email_mailing_list_mode = true + SiteSetting.default_email_always = true - SiteSetting.stubs(:default_other_new_topic_duration_minutes).returns(-1) # not viewed - SiteSetting.stubs(:default_other_auto_track_topics_after_msecs).returns(0) # immediately - SiteSetting.stubs(:default_other_external_links_in_new_tab).returns(true) - SiteSetting.stubs(:default_other_enable_quoting).returns(false) - SiteSetting.stubs(:default_other_dynamic_favicon).returns(true) - SiteSetting.stubs(:default_other_disable_jump_reply).returns(true) - SiteSetting.stubs(:default_other_edit_history_public).returns(true) + SiteSetting.default_other_new_topic_duration_minutes = -1 # not viewed + SiteSetting.default_other_auto_track_topics_after_msecs = 0 # immediately + SiteSetting.default_other_external_links_in_new_tab = true + SiteSetting.default_other_enable_quoting = false + SiteSetting.default_other_dynamic_favicon = true + SiteSetting.default_other_disable_jump_reply = true + SiteSetting.default_other_edit_history_public = true - SiteSetting.stubs(:default_topics_automatic_unpin).returns(false) + SiteSetting.default_topics_automatic_unpin = false - SiteSetting.stubs(:default_categories_watching).returns("1") - SiteSetting.stubs(:default_categories_tracking).returns("2") - SiteSetting.stubs(:default_categories_muted).returns("3") + SiteSetting.default_categories_watching = "1" + SiteSetting.default_categories_tracking = "2" + SiteSetting.default_categories_muted = "3" end it "has overriden preferences" do user = Fabricate(:user) - - expect(user.digest_after_days).to eq(1) - expect(user.email_private_messages).to eq(false) - expect(user.email_direct).to eq(false) - expect(user.mailing_list_mode).to eq(true) - expect(user.email_always).to eq(true) + options = user.user_option + expect(options.email_always).to eq(true) + expect(options.mailing_list_mode).to eq(true) + expect(options.digest_after_days).to eq(1) + expect(options.email_private_messages).to eq(false) + expect(options.external_links_in_new_tab).to eq(true) + expect(options.enable_quoting).to eq(false) + expect(options.dynamic_favicon).to eq(true) + expect(options.disable_jump_reply).to eq(true) + expect(options.edit_history_public).to eq(true) + expect(options.automatically_unpin_topics).to eq(false) + expect(options.email_direct).to eq(false) expect(user.new_topic_duration_minutes).to eq(-1) expect(user.auto_track_topics_after_msecs).to eq(0) - expect(user.external_links_in_new_tab).to eq(true) - expect(user.enable_quoting).to eq(false) - expect(user.dynamic_favicon).to eq(true) - expect(user.disable_jump_reply).to eq(true) - expect(user.edit_history_public).to eq(true) - - expect(user.automatically_unpin_topics).to eq(false) expect(CategoryUser.lookup(user, :watching).pluck(:category_id)).to eq([1]) expect(CategoryUser.lookup(user, :tracking).pluck(:category_id)).to eq([2]) expect(CategoryUser.lookup(user, :muted).pluck(:category_id)).to eq([3]) end + end + + context UserOption do + + it "Creates a UserOption row when a user record is created and destroys once done" do + user = Fabricate(:user) + expect(user.user_option.email_always).to eq(false) + + user_id = user.id + user.destroy! + expect(UserOption.find_by(user_id: user_id)).to eq(nil) + + end end diff --git a/spec/serializers/user_serializer_spec.rb b/spec/serializers/user_serializer_spec.rb index 7f16958a2..3a9db1a23 100644 --- a/spec/serializers/user_serializer_spec.rb +++ b/spec/serializers/user_serializer_spec.rb @@ -15,6 +15,24 @@ describe UserSerializer do end end + context "as current user" do + it "serializes options correctly" do + # so we serialize more stuff + SiteSetting.edit_history_visible_to_public = false + + user = Fabricate.build(:user, + user_profile: Fabricate.build(:user_profile), + user_option: UserOption.new(edit_history_public: true), + user_stat: UserStat.new + ) + + json = UserSerializer.new(user, scope: Guardian.new(user), root: false).as_json + + expect(json[:user_option][:edit_history_public]).to eq(true) + + end + end + context "with a user" do let(:user) { Fabricate.build(:user, user_profile: Fabricate.build(:user_profile) ) } let(:serializer) { UserSerializer.new(user, scope: Guardian.new, root: false) } @@ -26,7 +44,7 @@ describe UserSerializer do context "with `enable_names` true" do before do - SiteSetting.stubs(:enable_names?).returns(true) + SiteSetting.enable_names = true end it "has a name" do @@ -34,6 +52,7 @@ describe UserSerializer do end end + context "with `enable_names` false" do before do SiteSetting.stubs(:enable_names?).returns(false) diff --git a/spec/services/anonymous_shadow_creator_spec.rb b/spec/services/anonymous_shadow_creator_spec.rb index 1483f59c7..bda16a782 100644 --- a/spec/services/anonymous_shadow_creator_spec.rb +++ b/spec/services/anonymous_shadow_creator_spec.rb @@ -30,6 +30,9 @@ describe AnonymousShadowCreator do freeze_time 4.minutes.from_now shadow3 = AnonymousShadowCreator.get(user) + expect(shadow3.user_option.email_digests).to eq(false) + expect(shadow3.user_option.email_private_messages).to eq(false) + expect(shadow2.id).not_to eq(shadow3.id) end diff --git a/spec/services/user_anonymizer_spec.rb b/spec/services/user_anonymizer_spec.rb index b641c1c04..9cef2507f 100644 --- a/spec/services/user_anonymizer_spec.rb +++ b/spec/services/user_anonymizer_spec.rb @@ -19,13 +19,17 @@ describe UserAnonymizer do end it "turns off all notifications" do + user.user_option.update_columns( + email_always: true + ) + make_anonymous user.reload - expect(user.email_digests).to eq(false) - expect(user.email_private_messages).to eq(false) - expect(user.email_direct).to eq(false) - expect(user.email_always).to eq(false) - expect(user.mailing_list_mode).to eq(false) + expect(user.user_option.email_digests).to eq(false) + expect(user.user_option.email_private_messages).to eq(false) + expect(user.user_option.email_direct).to eq(false) + expect(user.user_option.email_always).to eq(false) + expect(user.user_option.mailing_list_mode).to eq(false) end it "resets profile to default values" do diff --git a/spec/services/user_updater_spec.rb b/spec/services/user_updater_spec.rb index 1f46e5eb5..f94d2bb9e 100644 --- a/spec/services/user_updater_spec.rb +++ b/spec/services/user_updater_spec.rb @@ -32,26 +32,33 @@ describe UserUpdater do describe '#update' do it 'saves user' do user = Fabricate(:user, name: 'Billy Bob') - updater = described_class.new(acting_user, user) + updater = UserUpdater.new(acting_user, user) updater.update(name: 'Jim Tom') expect(user.reload.name).to eq 'Jim Tom' end - it 'updates bio' do + it 'updates various fields' do user = Fabricate(:user) - updater = described_class.new(acting_user, user) + updater = UserUpdater.new(acting_user, user) - updater.update(bio_raw: 'my new bio') + updater.update(bio_raw: 'my new bio', + email_always: 'true', + mailing_list_mode: true, + digest_after_days: "8") + user.reload - expect(user.reload.user_profile.bio_raw).to eq 'my new bio' + expect(user.user_profile.bio_raw).to eq 'my new bio' + expect(user.user_option.email_always).to eq true + expect(user.user_option.mailing_list_mode).to eq true + expect(user.user_option.digest_after_days).to eq 8 end context 'when update succeeds' do it 'returns true' do user = Fabricate(:user) - updater = described_class.new(acting_user, user) + updater = UserUpdater.new(acting_user, user) expect(updater.update).to be_truthy end @@ -61,7 +68,7 @@ describe UserUpdater do it 'returns false' do user = Fabricate(:user) user.stubs(save: false) - updater = described_class.new(acting_user, user) + updater = UserUpdater.new(acting_user, user) expect(updater.update).to be_falsey end @@ -73,7 +80,7 @@ describe UserUpdater do guardian = stub guardian.stubs(:can_grant_title?).with(user).returns(true) Guardian.stubs(:new).with(acting_user).returns(guardian) - updater = described_class.new(acting_user, user) + updater = UserUpdater.new(acting_user, user) updater.update(title: 'Minion')