require_dependency 'post_creator' require_dependency 'post_destroyer' require_dependency 'distributed_memoizer' class PostsController < ApplicationController # Need to be logged in for all actions here before_filter :ensure_logged_in, except: [:show, :replies, :by_number, :short_link, :versions] skip_before_filter :store_incoming_links, only: [:short_link] skip_before_filter :check_xhr, only: [:markdown,:short_link] def markdown post = Post.where(topic_id: params[:topic_id].to_i, post_number: (params[:post_number] || 1).to_i).first if post && guardian.can_see?(post) render text: post.raw, content_type: 'text/plain' else raise Discourse::NotFound end end def short_link post = Post.find(params[:post_id].to_i) IncomingLink.add(request,current_user) redirect_to post.url end def create params = create_params key = params_key(params) result = DistributedMemoizer.memoize(key, 120) do post_creator = PostCreator.new(current_user, params) post = post_creator.create if post_creator.errors.present? # If the post was spam, flag all the user's posts as spam current_user.flag_linked_posts_as_spam if post_creator.spam? "e" << MultiJson.dump(errors: post_creator.errors.full_messages) else post_serializer = PostSerializer.new(post, scope: guardian, root: false) post_serializer.topic_slug = post.topic.slug if post.topic.present? post_serializer.draft_sequence = DraftSequence.current(current_user, post.topic.draft_key) "s" << MultiJson.dump(post_serializer) end end payload = result[1..-1] if result[0] == "e" # don't memoize errors $redis.del(DistributedMemoizer.redis_key(key)) render json: payload, status: 422 else render json: payload end end def update params.require(:post) post = Post.where(id: params[:id]).first post.image_sizes = params[:image_sizes] if params[:image_sizes].present? guardian.ensure_can_edit!(post) # to stay consistent with the create api, # we should allow for title changes and category changes here # we should also move all of this to a post updater. if post.post_number == 1 && (params[:title] || params[:post][:category]) post.topic.title = params[:title] if params[:title] Topic.transaction do post.topic.change_category(params[:post][:category]) post.topic.save end if post.topic.errors.present? render_json_error(post.topic) return end end revisor = PostRevisor.new(post) if revisor.revise!(current_user, params[:post][:raw]) TopicLink.extract_from(post) end if post.errors.present? render_json_error(post) return end post_serializer = PostSerializer.new(post, scope: guardian, root: false) post_serializer.draft_sequence = DraftSequence.current(current_user, post.topic.draft_key) link_counts = TopicLink.counts_for(guardian,post.topic, [post]) post_serializer.single_post_link_counts = link_counts[post.id] if link_counts.present? post_serializer.topic_slug = post.topic.slug if post.topic.present? result = {post: post_serializer.as_json} if revisor.category_changed.present? result[:category] = BasicCategorySerializer.new(revisor.category_changed, scope: guardian, root: false).as_json end render_json_dump(result) end def by_number @post = Post.where(topic_id: params[:topic_id], post_number: params[:post_number]).first guardian.ensure_can_see!(@post) @post.revert_to(params[:version].to_i) if params[:version].present? render_post_json(@post) end def show @post = find_post_from_params @post.revert_to(params[:version].to_i) if params[:version].present? render_post_json(@post) end def destroy post = find_post_from_params guardian.ensure_can_delete!(post) destroyer = PostDestroyer.new(current_user, post) destroyer.destroy render nothing: true end def recover post = find_post_from_params guardian.ensure_can_recover_post!(post) destroyer = PostDestroyer.new(current_user, post) destroyer.recover post.reload render_post_json(post) end def destroy_many params.require(:post_ids) posts = Post.where(id: params[:post_ids]) raise Discourse::InvalidParameters.new(:post_ids) if posts.blank? # Make sure we can delete the posts posts.each {|p| guardian.ensure_can_delete!(p) } Post.transaction do topic_id = posts.first.topic_id posts.each {|p| p.destroy } Topic.reset_highest(topic_id) end render nothing: true end # Retrieves a list of versions and who made them for a post def versions post = find_post_from_params render_serialized(post.all_versions, VersionSerializer) end # Direct replies to this post def replies post = find_post_from_params render_serialized(post.replies, PostSerializer) end # Returns the "you're creating a post education" def education_text end def bookmark post = find_post_from_params if current_user if params[:bookmarked] == "true" PostAction.act(current_user, post, PostActionType.types[:bookmark]) else PostAction.remove_act(current_user, post, PostActionType.types[:bookmark]) end end render nothing: true end protected def find_post_from_params finder = Post.where(id: params[:id] || params[:post_id]) # Include deleted posts if the user is staff finder = finder.with_deleted if current_user.try(:staff?) post = finder.first guardian.ensure_can_see!(post) post end def render_post_json(post) post_serializer = PostSerializer.new(post, scope: guardian, root: false) post_serializer.add_raw = true render_json_dump(post_serializer) end private def params_key(params) "post##" << Digest::SHA1.hexdigest(params .to_a .concat([["user", current_user.id]]) .sort{|x,y| x[0] <=> y[0]}.join do |x,y| "#{x}:#{y}" end) end def create_params permitted = [ :raw, :topic_id, :title, :archetype, :category, :target_usernames, :reply_to_post_number, :image_sizes, :auto_close_days, :auto_track ] # param munging for WordPress params[:auto_track] = !(params[:auto_track].to_s == "false") if params[:auto_track] if api_key_valid? # php seems to be sending this incorrectly, don't fight with it params[:skip_validations] = params[:skip_validations].to_s == "true" permitted << :skip_validations end params.require(:raw) params.permit(*permitted).tap do |whitelisted| # TODO this does not feel right, we should name what meta_data is allowed whitelisted[:meta_data] = params[:meta_data] end end end