From 09e8be3209cf0d154325ec6c95226cd00b27b391 Mon Sep 17 00:00:00 2001 From: Dan Ungureanu Date: Wed, 29 Jan 2020 20:30:48 +0200 Subject: [PATCH] UX: Introduce automatic 'categories topics' setting (#8804) When 'categories topics' setting is set to 0, the system will automatically try to find a value to keep the two columns (categories and topics) symmetrical. The value is computed as 1.5x the number of top level categories and at least 5 topics will always be returned. --- app/controllers/categories_controller.rb | 16 +++++++- config/locales/server.en.yml | 2 +- config/site_settings.yml | 3 +- lib/validators/categories_topics_validator.rb | 19 ++++++++++ spec/requests/categories_controller_spec.rb | 37 +++++++++++++++++++ 5 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 lib/validators/categories_topics_validator.rb diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index c8a6a1f0e0b..9699b218696 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -8,6 +8,9 @@ class CategoriesController < ApplicationController before_action :initialize_staff_action_logger, only: [:create, :update, :destroy] skip_before_action :check_xhr, only: [:index, :categories_and_latest, :categories_and_top, :redirect] + SYMMETRICAL_CATEGORIES_TO_TOPICS_FACTOR = 1.5 + MIN_CATEGORIES_TOPICS = 5 + def redirect return if handle_permalink("/category/#{params[:path]}") redirect_to path("/c/#{params[:path]}") @@ -46,7 +49,7 @@ class CategoriesController < ApplicationController style = SiteSetting.desktop_category_page_style topic_options = { - per_page: SiteSetting.categories_topics, + per_page: CategoriesController.topics_per_page, no_definitions: true } @@ -226,6 +229,15 @@ class CategoriesController < ApplicationController end private + + 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 + def categories_and_topics(topics_filter) discourse_expires_in 1.minute @@ -236,7 +248,7 @@ class CategoriesController < ApplicationController } topic_options = { - per_page: SiteSetting.categories_topics, + per_page: CategoriesController.topics_per_page, no_definitions: true } diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index a168282daca..6b8ebd02d5d 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1686,7 +1686,7 @@ en: alert_admins_if_errors_per_minute: "Number of errors per minute in order to trigger an admin alert. A value of 0 disables this feature. NOTE: requires restart." alert_admins_if_errors_per_hour: "Number of errors per hour in order to trigger an admin alert. A value of 0 disables this feature. NOTE: requires restart." - categories_topics: "Number of topics to show in /categories page." + categories_topics: "Number of topics to show in /categories page. If set to 0, it will automatically try to find a value to keep the two columns symmetrical (categories and topics)." suggested_topics: "Number of suggested topics shown at the bottom of a topic." limit_suggested_to_category: "Only show topics from the current category in suggested topics." suggested_topics_max_days_old: "Suggested topics should not be more than n days old." diff --git a/config/site_settings.yml b/config/site_settings.yml index f64a36673d8..33f8e750f57 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -135,8 +135,7 @@ basic: default: false categories_topics: default: 20 - min: 5 - max: 2000 + validator: "CategoriesTopicsValidator" suggested_topics: client: true default: 5 diff --git a/lib/validators/categories_topics_validator.rb b/lib/validators/categories_topics_validator.rb new file mode 100644 index 00000000000..23552803d82 --- /dev/null +++ b/lib/validators/categories_topics_validator.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class CategoriesTopicsValidator + def initialize(opts = {}) + @opts = opts + end + + def valid_value?(val) + num = val.to_i + return false if num.to_s != val.to_s + return false if num != 0 && num < CategoriesController::MIN_CATEGORIES_TOPICS + + true + end + + def error_message + I18n.t('site_settings.errors.invalid_integer_min', min: 5) + end +end diff --git a/spec/requests/categories_controller_spec.rb b/spec/requests/categories_controller_spec.rb index 941e4b06ff3..ff5110a0ac3 100644 --- a/spec/requests/categories_controller_spec.rb +++ b/spec/requests/categories_controller_spec.rb @@ -481,4 +481,41 @@ describe CategoriesController do end end end + + context '#categories_and_topics' do + before do + 10.times.each { Fabricate(:topic) } + end + + it 'works when SiteSetting.categories_topics is non-null' do + SiteSetting.categories_topics = 5 + + get '/categories_and_latest.json' + expect(JSON.parse(response.body)['topic_list']['topics'].size).to eq(5) + end + + it 'works when SiteSetting.categories_topics is null' do + SiteSetting.categories_topics = 0 + + get '/categories_and_latest.json' + json = JSON.parse(response.body) + expect(json['category_list']['categories'].size).to eq(2) # 'Uncategorized' and category + expect(json['topic_list']['topics'].size).to eq(5) + + Fabricate(:category, parent_category: category) + + get '/categories_and_latest.json' + json = JSON.parse(response.body) + expect(json['category_list']['categories'].size).to eq(2) + expect(json['topic_list']['topics'].size).to eq(5) + + Fabricate(:category) + Fabricate(:category) + + get '/categories_and_latest.json' + json = JSON.parse(response.body) + expect(json['category_list']['categories'].size).to eq(4) + expect(json['topic_list']['topics'].size).to eq(6) + end + end end