From 8855a0bfbee7e9a81b8f66c4df22d6cff69e68ae Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 21 Feb 2013 10:20:00 -0800 Subject: [PATCH] RSS of a topic via new route Adds TopicView#recent_posts; Post#by_newest, #with_user, #author_readable; User#readable_name Autodiscovery tag in topic show HTML. --- app/controllers/topics_controller.rb | 7 ++++++- app/models/post.rb | 7 +++++++ app/models/user.rb | 8 ++++++++ app/views/layouts/application.html.erb | 2 ++ app/views/topics/show.html.erb | 4 ++++ app/views/topics/show.rss.erb | 22 ++++++++++++++++++++ config/routes.rb | 1 + lib/topic_view.rb | 4 ++++ spec/components/topic_view_spec.rb | 19 +++++++++++++++++ spec/controllers/topics_controller_spec.rb | 10 +++++++++ spec/models/post_spec.rb | 24 ++++++++++++++++++++++ spec/models/user_spec.rb | 18 ++++++++++++++++ 12 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 app/views/topics/show.rss.erb diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index 682e52b5a7f..80e8a8ccea0 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -17,7 +17,7 @@ class TopicsController < ApplicationController :move_posts] before_filter :consider_user_for_promotion, only: :show - skip_before_filter :check_xhr, only: [:avatar, :show] + skip_before_filter :check_xhr, only: [:avatar, :show, :feed] caches_action :avatar, :cache_path => Proc.new {|c| "#{c.params[:post_number]}-#{c.params[:topic_id]}" } @@ -141,6 +141,11 @@ class TopicsController < ApplicationController render nothing: true end + def feed + @topic_view = TopicView.new(params[:topic_id]) + render 'topics/show', formats: [:rss] + end + private def create_topic_view diff --git a/app/models/post.rb b/app/models/post.rb index 4a5ee0b23df..4f5e4007fda 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -57,6 +57,9 @@ class Post < ActiveRecord::Base TopicUser.auto_track(self.user_id, self.topic_id, TopicUser::NotificationReasons::CREATED_POST) end + scope :by_newest, order('created_at desc, id desc') + scope :with_user, includes(:user) + def raw_quality sentinel = TextSentinel.new(self.raw, min_entropy: SiteSetting.body_min_entropy) @@ -298,6 +301,10 @@ class Post < ActiveRecord::Base "/t/#{Slug.for(topic.title)}/#{topic.id}/#{post_number}" end + def author_readable + user.readable_name + end + def revise(updated_by, new_raw, opts={}) PostRevisor.new(self).revise!(updated_by, new_raw, opts) end diff --git a/app/models/user.rb b/app/models/user.rb index 9626cc33899..564aa480fb6 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -458,6 +458,14 @@ class User < ActiveRecord::Base $redis.set(last_seen_key, Time.now.to_f) end + def readable_name + if name.present? && name != username + "#{name} (#{username})" + else + username + end + end + protected def cook diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 9237665816b..e1b44ca5b54 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -19,6 +19,8 @@ <%=csrf_meta_tags%> + + <%= yield :head %> diff --git a/app/views/topics/show.html.erb b/app/views/topics/show.html.erb index c61b8062143..2c669554f62 100644 --- a/app/views/topics/show.html.erb +++ b/app/views/topics/show.html.erb @@ -21,3 +21,7 @@

Powered by Discourse, best viewed with JavaScript enabled

+ +<% content_for :head do %> + <%= auto_discovery_link_tag(@topic_view, {action: :feed, format: :rss}, title: "RSS feed of '#{@topic_view.title}'") %> +<% end %> diff --git a/app/views/topics/show.rss.erb b/app/views/topics/show.rss.erb new file mode 100644 index 00000000000..3485c8a2659 --- /dev/null +++ b/app/views/topics/show.rss.erb @@ -0,0 +1,22 @@ + + + + <%= @topic_view.title %> + <%= Discourse.base_url %><%= @topic_view.relative_url %> + <%= @topic_view.posts.first.raw %> + + <% @topic_view.recent_posts.each do |post| %> + + <%= @topic_view.title %> at <%= post.created_at %> + <%= post.author_readable %> wrote:

