DEV: Server-side category routing changes
The routes for categories are changing. The scheme that I intend to move us to is: /c/*slug_path/(:id)/ENDPOINT /c/*slug_path/(:id) This commit adds support for the new scheme to the server side without dropping support for existing URLs. It is necessary to support existing URLs for two reasons: * This commit does not change any client side routing code, * Posts that contain category hashtags that refer to a root category are baked into URLs that do not fit this new scheme, (/c/[id]-[slug])
This commit is contained in:
parent
4434f419c6
commit
d84c34ad75
|
@ -11,16 +11,12 @@ class ListController < ApplicationController
|
|||
# filtered topics lists
|
||||
Discourse.filters.map { |f| :"category_#{f}" },
|
||||
Discourse.filters.map { |f| :"category_none_#{f}" },
|
||||
Discourse.filters.map { |f| :"parent_category_category_#{f}" },
|
||||
Discourse.filters.map { |f| :"parent_category_category_none_#{f}" },
|
||||
# top summaries
|
||||
:category_top,
|
||||
:category_none_top,
|
||||
:parent_category_category_top,
|
||||
# top pages (ie. with a period)
|
||||
TopTopic.periods.map { |p| :"category_top_#{p}" },
|
||||
TopTopic.periods.map { |p| :"category_none_top_#{p}" },
|
||||
TopTopic.periods.map { |p| :"parent_category_category_top_#{p}" },
|
||||
# category feeds
|
||||
:category_feed,
|
||||
].flatten
|
||||
|
@ -34,8 +30,6 @@ class ListController < ApplicationController
|
|||
: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}" },
|
||||
Discourse.anonymous_filters.map { |f| :"parent_category_category_none_#{f}" },
|
||||
# category feeds
|
||||
:category_feed,
|
||||
# user topics feed
|
||||
|
@ -44,13 +38,11 @@ class ListController < ApplicationController
|
|||
:top,
|
||||
:category_top,
|
||||
:category_none_top,
|
||||
:parent_category_category_top,
|
||||
# top pages (ie. with a period)
|
||||
TopTopic.periods.map { |p| :"top_#{p}" },
|
||||
TopTopic.periods.map { |p| :"top_#{p}_feed" },
|
||||
TopTopic.periods.map { |p| :"category_top_#{p}" },
|
||||
TopTopic.periods.map { |p| :"category_none_top_#{p}" },
|
||||
TopTopic.periods.map { |p| :"parent_category_category_top_#{p}" },
|
||||
:group_topics
|
||||
].flatten
|
||||
|
||||
|
@ -124,15 +116,6 @@ class ListController < ApplicationController
|
|||
define_method("category_none_#{filter}") do
|
||||
self.public_send(filter, category: @category.id, no_subcategories: true)
|
||||
end
|
||||
|
||||
define_method("parent_category_category_#{filter}") do
|
||||
canonical_url "#{Discourse.base_url_no_prefix}#{@category.url}"
|
||||
self.public_send(filter, category: @category.id)
|
||||
end
|
||||
|
||||
define_method("parent_category_category_none_#{filter}") do
|
||||
self.public_send(filter, category: @category.id)
|
||||
end
|
||||
end
|
||||
|
||||
def category_default
|
||||
|
@ -257,10 +240,6 @@ class ListController < ApplicationController
|
|||
top(category: @category.id, no_subcategories: true)
|
||||
end
|
||||
|
||||
def parent_category_category_top
|
||||
top(category: @category.id)
|
||||
end
|
||||
|
||||
TopTopic.periods.each do |period|
|
||||
define_method("top_#{period}") do |options = nil|
|
||||
top_options = build_topic_list_options
|
||||
|
@ -296,10 +275,6 @@ class ListController < ApplicationController
|
|||
)
|
||||
end
|
||||
|
||||
define_method("parent_category_category_top_#{period}") do
|
||||
self.public_send("top_#{period}", category: @category.id)
|
||||
end
|
||||
|
||||
# rss feed
|
||||
define_method("top_#{period}_feed") do |options = nil|
|
||||
discourse_expires_in 1.minute
|
||||
|
@ -333,32 +308,42 @@ class ListController < ApplicationController
|
|||
|
||||
def page_params
|
||||
route_params = { format: 'json' }
|
||||
route_params[:category] = @category.slug_for_url if @category
|
||||
route_params[:parent_category] = @category.parent_category.slug_for_url if @category && @category.parent_category
|
||||
route_params[:username] = UrlHelper.escape_uri(params[:username]) if params[:username].present?
|
||||
|
||||
if @category.present?
|
||||
slug_path = @category.slug_path
|
||||
|
||||
route_params[:category_slug_path_with_id] =
|
||||
(slug_path + [@category.id.to_s]).join("/")
|
||||
end
|
||||
|
||||
route_params[:username] = UrlHelper.escape_uri(params[:username]) if params[:username].present?
|
||||
route_params
|
||||
end
|
||||
|
||||
def set_category
|
||||
slug_or_id = params.fetch(:category)
|
||||
parent_slug_or_id = params[:parent_category]
|
||||
id = params[:id].to_i
|
||||
parts = params.require(:category_slug_path_with_id).split('/')
|
||||
|
||||
parent_category_id = nil
|
||||
if parent_slug_or_id.present?
|
||||
parent_category_id = Category.query_parent_category(parent_slug_or_id)
|
||||
raise Discourse::NotFound.new("category not found", check_permalinks: true) if parent_category_id.blank? && !id
|
||||
if !parts.empty? && parts.last =~ /\A\d+\Z/
|
||||
id = parts.pop.to_i
|
||||
end
|
||||
slug_path = parts unless parts.empty?
|
||||
|
||||
if id.present?
|
||||
@category = Category.find_by_id(id)
|
||||
elsif slug_path.present?
|
||||
if (1..2).include?(slug_path.size)
|
||||
@category = Category.find_by_slug(*slug_path.reverse)
|
||||
end
|
||||
|
||||
# Legacy paths
|
||||
if @category.nil? && parts.last =~ /\A\d+-/
|
||||
@category = Category.find_by_id(parts.last.to_i)
|
||||
end
|
||||
end
|
||||
|
||||
@category = Category.query_category(slug_or_id, parent_category_id)
|
||||
raise Discourse::NotFound.new("category not found", check_permalinks: true) if @category.nil?
|
||||
|
||||
# Redirect if we have `/c/:parent_category/:category/:id`
|
||||
if params.include?(:id)
|
||||
category = Category.find_by_id(id)
|
||||
(redirect_to category.url, status: 301) && return if category
|
||||
end
|
||||
|
||||
raise Discourse::NotFound.new("category not found", check_permalinks: true) if !@category
|
||||
params[:category] = @category.id.to_s
|
||||
|
||||
@description_meta = @category.description_text
|
||||
if !guardian.can_see?(@category)
|
||||
|
@ -388,12 +373,21 @@ class ListController < ApplicationController
|
|||
|
||||
def construct_url_with(action, opts, url_prefix = nil)
|
||||
method = url_prefix.blank? ? "#{action_name}_path" : "#{url_prefix}_#{action_name}_path"
|
||||
url = if action == :prev
|
||||
public_send(method, opts.merge(prev_page_params))
|
||||
else # :next
|
||||
public_send(method, opts.merge(next_page_params))
|
||||
end
|
||||
url.sub('.json?', '?')
|
||||
|
||||
page_params =
|
||||
case action
|
||||
when :prev
|
||||
prev_page_params
|
||||
when :next
|
||||
next_page_params
|
||||
else
|
||||
raise "unreachable"
|
||||
end
|
||||
|
||||
opts = opts.dup
|
||||
opts.delete(:category) if page_params.include?(:category_slug_path_with_id)
|
||||
|
||||
public_send(method, opts.merge(page_params)).sub('.json?', '?')
|
||||
end
|
||||
|
||||
def get_excluded_category_ids(current_category = nil)
|
||||
|
|
|
@ -808,6 +808,16 @@ class Category < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def slug_path
|
||||
if self.parent_category_id.present?
|
||||
slug_path = self.parent_category.slug_path
|
||||
slug_path.push(self.slug_for_url)
|
||||
slug_path
|
||||
else
|
||||
[self.slug_for_url]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_permissions_compatibility(parent_permissions, child_permissions)
|
||||
|
|
|
@ -620,35 +620,49 @@ Discourse::Application.routes.draw do
|
|||
|
||||
get '/c', to: redirect(relative_url_root + 'categories')
|
||||
|
||||
resources :categories, except: :show
|
||||
post "category/:category_id/move" => "categories#move"
|
||||
resources :categories, except: [:show, :new, :edit]
|
||||
post "categories/reorder" => "categories#reorder"
|
||||
post "category/:category_id/notifications" => "categories#set_notifications"
|
||||
put "category/:category_id/slug" => "categories#update_slug"
|
||||
|
||||
scope path: 'category/:category_id' do
|
||||
post "/move" => "categories#move"
|
||||
post "/notifications" => "categories#set_notifications"
|
||||
put "/slug" => "categories#update_slug"
|
||||
end
|
||||
|
||||
get "category/*path" => "categories#redirect"
|
||||
|
||||
get "categories_and_latest" => "categories#categories_and_latest"
|
||||
get "categories_and_top" => "categories#categories_and_top"
|
||||
|
||||
get "c/:id/show" => "categories#show"
|
||||
|
||||
get "c/:category_slug/find_by_slug" => "categories#find_by_slug"
|
||||
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_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"
|
||||
get "c/:category/none/l/top" => "list#category_none_top", as: "category_none_top"
|
||||
get "c/:parent_category/:category/l/top" => "list#parent_category_category_top", as: "parent_category_category_top"
|
||||
|
||||
get "c/*category_slug_path_with_id.rss" => "list#category_feed", format: :rss
|
||||
scope path: 'c/*category_slug_path_with_id' do
|
||||
get "/none" => "list#category_none_latest"
|
||||
get "/none/l/top" => "list#category_none_top", as: "category_none_top"
|
||||
get "/l/top" => "list#category_top", as: "category_top"
|
||||
|
||||
TopTopic.periods.each do |period|
|
||||
get "/none/l/top/#{period}" => "list#category_none_top_#{period}", as: "category_none_top_#{period}"
|
||||
get "/l/top/#{period}" => "list#category_top_#{period}", as: "category_top_#{period}"
|
||||
end
|
||||
|
||||
Discourse.filters.each do |filter|
|
||||
get "/none/l/#{filter}" => "list#category_none_#{filter}", as: "category_none_#{filter}"
|
||||
get "/l/#{filter}" => "list#category_#{filter}", as: "category_#{filter}"
|
||||
end
|
||||
|
||||
get "/" => "list#category_default", as: "category_default"
|
||||
end
|
||||
|
||||
get "category_hashtags/check" => "category_hashtags#check"
|
||||
|
||||
TopTopic.periods.each do |period|
|
||||
get "top/#{period}.rss" => "list#top_#{period}_feed", format: :rss
|
||||
get "top/#{period}" => "list#top_#{period}"
|
||||
get "c/:category/l/top/#{period}" => "list#category_top_#{period}", as: "category_top_#{period}"
|
||||
get "c/:category/none/l/top/#{period}" => "list#category_none_top_#{period}", as: "category_none_top_#{period}"
|
||||
get "c/:parent_category/:category/l/top/#{period}" => "list#parent_category_category_top_#{period}", as: "parent_category_category_top_#{period}"
|
||||
end
|
||||
|
||||
Discourse.anonymous_filters.each do |filter|
|
||||
|
@ -657,13 +671,8 @@ Discourse::Application.routes.draw do
|
|||
|
||||
Discourse.filters.each do |filter|
|
||||
get "#{filter}" => "list##{filter}"
|
||||
get "c/:category/l/#{filter}" => "list#category_#{filter}", as: "category_#{filter}"
|
||||
get "c/:category/none/l/#{filter}" => "list#category_none_#{filter}", as: "category_none_#{filter}"
|
||||
get "c/:parent_category/:category/l/#{filter}" => "list#parent_category_category_#{filter}", as: "parent_category_category_#{filter}"
|
||||
end
|
||||
|
||||
get "category/*path" => "categories#redirect"
|
||||
|
||||
get "top" => "list#top"
|
||||
get "search/query" => "search#query"
|
||||
get "search" => "search#show"
|
||||
|
|
|
@ -380,19 +380,15 @@ RSpec.describe ListController do
|
|||
let(:child_category) { Fabricate(:category_with_definition, parent_category: category) }
|
||||
|
||||
context "with valid slug" do
|
||||
it "redirects to the child category" do
|
||||
get "/c/#{category.slug}/#{child_category.slug}/l/latest", params: {
|
||||
id: child_category.id
|
||||
}
|
||||
expect(response).to redirect_to(child_category.url)
|
||||
it "succeeds" do
|
||||
get "/c/#{category.slug}/#{child_category.slug}/#{child_category.id}/l/latest"
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
|
||||
context "with invalid slug" do
|
||||
it "redirects to child category" do
|
||||
get "/c/random_slug/another_random_slug/l/latest", params: {
|
||||
id: child_category.id
|
||||
}
|
||||
xit "redirects" do
|
||||
get "/c/random_slug/another_random_slug/#{child_category.id}/l/latest"
|
||||
expect(response).to redirect_to(child_category.url)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue