diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index fdf3dbb687d..f97fc1aad1f 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -37,14 +37,29 @@ class PostsController < ApplicationController last_post_id = params[:before].to_i last_post_id = Post.last.id if last_post_id <= 0 - # last 50 post IDs only, to avoid counting deleted posts in security check - posts = Post.order(created_at: :desc) - .where('posts.id <= ?', last_post_id) - .where('posts.id > ?', last_post_id - 50) - .includes(topic: :category) - .includes(user: :primary_group) - .includes(:reply_to_user) - .limit(50) + if params[:id] == "private_posts" + raise Discourse::NotFound if current_user.nil? + posts = Post.private_posts + .order(created_at: :desc) + .where('posts.id <= ?', last_post_id) + .where('posts.id > ?', last_post_id - 50) + .includes(topic: :category) + .includes(user: :primary_group) + .includes(:reply_to_user) + .limit(50) + rss_description = I18n.t("rss_description.private_posts") + else + posts = Post.public_posts + .order(created_at: :desc) + .where('posts.id <= ?', last_post_id) + .where('posts.id > ?', last_post_id - 50) + .includes(topic: :category) + .includes(user: :primary_group) + .includes(:reply_to_user) + .limit(50) + rss_description = I18n.t("rss_description.posts") + end + # Remove posts the user doesn't have permission to see # This isn't leaking any information we weren't already through the post ID numbers posts = posts.reject { |post| !guardian.can_see?(post) || post.topic.blank? } @@ -53,7 +68,7 @@ class PostsController < ApplicationController respond_to do |format| format.rss do @posts = posts - @title = "#{SiteSetting.title} - #{I18n.t("rss_description.posts")}" + @title = "#{SiteSetting.title} - #{rss_description}" @link = Discourse.base_url @description = I18n.t("rss_description.posts") render 'posts/latest', formats: [:rss] @@ -62,7 +77,7 @@ class PostsController < ApplicationController render_json_dump(serialize_data(posts, PostSerializer, scope: guardian, - root: 'latest_posts', + root: params[:id], add_raw: true, add_title: true, all_post_actions: counts) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index adf0b0dcd82..020235985fb 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -204,6 +204,7 @@ en: hot: "Hot topics" top: "Top topics" posts: "Latest posts" + private_posts: "Latest private messages" group_posts: "Latest posts from %{group_name}" group_mentions: "Latest mentions from %{group_name}" too_late_to_edit: "That post was created too long ago. It can no longer be edited or deleted." diff --git a/config/routes.rb b/config/routes.rb index fab10862bb6..80a1533d4d3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -347,7 +347,8 @@ Discourse::Application.routes.draw do # used to download attachments (old route) get "uploads/:site/:id/:sha" => "uploads#show", constraints: { site: /\w+/, id: /\d+/, sha: /[a-f0-9]{16}/ } - get "posts" => "posts#latest" + get "posts" => "posts#latest", id: "latest_posts" + get "private-posts" => "posts#latest", id: "private_posts" get "posts/by_number/:topic_id/:post_number" => "posts#by_number" get "posts/:id/reply-history" => "posts#reply_history" get "posts/:username/deleted" => "posts#deleted_posts", constraints: {username: USERNAME_ROUTE_FORMAT} diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb index 67392c081c6..635798e8d9b 100644 --- a/spec/controllers/posts_controller_spec.rb +++ b/spec/controllers/posts_controller_spec.rb @@ -55,27 +55,56 @@ describe PostsController do describe 'latest' do let(:user) { log_in } - let!(:post) { Fabricate(:post, user: user) } + let!(:public_topic) { Fabricate(:topic) } + let!(:post) { Fabricate(:post, user: user, topic: public_topic) } + let!(:private_topic) { Fabricate(:topic, archetype: Archetype.private_message, category: nil) } + let!(:private_post) { Fabricate(:post, user: user, topic: private_topic) } let!(:topicless_post) { Fabricate(:post, user: user, raw: '

Car 54, where are you?

') } - before do - topicless_post.update topic_id: -100 + context "public posts" do + before do + topicless_post.update topic_id: -100 + end + + it 'returns public posts with topic for json' do + xhr :get, :latest, id: "latest_posts", format: :json + expect(response).to be_success + json = ::JSON.parse(response.body) + post_ids = json['latest_posts'].map { |p| p['id'] } + expect(post_ids).to include post.id + expect(post_ids).to_not include private_post.id + expect(post_ids).to_not include topicless_post.id + end + + it 'returns public posts with topic for rss' do + xhr :get, :latest, id: "latest_posts", format: :rss + expect(response).to be_success + expect(assigns(:posts)).to include post + expect(assigns(:posts)).to_not include private_post + expect(assigns(:posts)).to_not include topicless_post + end end - it 'does not return posts without a topic for json' do - xhr :get, :latest, format: :json - expect(response).to be_success - json = ::JSON.parse(response.body) - post_ids = json['latest_posts'].map { |p| p['id'] } - expect(post_ids).to include post.id - expect(post_ids).to_not include topicless_post.id - end + context 'private posts' do + before do + Guardian.any_instance.expects(:can_see?).with(private_post).returns(true) + end - it 'does not return posts without a topic for rss' do - xhr :get, :latest, format: :rss - expect(response).to be_success - expect(assigns(:posts)).to include post - expect(assigns(:posts)).to_not include topicless_post + it 'returns private posts for json' do + xhr :get, :latest, id: "private_posts", format: :json + expect(response).to be_success + json = ::JSON.parse(response.body) + post_ids = json['private_posts'].map { |p| p['id'] } + expect(post_ids).to include private_post.id + expect(post_ids).to_not include post.id + end + + it 'returns private posts for rss' do + xhr :get, :latest, id: "private_posts", format: :rss + expect(response).to be_success + expect(assigns(:posts)).to include private_post + expect(assigns(:posts)).to_not include post + end end end