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.
This commit is contained in:
Alexander 2013-02-21 10:20:00 -08:00
parent e363950bf5
commit 8855a0bfbe
12 changed files with 125 additions and 1 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -19,6 +19,8 @@
<%=csrf_meta_tags%>
<%= yield :head %>
</head>
<body>

View File

@ -21,3 +21,7 @@
<p>Powered by <a href="http://www.discourse.org">Discourse</a>, best viewed with JavaScript enabled</p>
<% content_for :head do %>
<%= auto_discovery_link_tag(@topic_view, {action: :feed, format: :rss}, title: "RSS feed of '#{@topic_view.title}'") %>
<% end %>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title><%= @topic_view.title %></title>
<link><%= Discourse.base_url %><%= @topic_view.relative_url %></link>
<description><%= @topic_view.posts.first.raw %></description>
<atom:link href="<%= Discourse.base_url %><%= @topic_view.relative_url %>.rss" rel="self" type="application/rss+xml" />
<% @topic_view.recent_posts.each do |post| %>
<item>
<title><%= @topic_view.title %> at <%= post.created_at %></title>
<description><![CDATA[
<p><%= post.author_readable %> wrote:</p>
<%= post.cooked.html_safe %>
]]></description>
<link><%= Discourse.base_url %><%= post.url %></link>
<pubDate><%= post.created_at.rfc2822 %></pubDate>
<guid><%= Discourse.base_url %><%= post.url %></guid>
<source url="<%= Discourse.base_url %><%= @topic_view.relative_url %>.rss"><%= @topic_view.title %></source>
</item>
<% end %>
</channel>
</rss>

View File

@ -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+/}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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