From 6aab8cb331ac3349dcf02cab0ec9a449b87f185c Mon Sep 17 00:00:00 2001 From: Neil Lalonde <neillalonde@gmail.com> Date: Thu, 2 Mar 2017 10:56:04 -0500 Subject: [PATCH] FEATURE: new category setting for whether to show latest topics or top topics by default --- .../components/edit-category-settings.js.es6 | 8 +++++++ .../discourse/models/category.js.es6 | 3 ++- .../dynamic-route-builders.js.es6 | 6 ++--- .../routes/build-category-route.js.es6 | 17 ++++++++----- .../components/edit-category-settings.hbs | 7 ++++++ app/controllers/categories_controller.rb | 1 + app/controllers/list_controller.rb | 10 ++++++++ app/serializers/basic_category_serializer.rb | 3 ++- config/locales/client.en.yml | 1 + config/routes.rb | 2 +- ...01215150_add_default_view_to_categories.rb | 5 ++++ spec/controllers/list_controller_spec.rb | 24 ++++++++++++++++++- 12 files changed, 74 insertions(+), 13 deletions(-) create mode 100644 db/migrate/20170301215150_add_default_view_to_categories.rb diff --git a/app/assets/javascripts/discourse/components/edit-category-settings.js.es6 b/app/assets/javascripts/discourse/components/edit-category-settings.js.es6 index 41a4d2b20f5..f1b089ac1ae 100644 --- a/app/assets/javascripts/discourse/components/edit-category-settings.js.es6 +++ b/app/assets/javascripts/discourse/components/edit-category-settings.js.es6 @@ -21,5 +21,13 @@ export default buildCategoryPanel('settings', { {name: I18n.t('category.sort_ascending'), value: 'true'}, {name: I18n.t('category.sort_descending'), value: 'false'} ]; + }, + + @computed + availableViews() { + return [ + {name: I18n.t('filters.latest.title'), value: 'latest'}, + {name: I18n.t('filters.top.title'), value: 'top'} + ]; } }); diff --git a/app/assets/javascripts/discourse/models/category.js.es6 b/app/assets/javascripts/discourse/models/category.js.es6 index 8839be9bc8d..1ac1a9ca0c4 100644 --- a/app/assets/javascripts/discourse/models/category.js.es6 +++ b/app/assets/javascripts/discourse/models/category.js.es6 @@ -104,7 +104,8 @@ const Category = RestModel.extend({ sort_ascending: this.get('sort_ascending'), topic_featured_link_allowed: this.get('topic_featured_link_allowed'), show_subcategory_list: this.get('show_subcategory_list'), - num_featured_topics: this.get('num_featured_topics') + num_featured_topics: this.get('num_featured_topics'), + default_view: this.get('default_view') }, type: id ? 'PUT' : 'POST' }); diff --git a/app/assets/javascripts/discourse/pre-initializers/dynamic-route-builders.js.es6 b/app/assets/javascripts/discourse/pre-initializers/dynamic-route-builders.js.es6 index 93eddde83cf..6fef97d3454 100644 --- a/app/assets/javascripts/discourse/pre-initializers/dynamic-route-builders.js.es6 +++ b/app/assets/javascripts/discourse/pre-initializers/dynamic-route-builders.js.es6 @@ -13,9 +13,9 @@ export default { app.DiscoveryCategoryNoneController = DiscoverySortableController.extend(); app.DiscoveryCategoryWithIDController = DiscoverySortableController.extend(); - app.DiscoveryCategoryRoute = buildCategoryRoute('latest'); - app.DiscoveryParentCategoryRoute = buildCategoryRoute('latest'); - app.DiscoveryCategoryNoneRoute = buildCategoryRoute('latest', {no_subcategories: true}); + app.DiscoveryCategoryRoute = buildCategoryRoute('default'); + app.DiscoveryParentCategoryRoute = buildCategoryRoute('default'); + app.DiscoveryCategoryNoneRoute = buildCategoryRoute('default', {no_subcategories: true}); const site = Discourse.Site.current(); site.get('filters').forEach(filter => { diff --git a/app/assets/javascripts/discourse/routes/build-category-route.js.es6 b/app/assets/javascripts/discourse/routes/build-category-route.js.es6 index b7b41676325..0f8fb98d398 100644 --- a/app/assets/javascripts/discourse/routes/build-category-route.js.es6 +++ b/app/assets/javascripts/discourse/routes/build-category-route.js.es6 @@ -6,7 +6,7 @@ import CategoryList from 'discourse/models/category-list'; import Category from 'discourse/models/category'; // A helper function to create a category route with parameters -export default (filter, params) => { +export default (filterArg, params) => { return Discourse.Route.extend({ queryParams, @@ -37,9 +37,13 @@ export default (filter, params) => { this._retrieveTopicList(model.category, transition)]); }, + filter(category) { + return filterArg === 'default' ? (category.get('default_view') || 'latest') : filterArg; + }, + _setupNavigation(category) { const noSubcategories = params && !!params.no_subcategories, - filterMode = `c/${Discourse.Category.slugFor(category)}${noSubcategories ? "/none" : ""}/l/${filter}`; + filterMode = `c/${Discourse.Category.slugFor(category)}${noSubcategories ? "/none" : ""}/l/${this.filter(category)}`; this.controllerFor('navigation/category').setProperties({ category, @@ -60,7 +64,7 @@ export default (filter, params) => { }, _retrieveTopicList(category, transition) { - const listFilter = `c/${Discourse.Category.slugFor(category)}/l/${filter}`, + const listFilter = `c/${Discourse.Category.slugFor(category)}/l/${this.filter(category)}`, findOpts = filterQueryParams(transition.queryParams, params), extras = { cached: this.isPoppedState(transition) }; @@ -72,8 +76,8 @@ export default (filter, params) => { }, titleToken() { - const filterText = I18n.t('filters.' + filter.replace('/', '.') + '.title'), - category = this.currentModel.category; + const category = this.currentModel.category, + filterText = I18n.t('filters.' + this.filter(category).replace('/', '.') + '.title'); return I18n.t('filters.with_category', { filter: filterText, category: category.get('name') }); }, @@ -82,7 +86,8 @@ export default (filter, params) => { const topics = this.get('topics'), category = model.category, canCreateTopic = topics.get('can_create_topic'), - canCreateTopicOnCategory = category.get('permission') === PermissionType.FULL; + canCreateTopicOnCategory = category.get('permission') === PermissionType.FULL, + filter = this.filter(category); this.controllerFor('navigation/category').setProperties({ canCreateTopicOnCategory: canCreateTopicOnCategory, diff --git a/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs b/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs index 77862d06f3e..d3e6a73d3a4 100644 --- a/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs +++ b/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs @@ -56,6 +56,13 @@ </label> </section> +<section class="field default-view-field"> + <label> + {{i18n "category.default_view"}} + {{combo-box valueAttribute="value" content=availableViews value=category.default_view}} + </label> +</section> + {{#if emailInEnabled}} <section class='field'> <label> diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index a787562065b..d11a8ad57bb 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -244,6 +244,7 @@ class CategoriesController < ApplicationController :topic_featured_link_allowed, :show_subcategory_list, :num_featured_topics, + :default_view, :custom_fields => [params[:custom_fields].try(:keys)], :permissions => [*p.try(:keys)], :allowed_tags => [], diff --git a/app/controllers/list_controller.rb b/app/controllers/list_controller.rb index f2a4aabac77..431b68f1439 100644 --- a/app/controllers/list_controller.rb +++ b/app/controllers/list_controller.rb @@ -6,6 +6,7 @@ class ListController < ApplicationController skip_before_filter :check_xhr before_filter :set_category, only: [ + :category_default, # filtered topics lists Discourse.filters.map { |f| :"category_#{f}" }, Discourse.filters.map { |f| :"category_none_#{f}" }, @@ -29,6 +30,7 @@ class ListController < ApplicationController Discourse.anonymous_filters, Discourse.anonymous_filters.map { |f| "#{f}_feed" }, # anonymous categorized filters + :category_default, Discourse.anonymous_filters.map { |f| :"category_#{f}" }, Discourse.anonymous_filters.map { |f| :"category_none_#{f}" }, Discourse.anonymous_filters.map { |f| :"parent_category_category_#{f}" }, @@ -106,6 +108,14 @@ class ListController < ApplicationController end end + def category_default + if @category.default_view == 'top' + top(category: @category.id) + else + self.send(@category.default_view || 'latest') + end + end + def topics_by list_opts = build_topic_list_options target_user = fetch_user_from_params({ include_inactive: current_user.try(:staff?) }, [:user_stat, :user_option]) diff --git a/app/serializers/basic_category_serializer.rb b/app/serializers/basic_category_serializer.rb index 81206a9c763..ab1cbac4a02 100644 --- a/app/serializers/basic_category_serializer.rb +++ b/app/serializers/basic_category_serializer.rb @@ -21,7 +21,8 @@ class BasicCategorySerializer < ApplicationSerializer :sort_order, :sort_ascending, :show_subcategory_list, - :num_featured_topics + :num_featured_topics, + :default_view has_one :uploaded_logo, embed: :object, serializer: CategoryUploadSerializer has_one :uploaded_background, embed: :object, serializer: CategoryUploadSerializer diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 0b31acdf64a..3f2a5f87c50 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1974,6 +1974,7 @@ en: subcategory_num_featured_topics: "Number of featured topics on parent category's page:" all_topics_wiki: "Make new topics wikis by default." sort_order: "Default Sort:" + default_view: "Default View:" allow_badges_label: "Allow badges to be awarded in this category" edit_permissions: "Edit Permissions" add_permission: "Add Permission" diff --git a/config/routes.rb b/config/routes.rb index bc75ad0c621..2a1378e4cc9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -491,7 +491,7 @@ Discourse::Application.routes.draw do get "c/:parent_category_slug/:category_slug/find_by_slug" => "categories#find_by_slug" get "c/:category.rss" => "list#category_feed", format: :rss get "c/:parent_category/:category.rss" => "list#category_feed", format: :rss - get "c/:category" => "list#category_latest" + get "c/:category" => "list#category_default", as: "category_default" get "c/:category/none" => "list#category_none_latest" get "c/:parent_category/:category/(:id)" => "list#parent_category_category_latest", constraints: { id: /\d+/ } get "c/:category/l/top" => "list#category_top", as: "category_top" diff --git a/db/migrate/20170301215150_add_default_view_to_categories.rb b/db/migrate/20170301215150_add_default_view_to_categories.rb new file mode 100644 index 00000000000..a70d1a7bab5 --- /dev/null +++ b/db/migrate/20170301215150_add_default_view_to_categories.rb @@ -0,0 +1,5 @@ +class AddDefaultViewToCategories < ActiveRecord::Migration + def change + add_column :categories, :default_view, :string, null: true, limit: 50 + end +end diff --git a/spec/controllers/list_controller_spec.rb b/spec/controllers/list_controller_spec.rb index 23c3d866abc..db81150915e 100644 --- a/spec/controllers/list_controller_spec.rb +++ b/spec/controllers/list_controller_spec.rb @@ -141,7 +141,6 @@ describe ListController do it { is_expected.not_to respond_with(:success) } end - end describe 'feed' do @@ -151,6 +150,29 @@ describe ListController do expect(response.content_type).to eq('application/rss+xml') end end + + describe "category default views" do + it "top default view" do + category.update_attributes!(default_view: 'top') + described_class.expects(:best_period_for).returns('yearly') + xhr :get, :category_default, category: category.slug + expect(response).to be_success + end + + it "default view is nil" do + category.update_attributes!(default_view: nil) + described_class.expects(:best_period_for).never + xhr :get, :category_default, category: category.slug + expect(response).to be_success + end + + it "default view is latest" do + category.update_attributes!(default_view: 'latest') + described_class.expects(:best_period_for).never + xhr :get, :category_default, category: category.slug + expect(response).to be_success + end + end end end