mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 20:36:39 +08:00
FEATURE: Configurable auto-bump cooldown (#20507)
Currently the auto-bump cooldown is hard-coded to 24 hours. This change makes the highlighted 24 hours part configurable (defaulting to 24 hours), and the rest of the process remains the same. This uses the new CategorySetting model associated with Category. We decided to add this because we want to move away from custom fields due to the lack of type casting and validations, but we want to keep the loading of these optional as they are not needed for almost all of the flows. Category settings will be back-filled to all categories as part of this change, and creating a new category will now also create a category setting.
This commit is contained in:
parent
168de52538
commit
87ec058b8b
|
@ -193,6 +193,18 @@
|
|||
@min="0"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section class="field auto-bump-cooldown-days">
|
||||
<label for="category-auto-bump-cooldown-days">
|
||||
{{i18n "category.auto_bump_cooldown_days"}}
|
||||
</label>
|
||||
<NumberField
|
||||
@number={{this.category.category_setting.auto_bump_cooldown_days}}
|
||||
@id="category-auto-bump-cooldown-days"
|
||||
@type="number"
|
||||
@min="0"
|
||||
/>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
|
|
@ -219,6 +219,7 @@ const Category = RestModel.extend({
|
|||
uploaded_logo_dark_id: this.get("uploaded_logo_dark.id"),
|
||||
uploaded_background_id: this.get("uploaded_background.id"),
|
||||
allow_badges: this.allow_badges,
|
||||
category_setting_attributes: this.category_setting,
|
||||
custom_fields: this.custom_fields,
|
||||
topic_template: this.topic_template,
|
||||
form_template_ids: this.form_template_ids,
|
||||
|
|
|
@ -31,6 +31,7 @@ export default DiscourseRoute.extend({
|
|||
allow_badges: true,
|
||||
topic_featured_link_allowed: true,
|
||||
custom_fields: {},
|
||||
category_setting: {},
|
||||
search_priority: SEARCH_PRIORITIES.normal,
|
||||
required_tag_groups: [],
|
||||
form_template_ids: [],
|
||||
|
|
|
@ -258,7 +258,8 @@ class CategoriesController < ApplicationController
|
|||
|
||||
def find_by_slug
|
||||
params.require(:category_slug)
|
||||
@category = Category.find_by_slug_path(params[:category_slug].split("/"))
|
||||
@category =
|
||||
Category.includes(:category_setting).find_by_slug_path(params[:category_slug].split("/"))
|
||||
|
||||
raise Discourse::NotFound unless @category.present?
|
||||
|
||||
|
@ -405,6 +406,7 @@ class CategoriesController < ApplicationController
|
|||
:read_only_banner,
|
||||
:default_list_filter,
|
||||
:reviewable_by_group_id,
|
||||
category_setting_attributes: %i[auto_bump_cooldown_days],
|
||||
custom_fields: [custom_field_params],
|
||||
permissions: [*p.try(:keys)],
|
||||
allowed_tags: [],
|
||||
|
|
|
@ -48,8 +48,12 @@ class Category < ActiveRecord::Base
|
|||
|
||||
has_one :category_setting, dependent: :destroy
|
||||
|
||||
delegate :auto_bump_cooldown_days, to: :category_setting, allow_nil: true
|
||||
|
||||
has_and_belongs_to_many :web_hooks
|
||||
|
||||
accepts_nested_attributes_for :category_setting, update_only: true
|
||||
|
||||
validates :user_id, presence: true
|
||||
|
||||
validates :name,
|
||||
|
@ -96,6 +100,7 @@ class Category < ActiveRecord::Base
|
|||
before_save :apply_permissions
|
||||
before_save :downcase_email
|
||||
before_save :downcase_name
|
||||
before_save :ensure_category_setting
|
||||
|
||||
after_save :publish_discourse_stylesheet
|
||||
after_save :publish_category
|
||||
|
@ -682,7 +687,7 @@ class Category < ActiveRecord::Base
|
|||
.exclude_scheduled_bump_topics
|
||||
.where(category_id: self.id)
|
||||
.where("id <> ?", self.topic_id)
|
||||
.where("bumped_at < ?", 1.day.ago)
|
||||
.where("bumped_at < ?", (self.auto_bump_cooldown_days || 1).days.ago)
|
||||
.where("pinned_at IS NULL AND NOT closed AND NOT archived")
|
||||
.order("bumped_at ASC")
|
||||
.limit(1)
|
||||
|
@ -1040,6 +1045,10 @@ class Category < ActiveRecord::Base
|
|||
|
||||
private
|
||||
|
||||
def ensure_category_setting
|
||||
self.build_category_setting if self.category_setting.blank?
|
||||
end
|
||||
|
||||
def should_update_reviewables?
|
||||
SiteSetting.enable_category_group_moderation? && saved_change_to_reviewable_by_group_id?
|
||||
end
|
||||
|
|
|
@ -9,6 +9,13 @@ class CategorySetting < ActiveRecord::Base
|
|||
greater_than_or_equal_to: 0,
|
||||
allow_nil: true,
|
||||
}
|
||||
|
||||
validates :auto_bump_cooldown_days,
|
||||
numericality: {
|
||||
only_integer: true,
|
||||
greater_than_or_equal_to: 0,
|
||||
allow_nil: true,
|
||||
}
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
|
@ -22,6 +29,7 @@ end
|
|||
# num_auto_bump_daily :integer
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# auto_bump_cooldown_days :integer default(1)
|
||||
# Indexes
|
||||
#
|
||||
# index_category_settings_on_category_id (category_id) UNIQUE
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CategorySerializer < SiteCategorySerializer
|
||||
class CategorySettingSerializer < ApplicationSerializer
|
||||
attributes :auto_bump_cooldown_days,
|
||||
:num_auto_bump_daily,
|
||||
:require_reply_approval,
|
||||
:require_topic_approval
|
||||
end
|
||||
|
||||
attributes :read_restricted,
|
||||
:available_groups,
|
||||
:auto_close_hours,
|
||||
|
@ -22,6 +29,8 @@ class CategorySerializer < SiteCategorySerializer
|
|||
:reviewable_by_group_name,
|
||||
:default_slow_mode_seconds
|
||||
|
||||
has_one :category_setting, serializer: CategorySettingSerializer, embed: :objects
|
||||
|
||||
def reviewable_by_group_name
|
||||
object.reviewable_by_group.name
|
||||
end
|
||||
|
@ -30,6 +39,10 @@ class CategorySerializer < SiteCategorySerializer
|
|||
SiteSetting.enable_category_group_moderation? && object.reviewable_by_group_id.present?
|
||||
end
|
||||
|
||||
def include_category_setting?
|
||||
object.association(:category_setting).loaded?
|
||||
end
|
||||
|
||||
def group_permissions
|
||||
@group_permissions ||=
|
||||
begin
|
||||
|
|
|
@ -3740,6 +3740,7 @@ en:
|
|||
default_slow_mode: 'Enable "Slow Mode" for new topics in this category.'
|
||||
parent: "Parent Category"
|
||||
num_auto_bump_daily: "Number of open topics to automatically bump daily:"
|
||||
auto_bump_cooldown_days: "Minimum days before bumping the same topic again:"
|
||||
navigate_to_first_post_after_read: "Navigate to first post after topics are read"
|
||||
notifications:
|
||||
title: "change notification level for this category"
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddAutoBumpCooldownDaysToCategorySettings < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :category_settings, :auto_bump_cooldown_days, :integer, default: 1
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class BackfillAutoBumpCooldownDaysCategorySetting < ActiveRecord::Migration[7.0]
|
||||
def up
|
||||
execute(<<~SQL)
|
||||
INSERT INTO
|
||||
category_settings(
|
||||
category_id,
|
||||
auto_bump_cooldown_days,
|
||||
created_at,
|
||||
updated_at
|
||||
)
|
||||
SELECT
|
||||
id,
|
||||
1,
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM categories
|
||||
ON CONFLICT (category_id)
|
||||
DO
|
||||
UPDATE SET
|
||||
auto_bump_cooldown_days = 1,
|
||||
updated_at = NOW();
|
||||
SQL
|
||||
end
|
||||
end
|
|
@ -9,4 +9,11 @@ RSpec.describe CategorySetting do
|
|||
.is_greater_than_or_equal_to(0)
|
||||
.allow_nil
|
||||
end
|
||||
|
||||
it do
|
||||
is_expected.to validate_numericality_of(:auto_bump_cooldown_days)
|
||||
.only_integer
|
||||
.is_greater_than_or_equal_to(0)
|
||||
.allow_nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,6 +39,10 @@ RSpec.describe Category do
|
|||
describe "Associations" do
|
||||
it { is_expected.to have_one(:category_setting).dependent(:destroy) }
|
||||
|
||||
it "automatically creates a category setting" do
|
||||
expect { Fabricate(:category) }.to change { CategorySetting.count }.by(1)
|
||||
end
|
||||
|
||||
it "should delete associated sidebar_section_links when category is destroyed" do
|
||||
category_sidebar_section_link = Fabricate(:category_sidebar_section_link)
|
||||
category_sidebar_section_link_2 =
|
||||
|
@ -965,6 +969,40 @@ RSpec.describe Category do
|
|||
expect(Category.auto_bump_topic!).to eq(false)
|
||||
end
|
||||
|
||||
it "should not auto-bump the same topic within the cooldown" do
|
||||
freeze_time
|
||||
category =
|
||||
Fabricate(
|
||||
:category_with_definition,
|
||||
num_auto_bump_daily: 2,
|
||||
created_at: 1.minute.ago,
|
||||
category_setting_attributes: {
|
||||
auto_bump_cooldown_days: 1,
|
||||
},
|
||||
)
|
||||
category.clear_auto_bump_cache!
|
||||
|
||||
post1 = create_post(category: category, created_at: 15.seconds.ago)
|
||||
|
||||
# no limits on post creation or category creation please
|
||||
RateLimiter.enable
|
||||
|
||||
time = freeze_time 1.month.from_now
|
||||
|
||||
expect(category.auto_bump_topic!).to eq(true)
|
||||
expect(Topic.where(bumped_at: time).count).to eq(1)
|
||||
|
||||
time = freeze_time 13.hours.from_now
|
||||
|
||||
expect(category.auto_bump_topic!).to eq(false)
|
||||
expect(Topic.where(bumped_at: time).count).to eq(0)
|
||||
|
||||
time = freeze_time 13.hours.from_now
|
||||
|
||||
expect(category.auto_bump_topic!).to eq(true)
|
||||
expect(Topic.where(bumped_at: time).count).to eq(1)
|
||||
end
|
||||
|
||||
it "should not automatically bump topics with a bump scheduled" do
|
||||
freeze_time
|
||||
category = Fabricate(:category_with_definition, created_at: 1.second.ago)
|
||||
|
|
|
@ -160,6 +160,12 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"category_setting": {
|
||||
"auto_bump_cooldown_days": 1,
|
||||
"num_auto_bump_daily": null,
|
||||
"require_reply_approval": null,
|
||||
"require_topic_approval": null
|
||||
},
|
||||
"read_only_banner": {
|
||||
"type": [
|
||||
"string",
|
||||
|
|
|
@ -163,6 +163,12 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"category_setting": {
|
||||
"auto_bump_cooldown_days": 1,
|
||||
"num_auto_bump_daily": null,
|
||||
"require_reply_approval": null,
|
||||
"require_topic_approval": null
|
||||
},
|
||||
"read_only_banner": {
|
||||
"type": [
|
||||
"string",
|
||||
|
|
Loading…
Reference in New Issue
Block a user