From 6b976433c9b918f31a646534140af99b7dc2f92a Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 27 Mar 2017 15:34:54 -0400 Subject: [PATCH] Support for both `/users/` and `/u/` paths --- .../initializers/url-redirects.js.es6 | 6 +- .../discourse/lib/discourse-location.js.es6 | 2 - .../javascripts/discourse/lib/url.js.es6 | 20 +++- .../discourse/mapping-router.js.es6 | 4 + .../discourse/routes/app-route-map.js.es6 | 6 +- config/routes.rb | 107 ++++++++++++++---- spec/controllers/email_controller_spec.rb | 2 +- spec/controllers/users_controller_spec.rb | 2 +- .../acceptance/user-anonymous-test.js.es6 | 2 +- test/javascripts/helpers/qunit-helpers.js.es6 | 2 + 10 files changed, 119 insertions(+), 34 deletions(-) diff --git a/app/assets/javascripts/discourse/initializers/url-redirects.js.es6 b/app/assets/javascripts/discourse/initializers/url-redirects.js.es6 index b0e9c79c10b..e183bda7424 100644 --- a/app/assets/javascripts/discourse/initializers/url-redirects.js.es6 +++ b/app/assets/javascripts/discourse/initializers/url-redirects.js.es6 @@ -12,12 +12,14 @@ export default { DiscourseURL.rewrite(/^\/category\//, "/c/"); DiscourseURL.rewrite(/^\/group\//, "/groups/"); DiscourseURL.rewrite(/\/private-messages\/$/, "/messages/"); + DiscourseURL.rewrite(/^\/users$/, "/u"); + DiscourseURL.rewrite(/^\/users\//, "/u/"); if (currentUser) { const username = currentUser.get('username'); - DiscourseURL.rewrite(new RegExp(`^/users/${username}/?$`, "i"), `/users/${username}/activity`); + DiscourseURL.rewrite(new RegExp(`^/u/${username}/?$`, "i"), `/u/${username}/activity`); } - DiscourseURL.rewrite(/^\/users\/([^\/]+)\/?$/, "/users/$1/activity"); + DiscourseURL.rewrite(/^\/u\/([^\/]+)\/?$/, "/u/$1/summary"); } }; diff --git a/app/assets/javascripts/discourse/lib/discourse-location.js.es6 b/app/assets/javascripts/discourse/lib/discourse-location.js.es6 index 43b74ab1efa..c2e4fe0437a 100644 --- a/app/assets/javascripts/discourse/lib/discourse-location.js.es6 +++ b/app/assets/javascripts/discourse/lib/discourse-location.js.es6 @@ -66,12 +66,10 @@ const DiscourseLocation = Ember.Object.extend({ getURL() { const location = get(this, 'location'); let url = location.pathname; - url = url.replace(Discourse.BaseUri, ''); const search = location.search || ''; url += search; - return url; }, diff --git a/app/assets/javascripts/discourse/lib/url.js.es6 b/app/assets/javascripts/discourse/lib/url.js.es6 index e4dbd44f036..f4858447dbf 100644 --- a/app/assets/javascripts/discourse/lib/url.js.es6 +++ b/app/assets/javascripts/discourse/lib/url.js.es6 @@ -18,6 +18,23 @@ const SERVER_SIDE_ONLY = [ /\.json$/, ]; +export function rewritePath(path) { + const params = path.split("?"); + + let result = params[0]; + rewrites.forEach(rw => result = result.replace(rw.regexp, rw.replacement)); + + if (params.length > 1) { + result += `?${params[1]}`; + } + + return result; +} + +export function clearRewrites() { + rewrites.length = 0; +} + let _jumpScheduled = false; export function jumpToElement(elementId) { if (_jumpScheduled || Ember.isEmpty(elementId)) { return; } @@ -180,8 +197,7 @@ const DiscourseURL = Ember.Object.extend({ } } - rewrites.forEach(rw => path = path.replace(rw.regexp, rw.replacement)); - + path = rewritePath(path); if (this.navigatedToPost(oldPath, path, opts)) { return; } if (oldPath === path) { diff --git a/app/assets/javascripts/discourse/mapping-router.js.es6 b/app/assets/javascripts/discourse/mapping-router.js.es6 index e88b7f746d3..e1428039d94 100644 --- a/app/assets/javascripts/discourse/mapping-router.js.es6 +++ b/app/assets/javascripts/discourse/mapping-router.js.es6 @@ -1,4 +1,6 @@ import { defaultHomepage } from 'discourse/lib/utilities'; +import { rewritePath } from 'discourse/lib/url'; + const rootURL = Discourse.BaseUri; const BareRouter = Ember.Router.extend({ @@ -6,6 +8,7 @@ const BareRouter = Ember.Router.extend({ location: Ember.testing ? 'none': 'discourse-location', handleURL(url) { + url = rewritePath(url); const params = url.split('?'); if (params[0] === "/") { @@ -14,6 +17,7 @@ const BareRouter = Ember.Router.extend({ url = `${url}?${params[1]}`; } } + console.log(url); return this._super(url); } }); diff --git a/app/assets/javascripts/discourse/routes/app-route-map.js.es6 b/app/assets/javascripts/discourse/routes/app-route-map.js.es6 index 99a2aa0886a..cb0bdb8d5d3 100644 --- a/app/assets/javascripts/discourse/routes/app-route-map.js.es6 +++ b/app/assets/javascripts/discourse/routes/app-route-map.js.es6 @@ -62,9 +62,9 @@ export default function() { }); // User routes - this.route('users', { resetNamespace: true }); - this.route('password-reset', { path: '/users/password-reset/:token' }); - this.route('user', { path: '/users/:username', resetNamespace: true }, function() { + this.route('users', { resetNamespace: true, path: '/u' }); + this.route('password-reset', { path: '/u/password-reset/:token' }); + this.route('user', { path: '/u/:username', resetNamespace: true }, function() { this.route('summary'); this.route('userActivity', { path: '/activity', resetNamespace: true }, function() { this.route('topics'); diff --git a/config/routes.rb b/config/routes.rb index 292824ec6e1..2a239f15267 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -286,13 +286,6 @@ Discourse::Application.routes.draw do get "session/csrf" => "session#csrf" get "composer_messages" => "composer_messages#index" - resources :users, except: [:show, :update, :destroy] do - collection do - get "check_username" - get "is_local_username" - end - end - resources :static post "login" => "static#enter", constraints: { format: /(json|html)/ } get "login" => "static#show", id: "login", constraints: { format: /(json|html)/ } @@ -304,10 +297,90 @@ Discourse::Application.routes.draw do get "signup" => "static#show", id: "signup", constraints: { format: /(json|html)/ } get "login-preferences" => "static#show", id: "login", constraints: { format: /(json|html)/ } + get "my/*path", to: 'users#my_redirect' + get "user_preferences" => "users#user_preferences_redirect" + + # New /u/ routes + get 'u' => 'users#index' + get 'u/check_username' => 'users#check_username' + get 'u/is_local_username' => 'users#is_local_username' + post 'u' => 'users#create' + + get "u/admin-login" => "users#admin_login" + put "u/admin-login" => "users#admin_login" + get "u/admin-login/:token" => "users#admin_login" + + post "u/toggle-anon" => "users#toggle_anon" + post "u/read-faq" => "users#read_faq" + get "u/search/users" => "users#search_users" + get "u/account-created/" => "users#account_created" + get "u/password-reset/:token" => "users#password_reset" + get "u/confirm-email-token/:token" => "users#confirm_email_token", constraints: { format: 'json' } + put "u/password-reset/:token" => "users#password_reset" + get "u/activate-account/:token" => "users#activate_account" + put "u/activate-account/:token" => "users#perform_account_activation", as: 'perform_activate_account' + get "u/authorize-email/:token" => "users_email#confirm" + get "u/hp" => "users#get_honeypot_value" + + get "u/:username/private-messages" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/private-messages/:filter" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/messages" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/messages/:filter" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/messages/group/:group_name" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT, group_name: USERNAME_ROUTE_FORMAT} + + get "u/:username/messages/group/:group_name/archive" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT, group_name: USERNAME_ROUTE_FORMAT} + + get "u/:username.json" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT}, defaults: {format: :json} + get "u/:username" => "users#show", as: 'user', constraints: {username: USERNAME_ROUTE_FORMAT, format: /(json|html)/} + put "u/:username" => "users#update", constraints: {username: USERNAME_ROUTE_FORMAT}, defaults: { format: :json } + get "u/:username/emails" => "users#check_emails", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/preferences" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT}, as: :email_preferences + get "u/:username/preferences/email" => "users_email#index", constraints: {username: USERNAME_ROUTE_FORMAT} + put "u/:username/preferences/email" => "users_email#update", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/preferences/about-me" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/preferences/badge_title" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT} + put "u/:username/preferences/badge_title" => "users#badge_title", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/preferences/username" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT} + put "u/:username/preferences/username" => "users#username", constraints: {username: USERNAME_ROUTE_FORMAT} + delete "u/:username/preferences/user_image" => "users#destroy_user_image", constraints: {username: USERNAME_ROUTE_FORMAT} + put "u/:username/preferences/avatar/pick" => "users#pick_avatar", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/preferences/card-badge" => "users#card_badge", constraints: {username: USERNAME_ROUTE_FORMAT} + put "u/:username/preferences/card-badge" => "users#update_card_badge", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/staff-info" => "users#staff_info", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/summary" => "users#summary", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/invited" => "users#invited", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/invited_count" => "users#invited_count", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/invited/:filter" => "users#invited", constraints: {username: USERNAME_ROUTE_FORMAT} + post "u/action/send_activation_email" => "users#send_activation_email" + get "u/:username/summary" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} + + # user activity RSS feed + get "u/:username/activity/topics.rss" => "list#user_topics_feed", format: :rss, constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/activity.rss" => "posts#user_posts_feed", format: :rss, constraints: {username: USERNAME_ROUTE_FORMAT} + + get "u/:username/activity" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/activity/:filter" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/badges" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/notifications" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/notifications/:filter" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/activity/pending" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} + delete "u/:username" => "users#destroy", constraints: {username: USERNAME_ROUTE_FORMAT} + # The external_id constraint is to allow periods to be used in the value without becoming part of the format. ie: foo.bar.json + get "u/by-external/:external_id" => "users#show", constraints: {external_id: /[^\/]+/} + get "u/:username/flagged-posts" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} + get "u/:username/deleted-posts" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} + + get "u/:username/topic-tracking-state" => "users#topic_tracking_state", constraints: {username: USERNAME_ROUTE_FORMAT} + + # Old User routes, will be removed when /u/ is everywhere + get 'users' => 'users#index' + get 'users/check_username' => 'users#check_username' + get 'users/is_local_username' => 'users#is_local_username' + post 'users' => 'users#create' + get "users/admin-login" => "users#admin_login" put "users/admin-login" => "users#admin_login" get "users/admin-login/:token" => "users#admin_login" - post "users/toggle-anon" => "users#toggle_anon" post "users/read-faq" => "users#read_faq" get "users/search/users" => "users#search_users" @@ -316,25 +389,19 @@ Discourse::Application.routes.draw do get "users/confirm-email-token/:token" => "users#confirm_email_token", constraints: { format: 'json' } put "users/password-reset/:token" => "users#password_reset" get "users/activate-account/:token" => "users#activate_account" - put "users/activate-account/:token" => "users#perform_account_activation", as: 'perform_activate_account' + put "users/activate-account/:token" => "users#perform_account_activation" get "users/authorize-email/:token" => "users_email#confirm" - get "users/hp" => "users#get_honeypot_value" - get "my/*path", to: 'users#my_redirect' - - get "user_preferences" => "users#user_preferences_redirect" get "users/:username/private-messages" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/private-messages/:filter" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/messages" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/messages/:filter" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/messages/group/:group_name" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT, group_name: USERNAME_ROUTE_FORMAT} - get "users/:username/messages/group/:group_name/archive" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT, group_name: USERNAME_ROUTE_FORMAT} - get "users/:username.json" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT}, defaults: {format: :json} - get "users/:username" => "users#show", as: 'user', constraints: {username: USERNAME_ROUTE_FORMAT, format: /(json|html)/} + get "users/:username" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT, format: /(json|html)/} put "users/:username" => "users#update", constraints: {username: USERNAME_ROUTE_FORMAT}, defaults: { format: :json } get "users/:username/emails" => "users#check_emails", constraints: {username: USERNAME_ROUTE_FORMAT} - get "users/:username/preferences" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT}, as: :email_preferences + get "users/:username/preferences" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/preferences/email" => "users_email#index", constraints: {username: USERNAME_ROUTE_FORMAT} put "users/:username/preferences/email" => "users_email#update", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/preferences/about-me" => "users#preferences", constraints: {username: USERNAME_ROUTE_FORMAT} @@ -353,11 +420,8 @@ Discourse::Application.routes.draw do get "users/:username/invited/:filter" => "users#invited", constraints: {username: USERNAME_ROUTE_FORMAT} post "users/action/send_activation_email" => "users#send_activation_email" get "users/:username/summary" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} - - # user activity RSS feed get "users/:username/activity/topics.rss" => "list#user_topics_feed", format: :rss, constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/activity.rss" => "posts#user_posts_feed", format: :rss, constraints: {username: USERNAME_ROUTE_FORMAT} - get "users/:username/activity" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/activity/:filter" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/badges" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} @@ -365,12 +429,11 @@ Discourse::Application.routes.draw do get "users/:username/notifications/:filter" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/activity/pending" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} delete "users/:username" => "users#destroy", constraints: {username: USERNAME_ROUTE_FORMAT} - # The external_id constraint is to allow periods to be used in the value without becoming part of the format. ie: foo.bar.json get "users/by-external/:external_id" => "users#show", constraints: {external_id: /[^\/]+/} get "users/:username/flagged-posts" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/deleted-posts" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT} - get "users/:username/topic-tracking-state" => "users#topic_tracking_state", constraints: {username: USERNAME_ROUTE_FORMAT} + # -- end of old users paths get "user-badges/:username.json" => "user_badges#username", constraints: {username: USERNAME_ROUTE_FORMAT}, defaults: {format: :json} get "user-badges/:username" => "user_badges#username", constraints: {username: USERNAME_ROUTE_FORMAT} diff --git a/spec/controllers/email_controller_spec.rb b/spec/controllers/email_controller_spec.rb index 03c0df3c3ee..63fd6865adc 100644 --- a/spec/controllers/email_controller_spec.rb +++ b/spec/controllers/email_controller_spec.rb @@ -13,7 +13,7 @@ describe EmailController do it 'redirects to your user preferences' do get :preferences_redirect - expect(response).to redirect_to("/users/#{user.username}/preferences") + expect(response).to redirect_to("/u/#{user.username}/preferences") end end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index d69165c35f3..119859d3f5e 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -100,7 +100,7 @@ describe UsersController do it "redirects to their profile when logged in" do user = log_in get :user_preferences_redirect - expect(response).to redirect_to("/users/#{user.username_lower}/preferences") + expect(response).to redirect_to("/u/#{user.username_lower}/preferences") end end diff --git a/test/javascripts/acceptance/user-anonymous-test.js.es6 b/test/javascripts/acceptance/user-anonymous-test.js.es6 index 62e458cea52..2d93a45c3b4 100644 --- a/test/javascripts/acceptance/user-anonymous-test.js.es6 +++ b/test/javascripts/acceptance/user-anonymous-test.js.es6 @@ -49,6 +49,6 @@ test("Restricted Routes", () => { visit("/users/eviltrout/preferences"); andThen(() => { - equal(currentURL(), '/users/eviltrout/activity', "it redirects from preferences"); + equal(currentURL(), '/u/eviltrout/activity', "it redirects from preferences"); }); }); diff --git a/test/javascripts/helpers/qunit-helpers.js.es6 b/test/javascripts/helpers/qunit-helpers.js.es6 index 1fdc796b693..32747f9c5a2 100644 --- a/test/javascripts/helpers/qunit-helpers.js.es6 +++ b/test/javascripts/helpers/qunit-helpers.js.es6 @@ -8,6 +8,7 @@ import { resetPluginApi } from 'discourse/lib/plugin-api'; import { clearCache as clearOutletCache, resetExtraClasses } from 'discourse/lib/plugin-connectors'; import { clearHTMLCache } from 'discourse/helpers/custom-html'; import { flushMap } from 'discourse/models/store'; +import { clearRewrites } from 'discourse/lib/url'; function currentUser() { @@ -88,6 +89,7 @@ function acceptance(name, options) { clearOutletCache(); clearHTMLCache(); resetPluginApi(); + clearRewrites(); Discourse.reset(); } });