+ <%= post.cooked.html_safe %> + ]]>
+ <%= Discourse.base_url %><%= post.url %> + <%= post.created_at.rfc2822 %> + <%= Discourse.base_url %><%= post.url %> + <%= @topic_view.title %> +
+ <% end %> +
+
diff --git a/config/routes.rb b/config/routes.rb index 8229cd2e43e..b153d30c245 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -176,6 +176,7 @@ Discourse::Application.routes.draw do put 't/:topic_id/unmute' => 'topics#unmute', :constraints => {:topic_id => /\d+/} get 't/:topic_id/:post_number' => 'topics#show', :constraints => {:topic_id => /\d+/, :post_number => /\d+/} + get 't/:slug/:topic_id.rss' => 'topics#feed', :format => :rss, :constraints => {:topic_id => /\d+/} get 't/:slug/:topic_id' => 'topics#show', :constraints => {:topic_id => /\d+/} get 't/:slug/:topic_id/:post_number' => 'topics#show', :constraints => {:topic_id => /\d+/, :post_number => /\d+/} post 't/:topic_id/timings' => 'topics#timings', :constraints => {:topic_id => /\d+/} diff --git a/lib/topic_view.rb b/lib/topic_view.rb index 5504aef580c..4fec47d641b 100644 --- a/lib/topic_view.rb +++ b/lib/topic_view.rb @@ -234,6 +234,10 @@ class TopicView @highest_post_number ||= @all_posts.maximum(:post_number) end + def recent_posts + @all_posts.by_newest.with_user.first(25) + end + protected def read_posts_set diff --git a/spec/components/topic_view_spec.rb b/spec/components/topic_view_spec.rb index d9d49572112..090241f4eab 100644 --- a/spec/components/topic_view_spec.rb +++ b/spec/components/topic_view_spec.rb @@ -283,5 +283,24 @@ describe TopicView do end + context '#recent_posts' do + before do + 24.times do # our let()s have already created 3 + Fabricate(:post, topic: topic, user: first_poster) + end + end + it 'returns at most 25 recent posts ordered newest first' do + recent_posts = topic_view.recent_posts + + # count + recent_posts.count.should == 25 + + # ordering + recent_posts.include?(p1).should be_false + recent_posts.include?(p3).should be_true + recent_posts.first.created_at.should > recent_posts.last.created_at + end + end + end diff --git a/spec/controllers/topics_controller_spec.rb b/spec/controllers/topics_controller_spec.rb index fccd19ae225..f73527e0227 100644 --- a/spec/controllers/topics_controller_spec.rb +++ b/spec/controllers/topics_controller_spec.rb @@ -299,6 +299,16 @@ describe TopicsController do end + describe '#feed' do + let(:topic) { Fabricate(:post).topic } + + it 'renders rss of the topic' do + get :feed, topic_id: topic.id, slug: 'foo', format: :rss + response.should be_success + response.content_type.should == 'application/rss+xml' + end + end + describe 'update' do it "won't allow us to update a topic when we're not logged in" do lambda { xhr :put, :update, topic_id: 1, slug: 'xyz' }.should raise_error(Discourse::NotLoggedIn) diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index daaa57eccba..375eec091a3 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -23,6 +23,24 @@ describe Post do it_behaves_like "a versioned model" + describe 'scopes' do + + describe '#by_newest' do + it 'returns posts ordered by created_at desc' do + 2.times { Fabricate(:post) } + Post.by_newest.first.created_at.should > Post.by_newest.last.created_at + end + end + + describe '#with_user' do + it 'gives you a user' do + Fabricate(:post, user: Fabricate(:user)) + Post.with_user.first.user.should be_a User + end + end + + end + describe 'post uniqueness' do context "disabled" do @@ -760,5 +778,11 @@ describe Post do end + describe '#readable_author' do + it 'delegates to the associated user' do + User.any_instance.expects(:readable_name) + Fabricate(:post).author_readable + end + end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index ef56c1071cb..6ca68b59c92 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -716,4 +716,22 @@ describe User do end + describe '#readable_name' do + context 'when name is missing' do + it 'returns just the username' do + Fabricate(:user, username: 'foo', name: nil).readable_name.should == 'foo' + end + end + context 'when name and username are identical' do + it 'returns just the username' do + Fabricate(:user, username: 'foo', name: 'foo').readable_name.should == 'foo' + end + end + context 'when name and username are not identical' do + it 'returns the name and username' do + Fabricate(:user, username: 'foo', name: 'Bar Baz').readable_name.should == 'Bar Baz (foo)' + end + end + end + end