mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FEATURE: navigate to first post and auto bump category settings
### navigate_to_first_post_after_read setting for categories When enabled on categories logged on users will return to OP after reading the entire category. (useful for documentation categories) ### num_auto_bump_daily Set a number of topics that will automatically bump daily on a category. - Every 15 minutes we will check if any category has this setting - Categories with the setting are shuffled - We exclude pinned, closed, category description and archived topics - Maximum of 1 topic for the list of categories is bumped till limit reached per category - We always try to bump oldest first - Limit is elastic using a RateLimiter that ensures that we only bump N per day Also some minor organisation on category settings Froze strings on category.rb
This commit is contained in:
parent
259d16a781
commit
ac0053f491
@ -115,7 +115,10 @@ const Category = RestModel.extend({
|
|||||||
default_view: this.get("default_view"),
|
default_view: this.get("default_view"),
|
||||||
subcategory_list_style: this.get("subcategory_list_style"),
|
subcategory_list_style: this.get("subcategory_list_style"),
|
||||||
default_top_period: this.get("default_top_period"),
|
default_top_period: this.get("default_top_period"),
|
||||||
minimum_required_tags: this.get("minimum_required_tags")
|
minimum_required_tags: this.get("minimum_required_tags"),
|
||||||
|
navigate_to_first_post_after_read: this.get(
|
||||||
|
"navigate_to_first_post_after_read"
|
||||||
|
)
|
||||||
},
|
},
|
||||||
type: id ? "PUT" : "POST"
|
type: id ? "PUT" : "POST"
|
||||||
});
|
});
|
||||||
|
@ -212,11 +212,18 @@ const Topic = RestModel.extend({
|
|||||||
}.property("url", "last_read_post_number"),
|
}.property("url", "last_read_post_number"),
|
||||||
|
|
||||||
lastUnreadUrl: function() {
|
lastUnreadUrl: function() {
|
||||||
const postNumber = Math.min(
|
const highest = this.get("highest_post_number");
|
||||||
this.get("last_read_post_number") + 1,
|
const lastRead = this.get("last_read_post_number");
|
||||||
this.get("highest_post_number")
|
|
||||||
);
|
if (highest <= lastRead) {
|
||||||
return this.urlForPostNumber(postNumber);
|
if (this.get("category.navigate_to_first_post_after_read")) {
|
||||||
|
return this.urlForPostNumber(1);
|
||||||
|
} else {
|
||||||
|
return this.urlForPostNumber(lastRead + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return this.urlForPostNumber(lastRead + 1);
|
||||||
|
}
|
||||||
}.property("url", "last_read_post_number", "highest_post_number"),
|
}.property("url", "last_read_post_number", "highest_post_number"),
|
||||||
|
|
||||||
lastPostUrl: function() {
|
lastPostUrl: function() {
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
{{#if showPositionInput}}
|
||||||
|
<section class='field position-fields'>
|
||||||
|
<label>
|
||||||
|
{{i18n 'category.position'}}
|
||||||
|
{{text-field value=category.position class="position-input" type="number"}}
|
||||||
|
</label>
|
||||||
|
</section>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#unless showPositionInput}}
|
||||||
|
<section class='field'>
|
||||||
|
{{i18n 'category.position_disabled'}}
|
||||||
|
<a href="/admin/site_settings/category/basic">{{i18n 'category.position_disabled_click'}}</a>
|
||||||
|
</section>
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
<section class='field'>
|
<section class='field'>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label>
|
<label>
|
||||||
@ -74,14 +90,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="field num-featured-topics-fields">
|
<section class="field">
|
||||||
<label>
|
<label>
|
||||||
{{#if category.parent_category_id}}
|
{{#if category.parent_category_id}}
|
||||||
{{i18n "category.subcategory_num_featured_topics"}}
|
{{i18n "category.subcategory_num_featured_topics"}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{i18n "category.num_featured_topics"}}
|
{{i18n "category.num_featured_topics"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{text-field value=category.num_featured_topics}}
|
{{text-field value=category.num_featured_topics type="number"}}
|
||||||
|
</label>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="field">
|
||||||
|
<label>
|
||||||
|
{{i18n "category.num_auto_bump_daily"}}
|
||||||
|
{{text-field value=category.custom_fields.num_auto_bump_daily type="number"}}
|
||||||
</label>
|
</label>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@ -92,6 +115,13 @@
|
|||||||
</label>
|
</label>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="field">
|
||||||
|
<label>
|
||||||
|
{{input type="checkbox" checked=category.navigate_to_first_post_after_read}}
|
||||||
|
{{i18n "category.navigate_to_first_post_after_read"}}
|
||||||
|
</label>
|
||||||
|
</section>
|
||||||
|
|
||||||
{{#if siteSettings.topic_featured_link_enabled}}
|
{{#if siteSettings.topic_featured_link_enabled}}
|
||||||
<section class='field'>
|
<section class='field'>
|
||||||
<div class="allowed-topic-featured-link-category">
|
<div class="allowed-topic-featured-link-category">
|
||||||
@ -129,29 +159,6 @@
|
|||||||
{{plugin-outlet name="category-email-in" args=(hash category=category)}}
|
{{plugin-outlet name="category-email-in" args=(hash category=category)}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if showPositionInput}}
|
|
||||||
<section class='field position-fields'>
|
|
||||||
<label>
|
|
||||||
{{i18n 'category.position'}}
|
|
||||||
{{text-field value=category.position class="position-input"}}
|
|
||||||
</label>
|
|
||||||
</section>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#unless emailInEnabled}}
|
|
||||||
<section class='field'>
|
|
||||||
{{i18n 'category.email_in_disabled'}}
|
|
||||||
<a href="/admin/site_settings/category/email">{{i18n 'category.email_in_disabled_click'}}</a>
|
|
||||||
</section>
|
|
||||||
{{/unless}}
|
|
||||||
|
|
||||||
{{#unless showPositionInput}}
|
|
||||||
<section class='field'>
|
|
||||||
{{i18n 'category.position_disabled'}}
|
|
||||||
<a href="/admin/site_settings/category/basic">{{i18n 'category.position_disabled_click'}}</a>
|
|
||||||
</section>
|
|
||||||
{{/unless}}
|
|
||||||
|
|
||||||
{{#if siteSettings.tagging_enabled}}
|
{{#if siteSettings.tagging_enabled}}
|
||||||
<section class='field minimum-required-tags'>
|
<section class='field minimum-required-tags'>
|
||||||
<label>
|
<label>
|
||||||
@ -176,3 +183,10 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{{plugin-outlet name="category-custom-settings" args=(hash category=category)}}
|
{{plugin-outlet name="category-custom-settings" args=(hash category=category)}}
|
||||||
|
|
||||||
|
{{#unless emailInEnabled}}
|
||||||
|
<section class='field'>
|
||||||
|
{{i18n 'category.email_in_disabled'}}
|
||||||
|
<a href="/admin/site_settings/category/email">{{i18n 'category.email_in_disabled_click'}}</a>
|
||||||
|
</section>
|
||||||
|
{{/unless}}
|
||||||
|
@ -369,12 +369,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.edit-category-modal {
|
.edit-category-modal {
|
||||||
.future-date-input,
|
input[type="number"] {
|
||||||
.num-featured-topics-fields,
|
width: 50px;
|
||||||
.position-fields {
|
|
||||||
input[type="text"] {
|
|
||||||
width: 50px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.subcategory-list-style-field {
|
.subcategory-list-style-field {
|
||||||
|
@ -283,6 +283,7 @@ class CategoriesController < ApplicationController
|
|||||||
:subcategory_list_style,
|
:subcategory_list_style,
|
||||||
:default_top_period,
|
:default_top_period,
|
||||||
:minimum_required_tags,
|
:minimum_required_tags,
|
||||||
|
:navigate_to_first_post_after_read,
|
||||||
custom_fields: [params[:custom_fields].try(:keys)],
|
custom_fields: [params[:custom_fields].try(:keys)],
|
||||||
permissions: [*p.try(:keys)],
|
permissions: [*p.try(:keys)],
|
||||||
allowed_tags: [],
|
allowed_tags: [],
|
||||||
|
@ -49,6 +49,14 @@ module Jobs
|
|||||||
SiteSetting.min_new_topics_time = last_new_topic.created_at.to_i
|
SiteSetting.min_new_topics_time = last_new_topic.created_at.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
|
auto_bumps = CategoryCustomField.where(name: Category::NUM_AUTO_BUMP_DAILY).pluck(:id)
|
||||||
|
|
||||||
|
if (auto_bumps.length > 0)
|
||||||
|
auto_bumps.shuffle.each do |category_id|
|
||||||
|
break if Category.find_by(id: category_id)&.auto_bump_topic!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_dependency 'distributed_cache'
|
require_dependency 'distributed_cache'
|
||||||
|
|
||||||
class Category < ActiveRecord::Base
|
class Category < ActiveRecord::Base
|
||||||
@ -9,9 +11,11 @@ class Category < ActiveRecord::Base
|
|||||||
|
|
||||||
REQUIRE_TOPIC_APPROVAL = 'require_topic_approval'
|
REQUIRE_TOPIC_APPROVAL = 'require_topic_approval'
|
||||||
REQUIRE_REPLY_APPROVAL = 'require_reply_approval'
|
REQUIRE_REPLY_APPROVAL = 'require_reply_approval'
|
||||||
|
NUM_AUTO_BUMP_DAILY = 'num_auto_bump_daily'
|
||||||
|
|
||||||
register_custom_field_type(REQUIRE_TOPIC_APPROVAL, :boolean)
|
register_custom_field_type(REQUIRE_TOPIC_APPROVAL, :boolean)
|
||||||
register_custom_field_type(REQUIRE_REPLY_APPROVAL, :boolean)
|
register_custom_field_type(REQUIRE_REPLY_APPROVAL, :boolean)
|
||||||
|
register_custom_field_type(NUM_AUTO_BUMP_DAILY, :integer)
|
||||||
|
|
||||||
belongs_to :topic, dependent: :destroy
|
belongs_to :topic, dependent: :destroy
|
||||||
belongs_to :topic_only_relative_url,
|
belongs_to :topic_only_relative_url,
|
||||||
@ -365,6 +369,51 @@ class Category < ActiveRecord::Base
|
|||||||
custom_fields[REQUIRE_REPLY_APPROVAL]
|
custom_fields[REQUIRE_REPLY_APPROVAL]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def num_auto_bump_daily
|
||||||
|
custom_fields[NUM_AUTO_BUMP_DAILY]
|
||||||
|
end
|
||||||
|
|
||||||
|
def num_auto_bump_daily=(v)
|
||||||
|
custom_fields[NUM_AUTO_BUMP_DAILY] = v
|
||||||
|
end
|
||||||
|
|
||||||
|
def auto_bump_limiter
|
||||||
|
RateLimiter.new(nil, "auto_bump_limit_#{self.id}", num_auto_bump_daily.to_i, 86400)
|
||||||
|
end
|
||||||
|
|
||||||
|
def clear_auto_bump_cache!
|
||||||
|
auto_bump_limiter.clear!
|
||||||
|
end
|
||||||
|
|
||||||
|
# will automatically bump a single topic
|
||||||
|
# if number of automatically bumped topics is smaller than threshold
|
||||||
|
def auto_bump_topic!
|
||||||
|
return false if num_auto_bump_daily.blank?
|
||||||
|
|
||||||
|
limiter = auto_bump_limiter
|
||||||
|
return false if !limiter.can_perform?
|
||||||
|
|
||||||
|
id = Topic
|
||||||
|
.visible
|
||||||
|
.listable_topics
|
||||||
|
.where(category_id: self.id)
|
||||||
|
.where('id <> ?', self.topic_id)
|
||||||
|
.where('bumped_at < ?', 1.day.ago)
|
||||||
|
.where('pinned_at IS NULL AND NOT closed AND NOT archived')
|
||||||
|
.order('bumped_at ASC')
|
||||||
|
.limit(1)
|
||||||
|
.pluck(:id).first
|
||||||
|
|
||||||
|
if id
|
||||||
|
Topic.where(id: id).update_all(bumped_at: Time.zone.now)
|
||||||
|
limiter.performed!
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
def allowed_tags=(tag_names_arg)
|
def allowed_tags=(tag_names_arg)
|
||||||
DiscourseTagging.add_or_create_tags_by_name(self, tag_names_arg, unlimited: true)
|
DiscourseTagging.add_or_create_tags_by_name(self, tag_names_arg, unlimited: true)
|
||||||
end
|
end
|
||||||
@ -459,12 +508,10 @@ class Category < ActiveRecord::Base
|
|||||||
def url
|
def url
|
||||||
url = @@url_cache[self.id]
|
url = @@url_cache[self.id]
|
||||||
unless url
|
unless url
|
||||||
url = "#{Discourse.base_uri}/c"
|
url = +"#{Discourse.base_uri}/c"
|
||||||
url << "/#{parent_category.slug}" if parent_category_id
|
url << "/#{parent_category.slug}" if parent_category_id
|
||||||
url << "/#{slug}"
|
url << "/#{slug}"
|
||||||
url.freeze
|
@@url_cache[self.id] = -url
|
||||||
|
|
||||||
@@url_cache[self.id] = url
|
|
||||||
end
|
end
|
||||||
|
|
||||||
url
|
url
|
||||||
@ -545,53 +592,54 @@ end
|
|||||||
#
|
#
|
||||||
# Table name: categories
|
# Table name: categories
|
||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# name :string(50) not null
|
# name :string(50) not null
|
||||||
# color :string(6) default("AB9364"), not null
|
# color :string(6) default("AB9364"), not null
|
||||||
# topic_id :integer
|
# topic_id :integer
|
||||||
# topic_count :integer default(0), not null
|
# topic_count :integer default(0), not null
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
# user_id :integer not null
|
# user_id :integer not null
|
||||||
# topics_year :integer default(0)
|
# topics_year :integer default(0)
|
||||||
# topics_month :integer default(0)
|
# topics_month :integer default(0)
|
||||||
# topics_week :integer default(0)
|
# topics_week :integer default(0)
|
||||||
# slug :string not null
|
# slug :string not null
|
||||||
# description :text
|
# description :text
|
||||||
# text_color :string(6) default("FFFFFF"), not null
|
# text_color :string(6) default("FFFFFF"), not null
|
||||||
# read_restricted :boolean default(FALSE), not null
|
# read_restricted :boolean default(FALSE), not null
|
||||||
# auto_close_hours :float
|
# auto_close_hours :float
|
||||||
# post_count :integer default(0), not null
|
# post_count :integer default(0), not null
|
||||||
# latest_post_id :integer
|
# latest_post_id :integer
|
||||||
# latest_topic_id :integer
|
# latest_topic_id :integer
|
||||||
# position :integer
|
# position :integer
|
||||||
# parent_category_id :integer
|
# parent_category_id :integer
|
||||||
# posts_year :integer default(0)
|
# posts_year :integer default(0)
|
||||||
# posts_month :integer default(0)
|
# posts_month :integer default(0)
|
||||||
# posts_week :integer default(0)
|
# posts_week :integer default(0)
|
||||||
# email_in :string
|
# email_in :string
|
||||||
# email_in_allow_strangers :boolean default(FALSE)
|
# email_in_allow_strangers :boolean default(FALSE)
|
||||||
# topics_day :integer default(0)
|
# topics_day :integer default(0)
|
||||||
# posts_day :integer default(0)
|
# posts_day :integer default(0)
|
||||||
# allow_badges :boolean default(TRUE), not null
|
# allow_badges :boolean default(TRUE), not null
|
||||||
# name_lower :string(50) not null
|
# name_lower :string(50) not null
|
||||||
# auto_close_based_on_last_post :boolean default(FALSE)
|
# auto_close_based_on_last_post :boolean default(FALSE)
|
||||||
# topic_template :text
|
# topic_template :text
|
||||||
# contains_messages :boolean
|
# contains_messages :boolean
|
||||||
# sort_order :string
|
# sort_order :string
|
||||||
# sort_ascending :boolean
|
# sort_ascending :boolean
|
||||||
# uploaded_logo_id :integer
|
# uploaded_logo_id :integer
|
||||||
# uploaded_background_id :integer
|
# uploaded_background_id :integer
|
||||||
# topic_featured_link_allowed :boolean default(TRUE)
|
# topic_featured_link_allowed :boolean default(TRUE)
|
||||||
# all_topics_wiki :boolean default(FALSE), not null
|
# all_topics_wiki :boolean default(FALSE), not null
|
||||||
# show_subcategory_list :boolean default(FALSE)
|
# show_subcategory_list :boolean default(FALSE)
|
||||||
# num_featured_topics :integer default(3)
|
# num_featured_topics :integer default(3)
|
||||||
# default_view :string(50)
|
# default_view :string(50)
|
||||||
# subcategory_list_style :string(50) default("rows_with_featured_topics")
|
# subcategory_list_style :string(50) default("rows_with_featured_topics")
|
||||||
# default_top_period :string(20) default("all")
|
# default_top_period :string(20) default("all")
|
||||||
# mailinglist_mirror :boolean default(FALSE), not null
|
# mailinglist_mirror :boolean default(FALSE), not null
|
||||||
# suppress_from_latest :boolean default(FALSE)
|
# suppress_from_latest :boolean default(FALSE)
|
||||||
# minimum_required_tags :integer default(0)
|
# minimum_required_tags :integer default(0)
|
||||||
|
# navigate_to_first_post_after_read :boolean default(FALSE), not null
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
@ -25,7 +25,8 @@ class BasicCategorySerializer < ApplicationSerializer
|
|||||||
:default_view,
|
:default_view,
|
||||||
:subcategory_list_style,
|
:subcategory_list_style,
|
||||||
:default_top_period,
|
:default_top_period,
|
||||||
:minimum_required_tags
|
:minimum_required_tags,
|
||||||
|
:navigate_to_first_post_after_read
|
||||||
|
|
||||||
has_one :uploaded_logo, embed: :object, serializer: CategoryUploadSerializer
|
has_one :uploaded_logo, embed: :object, serializer: CategoryUploadSerializer
|
||||||
has_one :uploaded_background, embed: :object, serializer: CategoryUploadSerializer
|
has_one :uploaded_background, embed: :object, serializer: CategoryUploadSerializer
|
||||||
|
@ -2275,7 +2275,7 @@ en:
|
|||||||
show_subcategory_list: "Show subcategory list above topics in this category."
|
show_subcategory_list: "Show subcategory list above topics in this category."
|
||||||
num_featured_topics: "Number of topics shown on the categories page:"
|
num_featured_topics: "Number of topics shown on the categories page:"
|
||||||
subcategory_num_featured_topics: "Number of featured topics on parent category's page:"
|
subcategory_num_featured_topics: "Number of featured topics on parent category's page:"
|
||||||
all_topics_wiki: "Make new topics wikis by default."
|
all_topics_wiki: "Make new topics wikis by default"
|
||||||
subcategory_list_style: "Subcategory List Style:"
|
subcategory_list_style: "Subcategory List Style:"
|
||||||
sort_order: "Topic List Sort By:"
|
sort_order: "Topic List Sort By:"
|
||||||
default_view: "Default Topic List:"
|
default_view: "Default Topic List:"
|
||||||
@ -2286,12 +2286,14 @@ en:
|
|||||||
require_topic_approval: "Require moderator approval of all new topics"
|
require_topic_approval: "Require moderator approval of all new topics"
|
||||||
require_reply_approval: "Require moderator approval of all new replies"
|
require_reply_approval: "Require moderator approval of all new replies"
|
||||||
this_year: "this year"
|
this_year: "this year"
|
||||||
position: "position"
|
position: "Position:"
|
||||||
default_position: "Default Position"
|
default_position: "Default Position"
|
||||||
position_disabled: "Categories will be displayed in order of activity. To control the order of categories in lists, "
|
position_disabled: "Categories will be displayed in order of activity. To control the order of categories in lists, "
|
||||||
position_disabled_click: 'enable the "fixed category positions" setting.'
|
position_disabled_click: 'enable the "fixed category positions" setting.'
|
||||||
minimum_required_tags: 'Minimum number of tags required in a topic:'
|
minimum_required_tags: 'Minimum number of tags required in a topic:'
|
||||||
parent: "Parent Category"
|
parent: "Parent Category"
|
||||||
|
num_auto_bump_daily: 'Number of open topics to automatically bump daily:'
|
||||||
|
navigate_to_first_post_after_read: 'Navigate to first post after topics are read'
|
||||||
notifications:
|
notifications:
|
||||||
watching:
|
watching:
|
||||||
title: "Watching"
|
title: "Watching"
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
class AddNavigateToFirstPostAfterReadToCategories < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
add_column :categories, :navigate_to_first_post_after_read, :bool, null: false, default: false
|
||||||
|
end
|
||||||
|
end
|
@ -684,4 +684,44 @@ describe Category do
|
|||||||
it { expect(category.reload.require_reply_approval?).to eq(true) }
|
it { expect(category.reload.require_reply_approval?).to eq(true) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'auto bump' do
|
||||||
|
before do
|
||||||
|
RateLimiter.enable
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
RateLimiter.disable
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should correctly automatically bump topics' do
|
||||||
|
freeze_time 1.second.ago
|
||||||
|
category = Fabricate(:category)
|
||||||
|
category.clear_auto_bump_cache!
|
||||||
|
|
||||||
|
_post1 = create_post(category: category)
|
||||||
|
_post2 = create_post(category: category)
|
||||||
|
_post3 = create_post(category: category)
|
||||||
|
|
||||||
|
time = 1.month.from_now
|
||||||
|
freeze_time time
|
||||||
|
|
||||||
|
expect(category.auto_bump_topic!).to eq(false)
|
||||||
|
expect(Topic.where(bumped_at: time).count).to eq(0)
|
||||||
|
|
||||||
|
category.num_auto_bump_daily = 2
|
||||||
|
category.save!
|
||||||
|
|
||||||
|
expect(category.auto_bump_topic!).to eq(true)
|
||||||
|
expect(Topic.where(bumped_at: time).count).to eq(1)
|
||||||
|
|
||||||
|
expect(category.auto_bump_topic!).to eq(true)
|
||||||
|
expect(Topic.where(bumped_at: time).count).to eq(2)
|
||||||
|
|
||||||
|
expect(category.auto_bump_topic!).to eq(false)
|
||||||
|
expect(Topic.where(bumped_at: time).count).to eq(2)
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -297,24 +297,31 @@ describe CategoriesController do
|
|||||||
expect(UserHistory.count).to eq(5) # 2 + 3 (bootstrap mode)
|
expect(UserHistory.count).to eq(5) # 2 + 3 (bootstrap mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates per-category approval settings correctly' do
|
it 'updates per-category settings correctly' do
|
||||||
category.custom_fields[Category::REQUIRE_TOPIC_APPROVAL] = false
|
category.custom_fields[Category::REQUIRE_TOPIC_APPROVAL] = false
|
||||||
category.custom_fields[Category::REQUIRE_REPLY_APPROVAL] = false
|
category.custom_fields[Category::REQUIRE_REPLY_APPROVAL] = false
|
||||||
|
category.custom_fields[Category::NUM_AUTO_BUMP_DAILY] = 0
|
||||||
|
|
||||||
|
category.navigate_to_first_post_after_read = false
|
||||||
category.save!
|
category.save!
|
||||||
|
|
||||||
put "/categories/#{category.id}.json", params: {
|
put "/categories/#{category.id}.json", params: {
|
||||||
name: category.name,
|
name: category.name,
|
||||||
color: category.color,
|
color: category.color,
|
||||||
text_color: category.text_color,
|
text_color: category.text_color,
|
||||||
|
navigate_to_first_post_after_read: true,
|
||||||
custom_fields: {
|
custom_fields: {
|
||||||
require_reply_approval: true,
|
require_reply_approval: true,
|
||||||
require_topic_approval: true,
|
require_topic_approval: true,
|
||||||
|
num_auto_bump_daily: 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
category.reload
|
category.reload
|
||||||
expect(category.require_topic_approval?).to eq(true)
|
expect(category.require_topic_approval?).to eq(true)
|
||||||
expect(category.require_reply_approval?).to eq(true)
|
expect(category.require_reply_approval?).to eq(true)
|
||||||
|
expect(category.num_auto_bump_daily).to eq(10)
|
||||||
|
expect(category.navigate_to_first_post_after_read).to eq(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -32,6 +32,23 @@ QUnit.test("visited", assert => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
QUnit.test("lastUnreadUrl", assert => {
|
||||||
|
const category = Em.Object.create({
|
||||||
|
navigate_to_first_post_after_read: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const topic = Topic.create({
|
||||||
|
id: 101,
|
||||||
|
highest_post_number: 10,
|
||||||
|
last_read_post_number: 10,
|
||||||
|
slug: "hello"
|
||||||
|
});
|
||||||
|
|
||||||
|
topic.set("category", category);
|
||||||
|
|
||||||
|
assert.equal(topic.get("lastUnreadUrl"), "/t/hello/101/1");
|
||||||
|
});
|
||||||
|
|
||||||
QUnit.test("has details", assert => {
|
QUnit.test("has details", assert => {
|
||||||
const topic = Topic.create({ id: 1234 });
|
const topic = Topic.create({ id: 1234 });
|
||||||
const topicDetails = topic.get("details");
|
const topicDetails = topic.get("details");
|
||||||
|
Loading…
Reference in New Issue
Block a user