From c16b8364ab0edd83f19a9930e1fe9612a9f49875 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 17 Sep 2014 11:18:41 -0400 Subject: [PATCH] FIX: Support ember app routing to topics with only slugs --- .../javascripts/discourse/models/topic.js | 7 ++++--- .../discourse/routes/application_routes.js | 1 + .../discourse/routes/topic-by-slug.js.es6 | 9 +++++++++ app/controllers/topics_controller.rb | 7 +++++++ config/routes.rb | 1 + spec/controllers/topics_controller_spec.rb | 20 +++++++++++++++++++ .../helpers/create-pretender.js.es6 | 4 ++++ .../javascripts/integration/topic-test.js.es6 | 11 +++++++--- 8 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 app/assets/javascripts/discourse/routes/topic-by-slug.js.es6 diff --git a/app/assets/javascripts/discourse/models/topic.js b/app/assets/javascripts/discourse/models/topic.js index 5a0d372237d..9eab5f1a936 100644 --- a/app/assets/javascripts/discourse/models/topic.js +++ b/app/assets/javascripts/discourse/models/topic.js @@ -458,9 +458,10 @@ Discourse.Topic.reopenClass({ resetNew: function() { return Discourse.ajax("/topics/reset-new", {type: 'PUT'}); + }, + + idForSlug: function(slug) { + return Discourse.ajax("/t/id_for/" + slug); } - }); - - diff --git a/app/assets/javascripts/discourse/routes/application_routes.js b/app/assets/javascripts/discourse/routes/application_routes.js index 2a0cd141c54..d235940dd7e 100644 --- a/app/assets/javascripts/discourse/routes/application_routes.js +++ b/app/assets/javascripts/discourse/routes/application_routes.js @@ -17,6 +17,7 @@ Discourse.Route.buildRoutes(function() { this.route('fromParams', { path: '/' }); this.route('fromParamsNear', { path: '/:nearPost' }); }); + this.resource('topicBySlug', { path: '/t/:slug' }); this.resource('discovery', { path: '/' }, function() { router = this; diff --git a/app/assets/javascripts/discourse/routes/topic-by-slug.js.es6 b/app/assets/javascripts/discourse/routes/topic-by-slug.js.es6 new file mode 100644 index 00000000000..04d6cd1d6e8 --- /dev/null +++ b/app/assets/javascripts/discourse/routes/topic-by-slug.js.es6 @@ -0,0 +1,9 @@ +export default Ember.Route.extend({ + model: function(params) { + return Discourse.Topic.idForSlug(params.slug); + }, + + afterModel: function(result) { + Discourse.URL.routeTo(result.url); + } +}); diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index 557a9c6f414..24402ca661e 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -30,6 +30,13 @@ class TopicsController < ApplicationController skip_before_filter :check_xhr, only: [:show, :feed] + def id_for_slug + topic = Topic.find_by(slug: params[:slug].downcase) + guardian.ensure_can_see!(topic) + raise Discourse::NotFound unless topic + render json: {slug: topic.slug, topic_id: topic.id, url: topic.url} + end + def show flash["referer"] ||= request.referer diff --git a/config/routes.rb b/config/routes.rb index 13222d2c8b1..aaf51b3f8d8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -356,6 +356,7 @@ Discourse::Application.routes.draw do get 'embed/count' => 'embed#count' # Topic routes + get "t/id_for/:slug" => "topics#id_for_slug" get "t/:slug/:topic_id/wordpress" => "topics#wordpress", constraints: {topic_id: /\d+/} get "t/:slug/:topic_id/moderator-liked" => "topics#moderator_liked", constraints: {topic_id: /\d+/} get "t/:topic_id/wordpress" => "topics#wordpress", constraints: {topic_id: /\d+/} diff --git a/spec/controllers/topics_controller_spec.rb b/spec/controllers/topics_controller_spec.rb index 0d06495865b..b67b458b087 100644 --- a/spec/controllers/topics_controller_spec.rb +++ b/spec/controllers/topics_controller_spec.rb @@ -542,6 +542,26 @@ describe TopicsController do end end + describe 'id_for_slug' do + let(:topic) { Fabricate(:post).topic } + + it "returns JSON for the slug" do + xhr :get, :id_for_slug, slug: topic.slug + response.should be_success + json = ::JSON.parse(response.body) + json.should be_present + json['topic_id'].should == topic.id + json['url'].should == topic.url + json['slug'].should == topic.slug + end + + it "returns invalid access if the user can't see the topic" do + Guardian.any_instance.expects(:can_see?).with(topic).returns(false) + xhr :get, :id_for_slug, slug: topic.slug + response.should_not be_success + end + end + describe 'show' do let(:topic) { Fabricate(:post).topic } diff --git a/test/javascripts/helpers/create-pretender.js.es6 b/test/javascripts/helpers/create-pretender.js.es6 index 3a0ca4ef8a9..9f318133334 100644 --- a/test/javascripts/helpers/create-pretender.js.es6 +++ b/test/javascripts/helpers/create-pretender.js.es6 @@ -34,6 +34,10 @@ export default function() { } }); + this.get("/t/id_for/:slug", function() { + return response({id: 280, slug: "internationalization-localization", url: "/t/internationalization-localization/280"}); + }); + this.get("/404-body", function() { return [200, {"Content-Type": "text/html"}, "
not found
"]; }); diff --git a/test/javascripts/integration/topic-test.js.es6 b/test/javascripts/integration/topic-test.js.es6 index c57021bf925..503d3f912c5 100644 --- a/test/javascripts/integration/topic-test.js.es6 +++ b/test/javascripts/integration/topic-test.js.es6 @@ -1,11 +1,16 @@ integration("View Topic"); test("Enter a Topic", function() { - expect(2); - visit("/t/internationalization-localization/280"); andThen(function() { - ok(exists("#topic"), "The was rendered"); + ok(exists("#topic"), "The topic was rendered"); ok(exists("#topic .post-cloak"), "The topic has cloaked posts"); }); }); + +test("Enter without an id", function() { + visit("/t/internationalization-localization"); + andThen(function() { + ok(exists("#topic"), "The topic was rendered"); + }); +});