2019-05-02 18:17:27 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
class CategoriesController < ApplicationController
|
|
|
|
|
2018-03-02 21:53:29 -05:00
|
|
|
requires_login except: [:index, :categories_and_latest, :categories_and_top, :show, :redirect, :find_by_slug]
|
2018-01-31 23:17:59 -05:00
|
|
|
|
2017-08-31 00:06:56 -04:00
|
|
|
before_action :fetch_category, only: [:show, :update, :destroy]
|
|
|
|
before_action :initialize_staff_action_logger, only: [:create, :update, :destroy]
|
2018-03-02 21:53:29 -05:00
|
|
|
skip_before_action :check_xhr, only: [:index, :categories_and_latest, :categories_and_top, :redirect]
|
2014-10-16 12:15:31 -04:00
|
|
|
|
2020-01-29 13:30:48 -05:00
|
|
|
SYMMETRICAL_CATEGORIES_TO_TOPICS_FACTOR = 1.5
|
|
|
|
MIN_CATEGORIES_TOPICS = 5
|
|
|
|
|
2014-10-16 12:15:31 -04:00
|
|
|
def redirect
|
2019-03-18 10:24:46 -04:00
|
|
|
return if handle_permalink("/category/#{params[:path]}")
|
2015-03-08 20:45:36 -04:00
|
|
|
redirect_to path("/c/#{params[:path]}")
|
2014-10-16 12:15:31 -04:00
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
|
|
|
|
def index
|
2016-08-17 17:23:16 -04:00
|
|
|
discourse_expires_in 1.minute
|
|
|
|
|
2013-09-11 15:33:05 -04:00
|
|
|
@description = SiteSetting.site_description
|
|
|
|
|
2019-10-16 16:08:43 -04:00
|
|
|
parent_category = Category.find_by_slug(params[:parent_category_id]) || Category.find_by(id: params[:parent_category_id].to_i)
|
2017-03-08 11:31:30 -05:00
|
|
|
|
2016-08-18 19:47:00 -04:00
|
|
|
category_options = {
|
2020-04-30 02:48:34 -04:00
|
|
|
is_homepage: current_homepage == "categories",
|
2016-08-22 17:01:43 -04:00
|
|
|
parent_category_id: params[:parent_category_id],
|
2017-03-08 11:31:30 -05:00
|
|
|
include_topics: include_topics(parent_category)
|
2016-08-18 19:47:00 -04:00
|
|
|
}
|
2013-10-17 02:44:56 -04:00
|
|
|
|
2016-08-17 17:23:16 -04:00
|
|
|
@category_list = CategoryList.new(guardian, category_options)
|
2013-05-28 21:15:30 -04:00
|
|
|
|
2018-12-12 05:46:14 -05:00
|
|
|
if category_options[:is_homepage] && SiteSetting.short_site_description.present?
|
|
|
|
@title = "#{SiteSetting.title} - #{SiteSetting.short_site_description}"
|
|
|
|
elsif !category_options[:is_homepage]
|
|
|
|
@title = "#{I18n.t('js.filters.categories.title')} - #{SiteSetting.title}"
|
|
|
|
end
|
2015-06-08 12:07:35 -04:00
|
|
|
|
2013-04-26 13:10:41 -04:00
|
|
|
respond_to do |format|
|
2016-08-17 17:23:16 -04:00
|
|
|
format.html do
|
|
|
|
store_preloaded(@category_list.preload_key, MultiJson.dump(CategoryListSerializer.new(@category_list, scope: guardian)))
|
2016-08-22 17:01:43 -04:00
|
|
|
|
2018-03-02 21:53:29 -05:00
|
|
|
style = SiteSetting.desktop_category_page_style
|
|
|
|
topic_options = {
|
2020-01-29 13:30:48 -05:00
|
|
|
per_page: CategoriesController.topics_per_page,
|
2019-11-18 01:58:35 -05:00
|
|
|
no_definitions: true
|
2018-03-02 21:53:29 -05:00
|
|
|
}
|
|
|
|
|
2020-04-30 02:48:34 -04:00
|
|
|
if style == "categories_and_latest_topics"
|
2018-03-02 21:53:29 -05:00
|
|
|
@topic_list = TopicQuery.new(current_user, topic_options).list_latest
|
2018-08-14 19:22:03 -04:00
|
|
|
@topic_list.more_topics_url = url_for(public_send("latest_path"))
|
2020-04-30 02:48:34 -04:00
|
|
|
elsif style == "categories_and_top_topics"
|
2020-05-26 20:05:06 -04:00
|
|
|
@topic_list = TopicQuery.new(current_user, topic_options).list_top_for(SiteSetting.top_page_default_timeframe.to_sym)
|
2018-08-14 19:22:03 -04:00
|
|
|
@topic_list.more_topics_url = url_for(public_send("top_path"))
|
2018-03-02 21:53:29 -05:00
|
|
|
end
|
|
|
|
|
2018-08-14 19:22:03 -04:00
|
|
|
if @topic_list.present? && @topic_list.topics.present?
|
2018-03-02 21:53:29 -05:00
|
|
|
store_preloaded(
|
|
|
|
@topic_list.preload_key,
|
|
|
|
MultiJson.dump(TopicListSerializer.new(@topic_list, scope: guardian))
|
|
|
|
)
|
2016-08-22 17:01:43 -04:00
|
|
|
end
|
|
|
|
|
2016-08-17 17:23:16 -04:00
|
|
|
render
|
|
|
|
end
|
|
|
|
|
|
|
|
format.json { render_serialized(@category_list, CategoryListSerializer) }
|
2013-04-26 13:10:41 -04:00
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
2013-02-07 10:45:24 -05:00
|
|
|
|
2016-08-29 16:47:44 -04:00
|
|
|
def categories_and_latest
|
2018-03-02 21:53:29 -05:00
|
|
|
categories_and_topics(:latest)
|
|
|
|
end
|
2016-08-29 16:47:44 -04:00
|
|
|
|
2018-03-02 21:53:29 -05:00
|
|
|
def categories_and_top
|
|
|
|
categories_and_topics(:top)
|
2016-08-29 16:47:44 -04:00
|
|
|
end
|
|
|
|
|
2013-10-21 00:24:37 -04:00
|
|
|
def move
|
2015-08-27 13:14:59 -04:00
|
|
|
guardian.ensure_can_create_category!
|
2013-10-21 00:33:42 -04:00
|
|
|
|
2013-10-21 00:24:37 -04:00
|
|
|
params.require("category_id")
|
|
|
|
params.require("position")
|
|
|
|
|
|
|
|
if category = Category.find(params["category_id"])
|
|
|
|
category.move_to(params["position"].to_i)
|
|
|
|
render json: success_json
|
|
|
|
else
|
|
|
|
render status: 500, json: failed_json
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-08-27 13:14:59 -04:00
|
|
|
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?
|
|
|
|
raise Discourse::InvalidAccess unless by_category.keys.all? { |c| guardian.can_see_category? c }
|
|
|
|
end
|
|
|
|
|
|
|
|
by_category.each do |cat, pos|
|
|
|
|
cat.position = pos
|
2017-08-31 00:06:56 -04:00
|
|
|
cat.save! if cat.will_save_change_to_position?
|
2015-08-27 13:14:59 -04:00
|
|
|
end
|
2017-08-31 00:06:56 -04:00
|
|
|
|
2015-08-27 13:14:59 -04:00
|
|
|
render json: success_json
|
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
def show
|
2019-08-19 03:08:28 -04:00
|
|
|
guardian.ensure_can_see!(@category)
|
|
|
|
|
2014-03-05 17:21:55 -05:00
|
|
|
if Category.topic_create_allowed(guardian).where(id: @category.id).exists?
|
|
|
|
@category.permission = CategoryGroup.permission_types[:full]
|
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
render_serialized(@category, CategorySerializer)
|
|
|
|
end
|
|
|
|
|
|
|
|
def create
|
|
|
|
guardian.ensure_can_create!(Category)
|
2014-05-16 11:33:44 -04:00
|
|
|
position = category_params.delete(:position)
|
|
|
|
|
2018-09-02 23:04:58 -04:00
|
|
|
@category =
|
|
|
|
begin
|
2020-08-12 14:28:29 -04:00
|
|
|
Category.new(required_create_params.merge(user: current_user))
|
2018-09-02 23:04:58 -04:00
|
|
|
rescue ArgumentError => e
|
|
|
|
return render json: { errors: [e.message] }, status: 422
|
|
|
|
end
|
2013-02-07 10:45:24 -05:00
|
|
|
|
2015-09-17 03:51:32 -04:00
|
|
|
if @category.save
|
|
|
|
@category.move_to(position.to_i) if position
|
|
|
|
|
|
|
|
Scheduler::Defer.later "Log staff action create category" do
|
|
|
|
@staff_action_logger.log_category_creation(@category)
|
|
|
|
end
|
|
|
|
|
|
|
|
render_serialized(@category, CategorySerializer)
|
|
|
|
else
|
2019-12-09 19:48:27 -05:00
|
|
|
render_json_error(@category)
|
2015-09-17 03:51:32 -04:00
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def update
|
|
|
|
guardian.ensure_can_edit!(@category)
|
2014-10-10 12:21:44 -04:00
|
|
|
|
|
|
|
json_result(@category, serializer: CategorySerializer) do |cat|
|
2021-09-17 06:37:56 -04:00
|
|
|
old_category_params = category_params.dup
|
2014-10-10 12:21:44 -04:00
|
|
|
|
2014-05-16 11:33:44 -04:00
|
|
|
cat.move_to(category_params[:position].to_i) if category_params[:position]
|
2016-03-08 14:52:04 -05:00
|
|
|
category_params.delete(:position)
|
2014-10-10 12:21:44 -04:00
|
|
|
|
2021-09-17 06:37:56 -04:00
|
|
|
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)
|
|
|
|
|
2016-03-08 14:52:04 -05:00
|
|
|
# properly null the value so the database constraint doesn't catch us
|
2018-11-29 12:10:14 -05:00
|
|
|
category_params[:email_in] = nil if category_params[:email_in]&.blank?
|
|
|
|
category_params[:minimum_required_tags] = 0 if category_params[:minimum_required_tags]&.blank?
|
2014-10-10 12:21:44 -04:00
|
|
|
|
2016-03-08 14:52:04 -05:00
|
|
|
old_permissions = cat.permissions_params
|
2014-10-10 12:21:44 -04:00
|
|
|
|
2017-08-31 00:06:56 -04:00
|
|
|
if result = cat.update(category_params)
|
2015-09-17 03:51:32 -04:00
|
|
|
Scheduler::Defer.later "Log staff action change category settings" do
|
2021-09-17 06:37:56 -04:00
|
|
|
@staff_action_logger.log_category_settings_change(
|
|
|
|
@category,
|
|
|
|
old_category_params,
|
|
|
|
old_permissions: old_permissions,
|
|
|
|
old_custom_fields: old_custom_fields
|
|
|
|
)
|
2015-09-17 03:51:32 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-07-12 15:23:07 -04:00
|
|
|
if result
|
|
|
|
DiscourseEvent.trigger(:category_updated, cat)
|
|
|
|
end
|
|
|
|
|
2015-09-17 03:51:32 -04:00
|
|
|
result
|
2014-10-10 12:21:44 -04:00
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2014-12-20 09:07:29 -05:00
|
|
|
def update_slug
|
|
|
|
@category = Category.find(params[:category_id].to_i)
|
|
|
|
guardian.ensure_can_edit!(@category)
|
|
|
|
|
|
|
|
custom_slug = params[:slug].to_s
|
|
|
|
|
2020-04-20 21:50:20 -04:00
|
|
|
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)
|
2014-12-20 09:07:29 -05:00
|
|
|
render json: success_json
|
|
|
|
else
|
|
|
|
render_json_error(@category)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-04-17 05:17:39 -04:00
|
|
|
def set_notifications
|
|
|
|
category_id = params[:category_id].to_i
|
|
|
|
notification_level = params[:notification_level].to_i
|
|
|
|
|
2014-11-03 10:57:50 -05:00
|
|
|
CategoryUser.set_notification_level_for_category(current_user, notification_level, category_id)
|
2014-04-17 05:17:39 -04:00
|
|
|
render json: success_json
|
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
def destroy
|
2013-04-18 17:07:06 -04:00
|
|
|
guardian.ensure_can_delete!(@category)
|
|
|
|
@category.destroy
|
2013-10-21 00:24:37 -04:00
|
|
|
|
2015-09-17 03:51:32 -04:00
|
|
|
Scheduler::Defer.later "Log staff action delete category" do
|
|
|
|
@staff_action_logger.log_category_deletion(@category)
|
|
|
|
end
|
|
|
|
|
2013-10-21 00:24:37 -04:00
|
|
|
render json: success_json
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2016-03-14 12:38:29 -04:00
|
|
|
def find_by_slug
|
|
|
|
params.require(:category_slug)
|
2020-12-08 16:50:26 -05:00
|
|
|
@category = Category.find_by_slug_path(params[:category_slug].split('/'))
|
2019-10-15 11:39:09 -04:00
|
|
|
|
|
|
|
raise Discourse::NotFound unless @category.present?
|
|
|
|
|
2019-10-08 07:15:08 -04:00
|
|
|
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',
|
2021-06-14 17:42:09 -04:00
|
|
|
custom_message_params: { group: group.name },
|
2019-10-08 07:15:08 -04:00
|
|
|
group: group
|
|
|
|
)
|
|
|
|
else
|
|
|
|
raise Discourse::NotFound
|
|
|
|
end
|
|
|
|
end
|
2016-03-14 12:38:29 -04:00
|
|
|
|
|
|
|
@category.permission = CategoryGroup.permission_types[:full] if Category.topic_create_allowed(guardian).where(id: @category.id).exists?
|
|
|
|
render_serialized(@category, CategorySerializer)
|
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
private
|
2020-01-29 13:30:48 -05:00
|
|
|
|
|
|
|
def self.topics_per_page
|
|
|
|
return SiteSetting.categories_topics if SiteSetting.categories_topics > 0
|
|
|
|
|
|
|
|
count = Category.where(parent_category: nil).count
|
|
|
|
count = (SYMMETRICAL_CATEGORIES_TO_TOPICS_FACTOR * count).to_i
|
|
|
|
count > MIN_CATEGORIES_TOPICS ? count : MIN_CATEGORIES_TOPICS
|
|
|
|
end
|
|
|
|
|
2018-03-02 21:53:29 -05:00
|
|
|
def categories_and_topics(topics_filter)
|
|
|
|
discourse_expires_in 1.minute
|
|
|
|
|
|
|
|
category_options = {
|
2020-04-30 02:48:34 -04:00
|
|
|
is_homepage: current_homepage == "categories",
|
2018-03-02 21:53:29 -05:00
|
|
|
parent_category_id: params[:parent_category_id],
|
|
|
|
include_topics: false
|
2018-06-07 01:28:18 -04:00
|
|
|
}
|
2018-03-02 21:53:29 -05:00
|
|
|
|
|
|
|
topic_options = {
|
2020-01-29 13:30:48 -05:00
|
|
|
per_page: CategoriesController.topics_per_page,
|
2019-11-18 01:58:35 -05:00
|
|
|
no_definitions: true
|
2018-06-07 01:28:18 -04:00
|
|
|
}
|
|
|
|
|
2018-03-02 21:53:29 -05:00
|
|
|
result = CategoryAndTopicLists.new
|
|
|
|
result.category_list = CategoryList.new(guardian, category_options)
|
|
|
|
|
|
|
|
if topics_filter == :latest
|
|
|
|
result.topic_list = TopicQuery.new(current_user, topic_options).list_latest
|
|
|
|
elsif topics_filter == :top
|
|
|
|
result.topic_list = TopicQuery.new(nil, topic_options).list_top_for(SiteSetting.top_page_default_timeframe.to_sym)
|
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
render_serialized(result, CategoryAndTopicListsSerializer, root: false)
|
2013-10-21 02:17:40 -04:00
|
|
|
end
|
2013-06-05 02:45:25 -04:00
|
|
|
|
2013-10-21 02:17:40 -04:00
|
|
|
def required_param_keys
|
2020-08-12 14:28:29 -04:00
|
|
|
[:name]
|
|
|
|
end
|
|
|
|
|
|
|
|
def required_create_params
|
|
|
|
required_param_keys.each do |key|
|
|
|
|
params.require(key)
|
|
|
|
end
|
|
|
|
category_params
|
2018-06-07 01:28:18 -04:00
|
|
|
end
|
|
|
|
|
2013-10-21 02:17:40 -04:00
|
|
|
def category_params
|
|
|
|
@category_params ||= begin
|
2016-06-07 13:08:59 -04:00
|
|
|
if p = params[:permissions]
|
|
|
|
p.each do |k, v|
|
|
|
|
p[k] = v.to_i
|
|
|
|
end
|
2018-06-07 01:28:18 -04:00
|
|
|
end
|
2016-05-31 16:46:40 -04:00
|
|
|
|
2014-07-09 22:01:46 -04:00
|
|
|
if SiteSetting.tagging_enabled
|
2021-09-14 08:04:54 -04:00
|
|
|
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_group_name] = params[:required_tag_group_name].presence || '' if params[:required_tag_group_name]
|
|
|
|
end
|
|
|
|
|
|
|
|
if SiteSetting.enable_category_group_moderation?
|
|
|
|
params[:reviewable_by_group_id] = Group.where(name: params[:reviewable_by_group_name]).pluck_first(:id) if params[:reviewable_by_group_name]
|
2013-10-21 02:17:40 -04:00
|
|
|
end
|
2013-04-18 17:07:06 -04:00
|
|
|
|
2019-04-17 17:12:32 -04:00
|
|
|
result = params.permit(
|
|
|
|
*required_param_keys,
|
|
|
|
:position,
|
2020-08-12 14:28:29 -04:00
|
|
|
:name,
|
|
|
|
:color,
|
|
|
|
:text_color,
|
2019-04-17 17:12:32 -04:00
|
|
|
:email_in,
|
|
|
|
:email_in_allow_strangers,
|
|
|
|
:mailinglist_mirror,
|
|
|
|
:all_topics_wiki,
|
2021-04-14 01:54:09 -04:00
|
|
|
:allow_unlimited_owner_edits_on_first_post,
|
2021-06-27 15:46:11 -04:00
|
|
|
:default_slow_mode_seconds,
|
2019-04-17 17:12:32 -04:00
|
|
|
:parent_category_id,
|
|
|
|
:auto_close_hours,
|
|
|
|
:auto_close_based_on_last_post,
|
|
|
|
:uploaded_logo_id,
|
|
|
|
:uploaded_background_id,
|
|
|
|
:slug,
|
|
|
|
:allow_badges,
|
|
|
|
:topic_template,
|
|
|
|
: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,
|
2019-10-30 14:49:00 -04:00
|
|
|
:required_tag_group_name,
|
|
|
|
:min_tags_from_required_group,
|
2020-04-30 13:39:11 -04:00
|
|
|
:read_only_banner,
|
2020-06-03 15:26:56 -04:00
|
|
|
:default_list_filter,
|
2021-09-14 08:04:54 -04:00
|
|
|
:reviewable_by_group_id,
|
2019-04-17 17:12:32 -04:00
|
|
|
custom_fields: [params[:custom_fields].try(:keys)],
|
|
|
|
permissions: [*p.try(:keys)],
|
|
|
|
allowed_tags: [],
|
2021-09-14 08:04:54 -04:00
|
|
|
allowed_tag_groups: [],
|
2019-04-17 17:12:32 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
result
|
2013-04-18 17:07:06 -04:00
|
|
|
end
|
2018-06-07 01:28:18 -04:00
|
|
|
end
|
2015-09-17 03:51:32 -04:00
|
|
|
|
|
|
|
def fetch_category
|
2019-10-28 12:11:03 -04:00
|
|
|
@category = Category.find_by_slug(params[:id]) || Category.find_by(id: params[:id].to_i)
|
2015-09-17 03:51:32 -04:00
|
|
|
end
|
2016-08-29 16:47:44 -04:00
|
|
|
|
2015-09-17 03:51:32 -04:00
|
|
|
def initialize_staff_action_logger
|
|
|
|
@staff_action_logger = StaffActionLogger.new(current_user)
|
2018-06-07 01:28:18 -04:00
|
|
|
end
|
|
|
|
|
2017-03-08 11:31:30 -05:00
|
|
|
def include_topics(parent_category = nil)
|
2018-03-02 21:53:29 -05:00
|
|
|
style = SiteSetting.desktop_category_page_style
|
2018-11-02 10:29:44 -04:00
|
|
|
view_context.mobile_view? ||
|
|
|
|
params[:include_topics] ||
|
2018-03-02 21:53:29 -05:00
|
|
|
(parent_category && parent_category.subcategory_list_includes_topics?) ||
|
2020-04-30 02:48:34 -04:00
|
|
|
style == "categories_with_featured_topics" ||
|
|
|
|
style == "categories_boxes_with_topics" ||
|
|
|
|
style == "categories_with_top_topics"
|
2016-08-29 16:47:44 -04:00
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|