mirror of
https://github.com/discourse/discourse.git
synced 2026-03-04 01:15:08 +08:00
Currently, setting up a category for a specific use case (e.g. Q&A / support) requires manually toggling multiple site settings and category options. This is error-prone and not discoverable for admins. Introduce a category type system that streamlines category creation by letting admins pick a type (Discussion, Support, etc.) which automatically applies sensible defaults. Core framework: - `Categories::Types::Base` — base class that defines a type's icon, configuration schema (site settings + category settings), and hooks for `enable_plugin`, `configure_site_settings`, `configure_category` - `Categories::TypeRegistry` — singleton registry where core and plugins register their types, with ownership tracking to prevent conflicts - `Categories::Configure` — service object that orchestrates type configuration in a transaction: enables the plugin, applies site settings, configures the category, and logs a staff action Frontend: - Type chooser modal shown when creating a new category (gated behind `enable_simplified_category_creation` (UC) and `enable_category_type_setup` site setting) - `CategoryTypeChooser` service manages modal → route state transfer - Plugin tabs registered with `primary: true` appear alongside General even when advanced settings are collapsed - Dynamic page title reflects the selected type Solved plugin integration: - Registers a "Support" type that enables solved, sets filter/notify defaults, and configures accepted answers + auto-close on the category - Adds a primary "Support" tab with FormKit fields driven by the type's configuration schema Ref - t/174387 Ref - t/174388 --------- Co-authored-by: Martin Brennan <martin@discourse.org>
697 lines
22 KiB
Ruby
697 lines
22 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class CategoriesController < ApplicationController
|
|
include TopicQueryParams
|
|
|
|
requires_login except: %i[
|
|
index
|
|
categories_and_latest
|
|
categories_and_top
|
|
categories_and_hot
|
|
show
|
|
redirect
|
|
find_by_slug
|
|
visible_groups
|
|
find
|
|
search
|
|
]
|
|
|
|
before_action :fetch_category, only: %i[show update destroy visible_groups]
|
|
before_action :initialize_staff_action_logger, only: %i[create update destroy]
|
|
|
|
skip_before_action :check_xhr,
|
|
only: %i[
|
|
index
|
|
categories_and_latest
|
|
categories_and_top
|
|
categories_and_hot
|
|
redirect
|
|
]
|
|
skip_before_action :verify_authenticity_token, only: %i[search]
|
|
|
|
# The front-end is POSTing data to this endpoint, but we're not modifying anything
|
|
allow_in_readonly_mode :search
|
|
|
|
SYMMETRICAL_CATEGORIES_TO_TOPICS_FACTOR = 1.5
|
|
MIN_CATEGORIES_TOPICS = 5
|
|
MAX_CATEGORIES_TOPICS = 100
|
|
MAX_CATEGORIES_LIMIT = 25
|
|
|
|
def redirect
|
|
return if handle_permalink("/category/#{params[:path]}")
|
|
redirect_to path("/c/#{params[:path]}")
|
|
end
|
|
|
|
def index
|
|
discourse_expires_in 1.minute
|
|
|
|
@category_list = fetch_category_list
|
|
|
|
respond_to do |format|
|
|
format.html do
|
|
@title =
|
|
if current_homepage == "categories" && SiteSetting.short_site_description.present?
|
|
"#{SiteSetting.title} - #{SiteSetting.short_site_description}"
|
|
elsif current_homepage != "categories"
|
|
"#{I18n.t("js.filters.categories.title")} - #{SiteSetting.title}"
|
|
end
|
|
|
|
@description = SiteSetting.site_description
|
|
|
|
store_preloaded(
|
|
@category_list.preload_key,
|
|
MultiJson.dump(CategoryListSerializer.new(@category_list, scope: guardian)),
|
|
)
|
|
|
|
@topic_list = fetch_topic_list
|
|
|
|
if @topic_list.present? && @topic_list.topics.present?
|
|
store_preloaded(
|
|
@topic_list.preload_key,
|
|
MultiJson.dump(TopicListSerializer.new(@topic_list, scope: guardian)),
|
|
)
|
|
end
|
|
|
|
render
|
|
end
|
|
|
|
format.json { render_serialized(@category_list, CategoryListSerializer) }
|
|
end
|
|
end
|
|
|
|
def categories_and_latest
|
|
categories_and_topics(:latest)
|
|
end
|
|
|
|
def categories_and_top
|
|
categories_and_topics(:top)
|
|
end
|
|
|
|
def categories_and_hot
|
|
categories_and_topics(:hot)
|
|
end
|
|
|
|
def move
|
|
guardian.ensure_can_create_category!
|
|
|
|
params.require("category_id")
|
|
params.require("position")
|
|
|
|
if category = Category.find(params["category_id"])
|
|
guardian.ensure_can_see!(category)
|
|
category.move_to(params["position"].to_i)
|
|
render json: success_json
|
|
else
|
|
render status: :internal_server_error, json: failed_json
|
|
end
|
|
end
|
|
|
|
def reorder
|
|
guardian.ensure_can_create_category!
|
|
|
|
params.require(:mapping)
|
|
change_requests = MultiJson.load(params[:mapping])
|
|
by_category = Hash[change_requests.map { |cat, pos| [Category.find(cat.to_i), pos] }]
|
|
|
|
unless guardian.is_admin?
|
|
unless by_category.keys.all? { |c| guardian.can_see_category? c }
|
|
raise Discourse::InvalidAccess
|
|
end
|
|
end
|
|
|
|
by_category.each do |cat, pos|
|
|
cat.position = pos
|
|
cat.save! if cat.will_save_change_to_position?
|
|
end
|
|
|
|
render json: success_json
|
|
end
|
|
|
|
def types
|
|
guardian.ensure_can_create_category!
|
|
render json: { types: Categories::TypeRegistry.list }
|
|
end
|
|
|
|
def show
|
|
guardian.ensure_can_see!(@category)
|
|
|
|
if Category.topic_create_allowed(guardian).where(id: @category.id).exists?
|
|
@category.permission = CategoryGroup.permission_types[:full]
|
|
end
|
|
|
|
render_serialized(@category, CategorySerializer)
|
|
end
|
|
|
|
MAX_DESCRIPTION_PARAM_LENGTH = 1000
|
|
def create
|
|
guardian.ensure_can_create!(Category)
|
|
position = category_params.delete(:position)
|
|
category_type = params[:category_type]
|
|
|
|
if category_params[:description].present? &&
|
|
category_params[:description].size > MAX_DESCRIPTION_PARAM_LENGTH
|
|
render json: {
|
|
errors: [
|
|
I18n.t(
|
|
"category.errors.description_too_long",
|
|
count: MAX_DESCRIPTION_PARAM_LENGTH,
|
|
),
|
|
],
|
|
},
|
|
status: :unprocessable_entity
|
|
return
|
|
end
|
|
|
|
@category =
|
|
begin
|
|
Category.new(required_create_params.merge(user: current_user))
|
|
rescue ArgumentError => e
|
|
return render json: { errors: [e.message] }, status: :unprocessable_entity
|
|
end
|
|
|
|
if @category.save
|
|
@category.move_to(position.to_i) if position
|
|
|
|
if category_type.present? &&
|
|
UpcomingChanges.enabled_for_user?(:enable_simplified_category_creation, current_user)
|
|
Categories::Configure.call(guardian:, params: { category_id: @category.id, category_type: })
|
|
end
|
|
|
|
Scheduler::Defer.later "Log staff action create category" do
|
|
@staff_action_logger.log_category_creation(@category)
|
|
end
|
|
|
|
render_serialized(@category, CategorySerializer)
|
|
else
|
|
render_json_error(@category)
|
|
end
|
|
end
|
|
|
|
def update
|
|
guardian.ensure_can_edit!(@category)
|
|
|
|
json_result(@category, serializer: CategorySerializer) do |cat|
|
|
old_category_params = category_params.dup
|
|
|
|
cat.move_to(category_params[:position].to_i) if category_params[:position]
|
|
category_params.delete(:position)
|
|
|
|
old_custom_fields = cat.custom_fields.dup
|
|
if category_params[:custom_fields]
|
|
category_params[:custom_fields].each do |key, value|
|
|
if value.present?
|
|
cat.custom_fields[key] = value
|
|
else
|
|
cat.custom_fields.delete(key)
|
|
end
|
|
end
|
|
end
|
|
category_params.delete(:custom_fields)
|
|
|
|
# properly null the value so the database constraint doesn't catch us
|
|
category_params[:email_in] = nil if category_params[:email_in]&.blank?
|
|
category_params[:minimum_required_tags] = 0 if category_params[:minimum_required_tags]&.blank?
|
|
|
|
old_permissions = cat.permissions_params
|
|
old_permissions = { Group[:everyone].name => 1 } if old_permissions.empty?
|
|
|
|
if result = cat.update(category_params)
|
|
Category.preload_user_fields!(guardian, [cat])
|
|
|
|
Scheduler::Defer.later "Log staff action change category settings" do
|
|
@staff_action_logger.log_category_settings_change(
|
|
@category,
|
|
old_category_params,
|
|
old_permissions: old_permissions,
|
|
old_custom_fields: old_custom_fields,
|
|
)
|
|
end
|
|
end
|
|
|
|
DiscourseEvent.trigger(:category_updated, cat) if result
|
|
|
|
result
|
|
end
|
|
end
|
|
|
|
def update_slug
|
|
@category = Category.find(params[:category_id].to_i)
|
|
guardian.ensure_can_edit!(@category)
|
|
|
|
custom_slug = params[:slug].to_s
|
|
|
|
if custom_slug.blank?
|
|
error = @category.errors.full_message(:slug, I18n.t("errors.messages.blank"))
|
|
render_json_error(error)
|
|
elsif @category.update(slug: custom_slug)
|
|
render json: success_json
|
|
else
|
|
render_json_error(@category)
|
|
end
|
|
end
|
|
|
|
def set_notifications
|
|
category_id = params[:category_id].to_i
|
|
notification_level = params[:notification_level].to_i
|
|
|
|
CategoryUser.set_notification_level_for_category(current_user, notification_level, category_id)
|
|
render json:
|
|
success_json.merge(
|
|
{
|
|
indirectly_muted_category_ids:
|
|
CategoryUser.indirectly_muted_category_ids(current_user),
|
|
},
|
|
)
|
|
end
|
|
|
|
def destroy
|
|
guardian.ensure_can_delete!(@category)
|
|
@category.destroy
|
|
|
|
Scheduler::Defer.later "Log staff action delete category" do
|
|
@staff_action_logger.log_category_deletion(@category)
|
|
end
|
|
|
|
render json: success_json
|
|
end
|
|
|
|
def find_by_slug
|
|
params.require(:category_slug)
|
|
@category =
|
|
Category.includes(:category_setting).find_by_slug_path(params[:category_slug].split("/"))
|
|
|
|
raise Discourse::NotFound if @category.blank?
|
|
|
|
if !guardian.can_see?(@category)
|
|
if SiteSetting.detailed_404 && group = @category.access_category_via_group
|
|
raise Discourse::InvalidAccess.new(
|
|
"not in group",
|
|
@category,
|
|
custom_message: "not_in_group.title_category",
|
|
custom_message_params: {
|
|
group: group.name,
|
|
},
|
|
group: group,
|
|
)
|
|
else
|
|
raise Discourse::NotFound
|
|
end
|
|
end
|
|
|
|
@category.permission = CategoryGroup.permission_types[:full] if Category
|
|
.topic_create_allowed(guardian)
|
|
.where(id: @category.id)
|
|
.exists?
|
|
Category.preload_user_fields!(guardian, [@category])
|
|
|
|
render_serialized(@category, CategorySerializer)
|
|
end
|
|
|
|
def visible_groups
|
|
@guardian.ensure_can_see!(@category)
|
|
|
|
groups =
|
|
if !@category.groups.exists?(id: Group::AUTO_GROUPS[:everyone])
|
|
@category.groups.merge(Group.visible_groups(current_user)).pluck("name")
|
|
end
|
|
|
|
render json: success_json.merge(groups: groups || [])
|
|
end
|
|
|
|
def find
|
|
categories = []
|
|
serializer = params[:include_permissions] ? CategorySerializer : SiteCategorySerializer
|
|
|
|
if params[:ids].present?
|
|
categories = Category.secured(guardian).where(id: params[:ids])
|
|
elsif params[:slug_path].present?
|
|
category = Category.find_by_slug_path(params[:slug_path].split("/"))
|
|
raise Discourse::NotFound if category.blank?
|
|
guardian.ensure_can_see!(category)
|
|
|
|
ancestors = Category.secured(guardian).with_ancestors(category.id).where.not(id: category.id)
|
|
categories = [*ancestors, category]
|
|
elsif params[:slug_path_with_id].present?
|
|
category = Category.find_by_slug_path_with_id(params[:slug_path_with_id])
|
|
raise Discourse::NotFound if category.blank?
|
|
guardian.ensure_can_see!(category)
|
|
|
|
ancestors = Category.secured(guardian).with_ancestors(category.id).where.not(id: category.id)
|
|
categories = [*ancestors, category]
|
|
end
|
|
|
|
raise Discourse::NotFound if categories.blank?
|
|
|
|
Category.preload_user_fields!(guardian, categories)
|
|
|
|
render_serialized(categories, serializer, root: :categories, scope: guardian)
|
|
end
|
|
|
|
def hierarchical_search
|
|
Category::HierarchicalSearch.call(service_params) do
|
|
on_success do |categories:|
|
|
render_json_dump(
|
|
categories: serialize_data(categories, SiteCategorySerializer, scope: guardian),
|
|
)
|
|
end
|
|
on_failed_contract do |contract|
|
|
render json: failed_json.merge(errors: contract.errors.full_messages), status: :bad_request
|
|
end
|
|
on_failure { render json: failed_json, status: :unprocessable_entity }
|
|
end
|
|
end
|
|
|
|
def search
|
|
term = params[:term].to_s.strip
|
|
parent_category_id = params[:parent_category_id].to_i if params[:parent_category_id].present?
|
|
include_uncategorized =
|
|
(
|
|
if params[:include_uncategorized].present?
|
|
ActiveModel::Type::Boolean.new.cast(params[:include_uncategorized])
|
|
else
|
|
true
|
|
end
|
|
)
|
|
if params[:select_category_ids].is_a?(Array)
|
|
select_category_ids = params[:select_category_ids].map(&:presence)
|
|
end
|
|
if params[:reject_category_ids].is_a?(Array)
|
|
reject_category_ids = params[:reject_category_ids].map(&:presence)
|
|
end
|
|
include_subcategories =
|
|
if params[:include_subcategories].present?
|
|
ActiveModel::Type::Boolean.new.cast(params[:include_subcategories])
|
|
else
|
|
true
|
|
end
|
|
include_ancestors =
|
|
if params[:include_ancestors].present?
|
|
ActiveModel::Type::Boolean.new.cast(params[:include_ancestors])
|
|
else
|
|
false
|
|
end
|
|
prioritized_category_id = params[:prioritized_category_id].to_i if params[
|
|
:prioritized_category_id
|
|
].present?
|
|
limit =
|
|
(
|
|
if params[:limit].present?
|
|
params[:limit].to_i.clamp(1, MAX_CATEGORIES_LIMIT)
|
|
else
|
|
MAX_CATEGORIES_LIMIT
|
|
end
|
|
)
|
|
page = [1, params[:page].to_i].max
|
|
|
|
categories = Category.secured(guardian)
|
|
|
|
if term.present? && words = term.split
|
|
words.each do |word|
|
|
categories =
|
|
categories.where(
|
|
"#{Category.normalize_sql("name")} ILIKE #{Category.normalize_sql("?")}",
|
|
"%#{word}%",
|
|
)
|
|
end
|
|
end
|
|
|
|
categories =
|
|
(
|
|
if parent_category_id != -1
|
|
categories.where(parent_category_id: parent_category_id)
|
|
else
|
|
categories.where(parent_category_id: nil)
|
|
end
|
|
) if parent_category_id.present?
|
|
|
|
categories =
|
|
categories.where.not(id: SiteSetting.uncategorized_category_id) if !include_uncategorized
|
|
|
|
categories = categories.where(id: select_category_ids) if select_category_ids
|
|
|
|
categories = categories.where.not(id: reject_category_ids) if reject_category_ids
|
|
|
|
categories = categories.where(parent_category_id: nil) if !include_subcategories
|
|
|
|
categories_count = categories.count
|
|
|
|
categories =
|
|
categories
|
|
.includes(
|
|
:uploaded_logo,
|
|
:uploaded_logo_dark,
|
|
:uploaded_background,
|
|
:uploaded_background_dark,
|
|
:tags,
|
|
:tag_groups,
|
|
:form_templates,
|
|
category_required_tag_groups: :tag_group,
|
|
)
|
|
.joins("LEFT JOIN topics t on t.id = categories.topic_id")
|
|
.select("categories.*, t.slug topic_slug")
|
|
.order(
|
|
"starts_with(#{Category.normalize_sql("categories.name")}, #{Category.normalize_sql(ActiveRecord::Base.connection.quote(term))}) DESC",
|
|
"categories.parent_category_id IS NULL DESC",
|
|
"categories.id IS NOT DISTINCT FROM #{ActiveRecord::Base.connection.quote(prioritized_category_id)} DESC",
|
|
"categories.parent_category_id IS NOT DISTINCT FROM #{ActiveRecord::Base.connection.quote(prioritized_category_id)} DESC",
|
|
"categories.id ASC",
|
|
)
|
|
.limit(limit)
|
|
.offset((page - 1) * limit)
|
|
|
|
if Site.preloaded_category_custom_fields.present?
|
|
Category.preload_custom_fields(categories, Site.preloaded_category_custom_fields)
|
|
end
|
|
|
|
Category.preload_user_fields!(guardian, categories)
|
|
|
|
response = {
|
|
categories_count: categories_count,
|
|
categories: serialize_data(categories, SiteCategorySerializer, scope: guardian),
|
|
}
|
|
|
|
if include_ancestors
|
|
ancestors = Category.secured(guardian).ancestors_of(categories.map(&:id))
|
|
Category.preload_user_fields!(guardian, ancestors)
|
|
response[:ancestors] = serialize_data(ancestors, SiteCategorySerializer, scope: guardian)
|
|
end
|
|
|
|
render_json_dump(response)
|
|
end
|
|
|
|
private
|
|
|
|
def topics_per_page
|
|
return SiteSetting.categories_topics if SiteSetting.categories_topics > 0
|
|
|
|
count = Category.secured(guardian).where(parent_category: nil).count
|
|
count = (SYMMETRICAL_CATEGORIES_TO_TOPICS_FACTOR * count).to_i
|
|
count.clamp(MIN_CATEGORIES_TOPICS, MAX_CATEGORIES_TOPICS)
|
|
end
|
|
|
|
def categories_and_topics(topics_filter)
|
|
discourse_expires_in 1.minute
|
|
|
|
result = CategoryAndTopicLists.new
|
|
result.category_list = fetch_category_list
|
|
result.topic_list = fetch_topic_list(topics_filter:)
|
|
|
|
render_serialized(result, CategoryAndTopicListsSerializer, root: false)
|
|
end
|
|
|
|
def required_param_keys
|
|
[:name]
|
|
end
|
|
|
|
def required_create_params
|
|
required_param_keys.each { |key| params.require(key) }
|
|
category_params
|
|
end
|
|
|
|
def category_params
|
|
@category_params ||=
|
|
begin
|
|
if p = params[:permissions]
|
|
p.each { |k, v| p[k] = v.to_i }
|
|
end
|
|
|
|
if SiteSetting.tagging_enabled
|
|
params[:allowed_tags] = params[:allowed_tags].presence || [] if params[:allowed_tags]
|
|
params[:allowed_tag_groups] = params[:allowed_tag_groups].presence || [] if params[
|
|
:allowed_tag_groups
|
|
]
|
|
params[:required_tag_groups] = params[:required_tag_groups].presence || [] if params[
|
|
:required_tag_groups
|
|
]
|
|
end
|
|
|
|
conditional_param_keys = []
|
|
if SiteSetting.enable_category_group_moderation?
|
|
conditional_param_keys << { moderating_group_ids: [] }
|
|
end
|
|
|
|
if SiteSetting.content_localization_enabled?
|
|
conditional_param_keys << { category_localizations: %i[id locale name description] }
|
|
end
|
|
|
|
permitted_params = [
|
|
*required_param_keys,
|
|
:position,
|
|
:name,
|
|
:color,
|
|
:text_color,
|
|
:style_type,
|
|
:emoji,
|
|
:icon,
|
|
:email_in,
|
|
:email_in_allow_strangers,
|
|
:mailinglist_mirror,
|
|
:all_topics_wiki,
|
|
:allow_unlimited_owner_edits_on_first_post,
|
|
:default_slow_mode_seconds,
|
|
:parent_category_id,
|
|
:auto_close_hours,
|
|
:auto_close_based_on_last_post,
|
|
:uploaded_logo_id,
|
|
:uploaded_logo_dark_id,
|
|
:uploaded_background_id,
|
|
:uploaded_background_dark_id,
|
|
:slug,
|
|
:allow_badges,
|
|
:topic_template,
|
|
:description,
|
|
:sort_order,
|
|
:sort_ascending,
|
|
:topic_featured_link_allowed,
|
|
:show_subcategory_list,
|
|
:num_featured_topics,
|
|
:default_view,
|
|
:subcategory_list_style,
|
|
:default_top_period,
|
|
:minimum_required_tags,
|
|
:navigate_to_first_post_after_read,
|
|
:search_priority,
|
|
:allow_global_tags,
|
|
:read_only_banner,
|
|
:default_list_filter,
|
|
*conditional_param_keys,
|
|
]
|
|
|
|
DiscoursePluginRegistry.category_update_param_with_callback.each do |param_name, config|
|
|
permitted_params << param_name if config[:plugin].enabled?
|
|
end
|
|
|
|
result =
|
|
params.permit(
|
|
*permitted_params,
|
|
category_setting_attributes: %i[
|
|
auto_bump_cooldown_days
|
|
num_auto_bump_daily
|
|
require_reply_approval
|
|
require_topic_approval
|
|
],
|
|
custom_fields: {
|
|
},
|
|
permissions: [*p.try(:keys)],
|
|
allowed_tags: [],
|
|
allowed_tag_groups: [],
|
|
required_tag_groups: %i[name min_count],
|
|
form_template_ids: [],
|
|
)
|
|
|
|
if result[:required_tag_groups] && !result[:required_tag_groups].is_a?(Array)
|
|
raise Discourse::InvalidParameters.new(:required_tag_groups)
|
|
end
|
|
|
|
if @category
|
|
DiscoursePluginRegistry.category_update_param_with_callback.each do |param_name, config|
|
|
next if !config[:plugin].enabled?
|
|
next if !result.key?(param_name)
|
|
|
|
@category.instance_variable_set(:"@#{param_name}_callback_value", result[param_name])
|
|
# remove from params so that AR doesn't try to set it as an attribute
|
|
result.delete(param_name)
|
|
end
|
|
end
|
|
|
|
result
|
|
end
|
|
end
|
|
|
|
def fetch_category
|
|
@category = Category.find_by_slug(params[:id]) || Category.find_by(id: params[:id].to_i)
|
|
raise Discourse::NotFound if @category.blank?
|
|
end
|
|
|
|
def fetch_category_list
|
|
parent_category =
|
|
if params[:parent_category_id].present?
|
|
Category.find_by_slug(params[:parent_category_id]) ||
|
|
Category.find_by(id: params[:parent_category_id].to_i)
|
|
elsif params[:category_slug_path_with_id].present?
|
|
Category.find_by_slug_path_with_id(params[:category_slug_path_with_id])
|
|
end
|
|
|
|
include_topics =
|
|
params[:include_topics] ||
|
|
(parent_category && parent_category.subcategory_list_includes_topics?) ||
|
|
SiteSetting.desktop_category_page_style == "categories_with_featured_topics" ||
|
|
SiteSetting.desktop_category_page_style == "subcategories_with_featured_topics" ||
|
|
SiteSetting.desktop_category_page_style == "categories_boxes_with_topics" ||
|
|
SiteSetting.desktop_category_page_style == "categories_with_top_topics" ||
|
|
SiteSetting.mobile_category_page_style == "categories_with_featured_topics" ||
|
|
SiteSetting.mobile_category_page_style == "categories_boxes_with_topics" ||
|
|
SiteSetting.mobile_category_page_style == "subcategories_with_featured_topics"
|
|
|
|
include_subcategories =
|
|
SiteSetting.desktop_category_page_style == "subcategories_with_featured_topics" ||
|
|
SiteSetting.mobile_category_page_style == "subcategories_with_featured_topics" ||
|
|
params[:include_subcategories] == "true"
|
|
|
|
category_options = {
|
|
is_homepage: current_homepage == "categories",
|
|
parent_category_id: parent_category&.id,
|
|
include_topics: include_topics,
|
|
include_subcategories: include_subcategories,
|
|
tag: params[:tag],
|
|
page: params[:page].try(:to_i) || 1,
|
|
}
|
|
|
|
@category_list = CategoryList.new(guardian, category_options)
|
|
end
|
|
|
|
def fetch_topic_list(topics_filter: nil)
|
|
style =
|
|
if topics_filter
|
|
"categories_and_#{topics_filter}_topics"
|
|
else
|
|
SiteSetting.desktop_category_page_style
|
|
end
|
|
|
|
topic_options = { per_page: topics_per_page, no_definitions: true }
|
|
topic_options.merge!(build_topic_list_options)
|
|
topic_options[:order] = "created" if SiteSetting.desktop_category_page_style ==
|
|
"categories_and_latest_topics_created_date"
|
|
|
|
case style
|
|
when "categories_and_latest_topics", "categories_and_latest_topics_created_date"
|
|
@topic_list = TopicQuery.new(current_user, topic_options).list_latest
|
|
@topic_list.more_topics_url = url_for(latest_path(sort: topic_options[:order]))
|
|
when "categories_and_top_topics"
|
|
@topic_list =
|
|
TopicQuery.new(current_user, topic_options).list_top_for(
|
|
SiteSetting.top_page_default_timeframe.to_sym,
|
|
)
|
|
@topic_list.more_topics_url = url_for(top_path)
|
|
when "categories_and_hot_topics"
|
|
@topic_list = TopicQuery.new(current_user, topic_options).list_hot
|
|
@topic_list.more_topics_url = url_for(hot_path)
|
|
end
|
|
|
|
@topic_list
|
|
end
|
|
|
|
def initialize_staff_action_logger
|
|
@staff_action_logger = StaffActionLogger.new(current_user)
|
|
end
|
|
end
|