mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-30 10:58:31 -05:00
PERF: move 3 more option columns out of the user table
This commit is contained in:
parent
f9c5cded6f
commit
f0e942f647
22 changed files with 278 additions and 250 deletions
|
@ -141,13 +141,11 @@ const User = RestModel.extend({
|
|||
|
||||
save() {
|
||||
const data = this.getProperties(
|
||||
'auto_track_topics_after_msecs',
|
||||
'bio_raw',
|
||||
'website',
|
||||
'location',
|
||||
'name',
|
||||
'locale',
|
||||
'new_topic_duration_minutes',
|
||||
'custom_fields',
|
||||
'user_fields',
|
||||
'muted_usernames',
|
||||
|
@ -165,7 +163,9 @@ const User = RestModel.extend({
|
|||
'enable_quoting',
|
||||
'disable_jump_reply',
|
||||
'automatically_unpin_topics',
|
||||
'digest_after_days'
|
||||
'digest_after_days',
|
||||
'new_topic_duration_minutes',
|
||||
'auto_track_topics_after_msecs'
|
||||
].forEach(s => {
|
||||
data[s] = this.get(`user_option.${s}`);
|
||||
});
|
||||
|
|
|
@ -201,12 +201,12 @@
|
|||
|
||||
<div class="controls controls-dropdown">
|
||||
<label>{{i18n 'user.new_topic_duration.label'}}</label>
|
||||
{{combo-box valueAttribute="value" content=considerNewTopicOptions value=model.new_topic_duration_minutes}}
|
||||
{{combo-box valueAttribute="value" content=considerNewTopicOptions value=model.user_option.new_topic_duration_minutes}}
|
||||
</div>
|
||||
|
||||
<div class="controls controls-dropdown">
|
||||
<label>{{i18n 'user.auto_track_topics'}}</label>
|
||||
{{combo-box valueAttribute="value" content=autoTrackDurations value=model.auto_track_topics_after_msecs}}
|
||||
{{combo-box valueAttribute="value" content=autoTrackDurations value=model.user_option.auto_track_topics_after_msecs}}
|
||||
</div>
|
||||
|
||||
{{preference-checkbox labelKey="user.external_links_in_new_tab" checked=model.user_option.external_links_in_new_tab}}
|
||||
|
|
|
@ -4,7 +4,7 @@ module Jobs
|
|||
|
||||
def execute(args)
|
||||
if user = User.find_by(id: args[:user_id])
|
||||
user.update_column(:last_redirected_to_top_at, args[:redirected_at])
|
||||
user.user_option.update_column(:last_redirected_to_top_at, args[:redirected_at])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -104,9 +104,9 @@ class TopicTrackingState
|
|||
|
||||
def self.treat_as_new_topic_clause
|
||||
User.where("GREATEST(CASE
|
||||
WHEN COALESCE(u.new_topic_duration_minutes, :default_duration) = :always THEN u.created_at
|
||||
WHEN COALESCE(u.new_topic_duration_minutes, :default_duration) = :last_visit THEN COALESCE(u.previous_visit_at,u.created_at)
|
||||
ELSE (:now::timestamp - INTERVAL '1 MINUTE' * COALESCE(u.new_topic_duration_minutes, :default_duration))
|
||||
WHEN COALESCE(uo.new_topic_duration_minutes, :default_duration) = :always THEN u.created_at
|
||||
WHEN COALESCE(uo.new_topic_duration_minutes, :default_duration) = :last_visit THEN COALESCE(u.previous_visit_at,u.created_at)
|
||||
ELSE (:now::timestamp - INTERVAL '1 MINUTE' * COALESCE(uo.new_topic_duration_minutes, :default_duration))
|
||||
END, us.new_since, :min_date)",
|
||||
now: DateTime.now,
|
||||
last_visit: User::NewTopicDuration::LAST_VISIT,
|
||||
|
@ -169,6 +169,7 @@ class TopicTrackingState
|
|||
FROM topics
|
||||
JOIN users u on u.id = :user_id
|
||||
JOIN user_stats AS us ON us.user_id = u.id
|
||||
JOIN user_options AS uo ON uo.user_id = u.id
|
||||
JOIN categories c ON c.id = topics.category_id
|
||||
LEFT JOIN topic_users tu ON tu.topic_id = topics.id AND tu.user_id = u.id
|
||||
WHERE u.id = :user_id AND
|
||||
|
|
|
@ -104,7 +104,7 @@ class TopicUser < ActiveRecord::Base
|
|||
|
||||
if rows == 0
|
||||
now = DateTime.now
|
||||
auto_track_after = User.select(:auto_track_topics_after_msecs).find_by(id: user_id).try(:auto_track_topics_after_msecs)
|
||||
auto_track_after = UserOption.where(user_id: user_id).pluck(:auto_track_topics_after_msecs).first
|
||||
auto_track_after ||= SiteSetting.default_other_auto_track_topics_after_msecs
|
||||
|
||||
if auto_track_after >= 0 && auto_track_after <= (attrs[:total_msecs_viewed].to_i || 0)
|
||||
|
@ -140,6 +140,31 @@ class TopicUser < ActiveRecord::Base
|
|||
|
||||
# Update the last read and the last seen post count, but only if it doesn't exist.
|
||||
# This would be a lot easier if psql supported some kind of upsert
|
||||
UPDATE_TOPIC_USER_SQL = "UPDATE topic_users
|
||||
SET
|
||||
last_read_post_number = GREATEST(:post_number, tu.last_read_post_number),
|
||||
highest_seen_post_number = t.highest_post_number,
|
||||
total_msecs_viewed = LEAST(tu.total_msecs_viewed + :msecs,86400000),
|
||||
notification_level =
|
||||
case when tu.notifications_reason_id is null and (tu.total_msecs_viewed + :msecs) >
|
||||
coalesce(uo.auto_track_topics_after_msecs,:threshold) and
|
||||
coalesce(uo.auto_track_topics_after_msecs, :threshold) >= 0 then
|
||||
:tracking
|
||||
else
|
||||
tu.notification_level
|
||||
end
|
||||
FROM topic_users tu
|
||||
join topics t on t.id = tu.topic_id
|
||||
join users u on u.id = :user_id
|
||||
join user_options uo on uo.user_id = :user_id
|
||||
WHERE
|
||||
tu.topic_id = topic_users.topic_id AND
|
||||
tu.user_id = topic_users.user_id AND
|
||||
tu.topic_id = :topic_id AND
|
||||
tu.user_id = :user_id
|
||||
RETURNING
|
||||
topic_users.notification_level, tu.notification_level old_level, tu.last_read_post_number
|
||||
"
|
||||
def update_last_read(user, topic_id, post_number, msecs, opts={})
|
||||
return if post_number.blank?
|
||||
msecs = 0 if msecs.to_i < 0
|
||||
|
@ -160,31 +185,7 @@ class TopicUser < ActiveRecord::Base
|
|||
# ... user visited the topic but did not read the posts
|
||||
#
|
||||
# 86400000 = 1 day
|
||||
rows = exec_sql("UPDATE topic_users
|
||||
SET
|
||||
last_read_post_number = GREATEST(:post_number, tu.last_read_post_number),
|
||||
highest_seen_post_number = t.highest_post_number,
|
||||
total_msecs_viewed = LEAST(tu.total_msecs_viewed + :msecs,86400000),
|
||||
notification_level =
|
||||
case when tu.notifications_reason_id is null and (tu.total_msecs_viewed + :msecs) >
|
||||
coalesce(u.auto_track_topics_after_msecs,:threshold) and
|
||||
coalesce(u.auto_track_topics_after_msecs, :threshold) >= 0 then
|
||||
:tracking
|
||||
else
|
||||
tu.notification_level
|
||||
end
|
||||
FROM topic_users tu
|
||||
join topics t on t.id = tu.topic_id
|
||||
join users u on u.id = :user_id
|
||||
WHERE
|
||||
tu.topic_id = topic_users.topic_id AND
|
||||
tu.user_id = topic_users.user_id AND
|
||||
tu.topic_id = :topic_id AND
|
||||
tu.user_id = :user_id
|
||||
RETURNING
|
||||
topic_users.notification_level, tu.notification_level old_level, tu.last_read_post_number
|
||||
",
|
||||
args).values
|
||||
rows = exec_sql(UPDATE_TOPIC_USER_SQL,args).values
|
||||
|
||||
if rows.length == 1
|
||||
before = rows[0][1].to_i
|
||||
|
@ -206,7 +207,7 @@ class TopicUser < ActiveRecord::Base
|
|||
if rows.length == 0
|
||||
# The user read at least one post in a topic that they haven't viewed before.
|
||||
args[:new_status] = notification_levels[:regular]
|
||||
if (user.auto_track_topics_after_msecs || SiteSetting.default_other_auto_track_topics_after_msecs) == 0
|
||||
if (user.user_option.auto_track_topics_after_msecs || SiteSetting.default_other_auto_track_topics_after_msecs) == 0
|
||||
args[:new_status] = notification_levels[:tracking]
|
||||
end
|
||||
TopicTrackingState.publish_read(topic_id, post_number, user.id, args[:new_status])
|
||||
|
|
|
@ -77,8 +77,6 @@ class User < ActiveRecord::Base
|
|||
|
||||
after_initialize :add_trust_level
|
||||
|
||||
before_create :set_default_user_preferences
|
||||
|
||||
after_create :create_email_token
|
||||
after_create :create_user_stat
|
||||
after_create :create_user_option
|
||||
|
@ -90,7 +88,6 @@ class User < ActiveRecord::Base
|
|||
before_save :update_username_lower
|
||||
before_save :ensure_password_is_hashed
|
||||
|
||||
after_save :update_tracked_topics
|
||||
after_save :clear_global_notice_if_needed
|
||||
after_save :refresh_avatar
|
||||
after_save :badge_grant
|
||||
|
@ -626,20 +623,6 @@ class User < ActiveRecord::Base
|
|||
Promotion.new(self).change_trust_level!(level, opts)
|
||||
end
|
||||
|
||||
def treat_as_new_topic_start_date
|
||||
duration = new_topic_duration_minutes || SiteSetting.default_other_new_topic_duration_minutes.to_i
|
||||
times = [case duration
|
||||
when User::NewTopicDuration::ALWAYS
|
||||
created_at
|
||||
when User::NewTopicDuration::LAST_VISIT
|
||||
previous_visit_at || user_stat.new_since
|
||||
else
|
||||
duration.minutes.ago
|
||||
end, user_stat.new_since, Time.at(SiteSetting.min_new_topics_time).to_datetime]
|
||||
|
||||
times.max
|
||||
end
|
||||
|
||||
def readable_name
|
||||
return "#{name} (#{username})" if name.present? && name != username
|
||||
username
|
||||
|
@ -730,51 +713,6 @@ class User < ActiveRecord::Base
|
|||
.exists?
|
||||
end
|
||||
|
||||
def should_be_redirected_to_top
|
||||
redirected_to_top.present?
|
||||
end
|
||||
|
||||
def redirected_to_top
|
||||
# redirect is enabled
|
||||
return unless SiteSetting.redirect_users_to_top_page
|
||||
# top must be in the top_menu
|
||||
return unless SiteSetting.top_menu =~ /(^|\|)top(\||$)/i
|
||||
# not enough topics
|
||||
return unless period = SiteSetting.min_redirected_to_top_period
|
||||
|
||||
if !seen_before? || (trust_level == 0 && !redirected_to_top_yet?)
|
||||
update_last_redirected_to_top!
|
||||
return {
|
||||
reason: I18n.t('redirected_to_top_reasons.new_user'),
|
||||
period: period
|
||||
}
|
||||
elsif last_seen_at < 1.month.ago
|
||||
update_last_redirected_to_top!
|
||||
return {
|
||||
reason: I18n.t('redirected_to_top_reasons.not_seen_in_a_month'),
|
||||
period: period
|
||||
}
|
||||
end
|
||||
|
||||
# don't redirect to top
|
||||
nil
|
||||
end
|
||||
|
||||
def redirected_to_top_yet?
|
||||
last_redirected_to_top_at.present?
|
||||
end
|
||||
|
||||
def update_last_redirected_to_top!
|
||||
key = "user:#{id}:update_last_redirected_to_top"
|
||||
delay = SiteSetting.active_user_rate_limit_secs
|
||||
|
||||
# only update last_redirected_to_top_at once every minute
|
||||
return unless $redis.setnx(key, "1")
|
||||
$redis.expire(key, delay)
|
||||
|
||||
# delay the update
|
||||
Jobs.enqueue_in(delay / 2, :update_top_redirection, user_id: self.id, redirected_at: Time.zone.now)
|
||||
end
|
||||
|
||||
def refresh_avatar
|
||||
return if @import_mode
|
||||
|
@ -880,11 +818,6 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def update_tracked_topics
|
||||
return unless auto_track_topics_after_msecs_changed?
|
||||
TrackedTopicsUpdater.new(id, auto_track_topics_after_msecs).call
|
||||
end
|
||||
|
||||
def clear_global_notice_if_needed
|
||||
if admin && SiteSetting.has_login_hint
|
||||
SiteSetting.has_login_hint = false
|
||||
|
@ -970,14 +903,6 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def set_default_user_preferences
|
||||
set_default_other_new_topic_duration_minutes
|
||||
set_default_other_auto_track_topics_after_msecs
|
||||
|
||||
# needed, otherwise the callback chain is broken...
|
||||
true
|
||||
end
|
||||
|
||||
def set_default_categories_preferences
|
||||
values = []
|
||||
|
||||
|
@ -1025,12 +950,6 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
|
||||
|
||||
%w{new_topic_duration_minutes auto_track_topics_after_msecs}.each do |s|
|
||||
define_method("set_default_other_#{s}") do
|
||||
self.send("#{s}=", SiteSetting.send("default_other_#{s}").to_i) if has_attribute?(s)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
|
@ -1054,7 +973,6 @@ end
|
|||
# admin :boolean default(FALSE), not null
|
||||
# last_emailed_at :datetime
|
||||
# trust_level :integer not null
|
||||
# email_private_messages :boolean default(TRUE)
|
||||
# approved :boolean default(FALSE), not null
|
||||
# approved_by_id :integer
|
||||
# approved_at :datetime
|
||||
|
@ -1062,11 +980,9 @@ end
|
|||
# suspended_at :datetime
|
||||
# suspended_till :datetime
|
||||
# date_of_birth :date
|
||||
# auto_track_topics_after_msecs :integer
|
||||
# views :integer default(0), not null
|
||||
# flag_level :integer default(0), not null
|
||||
# ip_address :inet
|
||||
# new_topic_duration_minutes :integer
|
||||
# moderator :boolean default(FALSE)
|
||||
# blocked :boolean default(FALSE)
|
||||
# title :string(255)
|
||||
|
@ -1074,7 +990,6 @@ end
|
|||
# primary_group_id :integer
|
||||
# locale :string(10)
|
||||
# registration_ip_address :inet
|
||||
# last_redirected_to_top_at :datetime
|
||||
# trust_level_locked :boolean default(FALSE), not null
|
||||
# staged :boolean default(FALSE), not null
|
||||
#
|
||||
|
|
|
@ -3,6 +3,8 @@ class UserOption < ActiveRecord::Base
|
|||
belongs_to :user
|
||||
before_create :set_defaults
|
||||
|
||||
after_save :update_tracked_topics
|
||||
|
||||
def set_defaults
|
||||
self.email_always = SiteSetting.default_email_always
|
||||
self.mailing_list_mode = SiteSetting.default_email_mailing_list_mode
|
||||
|
@ -16,6 +18,9 @@ class UserOption < ActiveRecord::Base
|
|||
self.disable_jump_reply = SiteSetting.default_other_disable_jump_reply
|
||||
self.edit_history_public = SiteSetting.default_other_edit_history_public
|
||||
|
||||
self.new_topic_duration_minutes = SiteSetting.default_other_new_topic_duration_minutes
|
||||
self.auto_track_topics_after_msecs = SiteSetting.default_other_auto_track_topics_after_msecs
|
||||
|
||||
|
||||
if SiteSetting.default_email_digest_frequency.to_i <= 0
|
||||
self.email_digests = false
|
||||
|
@ -26,4 +31,70 @@ class UserOption < ActiveRecord::Base
|
|||
|
||||
true
|
||||
end
|
||||
|
||||
def update_tracked_topics
|
||||
return unless auto_track_topics_after_msecs_changed?
|
||||
TrackedTopicsUpdater.new(id, auto_track_topics_after_msecs).call
|
||||
end
|
||||
|
||||
def redirected_to_top_yet?
|
||||
last_redirected_to_top_at.present?
|
||||
end
|
||||
|
||||
def update_last_redirected_to_top!
|
||||
key = "user:#{id}:update_last_redirected_to_top"
|
||||
delay = SiteSetting.active_user_rate_limit_secs
|
||||
|
||||
# only update last_redirected_to_top_at once every minute
|
||||
return unless $redis.setnx(key, "1")
|
||||
$redis.expire(key, delay)
|
||||
|
||||
# delay the update
|
||||
Jobs.enqueue_in(delay / 2, :update_top_redirection, user_id: self.id, redirected_at: Time.zone.now)
|
||||
end
|
||||
|
||||
def should_be_redirected_to_top
|
||||
redirected_to_top.present?
|
||||
end
|
||||
|
||||
def redirected_to_top
|
||||
# redirect is enabled
|
||||
return unless SiteSetting.redirect_users_to_top_page
|
||||
# top must be in the top_menu
|
||||
return unless SiteSetting.top_menu =~ /(^|\|)top(\||$)/i
|
||||
# not enough topics
|
||||
return unless period = SiteSetting.min_redirected_to_top_period
|
||||
|
||||
if !user.seen_before? || (user.trust_level == 0 && !redirected_to_top_yet?)
|
||||
update_last_redirected_to_top!
|
||||
return {
|
||||
reason: I18n.t('redirected_to_top_reasons.new_user'),
|
||||
period: period
|
||||
}
|
||||
elsif user.last_seen_at < 1.month.ago
|
||||
update_last_redirected_to_top!
|
||||
return {
|
||||
reason: I18n.t('redirected_to_top_reasons.not_seen_in_a_month'),
|
||||
period: period
|
||||
}
|
||||
end
|
||||
|
||||
# don't redirect to top
|
||||
nil
|
||||
end
|
||||
|
||||
def treat_as_new_topic_start_date
|
||||
duration = new_topic_duration_minutes || SiteSetting.default_other_new_topic_duration_minutes.to_i
|
||||
times = [case duration
|
||||
when User::NewTopicDuration::ALWAYS
|
||||
user.created_at
|
||||
when User::NewTopicDuration::LAST_VISIT
|
||||
user.previous_visit_at || user.user_stat.new_since
|
||||
else
|
||||
duration.minutes.ago
|
||||
end, user.user_stat.new_since, Time.at(SiteSetting.min_new_topics_time).to_datetime]
|
||||
|
||||
times.max
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -70,6 +70,14 @@ class CurrentUserSerializer < BasicUserSerializer
|
|||
object.user_option.automatically_unpin_topics
|
||||
end
|
||||
|
||||
def should_be_redirected_to_top
|
||||
object.user_option.should_be_redirected_to_top
|
||||
end
|
||||
|
||||
def redirected_to_top
|
||||
object.user_option.redirected_to_top
|
||||
end
|
||||
|
||||
def site_flagged_posts_count
|
||||
PostAction.flagged_posts_count
|
||||
end
|
||||
|
@ -103,7 +111,7 @@ class CurrentUserSerializer < BasicUserSerializer
|
|||
end
|
||||
|
||||
def include_redirected_to_top?
|
||||
object.redirected_to_top.present?
|
||||
object.user_option.redirected_to_top.present?
|
||||
end
|
||||
|
||||
def custom_fields
|
||||
|
|
|
@ -45,7 +45,7 @@ class ListableTopicSerializer < BasicTopicSerializer
|
|||
def seen
|
||||
return true if !scope || !scope.user
|
||||
return true if object.user_data && !object.user_data.last_read_post_number.nil?
|
||||
return true if object.created_at < scope.user.treat_as_new_topic_start_date
|
||||
return true if object.created_at < scope.user.user_option.treat_as_new_topic_start_date
|
||||
false
|
||||
end
|
||||
|
||||
|
|
|
@ -11,10 +11,21 @@ class UserOptionSerializer < ApplicationSerializer
|
|||
:disable_jump_reply,
|
||||
:digest_after_days,
|
||||
:automatically_unpin_topics,
|
||||
:edit_history_public
|
||||
:edit_history_public,
|
||||
:auto_track_topics_after_msecs,
|
||||
:new_topic_duration_minutes
|
||||
|
||||
|
||||
def include_edit_history_public?
|
||||
!SiteSetting.edit_history_visible_to_public
|
||||
end
|
||||
|
||||
def auto_track_topics_after_msecs
|
||||
object.auto_track_topics_after_msecs || SiteSetting.default_other_auto_track_topics_after_msecs
|
||||
end
|
||||
|
||||
def new_topic_duration_minutes
|
||||
object.new_topic_duration_minutes || SiteSetting.default_other_new_topic_duration_minutes
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -82,8 +82,6 @@ class UserSerializer < BasicUserSerializer
|
|||
:can_delete_all_posts
|
||||
|
||||
private_attributes :locale,
|
||||
:auto_track_topics_after_msecs,
|
||||
:new_topic_duration_minutes,
|
||||
:muted_category_ids,
|
||||
:tracked_category_ids,
|
||||
:watched_category_ids,
|
||||
|
@ -253,14 +251,6 @@ class UserSerializer < BasicUserSerializer
|
|||
### PRIVATE ATTRIBUTES
|
||||
###
|
||||
|
||||
def auto_track_topics_after_msecs
|
||||
object.auto_track_topics_after_msecs || SiteSetting.default_other_auto_track_topics_after_msecs
|
||||
end
|
||||
|
||||
def new_topic_duration_minutes
|
||||
object.new_topic_duration_minutes || SiteSetting.default_other_new_topic_duration_minutes
|
||||
end
|
||||
|
||||
def muted_category_ids
|
||||
CategoryUser.lookup(object, :muted).pluck(:category_id)
|
||||
end
|
||||
|
|
|
@ -18,7 +18,9 @@ class UserUpdater
|
|||
:disable_jump_reply,
|
||||
:edit_history_public,
|
||||
:automatically_unpin_topics,
|
||||
:digest_after_days
|
||||
:digest_after_days,
|
||||
:new_topic_duration_minutes,
|
||||
:auto_track_topics_after_msecs
|
||||
]
|
||||
|
||||
def initialize(actor, user)
|
||||
|
@ -38,14 +40,6 @@ class UserUpdater
|
|||
user.name = attributes.fetch(:name) { user.name }
|
||||
user.locale = attributes.fetch(:locale) { user.locale }
|
||||
|
||||
if attributes[:auto_track_topics_after_msecs]
|
||||
user.auto_track_topics_after_msecs = attributes[:auto_track_topics_after_msecs].to_i
|
||||
end
|
||||
|
||||
if attributes[:new_topic_duration_minutes]
|
||||
user.new_topic_duration_minutes = attributes[:new_topic_duration_minutes].to_i
|
||||
end
|
||||
|
||||
if guardian.can_grant_title?(user)
|
||||
user.title = attributes.fetch(:title) { user.title }
|
||||
end
|
||||
|
|
|
@ -32,9 +32,9 @@ 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'
|
||||
WHERE table_name = 'users' AND column_name = 'last_redirected_to_top_at'
|
||||
) AND
|
||||
name = 'AllowDefaultsOnUsersTable' AND
|
||||
name = 'MoveTrackingOptionsToUserOptions' AND
|
||||
created_at < (current_timestamp at time zone 'UTC' - interval '#{duration} minutes')
|
||||
").to_a.length > 0
|
||||
|
||||
|
@ -54,7 +54,10 @@ if User.exec_sql("SELECT 1 FROM schema_migration_details
|
|||
edit_history_public
|
||||
automatically_unpin_topics
|
||||
digest_after_days
|
||||
].each do |column|
|
||||
auto_track_topics_after_msecs
|
||||
new_topic_duration_minutes
|
||||
last_redirected_to_top_at
|
||||
].each do |column|
|
||||
User.exec_sql("ALTER TABLE users DROP column IF EXISTS #{column}")
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
class MoveTrackingOptionsToUserOptions < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :user_options, :auto_track_topics_after_msecs, :integer
|
||||
add_column :user_options, :new_topic_duration_minutes, :integer
|
||||
add_column :user_options, :last_redirected_to_top_at, :datetime
|
||||
|
||||
execute <<SQL
|
||||
UPDATE user_options
|
||||
SET auto_track_topics_after_msecs = users.auto_track_topics_after_msecs,
|
||||
new_topic_duration_minutes = users.new_topic_duration_minutes,
|
||||
last_redirected_to_top_at = users.last_redirected_to_top_at
|
||||
FROM users
|
||||
WHERE users.id = user_options.user_id
|
||||
SQL
|
||||
end
|
||||
end
|
|
@ -331,7 +331,7 @@ class TopicQuery
|
|||
def new_results(options={})
|
||||
# TODO does this make sense or should it be ordered on created_at
|
||||
# it is ordering on bumped_at now
|
||||
result = TopicQuery.new_filter(default_results(options.reverse_merge(:unordered => true)), @user.treat_as_new_topic_start_date)
|
||||
result = TopicQuery.new_filter(default_results(options.reverse_merge(:unordered => true)), @user.user_option.treat_as_new_topic_start_date)
|
||||
result = remove_muted_topics(result, @user)
|
||||
result = remove_muted_categories(result, @user, exclude: options[:category])
|
||||
|
||||
|
|
|
@ -294,8 +294,8 @@ describe TopicQuery do
|
|||
|
||||
context 'user with auto_track_topics list_unread' do
|
||||
before do
|
||||
user.auto_track_topics_after_msecs = 0
|
||||
user.save
|
||||
user.user_option.auto_track_topics_after_msecs = 0
|
||||
user.user_option.save
|
||||
end
|
||||
|
||||
it 'only contains the partially read topic' do
|
||||
|
@ -360,8 +360,8 @@ describe TopicQuery do
|
|||
|
||||
expect(topic_query.list_new.topics).to eq([new_topic])
|
||||
|
||||
user.new_topic_duration_minutes = 5
|
||||
user.save
|
||||
user.user_option.new_topic_duration_minutes = 5
|
||||
user.user_option.save
|
||||
new_topic.created_at = 10.minutes.ago
|
||||
new_topic.save
|
||||
expect(topic_query.list_new.topics).to eq([])
|
||||
|
@ -561,8 +561,8 @@ describe TopicQuery do
|
|||
let!(:fully_read_archived) { Fabricate(:post, user: creator).topic }
|
||||
|
||||
before do
|
||||
user.auto_track_topics_after_msecs = 0
|
||||
user.save
|
||||
user.user_option.auto_track_topics_after_msecs = 0
|
||||
user.user_option.save
|
||||
TopicUser.update_last_read(user, partially_read.id, 0, 0)
|
||||
TopicUser.update_last_read(user, fully_read.id, 1, 0)
|
||||
TopicUser.update_last_read(user, fully_read_closed.id, 1, 0)
|
||||
|
|
2
spec/fabricators/user_option_fabricator.rb
Normal file
2
spec/fabricators/user_option_fabricator.rb
Normal file
|
@ -0,0 +1,2 @@
|
|||
Fabricator(:user_option) do
|
||||
end
|
|
@ -48,7 +48,12 @@ describe TopicUser do
|
|||
let(:topic_creator_user) { TopicUser.get(topic, topic.user) }
|
||||
|
||||
let(:post) { Fabricate(:post, topic: topic, user: user) }
|
||||
let(:new_user) { Fabricate(:user, auto_track_topics_after_msecs: 1000) }
|
||||
let(:new_user) {
|
||||
u = Fabricate(:user)
|
||||
u.user_option.update_columns(auto_track_topics_after_msecs: 1000)
|
||||
u
|
||||
}
|
||||
|
||||
let(:topic_new_user) { TopicUser.get(topic, new_user)}
|
||||
let(:yesterday) { DateTime.now.yesterday }
|
||||
|
||||
|
@ -68,15 +73,15 @@ describe TopicUser do
|
|||
describe 'notifications' do
|
||||
|
||||
it 'should be set to tracking if auto_track_topics is enabled' do
|
||||
user.update_column(:auto_track_topics_after_msecs, 0)
|
||||
user.user_option.update_column(:auto_track_topics_after_msecs, 0)
|
||||
ensure_topic_user
|
||||
expect(TopicUser.get(topic, user).notification_level).to eq(TopicUser.notification_levels[:tracking])
|
||||
end
|
||||
|
||||
it 'should reset regular topics to tracking topics if auto track is changed' do
|
||||
ensure_topic_user
|
||||
user.auto_track_topics_after_msecs = 0
|
||||
user.save
|
||||
user.user_option.auto_track_topics_after_msecs = 0
|
||||
user.user_option.save
|
||||
expect(topic_user.notification_level).to eq(TopicUser.notification_levels[:tracking])
|
||||
end
|
||||
|
||||
|
|
92
spec/models/user_option_spec.rb
Normal file
92
spec/models/user_option_spec.rb
Normal file
|
@ -0,0 +1,92 @@
|
|||
require 'rails_helper'
|
||||
require_dependency 'user_option'
|
||||
|
||||
describe UserOption do
|
||||
|
||||
describe "should_be_redirected_to_top" do
|
||||
let!(:user) { Fabricate(:user) }
|
||||
|
||||
it "should be redirected to top when there is a reason to" do
|
||||
user.user_option.expects(:redirected_to_top).returns({ reason: "42" })
|
||||
expect(user.user_option.should_be_redirected_to_top).to eq(true)
|
||||
end
|
||||
|
||||
it "should not be redirected to top when there is no reason to" do
|
||||
user.user_option.expects(:redirected_to_top).returns(nil)
|
||||
expect(user.user_option.should_be_redirected_to_top).to eq(false)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe ".redirected_to_top" do
|
||||
let!(:user) { Fabricate(:user) }
|
||||
|
||||
it "should have no reason when `SiteSetting.redirect_users_to_top_page` is disabled" do
|
||||
SiteSetting.expects(:redirect_users_to_top_page).returns(false)
|
||||
expect(user.user_option.redirected_to_top).to eq(nil)
|
||||
end
|
||||
|
||||
context "when `SiteSetting.redirect_users_to_top_page` is enabled" do
|
||||
before { SiteSetting.expects(:redirect_users_to_top_page).returns(true) }
|
||||
|
||||
it "should have no reason when top is not in the `SiteSetting.top_menu`" do
|
||||
SiteSetting.expects(:top_menu).returns("latest")
|
||||
expect(user.user_option.redirected_to_top).to eq(nil)
|
||||
end
|
||||
|
||||
context "and when top is in the `SiteSetting.top_menu`" do
|
||||
before { SiteSetting.expects(:top_menu).returns("latest|top") }
|
||||
|
||||
it "should have no reason when there are not enough topics" do
|
||||
SiteSetting.expects(:min_redirected_to_top_period).returns(nil)
|
||||
expect(user.user_option.redirected_to_top).to eq(nil)
|
||||
end
|
||||
|
||||
context "and there are enough topics" do
|
||||
|
||||
before { SiteSetting.expects(:min_redirected_to_top_period).returns(:monthly) }
|
||||
|
||||
describe "a new user" do
|
||||
before do
|
||||
user.stubs(:trust_level).returns(0)
|
||||
user.stubs(:last_seen_at).returns(5.minutes.ago)
|
||||
end
|
||||
|
||||
it "should have a reason for the first visit" do
|
||||
expect(user.user_option.redirected_to_top).to eq({
|
||||
reason: I18n.t('redirected_to_top_reasons.new_user'),
|
||||
period: :monthly
|
||||
})
|
||||
end
|
||||
|
||||
it "should not have a reason for next visits" do
|
||||
user.user_option.expects(:last_redirected_to_top_at).returns(10.minutes.ago)
|
||||
user.user_option.expects(:update_last_redirected_to_top!).never
|
||||
|
||||
expect(user.user_option.redirected_to_top).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe "an older user" do
|
||||
before { user.stubs(:trust_level).returns(1) }
|
||||
|
||||
it "should have a reason when the user hasn't been seen in a month" do
|
||||
user.last_seen_at = 2.months.ago
|
||||
user.user_option.expects(:update_last_redirected_to_top!).once
|
||||
|
||||
expect(user.user_option.redirected_to_top).to eq({
|
||||
reason: I18n.t('redirected_to_top_reasons.not_seen_in_a_month'),
|
||||
period: :monthly
|
||||
})
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -986,95 +986,6 @@ describe User do
|
|||
end
|
||||
end
|
||||
|
||||
describe "should_be_redirected_to_top" do
|
||||
let!(:user) { Fabricate(:user) }
|
||||
|
||||
it "should be redirected to top when there is a reason to" do
|
||||
user.expects(:redirected_to_top).returns({ reason: "42" })
|
||||
expect(user.should_be_redirected_to_top).to eq(true)
|
||||
end
|
||||
|
||||
it "should not be redirected to top when there is no reason to" do
|
||||
user.expects(:redirected_to_top).returns(nil)
|
||||
expect(user.should_be_redirected_to_top).to eq(false)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe ".redirected_to_top" do
|
||||
let!(:user) { Fabricate(:user) }
|
||||
|
||||
it "should have no reason when `SiteSetting.redirect_users_to_top_page` is disabled" do
|
||||
SiteSetting.expects(:redirect_users_to_top_page).returns(false)
|
||||
expect(user.redirected_to_top).to eq(nil)
|
||||
end
|
||||
|
||||
context "when `SiteSetting.redirect_users_to_top_page` is enabled" do
|
||||
before { SiteSetting.expects(:redirect_users_to_top_page).returns(true) }
|
||||
|
||||
it "should have no reason when top is not in the `SiteSetting.top_menu`" do
|
||||
SiteSetting.expects(:top_menu).returns("latest")
|
||||
expect(user.redirected_to_top).to eq(nil)
|
||||
end
|
||||
|
||||
context "and when top is in the `SiteSetting.top_menu`" do
|
||||
before { SiteSetting.expects(:top_menu).returns("latest|top") }
|
||||
|
||||
it "should have no reason when there are not enough topics" do
|
||||
SiteSetting.expects(:min_redirected_to_top_period).returns(nil)
|
||||
expect(user.redirected_to_top).to eq(nil)
|
||||
end
|
||||
|
||||
context "and there are enough topics" do
|
||||
|
||||
before { SiteSetting.expects(:min_redirected_to_top_period).returns(:monthly) }
|
||||
|
||||
describe "a new user" do
|
||||
before do
|
||||
user.stubs(:trust_level).returns(0)
|
||||
user.stubs(:last_seen_at).returns(5.minutes.ago)
|
||||
end
|
||||
|
||||
it "should have a reason for the first visit" do
|
||||
user.expects(:last_redirected_to_top_at).returns(nil)
|
||||
user.expects(:update_last_redirected_to_top!).once
|
||||
|
||||
expect(user.redirected_to_top).to eq({
|
||||
reason: I18n.t('redirected_to_top_reasons.new_user'),
|
||||
period: :monthly
|
||||
})
|
||||
end
|
||||
|
||||
it "should not have a reason for next visits" do
|
||||
user.expects(:last_redirected_to_top_at).returns(10.minutes.ago)
|
||||
user.expects(:update_last_redirected_to_top!).never
|
||||
|
||||
expect(user.redirected_to_top).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe "an older user" do
|
||||
before { user.stubs(:trust_level).returns(1) }
|
||||
|
||||
it "should have a reason when the user hasn't been seen in a month" do
|
||||
user.last_seen_at = 2.months.ago
|
||||
user.expects(:update_last_redirected_to_top!).once
|
||||
|
||||
expect(user.redirected_to_top).to eq({
|
||||
reason: I18n.t('redirected_to_top_reasons.not_seen_in_a_month'),
|
||||
period: :monthly
|
||||
})
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "automatic avatar creation" do
|
||||
it "sets a system avatar for new users" do
|
||||
|
@ -1281,9 +1192,8 @@ describe User do
|
|||
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(options.new_topic_duration_minutes).to eq(-1)
|
||||
expect(options.auto_track_topics_after_msecs).to eq(0)
|
||||
|
||||
expect(CategoryUser.lookup(user, :watching).pluck(:category_id)).to eq([1])
|
||||
expect(CategoryUser.lookup(user, :tracking).pluck(:category_id)).to eq([2])
|
||||
|
|
|
@ -19,6 +19,8 @@ describe UserSerializer do
|
|||
it "serializes options correctly" do
|
||||
# so we serialize more stuff
|
||||
SiteSetting.edit_history_visible_to_public = false
|
||||
SiteSetting.default_other_auto_track_topics_after_msecs = 0
|
||||
SiteSetting.default_other_new_topic_duration_minutes = 60*24
|
||||
|
||||
user = Fabricate.build(:user,
|
||||
user_profile: Fabricate.build(:user_profile),
|
||||
|
@ -29,6 +31,8 @@ describe UserSerializer do
|
|||
json = UserSerializer.new(user, scope: Guardian.new(user), root: false).as_json
|
||||
|
||||
expect(json[:user_option][:edit_history_public]).to eq(true)
|
||||
expect(json[:user_option][:new_topic_duration_minutes]).to eq(60*24)
|
||||
expect(json[:user_option][:auto_track_topics_after_msecs]).to eq(0)
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -46,13 +46,18 @@ describe UserUpdater do
|
|||
updater.update(bio_raw: 'my new bio',
|
||||
email_always: 'true',
|
||||
mailing_list_mode: true,
|
||||
digest_after_days: "8")
|
||||
digest_after_days: "8",
|
||||
new_topic_duration_minutes: 100,
|
||||
auto_track_topics_after_msecs: 101
|
||||
)
|
||||
user.reload
|
||||
|
||||
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
|
||||
expect(user.user_option.new_topic_duration_minutes).to eq 100
|
||||
expect(user.user_option.auto_track_topics_after_msecs).to eq 101
|
||||
end
|
||||
|
||||
context 'when update succeeds' do
|
||||
|
|
Loading…
Reference in a new issue