2013-02-06 03:16:51 +08:00
|
|
|
require_dependency 'topic_view'
|
|
|
|
require_dependency 'promotion'
|
2013-12-13 00:24:00 +08:00
|
|
|
require_dependency 'url_helper'
|
2014-01-31 00:15:49 +08:00
|
|
|
require_dependency 'topics_bulk_action'
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
|
|
class TopicsController < ApplicationController
|
2013-12-13 00:24:00 +08:00
|
|
|
include UrlHelper
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2013-02-07 23:45:24 +08:00
|
|
|
before_filter :ensure_logged_in, only: [:timings,
|
|
|
|
:destroy_timings,
|
|
|
|
:update,
|
|
|
|
:star,
|
|
|
|
:destroy,
|
2013-07-13 00:08:23 +08:00
|
|
|
:recover,
|
2013-02-07 23:45:24 +08:00
|
|
|
:status,
|
|
|
|
:invite,
|
|
|
|
:mute,
|
|
|
|
:unmute,
|
2013-02-06 03:16:51 +08:00
|
|
|
:set_notifications,
|
2013-03-07 04:17:07 +08:00
|
|
|
:move_posts,
|
2013-05-17 03:55:14 +08:00
|
|
|
:merge_topic,
|
2013-05-08 02:25:41 +08:00
|
|
|
:clear_pin,
|
2014-01-30 04:48:52 +08:00
|
|
|
:autoclose,
|
|
|
|
:bulk]
|
2013-03-07 04:17:07 +08:00
|
|
|
|
2013-02-11 02:50:26 +08:00
|
|
|
before_filter :consider_user_for_promotion, only: :show
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2013-08-14 04:08:29 +08:00
|
|
|
skip_before_filter :check_xhr, only: [:show, :feed]
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2013-02-11 02:50:26 +08:00
|
|
|
def show
|
2013-06-29 01:55:34 +08:00
|
|
|
# We'd like to migrate the wordpress feed to another url. This keeps up backwards compatibility with
|
|
|
|
# existing installs.
|
|
|
|
return wordpress if params[:best].present?
|
|
|
|
|
2013-06-21 05:20:08 +08:00
|
|
|
opts = params.slice(:username_filters, :filter, :page, :post_number)
|
2013-10-28 14:12:07 +08:00
|
|
|
username_filters = opts[:username_filters]
|
2013-09-17 02:08:55 +08:00
|
|
|
|
2013-10-28 14:12:07 +08:00
|
|
|
opts[:username_filters] = [username_filters] if username_filters.is_a?(String)
|
2013-10-04 00:51:30 +08:00
|
|
|
|
2013-06-07 02:41:27 +08:00
|
|
|
begin
|
|
|
|
@topic_view = TopicView.new(params[:id] || params[:topic_id], current_user, opts)
|
|
|
|
rescue Discourse::NotFound
|
2013-06-08 02:17:12 +08:00
|
|
|
topic = Topic.where(slug: params[:id]).first if params[:id]
|
2013-06-07 02:41:27 +08:00
|
|
|
raise Discourse::NotFound unless topic
|
|
|
|
return redirect_to(topic.relative_url)
|
|
|
|
end
|
2013-05-20 08:29:49 +08:00
|
|
|
|
2013-10-16 13:39:18 +08:00
|
|
|
discourse_expires_in 1.minute
|
2013-07-05 02:08:23 +08:00
|
|
|
|
2013-10-16 13:39:18 +08:00
|
|
|
redirect_to_correct_topic && return if slugs_do_not_match
|
2013-07-05 02:08:23 +08:00
|
|
|
|
2013-10-16 13:39:18 +08:00
|
|
|
# render workaround pseudo-static HTML page for old crawlers which ignores <noscript>
|
|
|
|
# (see http://meta.discourse.org/t/noscript-tag-and-some-search-engines/8078)
|
2013-10-28 14:12:07 +08:00
|
|
|
return render 'topics/plain', layout: false if (SiteSetting.enable_escaped_fragments && params.key?('_escaped_fragment_'))
|
2013-10-04 15:00:23 +08:00
|
|
|
|
2013-10-16 13:39:18 +08:00
|
|
|
track_visit_to_topic
|
2013-10-04 15:00:23 +08:00
|
|
|
|
2013-10-16 13:39:18 +08:00
|
|
|
if should_track_visit_to_topic?
|
|
|
|
@topic_view.draft = Draft.get(current_user, @topic_view.draft_key, @topic_view.draft_sequence)
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|
2013-02-13 19:04:43 +08:00
|
|
|
|
2013-10-16 13:39:18 +08:00
|
|
|
perform_show_response
|
|
|
|
|
2013-12-17 07:35:34 +08:00
|
|
|
canonical_url absolute_without_cdn(@topic_view.canonical_path)
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|
|
|
|
|
2013-06-29 01:55:34 +08:00
|
|
|
def wordpress
|
|
|
|
params.require(:best)
|
|
|
|
params.require(:topic_id)
|
2013-07-06 04:07:24 +08:00
|
|
|
params.permit(:min_trust_level, :min_score, :min_replies, :bypass_trust_level_score, :only_moderator_liked)
|
2013-10-28 14:12:07 +08:00
|
|
|
opts = { best: params[:best].to_i,
|
|
|
|
min_trust_level: params[:min_trust_level] ? 1 : params[:min_trust_level].to_i,
|
|
|
|
min_score: params[:min_score].to_i,
|
|
|
|
min_replies: params[:min_replies].to_i,
|
|
|
|
bypass_trust_level_score: params[:bypass_trust_level_score].to_i, # safe cause 0 means ignore
|
|
|
|
only_moderator_liked: params[:only_moderator_liked].to_s == "true"
|
|
|
|
}
|
|
|
|
|
|
|
|
@topic_view = TopicView.new(params[:topic_id], current_user, opts)
|
2013-10-16 13:39:18 +08:00
|
|
|
discourse_expires_in 1.minute
|
|
|
|
|
|
|
|
wordpress_serializer = TopicViewWordpressSerializer.new(@topic_view, scope: guardian, root: false)
|
|
|
|
render_json_dump(wordpress_serializer)
|
2013-06-21 05:20:08 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def posts
|
|
|
|
params.require(:topic_id)
|
|
|
|
params.require(:post_ids)
|
2013-06-29 01:55:34 +08:00
|
|
|
|
2013-06-21 05:20:08 +08:00
|
|
|
@topic_view = TopicView.new(params[:topic_id], current_user, post_ids: params[:post_ids])
|
|
|
|
render_json_dump(TopicViewPostsSerializer.new(@topic_view, scope: guardian, root: false))
|
2013-06-29 01:55:34 +08:00
|
|
|
end
|
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
def destroy_timings
|
|
|
|
PostTiming.destroy_for(current_user.id, params[:topic_id].to_i)
|
|
|
|
render nothing: true
|
|
|
|
end
|
|
|
|
|
|
|
|
def update
|
|
|
|
topic = Topic.where(id: params[:topic_id]).first
|
2013-10-28 14:12:07 +08:00
|
|
|
title, archetype = params[:title], params[:archetype]
|
2013-02-07 23:45:24 +08:00
|
|
|
guardian.ensure_can_edit!(topic)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2013-10-28 14:12:07 +08:00
|
|
|
topic.title = params[:title] if title.present?
|
2013-02-06 03:16:51 +08:00
|
|
|
# TODO: we may need smarter rules about converting archetypes
|
2013-10-28 14:12:07 +08:00
|
|
|
topic.archetype = "regular" if current_user.admin? && archetype == 'regular'
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2013-12-12 10:41:34 +08:00
|
|
|
topic.acting_user = current_user
|
|
|
|
|
2013-06-01 03:22:34 +08:00
|
|
|
success = false
|
2013-02-06 03:16:51 +08:00
|
|
|
Topic.transaction do
|
2013-12-12 10:41:34 +08:00
|
|
|
success = topic.save && topic.change_category(params[:category])
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|
2013-12-12 10:41:34 +08:00
|
|
|
|
|
|
|
# this is used to return the title to the client as it may have been changed by "TextCleaner"
|
2013-10-28 14:12:07 +08:00
|
|
|
success ? render_serialized(topic, BasicTopicSerializer) : render_json_error(topic)
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|
|
|
|
|
2013-03-15 02:45:29 +08:00
|
|
|
def similar_to
|
2013-06-06 15:14:32 +08:00
|
|
|
params.require(:title)
|
|
|
|
params.require(:raw)
|
2013-03-15 02:45:29 +08:00
|
|
|
title, raw = params[:title], params[:raw]
|
2013-10-28 14:12:07 +08:00
|
|
|
[:title, :raw].each { |key| check_length_of(key, params[key]) }
|
2013-03-15 02:45:29 +08:00
|
|
|
|
2013-12-21 15:19:22 +08:00
|
|
|
# Only suggest similar topics if the site has a minimum amount of topics present.
|
2013-10-28 14:12:07 +08:00
|
|
|
topics = Topic.similar_to(title, raw, current_user).to_a if Topic.count_exceeds_minimum?
|
2013-06-20 01:13:12 +08:00
|
|
|
|
2013-03-15 02:45:29 +08:00
|
|
|
render_serialized(topics, BasicTopicSerializer)
|
|
|
|
end
|
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
def status
|
2013-06-06 15:14:32 +08:00
|
|
|
params.require(:status)
|
|
|
|
params.require(:enabled)
|
2013-10-28 14:12:07 +08:00
|
|
|
status, topic_id = params[:status], params[:topic_id].to_i
|
|
|
|
enabled = (params[:enabled] == 'true')
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2013-10-28 14:12:07 +08:00
|
|
|
check_for_status_presence(:status, status)
|
|
|
|
@topic = Topic.where(id: topic_id).first
|
2013-02-06 03:16:51 +08:00
|
|
|
guardian.ensure_can_moderate!(@topic)
|
2013-10-28 14:12:07 +08:00
|
|
|
@topic.update_status(status, enabled, current_user)
|
2013-02-06 03:16:51 +08:00
|
|
|
render nothing: true
|
|
|
|
end
|
|
|
|
|
|
|
|
def star
|
|
|
|
@topic = Topic.where(id: params[:topic_id].to_i).first
|
|
|
|
guardian.ensure_can_see!(@topic)
|
|
|
|
|
|
|
|
@topic.toggle_star(current_user, params[:starred] == 'true')
|
|
|
|
render nothing: true
|
|
|
|
end
|
|
|
|
|
2013-02-07 23:45:24 +08:00
|
|
|
def mute
|
2013-06-14 13:38:59 +08:00
|
|
|
toggle_mute
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def unmute
|
2013-06-14 13:38:59 +08:00
|
|
|
toggle_mute
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|
|
|
|
|
2013-05-08 02:25:41 +08:00
|
|
|
def autoclose
|
2013-11-27 08:06:20 +08:00
|
|
|
raise Discourse::InvalidParameters.new(:auto_close_time) unless params.has_key?(:auto_close_time)
|
|
|
|
topic = Topic.where(id: params[:topic_id].to_i).first
|
|
|
|
guardian.ensure_can_moderate!(topic)
|
|
|
|
topic.set_auto_close(params[:auto_close_time], current_user)
|
|
|
|
if topic.save
|
|
|
|
render json: success_json.merge!(auto_close_at: topic.auto_close_at)
|
|
|
|
else
|
|
|
|
render_json_error(topic)
|
|
|
|
end
|
2013-05-08 02:25:41 +08:00
|
|
|
end
|
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
def destroy
|
|
|
|
topic = Topic.where(id: params[:id]).first
|
|
|
|
guardian.ensure_can_delete!(topic)
|
2013-07-10 03:20:18 +08:00
|
|
|
topic.trash!(current_user)
|
2013-02-06 03:16:51 +08:00
|
|
|
render nothing: true
|
|
|
|
end
|
2013-02-07 23:45:24 +08:00
|
|
|
|
2013-07-13 00:08:23 +08:00
|
|
|
def recover
|
|
|
|
topic = Topic.where(id: params[:topic_id]).with_deleted.first
|
|
|
|
guardian.ensure_can_recover_topic!(topic)
|
|
|
|
topic.recover!
|
|
|
|
render nothing: true
|
|
|
|
end
|
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
def excerpt
|
2013-02-07 23:45:24 +08:00
|
|
|
render nothing: true
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|
|
|
|
|
2013-06-18 15:17:01 +08:00
|
|
|
def remove_allowed_user
|
|
|
|
params.require(:username)
|
|
|
|
topic = Topic.where(id: params[:topic_id]).first
|
|
|
|
guardian.ensure_can_remove_allowed_users!(topic)
|
|
|
|
|
|
|
|
if topic.remove_allowed_user(params[:username])
|
|
|
|
render json: success_json
|
|
|
|
else
|
|
|
|
render json: failed_json, status: 422
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
def invite
|
2013-10-28 14:12:07 +08:00
|
|
|
username_or_email = params[:user] ? fetch_username : fetch_email
|
2013-07-11 09:21:39 +08:00
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
topic = Topic.where(id: params[:topic_id]).first
|
|
|
|
guardian.ensure_can_invite_to!(topic)
|
|
|
|
|
2013-07-11 09:21:39 +08:00
|
|
|
if topic.invite(current_user, username_or_email)
|
|
|
|
user = User.find_by_username_or_email(username_or_email)
|
2013-06-18 15:17:01 +08:00
|
|
|
if user
|
|
|
|
render_json_dump BasicUserSerializer.new(user, scope: guardian, root: 'user')
|
|
|
|
else
|
|
|
|
render json: success_json
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
else
|
|
|
|
render json: failed_json, status: 422
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def set_notifications
|
|
|
|
topic = Topic.find(params[:topic_id].to_i)
|
|
|
|
TopicUser.change(current_user, topic.id, notification_level: params[:notification_level].to_i)
|
|
|
|
render json: success_json
|
|
|
|
end
|
|
|
|
|
2013-05-17 03:55:14 +08:00
|
|
|
def merge_topic
|
2013-06-06 15:14:32 +08:00
|
|
|
params.require(:destination_topic_id)
|
2013-05-17 03:55:14 +08:00
|
|
|
|
|
|
|
topic = Topic.where(id: params[:topic_id]).first
|
|
|
|
guardian.ensure_can_move_posts!(topic)
|
|
|
|
|
|
|
|
dest_topic = topic.move_posts(current_user, topic.posts.pluck(:id), destination_topic_id: params[:destination_topic_id].to_i)
|
2013-06-18 13:52:09 +08:00
|
|
|
render_topic_changes(dest_topic)
|
2013-05-17 03:55:14 +08:00
|
|
|
end
|
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
def move_posts
|
2013-06-06 15:14:32 +08:00
|
|
|
params.require(:post_ids)
|
2013-10-30 03:30:06 +08:00
|
|
|
params.require(:topic_id)
|
|
|
|
params.permit(:category_id)
|
2013-05-09 01:33:58 +08:00
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
topic = Topic.where(id: params[:topic_id]).first
|
|
|
|
guardian.ensure_can_move_posts!(topic)
|
|
|
|
|
2013-09-04 23:53:00 +08:00
|
|
|
dest_topic = move_posts_to_destination(topic)
|
2013-06-18 13:52:09 +08:00
|
|
|
render_topic_changes(dest_topic)
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|
|
|
|
|
2013-03-07 04:17:07 +08:00
|
|
|
def clear_pin
|
|
|
|
topic = Topic.where(id: params[:topic_id].to_i).first
|
|
|
|
guardian.ensure_can_see!(topic)
|
|
|
|
topic.clear_pin_for(current_user)
|
|
|
|
render nothing: true
|
|
|
|
end
|
2013-02-26 07:38:11 +08:00
|
|
|
|
2013-03-07 04:17:07 +08:00
|
|
|
def timings
|
2013-02-25 15:42:42 +08:00
|
|
|
PostTiming.process_timings(
|
2013-03-07 04:17:07 +08:00
|
|
|
current_user,
|
|
|
|
params[:topic_id].to_i,
|
|
|
|
params[:topic_time].to_i,
|
|
|
|
(params[:timings] || []).map{|post_number, t| [post_number.to_i, t.to_i]}
|
2013-02-25 15:42:42 +08:00
|
|
|
)
|
2013-02-06 03:16:51 +08:00
|
|
|
render nothing: true
|
|
|
|
end
|
|
|
|
|
2013-02-22 02:20:00 +08:00
|
|
|
def feed
|
|
|
|
@topic_view = TopicView.new(params[:topic_id])
|
2013-10-16 13:39:18 +08:00
|
|
|
discourse_expires_in 1.minute
|
|
|
|
render 'topics/show', formats: [:rss]
|
2013-02-22 02:20:00 +08:00
|
|
|
end
|
|
|
|
|
2014-01-30 04:48:52 +08:00
|
|
|
def bulk
|
|
|
|
topic_ids = params.require(:topic_ids).map {|t| t.to_i}
|
2014-01-31 00:15:49 +08:00
|
|
|
operation = params.require(:operation).symbolize_keys
|
|
|
|
raise ActionController::ParameterMissing.new(:operation_type) if operation[:type].blank?
|
|
|
|
operator = TopicsBulkAction.new(current_user, topic_ids, operation)
|
|
|
|
changed_topic_ids = operator.perform!
|
|
|
|
render_json_dump topic_ids: changed_topic_ids
|
2014-01-30 04:48:52 +08:00
|
|
|
end
|
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
private
|
|
|
|
|
2013-06-14 13:38:59 +08:00
|
|
|
def toggle_mute
|
2013-02-11 02:50:26 +08:00
|
|
|
@topic = Topic.where(id: params[:topic_id].to_i).first
|
|
|
|
guardian.ensure_can_see!(@topic)
|
|
|
|
|
2013-05-24 14:06:38 +08:00
|
|
|
@topic.toggle_mute(current_user)
|
2013-02-11 02:50:26 +08:00
|
|
|
render nothing: true
|
|
|
|
end
|
|
|
|
|
|
|
|
def consider_user_for_promotion
|
|
|
|
Promotion.new(current_user).review if current_user.present?
|
|
|
|
end
|
|
|
|
|
|
|
|
def slugs_do_not_match
|
|
|
|
params[:slug] && @topic_view.topic.slug != params[:slug]
|
|
|
|
end
|
|
|
|
|
|
|
|
def redirect_to_correct_topic
|
|
|
|
fullpath = request.fullpath
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2013-02-11 02:50:26 +08:00
|
|
|
split = fullpath.split('/')
|
|
|
|
split[2] = @topic_view.topic.slug
|
|
|
|
|
|
|
|
redirect_to split.join('/'), status: 301
|
|
|
|
end
|
|
|
|
|
|
|
|
def track_visit_to_topic
|
2013-10-04 15:00:23 +08:00
|
|
|
Jobs.enqueue(:view_tracker,
|
|
|
|
topic_id: @topic_view.topic.id,
|
|
|
|
ip: request.remote_ip,
|
|
|
|
user_id: (current_user.id if current_user),
|
|
|
|
track_visit: should_track_visit_to_topic?
|
|
|
|
)
|
|
|
|
|
2013-02-11 02:50:26 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def should_track_visit_to_topic?
|
2013-10-04 16:06:32 +08:00
|
|
|
!!((!request.xhr? || params[:track_visit]) && current_user)
|
2013-02-11 02:50:26 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def perform_show_response
|
|
|
|
topic_view_serializer = TopicViewSerializer.new(@topic_view, scope: guardian, root: false)
|
2013-06-29 01:55:34 +08:00
|
|
|
|
2013-02-11 02:50:26 +08:00
|
|
|
respond_to do |format|
|
|
|
|
format.html do
|
|
|
|
store_preloaded("topic_#{@topic_view.topic.id}", MultiJson.dump(topic_view_serializer))
|
|
|
|
end
|
|
|
|
|
|
|
|
format.json do
|
|
|
|
render_json_dump(topic_view_serializer)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2013-06-18 13:52:09 +08:00
|
|
|
|
|
|
|
def render_topic_changes(dest_topic)
|
|
|
|
if dest_topic.present?
|
|
|
|
render json: {success: true, url: dest_topic.relative_url}
|
|
|
|
else
|
|
|
|
render json: {success: false}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-09-04 23:53:00 +08:00
|
|
|
def move_posts_to_destination(topic)
|
2013-06-18 13:52:09 +08:00
|
|
|
args = {}
|
|
|
|
args[:title] = params[:title] if params[:title].present?
|
|
|
|
args[:destination_topic_id] = params[:destination_topic_id].to_i if params[:destination_topic_id].present?
|
2013-10-30 03:30:06 +08:00
|
|
|
args[:category_id] = params[:category_id].to_i if params[:category_id].present?
|
2013-06-18 13:52:09 +08:00
|
|
|
|
2013-09-04 23:53:00 +08:00
|
|
|
topic.move_posts(current_user, post_ids_including_replies, args)
|
2013-06-18 13:52:09 +08:00
|
|
|
end
|
|
|
|
|
2013-10-28 14:12:07 +08:00
|
|
|
def check_length_of(key, attr)
|
|
|
|
str = (key == :raw) ? "body" : key.to_s
|
|
|
|
invalid_param(key) if attr.length < SiteSetting.send("min_#{str}_similar_length")
|
|
|
|
end
|
|
|
|
|
|
|
|
def check_for_status_presence(key, attr)
|
|
|
|
invalid_param(key) unless %w(visible closed pinned archived).include?(attr)
|
|
|
|
end
|
|
|
|
|
|
|
|
def invalid_param(key)
|
|
|
|
raise Discourse::InvalidParameters.new(key.to_sym)
|
|
|
|
end
|
|
|
|
|
|
|
|
def fetch_username
|
|
|
|
params.require(:user)
|
|
|
|
params[:user]
|
|
|
|
end
|
|
|
|
|
|
|
|
def fetch_email
|
|
|
|
params.require(:email)
|
|
|
|
params[:email]
|
|
|
|
end
|
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|