BUGFIX: proper handling of /none subcategory

This commit is contained in:
Régis Hanol 2014-01-17 23:52:06 +01:00
parent 7f5ef60d5f
commit 8d2e5041bc
7 changed files with 144 additions and 99 deletions

View File

@ -1,5 +1,5 @@
/** /**
The controller for discoverying "Top" topics The controller for discoverying 'Top' topics
@class DiscoveryTopController @class DiscoveryTopController
@extends Discourse.Controller @extends Discourse.Controller
@ -10,14 +10,14 @@ Discourse.DiscoveryTopController = Discourse.ObjectController.extend({
category: null, category: null,
redirectedToTopPageReason: function() { redirectedToTopPageReason: function() {
// no need for a reason if the default homepage is "top" // no need for a reason if the default homepage is 'top'
if (Discourse.Utilities.defaultHomepage() === "top") { return null; } if (Discourse.Utilities.defaultHomepage() === 'top') { return null; }
// check if the user is authenticated // check if the user is authenticated
if (Discourse.User.current()) { if (Discourse.User.current()) {
if (Discourse.User.currentProp("trust_level") === 0) { if (Discourse.User.currentProp('trust_level') === 0) {
return I18n.t("filters.top.redirect_reasons.new_user"); return I18n.t('filters.top.redirect_reasons.new_user');
} else if (!Discourse.User.currentProp("hasBeenSeenInTheLastMonth")) { } else if (!Discourse.User.currentProp('hasBeenSeenInTheLastMonth')) {
return I18n.t("filters.top.redirect_reasons.not_seen_in_a_month"); return I18n.t('filters.top.redirect_reasons.not_seen_in_a_month');
} }
} }
// no reason detected // no reason detected
@ -27,14 +27,16 @@ Discourse.DiscoveryTopController = Discourse.ObjectController.extend({
hasDisplayedAllTopLists: Em.computed.and('content.yearly', 'content.monthly', 'content.weekly', 'content.daily'), hasDisplayedAllTopLists: Em.computed.and('content.yearly', 'content.monthly', 'content.weekly', 'content.daily'),
showMoreUrl: function(period) { showMoreUrl: function(period) {
var url = "", category = this.get("category"); var url = '', category = this.get('category');
if (category) { url += category.get("url") + "/l"; } if (category) {
url += "/top/" + period; url = '/category/' + Discourse.Category.slugFor(category) + (this.get('noSubcategories') ? '/none' : '') + '/l';
}
url += '/top/' + period;
return url; return url;
}, },
showMoreDailyUrl: function() { return this.showMoreUrl("daily"); }.property("category.url"), showMoreDailyUrl: function() { return this.showMoreUrl('daily'); }.property('category', 'noSubcategories'),
showMoreWeeklyUrl: function() { return this.showMoreUrl("weekly"); }.property("category.url"), showMoreWeeklyUrl: function() { return this.showMoreUrl('weekly'); }.property('category', 'noSubcategories'),
showMoreMonthlyUrl: function() { return this.showMoreUrl("monthly"); }.property("category.url"), showMoreMonthlyUrl: function() { return this.showMoreUrl('monthly'); }.property('category', 'noSubcategories'),
showMoreYearlyUrl: function() { return this.showMoreUrl("yearly"); }.property("category.url"), showMoreYearlyUrl: function() { return this.showMoreUrl('yearly'); }.property('category', 'noSubcategories'),
}); });

View File

@ -1,5 +1,5 @@
/** /**
Handles the routes related to "Top" Handles the routes related to 'Top'
@class DiscoveryTopRoute @class DiscoveryTopRoute
@extends Discourse.Route @extends Discourse.Route
@ -29,7 +29,7 @@ Discourse.DiscoveryTopRoute = Discourse.Route.extend({
}); });
/** /**
Handles the routes related to "Top" within a category Handles the routes related to 'Top' within a category
@class DiscoveryTopCategoryRoute @class DiscoveryTopCategoryRoute
@extends Discourse.Route @extends Discourse.Route
@ -44,7 +44,7 @@ Discourse.DiscoveryTopCategoryRoute = Discourse.Route.extend({
afterModel: function(model) { afterModel: function(model) {
var self = this, var self = this,
noSubcategories = this.get('no_subcategories'), noSubcategories = this.get('no_subcategories'),
filterMode = "category/" + Discourse.Category.slugFor(model) + (noSubcategories ? "/none" : "") + "/l/top"; filterMode = 'category/' + Discourse.Category.slugFor(model) + (noSubcategories ? '/none' : '') + '/l/top';
this.controllerFor('search').set('searchContext', model); this.controllerFor('search').set('searchContext', model);
@ -70,8 +70,12 @@ Discourse.DiscoveryTopCategoryRoute = Discourse.Route.extend({
var topList = this.get('topList'); var topList = this.get('topList');
var filterText = I18n.t('filters.top.title'); var filterText = I18n.t('filters.top.title');
Discourse.set('title', I18n.t('filters.with_category', {filter: filterText, category: model.get('name').capitalize()})); Discourse.set('title', I18n.t('filters.with_category', {filter: filterText, category: model.get('name').capitalize()}));
this.controllerFor('discoveryTop').setProperties({ model: topList, category: model });
this.controllerFor('navigationCategory').set('canCreateTopic', topList.get('can_create_topic')); this.controllerFor('navigationCategory').set('canCreateTopic', topList.get('can_create_topic'));
this.controllerFor('discoveryTop').setProperties({
model: topList,
category: model,
noSubcategories: this.get('no_subcategories')
});
this.set('topList', null); this.set('topList', null);
}, },

View File

@ -1,22 +1,40 @@
class ListController < ApplicationController class ListController < ApplicationController
skip_before_filter :check_xhr
@@categories = [
# filtered topics lists
Discourse.filters.map { |f| "#{f}_category".to_sym },
Discourse.filters.map { |f| "#{f}_category_none".to_sym },
# top summary
:top_category,
:top_category_none,
# top pages (ie. with a period)
TopTopic.periods.map { |p| "top_#{p}_category".to_sym },
TopTopic.periods.map { |p| "top_#{p}_category_none".to_sym },
# category feeds
:category_feed,
].flatten
before_filter :set_category, only: @@categories
before_filter :ensure_logged_in, except: [ before_filter :ensure_logged_in, except: [
:topics_by, :topics_by,
# anonymous filters # anonymous filters
Discourse.anonymous_filters, Discourse.anonymous_filters.map { |f| "#{f}_feed".to_sym }, Discourse.anonymous_filters,
# category Discourse.anonymous_filters.map { |f| "#{f}_feed".to_sym },
:category, :category_feed, # categories
@@categories,
# top # top
:top_lists, TopTopic.periods.map { |p| "top_#{p}".to_sym } :top,
TopTopic.periods.map { |p| "top_#{p}".to_sym }
].flatten ].flatten
before_filter :set_category, only: [:category, :category_none, :category_feed]
skip_before_filter :check_xhr
# Create our filters # Create our filters
Discourse.filters.each do |filter| Discourse.filters.each do |filter|
define_method(filter) do define_method(filter) do |options = nil|
list_opts = build_topic_list_options list_opts = build_topic_list_options
list_opts.merge!(options) if options
user = list_target_user user = list_target_user
list = TopicQuery.new(user, list_opts).public_send("list_#{filter}") list = TopicQuery.new(user, list_opts).public_send("list_#{filter}")
list.more_topics_url = construct_url_with(filter, list_opts) list.more_topics_url = construct_url_with(filter, list_opts)
@ -26,6 +44,14 @@ class ListController < ApplicationController
end end
respond(list) respond(list)
end end
define_method("#{filter}_category") do
self.send(filter, { category: @category.id })
end
define_method("#{filter}_category_none") do
self.send(filter, { category: @category.id, no_subcategories: true })
end
end end
Discourse.anonymous_filters.each do |filter| Discourse.anonymous_filters.each do |filter|
@ -55,14 +81,6 @@ class ListController < ApplicationController
end end
end end
def category
category_response
end
def category_none
category_response(no_subcategories: true)
end
def category_feed def category_feed
guardian.ensure_can_see!(@category) guardian.ensure_can_see!(@category)
discourse_expires_in 1.minute discourse_expires_in 1.minute
@ -72,6 +90,7 @@ class ListController < ApplicationController
@description = "#{I18n.t('topics_in_category', category: @category.name)} #{@category.description}" @description = "#{I18n.t('topics_in_category', category: @category.name)} #{@category.description}"
@atom_link = "#{Discourse.base_url}/category/#{@category.slug}.rss" @atom_link = "#{Discourse.base_url}/category/#{@category.slug}.rss"
@topic_list = TopicQuery.new.list_new_in_category(@category) @topic_list = TopicQuery.new.list_new_in_category(@category)
render 'list', formats: [:rss] render 'list', formats: [:rss]
end end
@ -81,11 +100,13 @@ class ListController < ApplicationController
redirect_to latest_path, :status => 301 redirect_to latest_path, :status => 301
end end
def top_lists def top(options = nil)
discourse_expires_in 1.minute discourse_expires_in 1.minute
options = build_topic_list_options top_options = build_topic_list_options
top = generate_top_lists(options) top_options.merge!(options) if options
top = generate_top_lists(top_options)
respond_to do |format| respond_to do |format|
format.html do format.html do
@ -99,28 +120,38 @@ class ListController < ApplicationController
end end
end end
def top_category
options = { category: @category.id }
top(options)
end
def top_category_none
options = { category: @category.id, no_subcategories: true }
top(options)
end
TopTopic.periods.each do |period| TopTopic.periods.each do |period|
define_method("top_#{period}") do define_method("top_#{period}") do |options = nil|
options = build_topic_list_options top_options = build_topic_list_options
options[:per_page] = SiteSetting.topics_per_period_in_top_page top_options.merge!(options) if options
top_options[:per_page] = SiteSetting.topics_per_period_in_top_page
user = list_target_user user = list_target_user
list = TopicQuery.new(user, options).public_send("list_top_#{period}") list = TopicQuery.new(user, top_options).public_send("list_top_#{period}")
list.more_topics_url = construct_url_with(period, options, "top") list.more_topics_url = construct_url_with(period, top_options, "top")
respond(list) respond(list)
end end
define_method("top_#{period}_category") do
self.send("top_#{period}", { category: @category.id })
end
define_method("top_#{period}_category_none") do
self.send("top_#{period}", { category: @category.id, no_subcategories: true })
end
end end
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)
discourse_expires_in 1.minute discourse_expires_in 1.minute
@ -158,12 +189,13 @@ class ListController < ApplicationController
if parent_slug_or_id.present? if parent_slug_or_id.present?
parent_category_id = Category.where(slug: parent_slug_or_id).pluck(:id).first || parent_category_id = Category.where(slug: parent_slug_or_id).pluck(:id).first ||
Category.where(id: parent_slug_or_id.to_i).pluck(:id).first Category.where(id: parent_slug_or_id.to_i).pluck(:id).first
raise Discourse::NotFound.new if parent_category_id.blank? raise Discourse::NotFound.new if parent_category_id.blank?
end end
@category = Category.where(slug: slug_or_id, parent_category_id: parent_category_id).includes(:featured_users).first || @category = Category.where(slug: slug_or_id, parent_category_id: parent_category_id).includes(:featured_users).first ||
Category.where(id: slug_or_id.to_i, parent_category_id: parent_category_id).includes(:featured_users).first Category.where(id: slug_or_id.to_i, parent_category_id: parent_category_id).includes(:featured_users).first
raise Discourse::NotFound.new if @category.blank?
end end
def build_topic_list_options def build_topic_list_options

View File

@ -7,28 +7,30 @@
<link><%= @link %></link> <link><%= @link %></link>
<description><%= @description %></description> <description><%= @description %></description>
<%= "<language>#{lang}</language>" if lang %> <%= "<language>#{lang}</language>" if lang %>
<lastBuildDate><%= @topic_list.topics.first.created_at.rfc2822 %></lastBuildDate> <% if @topic_list.topics && @topic_list.topics.length > 0 %>
<atom:link href="<%= @atom_link %>" rel="self" type="application/rss+xml" /> <lastBuildDate><%= @topic_list.topics.first.created_at.rfc2822 %></lastBuildDate>
<% @topic_list.topics.each do |topic| %> <atom:link href="<%= @atom_link %>" rel="self" type="application/rss+xml" />
<% topic_url = Discourse.base_url + topic.relative_url -%> <% @topic_list.topics.each do |topic| %>
<item> <% topic_url = Discourse.base_url + topic.relative_url -%>
<title><%= topic.title %></title> <item>
<author><%= "#{site_email} (@#{topic.user.username}#{" #{topic.user.name}" if topic.user.name.present?})" -%></author> <title><%= topic.title %></title>
<category><%= topic.category.name %></category> <author><%= "#{site_email} (@#{topic.user.username}#{" #{topic.user.name}" if topic.user.name.present?})" -%></author>
<description><![CDATA[ <category><%= topic.category.name %></category>
<p><%= t('author_wrote', author: link_to(topic.user.name, userpage_url(topic.user))).html_safe %></p> <description><![CDATA[
<blockquote> <p><%= t('author_wrote', author: link_to(topic.user.name, userpage_url(topic.user))).html_safe %></p>
<%= topic.posts.first.cooked.html_safe %> <blockquote>
</blockquote> <%= topic.posts.first.cooked.html_safe %>
<p><%= t 'num_posts' %> <%= topic.posts_count %></p> </blockquote>
<p><%= t 'num_participants' %> <%= topic.participant_count %></p> <p><%= t 'num_posts' %> <%= topic.posts_count %></p>
<p><%= link_to t('read_full_topic'), topic_url %></p> <p><%= t 'num_participants' %> <%= topic.participant_count %></p>
]]></description> <p><%= link_to t('read_full_topic'), topic_url %></p>
<link><%= topic_url %></link> ]]></description>
<pubDate><%= topic.created_at.rfc2822 %></pubDate> <link><%= topic_url %></link>
<guid><%= topic_url %></guid> <pubDate><%= topic.created_at.rfc2822 %></pubDate>
<source url="<%= topic_url %>.rss"><%= topic.title %></source> <guid><%= topic_url %></guid>
</item> <source url="<%= topic_url %>.rss"><%= topic.title %></source>
</item>
<% end %>
<% end %> <% end %>
</channel> </channel>
</rss> </rss>

View File

@ -200,24 +200,23 @@ Discourse::Application.routes.draw do
resources :categories, :except => :show resources :categories, :except => :show
get "category/:id/show" => "categories#show" get "category/:id/show" => "categories#show"
post "category/:category_id/move" => "categories#move", as: "category_move" post "category/:category_id/move" => "categories#move"
get "category/:category.rss" => "list#category_feed", format: :rss, as: "category_feed" get "category/:category.rss" => "list#category_feed", format: :rss
get "category/:category" => "list#category" get "category/:parent_category/:category.rss" => "list#category_feed", format: :rss
get "category/:category/none" => "list#category_none" get "category/:category" => "list#latest_category"
get "category/:parent_category/:category" => "list#category" get "category/:category/none" => "list#latest_category_none"
get "category/:parent_category/:category" => "list#latest_category"
get "top" => "list#top_lists" get "top" => "list#top"
get "category/:category/l/top" => "list#top_lists" get "category/:category/l/top" => "list#top_category"
get "category/:category/none/l/top" => "list#top_lists" get "category/:category/none/l/top" => "list#top_category_none"
get "category/:parent_category/:category/l/top" => "list#top_lists" get "category/:parent_category/:category/l/top" => "list#top_category"
TopTopic.periods.each do |period| TopTopic.periods.each do |period|
get "top/#{period}" => "list#top_#{period}" get "top/#{period}" => "list#top_#{period}"
get "top/#{period}/more" => "list#top_#{period}" get "category/:category/l/top/#{period}" => "list#top_#{period}_category"
get "category/:category/l/top/#{period}" => "list#top_#{period}" get "category/:category/none/l/top/#{period}" => "list#top_#{period}_category_none"
get "category/:category/l/top/#{period}/more" => "list#top_#{period}" get "category/:parent_category/:category/l/top/#{period}" => "list#top_#{period}_category"
get "category/:parent_category/:category/l/top/#{period}" => "list#top_#{period}"
get "category/:parent_category/:category/l/top/#{period}/more" => "list#top_#{period}"
end end
Discourse.anonymous_filters.each do |filter| Discourse.anonymous_filters.each do |filter|
@ -227,10 +226,12 @@ Discourse::Application.routes.draw do
Discourse.filters.each do |filter| Discourse.filters.each do |filter|
get "#{filter}" => "list##{filter}" get "#{filter}" => "list##{filter}"
get "#{filter}/more" => "list##{filter}" get "#{filter}/more" => "list##{filter}"
get "category/:category/l/#{filter}" => "list##{filter}" get "category/:category/l/#{filter}" => "list##{filter}_category"
get "category/:category/l/#{filter}/more" => "list##{filter}" get "category/:category/l/#{filter}/more" => "list##{filter}_category"
get "category/:parent_category/:category/l/#{filter}" => "list##{filter}" get "category/:category/none/l/#{filter}" => "list##{filter}_category_none"
get "category/:parent_category/:category/l/#{filter}/more" => "list##{filter}" get "category/:category/none/l/#{filter}/more" => "list##{filter}_category_none"
get "category/:parent_category/:category/l/#{filter}" => "list##{filter}_category"
get "category/:parent_category/:category/l/#{filter}/more" => "list##{filter}_category"
end end
get "search" => "search#query" get "search" => "search#query"
@ -303,6 +304,6 @@ Discourse::Application.routes.draw do
# special case for categories # special case for categories
root to: "categories#index", constraints: HomePageConstraint.new("categories"), :as => "categories_index" root to: "categories#index", constraints: HomePageConstraint.new("categories"), :as => "categories_index"
# special case for top # special case for top
root to: "list#top_lists", constraints: HomePageConstraint.new("top"), :as => "top_lists" root to: "list#top", constraints: HomePageConstraint.new("top"), :as => "top_lists"
end end

View File

@ -129,7 +129,11 @@ class TopicQuery
end end
def list_new_in_category(category) def list_new_in_category(category)
create_list(:new_in_category, unordered: true) {|l| l.where(category_id: category.id).by_newest.first(25)} create_list(:new_in_category, unordered: true) do |list|
list.where(category_id: category.id)
.by_newest
.first(25)
end
end end
def self.new_filter(list, treat_as_new_topic_start_date) def self.new_filter(list, treat_as_new_topic_start_date)

View File

@ -58,7 +58,7 @@ describe ListController do
context 'with access to see the category' do context 'with access to see the category' do
before do before do
xhr :get, :category, category: category.slug xhr :get, :latest_category, category: category.slug
end end
it { should respond_with(:success) } it { should respond_with(:success) }
@ -66,7 +66,7 @@ describe ListController do
context 'with a link that includes an id' do context 'with a link that includes an id' do
before do before do
xhr :get, :category, category: "#{category.id}-#{category.slug}" xhr :get, :latest_category, category: "#{category.id}-#{category.slug}"
end end
it { should respond_with(:success) } it { should respond_with(:success) }
@ -77,7 +77,7 @@ describe ListController do
let!(:other_category) { Fabricate(:category, name: "#{category.id} name") } let!(:other_category) { Fabricate(:category, name: "#{category.id} name") }
before do before do
xhr :get, :category, category: other_category.slug xhr :get, :latest_category, category: other_category.slug
end end
it { should respond_with(:success) } it { should respond_with(:success) }
@ -92,7 +92,7 @@ describe ListController do
context 'when parent and child are requested' do context 'when parent and child are requested' do
before do before do
xhr :get, :category, parent_category: category.slug, category: sub_category.slug xhr :get, :latest_category, parent_category: category.slug, category: sub_category.slug
end end
it { should respond_with(:success) } it { should respond_with(:success) }
@ -100,7 +100,7 @@ describe ListController do
context 'when child is requested with the wrong parent' do context 'when child is requested with the wrong parent' do
before do before do
xhr :get, :category, parent_category: 'not_the_right_slug', category: sub_category.slug xhr :get, :latest_category, parent_category: 'not_the_right_slug', category: sub_category.slug
end end
it { should_not respond_with(:success) } it { should_not respond_with(:success) }