Support for "no subcategories"
This commit is contained in:
parent
ccd0f9c371
commit
acf262b631
|
@ -16,12 +16,20 @@ Discourse.CategoryDropComponent = Ember.Component.extend({
|
||||||
}.property('expanded'),
|
}.property('expanded'),
|
||||||
|
|
||||||
allCategoriesUrl: function() {
|
allCategoriesUrl: function() {
|
||||||
return this.get('category.parentCategory.url') || "/";
|
if (this.get('subCategory')) {
|
||||||
}.property('category'),
|
return this.get('parentCategory.url') || "/";
|
||||||
|
} else {
|
||||||
|
return "/";
|
||||||
|
}
|
||||||
|
}.property('parentCategory.url', 'subCategory'),
|
||||||
|
|
||||||
|
noCategoriesUrl: function() {
|
||||||
|
return this.get('parentCategory.url') + "/none";
|
||||||
|
}.property('parentCategory.url'),
|
||||||
|
|
||||||
allCategoriesLabel: function() {
|
allCategoriesLabel: function() {
|
||||||
if (this.get('subCategory')) {
|
if (this.get('subCategory')) {
|
||||||
return I18n.t('categories.only_category', {categoryName: this.get('parentCategory.name')});
|
return I18n.t('categories.all_subcategories', {categoryName: this.get('parentCategory.name')});
|
||||||
}
|
}
|
||||||
return I18n.t('categories.all');
|
return I18n.t('categories.all');
|
||||||
}.property('category'),
|
}.property('category'),
|
||||||
|
|
|
@ -43,9 +43,10 @@ Discourse.ListController = Discourse.Controller.extend({
|
||||||
|
|
||||||
@method load
|
@method load
|
||||||
@param {String} filterMode the filter we want to load
|
@param {String} filterMode the filter we want to load
|
||||||
|
@param {Object} params for additional filtering
|
||||||
@returns {Ember.Deferred} the promise that will resolve to the list of items.
|
@returns {Ember.Deferred} the promise that will resolve to the list of items.
|
||||||
**/
|
**/
|
||||||
load: function(filterMode) {
|
load: function(filterMode, params) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.set('loading', true);
|
this.set('loading', true);
|
||||||
|
|
||||||
|
@ -74,13 +75,15 @@ Discourse.ListController = Discourse.Controller.extend({
|
||||||
current = Discourse.NavItem.create({ name: filterMode });
|
current = Discourse.NavItem.create({ name: filterMode });
|
||||||
}
|
}
|
||||||
|
|
||||||
return Discourse.TopicList.list(current).then(function(items) {
|
params = params || {};
|
||||||
|
return Discourse.TopicList.list(current, params).then(function(items) {
|
||||||
self.setProperties({
|
self.setProperties({
|
||||||
loading: false,
|
loading: false,
|
||||||
filterMode: filterMode,
|
filterMode: filterMode,
|
||||||
draft: items.draft,
|
draft: items.draft,
|
||||||
draft_key: items.draft_key,
|
draft_key: items.draft_key,
|
||||||
draft_sequence: items.draft_sequence
|
draft_sequence: items.draft_sequence,
|
||||||
|
noSubcategories: params.no_subcategories
|
||||||
});
|
});
|
||||||
if(trackingState) {
|
if(trackingState) {
|
||||||
trackingState.sync(items, filterMode);
|
trackingState.sync(items, filterMode);
|
||||||
|
|
|
@ -202,6 +202,8 @@ Discourse.Category.reopenClass({
|
||||||
if (parentSlug) {
|
if (parentSlug) {
|
||||||
var parentCategory = Discourse.Category.findSingleBySlug(parentSlug);
|
var parentCategory = Discourse.Category.findSingleBySlug(parentSlug);
|
||||||
if (parentCategory) {
|
if (parentCategory) {
|
||||||
|
if (slug === 'none') { return parentCategory; }
|
||||||
|
|
||||||
category = categories.find(function(item) {
|
category = categories.find(function(item) {
|
||||||
return item && item.get('parentCategory') === parentCategory && Discourse.Category.slugFor(item) === (parentSlug + "/" + slug);
|
return item && item.get('parentCategory') === parentCategory && Discourse.Category.slugFor(item) === (parentSlug + "/" + slug);
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
**/
|
**/
|
||||||
|
|
||||||
function finderFor(filter, params) {
|
function finderFor(filter, params) {
|
||||||
|
|
||||||
return function() {
|
return function() {
|
||||||
var url = Discourse.getURL("/") + filter + ".json";
|
var url = Discourse.getURL("/") + filter + ".json";
|
||||||
|
|
||||||
|
@ -185,9 +186,10 @@ Discourse.TopicList.reopenClass({
|
||||||
|
|
||||||
@method list
|
@method list
|
||||||
@param {Object} The menu item to filter to
|
@param {Object} The menu item to filter to
|
||||||
|
@param {Object} Any additional params
|
||||||
@returns {Promise} a promise that resolves to the list of topics
|
@returns {Promise} a promise that resolves to the list of topics
|
||||||
**/
|
**/
|
||||||
list: function(menuItem) {
|
list: function(menuItem, params) {
|
||||||
var filter = menuItem.get('name'),
|
var filter = menuItem.get('name'),
|
||||||
session = Discourse.Session.current(),
|
session = Discourse.Session.current(),
|
||||||
list = session.get('topicList');
|
list = session.get('topicList');
|
||||||
|
@ -197,11 +199,12 @@ Discourse.TopicList.reopenClass({
|
||||||
return Ember.RSVP.resolve(list);
|
return Ember.RSVP.resolve(list);
|
||||||
}
|
}
|
||||||
session.setProperties({topicList: null, topicListScrollPos: null});
|
session.setProperties({topicList: null, topicListScrollPos: null});
|
||||||
return Discourse.TopicList.find(filter, {exclude_category: menuItem.get('excludeCategory')});
|
|
||||||
|
var findParams = {exclude_category: menuItem.get('excludeCategory')};
|
||||||
|
return Discourse.TopicList.find(filter, _.extend(findParams, params || {}));
|
||||||
},
|
},
|
||||||
|
|
||||||
find: function(filter, params) {
|
find: function(filter, params) {
|
||||||
|
|
||||||
return PreloadStore.getAndRemove("topic_list", finderFor(filter, params)).then(function(result) {
|
return PreloadStore.getAndRemove("topic_list", finderFor(filter, params)).then(function(result) {
|
||||||
var topicList = Discourse.TopicList.create({
|
var topicList = Discourse.TopicList.create({
|
||||||
inserted: Em.A(),
|
inserted: Em.A(),
|
||||||
|
|
|
@ -40,6 +40,8 @@ Discourse.Route.buildRoutes(function() {
|
||||||
this.route('categories', { path: '/categories' });
|
this.route('categories', { path: '/categories' });
|
||||||
this.route('category', { path: '/category/:slug' });
|
this.route('category', { path: '/category/:slug' });
|
||||||
this.route('category', { path: '/category/:slug/more' });
|
this.route('category', { path: '/category/:slug/more' });
|
||||||
|
this.route('categoryNone', { path: '/category/:slug/none' });
|
||||||
|
this.route('categoryNone', { path: '/category/:slug/none/more' });
|
||||||
this.route('category', { path: '/category/:parentSlug/:slug' });
|
this.route('category', { path: '/category/:parentSlug/:slug' });
|
||||||
this.route('category', { path: '/category/:parentSlug/:slug/more' });
|
this.route('category', { path: '/category/:parentSlug/:slug/more' });
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.ListCategoryRoute = Discourse.FilteredListRoute.extend({
|
Discourse.ListCategoryRoute = Discourse.FilteredListRoute.extend({
|
||||||
|
|
||||||
model: function(params) {
|
model: function(params) {
|
||||||
return Discourse.Category.findBySlug(Em.get(params, 'slug'), Em.get(params, 'parentSlug'));
|
return Discourse.Category.findBySlug(Em.get(params, 'slug'), Em.get(params, 'parentSlug'));
|
||||||
},
|
},
|
||||||
|
@ -24,11 +23,16 @@ Discourse.ListCategoryRoute = Discourse.FilteredListRoute.extend({
|
||||||
var listController = this.controllerFor('list'),
|
var listController = this.controllerFor('list'),
|
||||||
categorySlug = Discourse.Category.slugFor(category),
|
categorySlug = Discourse.Category.slugFor(category),
|
||||||
self = this,
|
self = this,
|
||||||
filter = this.filter || "latest",
|
filter = this.get('filter') || "latest",
|
||||||
url = "category/" + categorySlug + "/l/" + filter;
|
url = "category/" + categorySlug + "/l/" + filter,
|
||||||
|
params = {};
|
||||||
|
|
||||||
|
if (this.get('noSubcategories')) {
|
||||||
|
params.no_subcategories = true;
|
||||||
|
}
|
||||||
|
|
||||||
listController.setProperties({ filterMode: url, category: null });
|
listController.setProperties({ filterMode: url, category: null });
|
||||||
listController.load(url).then(function(topicList) {
|
listController.load(url, params).then(function(topicList) {
|
||||||
listController.setProperties({
|
listController.setProperties({
|
||||||
canCreateTopic: topicList.get('can_create_topic'),
|
canCreateTopic: topicList.get('can_create_topic'),
|
||||||
category: category
|
category: category
|
||||||
|
@ -52,13 +56,16 @@ Discourse.ListCategoryRoute = Discourse.FilteredListRoute.extend({
|
||||||
// Clear the search context
|
// Clear the search context
|
||||||
this.controllerFor('search').set('searchContext', null);
|
this.controllerFor('search').set('searchContext', null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Discourse.ListCategoryNoneRoute = Discourse.ListCategoryRoute.extend({
|
||||||
|
noSubcategories: true
|
||||||
|
});
|
||||||
|
|
||||||
Discourse.ListController.filters.forEach(function(filter) {
|
Discourse.ListController.filters.forEach(function(filter) {
|
||||||
Discourse["List" + (filter.capitalize()) + "CategoryRoute"] = Discourse.ListCategoryRoute.extend({ filter: filter });
|
Discourse["List" + (filter.capitalize()) + "CategoryRoute"] = Discourse.ListCategoryRoute.extend({
|
||||||
|
filter: filter
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<li>
|
<li>
|
||||||
{{category-drop category=firstCategory categories=parentCategories}}
|
{{category-drop category=firstCategory categories=parentCategories}}
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
{{#if childCategories}}
|
{{#if childCategories}}
|
||||||
<li>
|
<li>
|
||||||
{{category-drop category=secondCategory parentCategory=firstCategory categories=childCategories subCategory="true"}}
|
{{category-drop category=secondCategory parentCategory=firstCategory categories=childCategories subCategory="true" noSubcategories=noSubcategories}}
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,21 @@
|
||||||
{{#if category}}
|
{{#if category}}
|
||||||
<a href="#" {{action expand}} class="badge-category" {{bindAttr style="badgeStyle"}}>{{category.name}}</a>
|
<a href="#" {{action expand}} class="badge-category" {{bindAttr style="badgeStyle"}}>{{category.name}}</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a href='#' {{action expand}} class='badge-category home' {{bindAttr style="badgeStyle"}}>{{allCategoriesLabel}}</i></a>
|
{{#if noSubcategories}}
|
||||||
|
<a href='#' {{action expand}} class='badge-category home' {{bindAttr style="badgeStyle"}}>{{i18n categories.no_subcategory}}</i></a>
|
||||||
|
{{else}}
|
||||||
|
<a href='#' {{action expand}} class='badge-category home' {{bindAttr style="badgeStyle"}}>{{allCategoriesLabel}}</i></a>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if categories}}
|
{{#if categories}}
|
||||||
<a href='#' {{action expand}} class='badge-category category-dropdown-button' {{bindAttr style="badgeStyle"}}><i {{bindAttr class="iconClass"}}></i></a>
|
<a href='#' {{action expand}} class='badge-category category-dropdown-button' {{bindAttr style="badgeStyle"}}><i {{bindAttr class="iconClass"}}></i></a>
|
||||||
<section {{bindAttr class="expanded::hidden :category-dropdown-menu"}} class='chooser'>
|
<section {{bindAttr class="expanded::hidden :category-dropdown-menu"}} class='chooser'>
|
||||||
<div class='cat'><a {{bindAttr href=allCategoriesUrl}} class='badge-category home'>{{allCategoriesLabel}}</a></div>
|
<div class='cat'><a {{bindAttr href=allCategoriesUrl}} class='badge-category home'>{{allCategoriesLabel}}</a></div>
|
||||||
|
{{#if subCategory}}
|
||||||
|
<div class='cat'><a {{bindAttr href=noCategoriesUrl}} class='badge-category home'>{{i18n categories.no_subcategory}}</a></div>
|
||||||
|
{{/if}}
|
||||||
{{#each categories}}<div class='cat'>{{categoryLink this allowUncategorized=true}}</div>{{/each}}
|
{{#each categories}}<div class='cat'>{{categoryLink this allowUncategorized=true}}</div>{{/each}}
|
||||||
</section>
|
</section>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<div class='list-controls'>
|
<div class='list-controls'>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
{{bread-crumbs category=category categories=categories}}
|
{{bread-crumbs category=category categories=categories noSubcategories=noSubcategories}}
|
||||||
|
|
||||||
<ul class="nav nav-pills" id='category-filter'>
|
<ul class="nav nav-pills" id='category-filter'>
|
||||||
{{each availableNavItems itemViewClass="Discourse.NavItemView"}}
|
{{each availableNavItems itemViewClass="Discourse.NavItemView"}}
|
||||||
|
|
|
@ -47,11 +47,11 @@ class ListController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def category
|
def category
|
||||||
list_opts = build_topic_list_options
|
category_response
|
||||||
query = TopicQuery.new(current_user, list_opts)
|
end
|
||||||
list = query.list_latest
|
|
||||||
list.more_topics_url = construct_url_with(:latest, list_opts)
|
def category_none
|
||||||
respond(list)
|
category_response(no_subcategories: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def category_feed
|
def category_feed
|
||||||
|
@ -74,6 +74,15 @@ class ListController < ApplicationController
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
def category_response(extra_opts=nil)
|
||||||
|
list_opts = build_topic_list_options
|
||||||
|
list_opts.merge!(extra_opts) if extra_opts
|
||||||
|
query = TopicQuery.new(current_user, list_opts)
|
||||||
|
list = query.list_latest
|
||||||
|
list.more_topics_url = construct_url_with(:latest, list_opts)
|
||||||
|
respond(list)
|
||||||
|
end
|
||||||
|
|
||||||
def respond(list)
|
def respond(list)
|
||||||
|
|
||||||
list.draft_key = Draft::NEW_TOPIC
|
list.draft_key = Draft::NEW_TOPIC
|
||||||
|
@ -128,14 +137,16 @@ class ListController < ApplicationController
|
||||||
menu_item = menu_items.select { |item| item.query_should_exclude_category?(action_name, params[:format]) }.first
|
menu_item = menu_items.select { |item| item.query_should_exclude_category?(action_name, params[:format]) }.first
|
||||||
|
|
||||||
# exclude_category = 1. from params / 2. parsed from top menu / 3. nil
|
# exclude_category = 1. from params / 2. parsed from top menu / 3. nil
|
||||||
return {
|
result = {
|
||||||
page: params[:page],
|
page: params[:page],
|
||||||
topic_ids: param_to_integer_list(:topic_ids),
|
topic_ids: param_to_integer_list(:topic_ids),
|
||||||
exclude_category: (params[:exclude_category] || menu_item.try(:filter)),
|
exclude_category: (params[:exclude_category] || menu_item.try(:filter)),
|
||||||
category: params[:category],
|
category: params[:category],
|
||||||
sort_order: params[:sort_order],
|
sort_order: params[:sort_order],
|
||||||
sort_descending: params[:sort_descending]
|
sort_descending: params[:sort_descending],
|
||||||
}
|
}
|
||||||
|
result[:no_subcategories] = true if params[:no_subcategories] == 'true'
|
||||||
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_target_user
|
def list_target_user
|
||||||
|
|
|
@ -185,7 +185,8 @@ en:
|
||||||
|
|
||||||
categories:
|
categories:
|
||||||
all: "all categories"
|
all: "all categories"
|
||||||
only_category: "only {{categoryName}}"
|
all_subcategories: "all subcategories"
|
||||||
|
no_subcategory: "no subcategory"
|
||||||
category: "Category"
|
category: "Category"
|
||||||
posts: "Posts"
|
posts: "Posts"
|
||||||
topics: "Topics"
|
topics: "Topics"
|
||||||
|
|
|
@ -200,6 +200,7 @@ Discourse::Application.routes.draw do
|
||||||
|
|
||||||
get 'category/:category.rss' => 'list#category_feed', format: :rss, as: 'category_feed'
|
get 'category/:category.rss' => 'list#category_feed', format: :rss, as: 'category_feed'
|
||||||
get 'category/:category' => 'list#category', as: 'category_list'
|
get 'category/:category' => 'list#category', as: 'category_list'
|
||||||
|
get 'category/:category/none' => 'list#category_none', as: 'category_list_none'
|
||||||
get 'category/:category/more' => 'list#category', as: 'category_list_more'
|
get 'category/:category/more' => 'list#category', as: 'category_list_more'
|
||||||
|
|
||||||
# We've renamed popular to latest. If people access it we want a permanent redirect.
|
# We've renamed popular to latest. If people access it we want a permanent redirect.
|
||||||
|
|
|
@ -17,6 +17,7 @@ class TopicQuery
|
||||||
visible
|
visible
|
||||||
category
|
category
|
||||||
sort_order
|
sort_order
|
||||||
|
no_subcategories
|
||||||
sort_descending).map(&:to_sym)
|
sort_descending).map(&:to_sym)
|
||||||
|
|
||||||
# Maps `sort_order` to a columns in `topics`
|
# Maps `sort_order` to a columns in `topics`
|
||||||
|
@ -31,7 +32,6 @@ class TopicQuery
|
||||||
|
|
||||||
def initialize(user=nil, options={})
|
def initialize(user=nil, options={})
|
||||||
options.assert_valid_keys(VALID_OPTIONS)
|
options.assert_valid_keys(VALID_OPTIONS)
|
||||||
|
|
||||||
@options = options
|
@options = options
|
||||||
@user = user
|
@user = user
|
||||||
end
|
end
|
||||||
|
@ -209,12 +209,16 @@ class TopicQuery
|
||||||
category_id = nil
|
category_id = nil
|
||||||
if options[:category].present?
|
if options[:category].present?
|
||||||
category_id = options[:category].to_i
|
category_id = options[:category].to_i
|
||||||
if category_id == 0
|
category_id = Category.where(slug: options[:category]).pluck(:id).first if category_id == 0
|
||||||
result = result.where('categories.slug = ?', options[:category])
|
|
||||||
else
|
if category_id
|
||||||
result = result.where('categories.id = ?', category_id)
|
if options[:no_subcategories]
|
||||||
|
result = result.where('categories.id = ?', category_id)
|
||||||
|
else
|
||||||
|
result = result.where('categories.id = ? or categories.parent_category_id = ?', category_id, category_id)
|
||||||
|
end
|
||||||
|
result = result.references(:categories)
|
||||||
end
|
end
|
||||||
result = result.references(:categories)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
result = apply_ordering(result, options)
|
result = apply_ordering(result, options)
|
||||||
|
|
|
@ -41,6 +41,7 @@ describe TopicQuery do
|
||||||
|
|
||||||
context 'category filter' do
|
context 'category filter' do
|
||||||
let(:category) { Fabricate(:category) }
|
let(:category) { Fabricate(:category) }
|
||||||
|
|
||||||
let(:diff_category) { Fabricate(:category) }
|
let(:diff_category) { Fabricate(:category) }
|
||||||
|
|
||||||
it "returns topics in the category when we filter to it" do
|
it "returns topics in the category when we filter to it" do
|
||||||
|
@ -50,9 +51,21 @@ describe TopicQuery do
|
||||||
TopicQuery.new(moderator, category: category.slug).list_latest.topics.size.should == 1
|
TopicQuery.new(moderator, category: category.slug).list_latest.topics.size.should == 1
|
||||||
TopicQuery.new(moderator, category: "#{category.id}-category").list_latest.topics.size.should == 1
|
TopicQuery.new(moderator, category: "#{category.id}-category").list_latest.topics.size.should == 1
|
||||||
TopicQuery.new(moderator, category: diff_category.slug).list_latest.topics.size.should == 1
|
TopicQuery.new(moderator, category: diff_category.slug).list_latest.topics.size.should == 1
|
||||||
TopicQuery.new(moderator, category: 'made up slug').list_latest.topics.size.should == 0
|
|
||||||
|
# Defaults to no category filter when slug does not exist
|
||||||
|
TopicQuery.new(moderator, category: 'made up slug').list_latest.topics.size.should == 2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'subcategories' do
|
||||||
|
let!(:subcategory) { Fabricate(:category, parent_category_id: category.id)}
|
||||||
|
|
||||||
|
it "works with subcategories" do
|
||||||
|
TopicQuery.new(moderator, category: category.id).list_latest.topics.size.should == 2
|
||||||
|
TopicQuery.new(moderator, category: subcategory.id).list_latest.topics.size.should == 1
|
||||||
|
TopicQuery.new(moderator, category: category.id, no_subcategories: true).list_latest.topics.size.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue