2019-05-02 17:17:27 -05:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2013-02-05 13:16:51 -06:00
|
|
|
class TopicsController < ApplicationController
|
2018-01-31 22:17:59 -06:00
|
|
|
requires_login only: [
|
2018-01-31 19:26:45 -06:00
|
|
|
:timings,
|
|
|
|
:destroy_timings,
|
|
|
|
:update,
|
2018-03-23 10:12:22 -05:00
|
|
|
:update_shared_draft,
|
2018-01-31 19:26:45 -06:00
|
|
|
:destroy,
|
|
|
|
:recover,
|
|
|
|
:status,
|
|
|
|
:invite,
|
|
|
|
:mute,
|
|
|
|
:unmute,
|
|
|
|
:set_notifications,
|
|
|
|
:move_posts,
|
|
|
|
:merge_topic,
|
|
|
|
:clear_pin,
|
|
|
|
:re_pin,
|
|
|
|
:status_update,
|
|
|
|
:timer,
|
|
|
|
:bulk,
|
|
|
|
:reset_new,
|
|
|
|
:change_post_owners,
|
|
|
|
:change_timestamps,
|
|
|
|
:archive_message,
|
|
|
|
:move_to_inbox,
|
|
|
|
:convert_topic,
|
2018-03-13 14:59:12 -05:00
|
|
|
:bookmark,
|
2018-08-09 19:51:03 -05:00
|
|
|
:publish,
|
2020-10-16 14:24:38 -05:00
|
|
|
:reset_bump_date,
|
|
|
|
:set_slow_mode
|
2018-01-31 19:26:45 -06:00
|
|
|
]
|
2013-03-06 14:17:07 -06:00
|
|
|
|
2017-08-30 23:06:56 -05:00
|
|
|
before_action :consider_user_for_promotion, only: :show
|
2013-02-05 13:16:51 -06:00
|
|
|
|
2018-04-16 05:32:23 -05:00
|
|
|
skip_before_action :check_xhr, only: [:show, :feed]
|
2013-02-05 13:16:51 -06:00
|
|
|
|
2014-09-17 10:18:41 -05:00
|
|
|
def id_for_slug
|
2019-10-16 15:08:43 -05:00
|
|
|
topic = Topic.find_by_slug(params[:slug])
|
2014-09-17 10:18:41 -05:00
|
|
|
guardian.ensure_can_see!(topic)
|
|
|
|
raise Discourse::NotFound unless topic
|
2017-07-27 20:20:09 -05:00
|
|
|
render json: { slug: topic.slug, topic_id: topic.id, url: topic.url }
|
2014-09-17 10:18:41 -05:00
|
|
|
end
|
|
|
|
|
2013-02-10 12:50:26 -06:00
|
|
|
def show
|
2015-04-22 11:41:28 -05:00
|
|
|
if request.referer
|
|
|
|
flash["referer"] ||= request.referer[0..255]
|
|
|
|
end
|
2014-08-03 20:06:06 -05:00
|
|
|
|
2013-06-28 12:55:34 -05: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?
|
|
|
|
|
2016-02-24 21:32:16 -06:00
|
|
|
# work around people somehow sending in arrays,
|
|
|
|
# arrays are not supported
|
|
|
|
params[:page] = params[:page].to_i rescue 1
|
|
|
|
|
2020-12-10 11:02:07 -06:00
|
|
|
opts = params.slice(:username_filters, :filter, :page, :post_number, :show_deleted, :replies_to_post_number, :filter_upwards_post_id)
|
2013-10-28 01:12:07 -05:00
|
|
|
username_filters = opts[:username_filters]
|
2013-09-16 13:08:55 -05:00
|
|
|
|
2016-07-31 23:45:05 -05:00
|
|
|
opts[:print] = true if params[:print].present?
|
2014-04-15 14:33:08 -05:00
|
|
|
opts[:username_filters] = username_filters.split(',') if username_filters.is_a?(String)
|
2013-10-03 11:51:30 -05:00
|
|
|
|
2016-09-19 12:31:19 -05:00
|
|
|
# Special case: a slug with a number in front should look by slug first before looking
|
|
|
|
# up that particular number
|
|
|
|
if params[:id] && params[:id] =~ /^\d+[^\d\\]+$/
|
2019-10-16 15:08:43 -05:00
|
|
|
topic = Topic.find_by_slug(params[:id])
|
2017-08-22 16:53:45 -05:00
|
|
|
return redirect_to_correct_topic(topic, opts[:post_number]) if topic
|
2016-09-19 12:31:19 -05:00
|
|
|
end
|
|
|
|
|
2016-08-02 23:28:46 -05:00
|
|
|
if opts[:print]
|
2016-08-05 00:12:35 -05:00
|
|
|
raise Discourse::InvalidAccess unless SiteSetting.max_prints_per_hour_per_user > 0
|
2016-08-02 23:28:46 -05:00
|
|
|
begin
|
2016-08-08 22:53:08 -05:00
|
|
|
RateLimiter.new(current_user, "print-topic-per-hour", SiteSetting.max_prints_per_hour_per_user, 1.hour).performed! unless @guardian.is_admin?
|
2016-08-02 23:28:46 -05:00
|
|
|
rescue RateLimiter::LimitExceeded
|
2017-12-12 03:18:58 -06:00
|
|
|
return render_json_error I18n.t("rate_limiter.slow_down")
|
2016-08-02 23:28:46 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-06 13:41:27 -05:00
|
|
|
begin
|
|
|
|
@topic_view = TopicView.new(params[:id] || params[:topic_id], current_user, opts)
|
2019-10-08 06:15:08 -05:00
|
|
|
rescue Discourse::NotFound => ex
|
2015-01-05 10:02:32 -06:00
|
|
|
if params[:id]
|
2019-10-16 15:08:43 -05:00
|
|
|
topic = Topic.find_by_slug(params[:id])
|
2017-08-22 16:53:45 -05:00
|
|
|
return redirect_to_correct_topic(topic, opts[:post_number]) if topic
|
2015-01-05 10:02:32 -06:00
|
|
|
end
|
2019-10-08 06:15:08 -05:00
|
|
|
|
|
|
|
raise ex
|
|
|
|
rescue Discourse::NotLoggedIn => ex
|
2020-05-28 12:29:36 -05:00
|
|
|
raise(SiteSetting.detailed_404 ? ex : Discourse::NotFound)
|
2019-10-08 06:15:08 -05:00
|
|
|
rescue Discourse::InvalidAccess => ex
|
|
|
|
# If the user can't see the topic, clean up notifications for it.
|
|
|
|
Notification.remove_for(current_user.id, params[:topic_id]) if current_user
|
|
|
|
|
|
|
|
deleted = guardian.can_see_topic?(ex.obj, false) ||
|
2020-04-21 22:44:19 -05:00
|
|
|
(!guardian.can_see_topic?(ex.obj) &&
|
|
|
|
ex.obj&.access_topic_via_group &&
|
|
|
|
ex.obj.deleted_at)
|
2019-10-08 06:15:08 -05:00
|
|
|
|
|
|
|
if SiteSetting.detailed_404
|
|
|
|
if deleted
|
|
|
|
raise Discourse::NotFound.new(
|
|
|
|
'deleted topic',
|
|
|
|
custom_message: 'deleted_topic',
|
|
|
|
status: 410,
|
|
|
|
check_permalinks: true,
|
|
|
|
original_path: ex.obj.relative_url
|
|
|
|
)
|
|
|
|
elsif !guardian.can_see_topic?(ex.obj) && group = ex.obj&.access_topic_via_group
|
|
|
|
raise Discourse::InvalidAccess.new(
|
|
|
|
'not in group',
|
|
|
|
ex.obj,
|
|
|
|
custom_message: 'not_in_group.title_topic',
|
2020-11-24 05:06:52 -06:00
|
|
|
custom_message_params: { group: group.name },
|
2019-10-08 06:15:08 -05:00
|
|
|
group: group
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
raise ex
|
|
|
|
else
|
|
|
|
raise Discourse::NotFound.new(
|
|
|
|
nil,
|
|
|
|
check_permalinks: deleted,
|
|
|
|
original_path: ex.obj.relative_url
|
|
|
|
)
|
|
|
|
end
|
2013-06-06 13:41:27 -05:00
|
|
|
end
|
2013-05-19 19:29:49 -05:00
|
|
|
|
2016-02-24 21:32:16 -06:00
|
|
|
page = params[:page]
|
2014-12-12 10:47:20 -06:00
|
|
|
if (page < 0) || ((page - 1) * @topic_view.chunk_size > @topic_view.topic.highest_post_number)
|
2014-09-22 02:08:11 -05:00
|
|
|
raise Discourse::NotFound
|
|
|
|
end
|
|
|
|
|
2013-10-16 00:39:18 -05:00
|
|
|
discourse_expires_in 1.minute
|
2013-07-04 13:08:23 -05:00
|
|
|
|
2016-06-09 19:53:03 -05:00
|
|
|
if slugs_do_not_match || (!request.format.json? && params[:slug].nil?)
|
|
|
|
redirect_to_correct_topic(@topic_view.topic, opts[:post_number])
|
|
|
|
return
|
|
|
|
end
|
2013-07-04 13:08:23 -05:00
|
|
|
|
2013-10-16 00:39:18 -05:00
|
|
|
track_visit_to_topic
|
2013-10-04 02:00:23 -05:00
|
|
|
|
2013-10-16 00:39:18 -05:00
|
|
|
if should_track_visit_to_topic?
|
|
|
|
@topic_view.draft = Draft.get(current_user, @topic_view.draft_key, @topic_view.draft_sequence)
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
2015-06-22 13:00:39 -05:00
|
|
|
|
|
|
|
unless @topic_view.topic.visible
|
|
|
|
response.headers['X-Robots-Tag'] = 'noindex'
|
|
|
|
end
|
2013-02-13 05:04:43 -06:00
|
|
|
|
2015-10-01 11:24:07 -05:00
|
|
|
canonical_url UrlHelper.absolute_without_cdn(@topic_view.canonical_path)
|
2015-09-21 18:37:23 -05:00
|
|
|
|
2018-04-12 23:58:33 -05:00
|
|
|
# provide hint to crawlers only for now
|
|
|
|
# we would like to give them a bit more signal about age of data
|
|
|
|
if use_crawler_layout?
|
|
|
|
if last_modified = @topic_view.posts&.map { |p| p.updated_at }&.max&.httpdate
|
|
|
|
response.headers['Last-Modified'] = last_modified
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-10-16 00:39:18 -05:00
|
|
|
perform_show_response
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
|
|
|
|
2018-03-13 14:59:12 -05:00
|
|
|
def publish
|
|
|
|
params.permit(:id, :destination_category_id)
|
|
|
|
|
|
|
|
topic = Topic.find(params[:id])
|
|
|
|
category = Category.find(params[:destination_category_id])
|
|
|
|
|
|
|
|
guardian.ensure_can_publish_topic!(topic, category)
|
|
|
|
topic = TopicPublisher.new(topic, current_user, category.id).publish!
|
|
|
|
|
|
|
|
render_serialized(topic.reload, BasicTopicSerializer)
|
|
|
|
end
|
|
|
|
|
2013-06-28 12:55:34 -05:00
|
|
|
def wordpress
|
|
|
|
params.require(:best)
|
|
|
|
params.require(:topic_id)
|
2013-07-05 15:07:24 -05:00
|
|
|
params.permit(:min_trust_level, :min_score, :min_replies, :bypass_trust_level_score, :only_moderator_liked)
|
2014-06-16 11:28:07 -05:00
|
|
|
|
2017-07-27 20:20:09 -05:00
|
|
|
opts = {
|
|
|
|
best: params[:best].to_i,
|
2014-08-19 19:15:24 -05:00
|
|
|
min_trust_level: params[:min_trust_level] ? params[:min_trust_level].to_i : 1,
|
2013-10-28 01:12:07 -05:00
|
|
|
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
|
2019-08-23 04:50:03 -05:00
|
|
|
only_moderator_liked: params[:only_moderator_liked].to_s == "true",
|
|
|
|
exclude_hidden: true
|
2013-10-28 01:12:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@topic_view = TopicView.new(params[:topic_id], current_user, opts)
|
2013-10-16 00:39:18 -05:00
|
|
|
discourse_expires_in 1.minute
|
|
|
|
|
|
|
|
wordpress_serializer = TopicViewWordpressSerializer.new(@topic_view, scope: guardian, root: false)
|
|
|
|
render_json_dump(wordpress_serializer)
|
2013-06-20 16:20:08 -05:00
|
|
|
end
|
|
|
|
|
2018-06-28 01:54:54 -05:00
|
|
|
def post_ids
|
|
|
|
params.require(:topic_id)
|
|
|
|
params.permit(:post_number, :username_filters, :filter)
|
|
|
|
|
|
|
|
options = {
|
|
|
|
filter_post_number: params[:post_number],
|
|
|
|
filter: params[:filter],
|
|
|
|
skip_limit: true,
|
|
|
|
asc: true,
|
|
|
|
skip_custom_fields: true
|
|
|
|
}
|
|
|
|
|
|
|
|
fetch_topic_view(options)
|
|
|
|
render_json_dump(post_ids: @topic_view.posts.pluck(:id))
|
|
|
|
end
|
|
|
|
|
2013-06-20 16:20:08 -05:00
|
|
|
def posts
|
|
|
|
params.require(:topic_id)
|
2019-02-21 17:37:18 -06:00
|
|
|
params.permit(:post_ids, :post_number, :username_filters, :filter, :include_suggested)
|
|
|
|
|
|
|
|
include_suggested = params[:include_suggested] == "true"
|
2013-06-28 12:55:34 -05:00
|
|
|
|
2018-06-28 01:54:54 -05:00
|
|
|
options = {
|
2018-07-11 02:41:26 -05:00
|
|
|
filter_post_number: params[:post_number],
|
|
|
|
post_ids: params[:post_ids],
|
|
|
|
asc: ActiveRecord::Type::Boolean.new.deserialize(params[:asc]),
|
2019-02-21 17:37:18 -06:00
|
|
|
filter: params[:filter],
|
|
|
|
include_suggested: include_suggested,
|
|
|
|
include_related: include_suggested,
|
2018-07-11 02:41:26 -05:00
|
|
|
}
|
|
|
|
|
2018-06-28 01:54:54 -05:00
|
|
|
fetch_topic_view(options)
|
2018-07-11 02:41:26 -05:00
|
|
|
|
2020-04-21 22:44:19 -05:00
|
|
|
render_json_dump(
|
|
|
|
TopicViewPostsSerializer.new(
|
|
|
|
@topic_view,
|
|
|
|
scope: guardian,
|
|
|
|
root: false,
|
|
|
|
include_raw: !!params[:include_raw]
|
|
|
|
)
|
|
|
|
)
|
2013-06-28 12:55:34 -05:00
|
|
|
end
|
|
|
|
|
2016-11-24 18:34:43 -06:00
|
|
|
def excerpts
|
|
|
|
params.require(:topic_id)
|
|
|
|
params.require(:post_ids)
|
|
|
|
|
|
|
|
post_ids = params[:post_ids].map(&:to_i)
|
|
|
|
unless Array === post_ids
|
|
|
|
render_json_error("Expecting post_ids to contain a list of posts ids")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
if post_ids.length > 100
|
|
|
|
render_json_error("Requested a chunk that is too big")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
@topic = Topic.with_deleted.where(id: params[:topic_id]).first
|
|
|
|
guardian.ensure_can_see!(@topic)
|
|
|
|
|
|
|
|
@posts = Post.where(hidden: false, deleted_at: nil, topic_id: @topic.id)
|
2017-07-27 20:20:09 -05:00
|
|
|
.where('posts.id in (?)', post_ids)
|
|
|
|
.joins("LEFT JOIN users u on u.id = posts.user_id")
|
|
|
|
.pluck(:id, :cooked, :username)
|
|
|
|
.map do |post_id, cooked, username|
|
2020-04-21 22:44:19 -05:00
|
|
|
{
|
|
|
|
post_id: post_id,
|
|
|
|
username: username,
|
|
|
|
excerpt: PrettyText.excerpt(cooked, 800, keep_emoji_images: true)
|
|
|
|
}
|
|
|
|
end
|
2016-11-24 18:34:43 -06:00
|
|
|
|
|
|
|
render json: @posts.to_json
|
|
|
|
end
|
|
|
|
|
2013-02-05 13:16:51 -06:00
|
|
|
def destroy_timings
|
2019-04-08 21:42:21 -05:00
|
|
|
topic_id = params[:topic_id].to_i
|
|
|
|
|
2018-11-12 23:07:48 -06:00
|
|
|
if params[:last].to_s == "1"
|
2019-04-08 21:42:21 -05:00
|
|
|
PostTiming.destroy_last_for(current_user, topic_id)
|
2018-11-12 23:07:48 -06:00
|
|
|
else
|
2019-04-08 21:42:21 -05:00
|
|
|
PostTiming.destroy_for(current_user.id, [topic_id])
|
|
|
|
end
|
|
|
|
|
|
|
|
last_notification = Notification
|
|
|
|
.where(
|
|
|
|
user_id: current_user.id,
|
|
|
|
topic_id: topic_id
|
|
|
|
)
|
|
|
|
.order(created_at: :desc)
|
|
|
|
.limit(1)
|
|
|
|
.first
|
|
|
|
|
|
|
|
if last_notification
|
|
|
|
last_notification.update!(read: false)
|
2018-11-12 23:07:48 -06:00
|
|
|
end
|
|
|
|
|
2017-08-30 23:06:56 -05:00
|
|
|
render body: nil
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
|
|
|
|
2018-03-23 10:12:22 -05:00
|
|
|
def update_shared_draft
|
|
|
|
topic = Topic.find_by(id: params[:id])
|
|
|
|
guardian.ensure_can_edit!(topic)
|
|
|
|
|
2018-03-23 10:33:02 -05:00
|
|
|
category = Category.where(id: params[:category_id].to_i).first
|
|
|
|
guardian.ensure_can_publish_topic!(topic, category)
|
|
|
|
|
|
|
|
row_count = SharedDraft.where(topic_id: topic.id).update_all(category_id: category.id)
|
|
|
|
if row_count == 0
|
|
|
|
SharedDraft.create(topic_id: topic.id, category_id: category.id)
|
|
|
|
end
|
2018-03-23 10:12:22 -05:00
|
|
|
|
|
|
|
render json: success_json
|
|
|
|
end
|
|
|
|
|
2013-02-05 13:16:51 -06:00
|
|
|
def update
|
2014-05-06 08:41:59 -05:00
|
|
|
topic = Topic.find_by(id: params[:topic_id])
|
2013-02-07 09:45:24 -06:00
|
|
|
guardian.ensure_can_edit!(topic)
|
2013-02-05 13:16:51 -06:00
|
|
|
|
2018-03-01 19:13:04 -06:00
|
|
|
if params[:category_id] && (params[:category_id].to_i != topic.category_id.to_i)
|
|
|
|
category = Category.find_by(id: params[:category_id])
|
2019-04-16 01:09:51 -05:00
|
|
|
|
2018-03-01 19:13:04 -06:00
|
|
|
if category || (params[:category_id].to_i == 0)
|
2018-07-12 21:51:08 -05:00
|
|
|
guardian.ensure_can_move_topic_to_category!(category)
|
2018-03-01 19:13:04 -06:00
|
|
|
else
|
|
|
|
return render_json_error(I18n.t('category.errors.not_found'))
|
|
|
|
end
|
2019-02-26 04:21:55 -06:00
|
|
|
|
2019-07-23 10:06:25 -05:00
|
|
|
if category && topic_tags = (params[:tags] || topic.tags.pluck(:name)).reject { |c| c.empty? }
|
|
|
|
if topic_tags.present?
|
|
|
|
allowed_tags = DiscourseTagging.filter_allowed_tags(
|
|
|
|
guardian,
|
|
|
|
category: category
|
2019-11-12 13:28:44 -06:00
|
|
|
).map(&:name)
|
2019-02-26 04:21:55 -06:00
|
|
|
|
|
|
|
invalid_tags = topic_tags - allowed_tags
|
2019-08-21 15:33:01 -05:00
|
|
|
|
|
|
|
# Do not raise an error on a topic's hidden tags when not modifying tags
|
|
|
|
if params[:tags].blank?
|
|
|
|
invalid_tags.each do |tag_name|
|
|
|
|
if DiscourseTagging.hidden_tag_names.include?(tag_name)
|
|
|
|
invalid_tags.delete(tag_name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-03-30 15:42:47 -05:00
|
|
|
invalid_tags = Tag.where_name(invalid_tags).pluck(:name)
|
|
|
|
|
2019-02-26 04:21:55 -06:00
|
|
|
if !invalid_tags.empty?
|
2019-08-21 15:33:01 -05:00
|
|
|
if (invalid_tags & DiscourseTagging.hidden_tag_names).present?
|
|
|
|
return render_json_error(I18n.t('category.errors.disallowed_tags_generic'))
|
|
|
|
else
|
|
|
|
return render_json_error(I18n.t('category.errors.disallowed_topic_tags', tags: invalid_tags.join(", ")))
|
|
|
|
end
|
2019-02-26 04:21:55 -06:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2018-03-01 19:13:04 -06:00
|
|
|
end
|
|
|
|
|
2014-10-27 16:06:43 -05:00
|
|
|
changes = {}
|
2019-05-17 03:26:00 -05:00
|
|
|
|
2015-04-18 06:53:53 -05:00
|
|
|
PostRevisor.tracked_topic_fields.each_key do |f|
|
2015-01-27 11:13:45 -06:00
|
|
|
changes[f] = params[f] if params.has_key?(f)
|
|
|
|
end
|
2014-10-27 16:06:43 -05:00
|
|
|
|
2015-01-27 11:13:45 -06:00
|
|
|
changes.delete(:title) if topic.title == changes[:title]
|
2015-02-17 17:35:52 -06:00
|
|
|
changes.delete(:category_id) if topic.category_id.to_i == changes[:category_id].to_i
|
2014-10-27 16:06:43 -05:00
|
|
|
|
2015-01-27 11:13:45 -06:00
|
|
|
success = true
|
2017-08-30 23:06:56 -05:00
|
|
|
|
2014-10-27 16:06:43 -05:00
|
|
|
if changes.length > 0
|
|
|
|
first_post = topic.ordered_posts.first
|
2020-04-20 20:50:20 -05:00
|
|
|
success = PostRevisor.new(first_post, topic).revise!(current_user, changes, validate_post: false)
|
2020-04-22 10:53:47 -05:00
|
|
|
|
|
|
|
if !success && topic.errors.blank?
|
|
|
|
topic.errors.add(:base, :unable_to_update)
|
|
|
|
end
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
2013-12-11 20:41:34 -06:00
|
|
|
|
|
|
|
# this is used to return the title to the client as it may have been changed by "TextCleaner"
|
2013-10-28 01:12:07 -05:00
|
|
|
success ? render_serialized(topic, BasicTopicSerializer) : render_json_error(topic)
|
2019-10-23 13:05:38 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def update_tags
|
|
|
|
params.require(:tags)
|
|
|
|
topic = Topic.find_by(id: params[:topic_id])
|
|
|
|
guardian.ensure_can_edit_tags!(topic)
|
|
|
|
|
|
|
|
success = PostRevisor.new(topic.first_post, topic).revise!(current_user, { tags: params[:tags] }, validate_post: false)
|
|
|
|
|
|
|
|
success ? render_serialized(topic, BasicTopicSerializer) : render_json_error(topic)
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
|
|
|
|
2015-03-13 19:18:05 -05:00
|
|
|
def feature_stats
|
|
|
|
params.require(:category_id)
|
|
|
|
category_id = params[:category_id].to_i
|
|
|
|
|
2015-06-22 07:08:30 -05:00
|
|
|
visible_topics = Topic.listable_topics.visible
|
2015-03-13 19:18:05 -05:00
|
|
|
|
|
|
|
render json: {
|
2015-06-22 07:08:30 -05:00
|
|
|
pinned_in_category_count: visible_topics.where(category_id: category_id).where(pinned_globally: false).where.not(pinned_at: nil).count,
|
|
|
|
pinned_globally_count: visible_topics.where(pinned_globally: true).where.not(pinned_at: nil).count,
|
|
|
|
banner_count: Topic.listable_topics.where(archetype: Archetype.banner).count,
|
2015-03-13 19:18:05 -05:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2013-02-05 13:16:51 -06:00
|
|
|
def status
|
2013-06-06 02:14:32 -05:00
|
|
|
params.require(:status)
|
|
|
|
params.require(:enabled)
|
2015-07-29 09:34:21 -05:00
|
|
|
params.permit(:until)
|
|
|
|
|
2017-07-27 20:20:09 -05:00
|
|
|
status = params[:status]
|
2015-07-29 09:34:21 -05:00
|
|
|
topic_id = params[:topic_id].to_i
|
|
|
|
enabled = params[:enabled] == 'true'
|
2013-02-05 13:16:51 -06:00
|
|
|
|
2013-10-28 01:12:07 -05:00
|
|
|
check_for_status_presence(:status, status)
|
2014-05-06 08:41:59 -05:00
|
|
|
@topic = Topic.find_by(id: topic_id)
|
2020-07-14 11:36:19 -05:00
|
|
|
|
|
|
|
case status
|
|
|
|
when 'closed'
|
|
|
|
guardian.ensure_can_close_topic!(@topic)
|
|
|
|
when 'archived'
|
|
|
|
guardian.ensure_can_archive_topic!(@topic)
|
2020-12-14 10:01:22 -06:00
|
|
|
when 'visible'
|
|
|
|
guardian.ensure_can_toggle_topic_visibility!(@topic)
|
2020-07-14 11:36:19 -05:00
|
|
|
else
|
|
|
|
guardian.ensure_can_moderate!(@topic)
|
|
|
|
end
|
|
|
|
|
2015-07-29 09:34:21 -05:00
|
|
|
@topic.update_status(status, enabled, current_user, until: params[:until])
|
2017-03-31 02:56:09 -05:00
|
|
|
|
|
|
|
render json: success_json.merge!(
|
2017-05-11 17:23:18 -05:00
|
|
|
topic_status_update: TopicTimerSerializer.new(
|
|
|
|
TopicTimer.find_by(topic: @topic), root: false
|
2017-03-31 02:56:09 -05:00
|
|
|
)
|
|
|
|
)
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
|
|
|
|
2013-02-07 09:45:24 -06:00
|
|
|
def mute
|
2013-06-14 00:38:59 -05:00
|
|
|
toggle_mute
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
|
|
|
|
|
|
|
def unmute
|
2013-06-14 00:38:59 -05:00
|
|
|
toggle_mute
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
|
|
|
|
2017-05-11 17:23:18 -05:00
|
|
|
def timer
|
2017-12-07 07:42:58 -06:00
|
|
|
params.permit(:time, :based_on_last_post, :category_id)
|
2017-03-21 22:12:02 -05:00
|
|
|
params.require(:status_type)
|
2014-10-10 11:21:44 -05:00
|
|
|
|
2017-03-21 22:12:02 -05:00
|
|
|
status_type =
|
|
|
|
begin
|
2017-05-11 17:23:18 -05:00
|
|
|
TopicTimer.types.fetch(params[:status_type].to_sym)
|
2017-03-21 22:12:02 -05:00
|
|
|
rescue
|
|
|
|
invalid_param(:status_type)
|
|
|
|
end
|
2020-03-19 10:36:31 -05:00
|
|
|
based_on_last_post = params[:based_on_last_post]
|
2020-05-18 04:47:08 -05:00
|
|
|
params.require(:duration) if based_on_last_post
|
2017-03-21 22:12:02 -05:00
|
|
|
|
|
|
|
topic = Topic.find_by(id: params[:topic_id])
|
2013-11-26 18:06:20 -06:00
|
|
|
guardian.ensure_can_moderate!(topic)
|
2014-10-10 11:21:44 -05:00
|
|
|
|
2017-04-03 04:28:41 -05:00
|
|
|
options = {
|
2017-03-21 22:12:02 -05:00
|
|
|
by_user: current_user,
|
2020-03-19 10:36:31 -05:00
|
|
|
based_on_last_post: based_on_last_post
|
2017-04-03 04:28:41 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
options.merge!(category_id: params[:category_id]) if !params[:category_id].blank?
|
2020-03-19 10:36:31 -05:00
|
|
|
options.merge!(duration: params[:duration].to_i) if params[:duration].present?
|
2017-04-03 04:28:41 -05:00
|
|
|
|
2017-05-11 17:23:18 -05:00
|
|
|
topic_status_update = topic.set_or_create_timer(
|
2017-04-03 04:28:41 -05:00
|
|
|
status_type,
|
|
|
|
params[:time],
|
2019-11-27 20:13:13 -06:00
|
|
|
**options
|
2017-03-21 22:12:02 -05:00
|
|
|
)
|
2014-10-10 11:21:44 -05:00
|
|
|
|
2013-11-26 18:06:20 -06:00
|
|
|
if topic.save
|
2017-07-27 20:20:09 -05:00
|
|
|
render json: success_json.merge!(
|
2017-03-21 22:12:02 -05:00
|
|
|
execute_at: topic_status_update&.execute_at,
|
|
|
|
duration: topic_status_update&.duration,
|
2017-03-31 02:02:36 -05:00
|
|
|
based_on_last_post: topic_status_update&.based_on_last_post,
|
2017-04-03 04:28:41 -05:00
|
|
|
closed: topic.closed,
|
|
|
|
category_id: topic_status_update&.category_id
|
2017-07-27 20:20:09 -05:00
|
|
|
)
|
2013-11-26 18:06:20 -06:00
|
|
|
else
|
|
|
|
render_json_error(topic)
|
|
|
|
end
|
2013-05-07 13:25:41 -05:00
|
|
|
end
|
|
|
|
|
2014-06-16 11:28:07 -05:00
|
|
|
def make_banner
|
|
|
|
topic = Topic.find_by(id: params[:topic_id].to_i)
|
2019-04-02 02:08:15 -05:00
|
|
|
guardian.ensure_can_banner_topic!(topic)
|
2014-06-16 11:28:07 -05:00
|
|
|
|
2014-06-16 12:21:21 -05:00
|
|
|
topic.make_banner!(current_user)
|
2014-06-16 11:28:07 -05:00
|
|
|
|
2017-08-30 23:06:56 -05:00
|
|
|
render body: nil
|
2014-06-16 11:28:07 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def remove_banner
|
|
|
|
topic = Topic.find_by(id: params[:topic_id].to_i)
|
2019-04-02 02:08:15 -05:00
|
|
|
guardian.ensure_can_banner_topic!(topic)
|
2014-06-16 11:28:07 -05:00
|
|
|
|
2014-06-16 12:21:21 -05:00
|
|
|
topic.remove_banner!(current_user)
|
2014-06-16 11:28:07 -05:00
|
|
|
|
2017-08-30 23:06:56 -05:00
|
|
|
render body: nil
|
2014-06-16 11:28:07 -05:00
|
|
|
end
|
|
|
|
|
2015-02-18 17:58:57 -06:00
|
|
|
def remove_bookmarks
|
|
|
|
topic = Topic.find(params[:topic_id].to_i)
|
2020-04-21 22:44:19 -05:00
|
|
|
BookmarkManager.new(current_user).destroy_for_topic(topic)
|
2017-08-30 23:06:56 -05:00
|
|
|
render body: nil
|
2015-02-18 17:58:57 -06:00
|
|
|
end
|
|
|
|
|
2015-12-29 20:26:21 -06:00
|
|
|
def archive_message
|
|
|
|
toggle_archive_message(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
def move_to_inbox
|
|
|
|
toggle_archive_message(false)
|
|
|
|
end
|
|
|
|
|
|
|
|
def toggle_archive_message(archive)
|
|
|
|
topic = Topic.find(params[:id].to_i)
|
2016-01-11 22:49:05 -06:00
|
|
|
|
|
|
|
group_id = nil
|
|
|
|
|
2015-12-29 20:26:21 -06:00
|
|
|
group_ids = current_user.groups.pluck(:id)
|
|
|
|
if group_ids.present?
|
|
|
|
allowed_groups = topic.allowed_groups
|
2017-07-27 20:20:09 -05:00
|
|
|
.where('topic_allowed_groups.group_id IN (?)', group_ids).pluck(:id)
|
2015-12-29 20:26:21 -06:00
|
|
|
allowed_groups.each do |id|
|
|
|
|
if archive
|
2018-03-06 00:38:43 -06:00
|
|
|
GroupArchivedMessage.archive!(id, topic)
|
2016-01-11 22:49:05 -06:00
|
|
|
group_id = id
|
2016-02-07 06:39:07 -06:00
|
|
|
else
|
2018-03-06 00:38:43 -06:00
|
|
|
GroupArchivedMessage.move_to_inbox!(id, topic)
|
2015-12-29 20:26:21 -06:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if topic.allowed_users.include?(current_user)
|
|
|
|
if archive
|
2018-03-06 00:38:43 -06:00
|
|
|
UserArchivedMessage.archive!(current_user.id, topic)
|
2016-02-07 06:39:07 -06:00
|
|
|
else
|
2018-03-06 00:38:43 -06:00
|
|
|
UserArchivedMessage.move_to_inbox!(current_user.id, topic)
|
2015-12-29 20:26:21 -06:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-01-11 22:49:05 -06:00
|
|
|
if group_id
|
|
|
|
name = Group.find_by(id: group_id).try(:name)
|
|
|
|
render_json_dump(group_name: name)
|
|
|
|
else
|
2017-08-30 23:06:56 -05:00
|
|
|
render body: nil
|
2016-01-11 22:49:05 -06:00
|
|
|
end
|
2015-12-29 20:26:21 -06:00
|
|
|
end
|
|
|
|
|
2015-01-12 05:10:15 -06:00
|
|
|
def bookmark
|
2015-02-18 17:58:57 -06:00
|
|
|
topic = Topic.find(params[:topic_id].to_i)
|
2015-01-12 05:10:15 -06:00
|
|
|
first_post = topic.ordered_posts.first
|
|
|
|
|
2020-04-21 22:44:19 -05:00
|
|
|
bookmark_manager = BookmarkManager.new(current_user)
|
|
|
|
bookmark_manager.create(post_id: first_post.id)
|
2020-03-11 19:16:00 -05:00
|
|
|
|
2020-04-21 22:44:19 -05:00
|
|
|
if bookmark_manager.errors.any?
|
|
|
|
return render_json_error(bookmark_manager, status: 400)
|
2020-02-13 00:26:02 -06:00
|
|
|
end
|
2015-01-12 05:10:15 -06:00
|
|
|
|
2017-08-30 23:06:56 -05:00
|
|
|
render body: nil
|
2015-01-12 05:10:15 -06:00
|
|
|
end
|
|
|
|
|
2013-02-05 13:16:51 -06:00
|
|
|
def destroy
|
2014-05-06 08:41:59 -05:00
|
|
|
topic = Topic.find_by(id: params[:id])
|
2013-02-05 13:16:51 -06:00
|
|
|
guardian.ensure_can_delete!(topic)
|
2014-08-07 12:12:35 -05:00
|
|
|
|
|
|
|
first_post = topic.ordered_posts.first
|
2017-07-27 20:20:09 -05:00
|
|
|
PostDestroyer.new(current_user, first_post, context: params[:context]).destroy
|
2014-08-07 12:12:35 -05:00
|
|
|
|
2017-08-30 23:06:56 -05:00
|
|
|
render body: nil
|
2020-01-15 07:41:41 -06:00
|
|
|
rescue Discourse::InvalidAccess
|
|
|
|
render_json_error I18n.t("delete_topic_failed")
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
2013-02-07 09:45:24 -06:00
|
|
|
|
2013-07-12 11:08:23 -05:00
|
|
|
def recover
|
|
|
|
topic = Topic.where(id: params[:topic_id]).with_deleted.first
|
|
|
|
guardian.ensure_can_recover_topic!(topic)
|
2014-08-07 12:12:35 -05:00
|
|
|
|
|
|
|
first_post = topic.posts.with_deleted.order(:post_number).first
|
2018-03-20 23:15:16 -05:00
|
|
|
PostDestroyer.new(current_user, first_post, context: params[:context]).recover
|
2014-08-07 12:12:35 -05:00
|
|
|
|
2017-08-30 23:06:56 -05:00
|
|
|
render body: nil
|
2013-07-12 11:08:23 -05:00
|
|
|
end
|
|
|
|
|
2013-02-05 13:16:51 -06:00
|
|
|
def excerpt
|
2017-08-30 23:06:56 -05:00
|
|
|
render body: nil
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
|
|
|
|
2013-06-18 02:17:01 -05:00
|
|
|
def remove_allowed_user
|
|
|
|
params.require(:username)
|
2014-05-06 08:41:59 -05:00
|
|
|
topic = Topic.find_by(id: params[:topic_id])
|
2018-11-20 23:27:42 -06:00
|
|
|
raise Discourse::NotFound unless topic
|
2017-10-10 03:26:56 -05:00
|
|
|
user = User.find_by(username: params[:username])
|
2018-11-20 23:27:42 -06:00
|
|
|
raise Discourse::NotFound unless user
|
2018-11-20 22:18:38 -06:00
|
|
|
|
2017-10-10 03:26:56 -05:00
|
|
|
guardian.ensure_can_remove_allowed_users!(topic, user)
|
2013-06-18 02:17:01 -05:00
|
|
|
|
2018-11-20 22:18:38 -06:00
|
|
|
if topic.remove_allowed_user(current_user, user)
|
2013-06-18 02:17:01 -05:00
|
|
|
render json: success_json
|
|
|
|
else
|
|
|
|
render json: failed_json, status: 422
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-06-20 01:29:11 -05:00
|
|
|
def remove_allowed_group
|
|
|
|
params.require(:name)
|
|
|
|
topic = Topic.find_by(id: params[:topic_id])
|
|
|
|
guardian.ensure_can_remove_allowed_users!(topic)
|
|
|
|
|
|
|
|
if topic.remove_allowed_group(current_user, params[:name])
|
|
|
|
render json: success_json
|
|
|
|
else
|
|
|
|
render json: failed_json, status: 422
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def invite_group
|
|
|
|
group = Group.find_by(name: params[:group])
|
|
|
|
raise Discourse::NotFound unless group
|
|
|
|
|
|
|
|
topic = Topic.find_by(id: params[:topic_id])
|
|
|
|
|
2018-08-22 23:36:49 -05:00
|
|
|
unless pm_has_slots?(topic)
|
2020-04-21 22:44:19 -05:00
|
|
|
return render_json_error(
|
|
|
|
I18n.t("pm_reached_recipients_limit", recipients_limit: SiteSetting.max_allowed_message_recipients)
|
|
|
|
)
|
2018-08-22 23:36:49 -05:00
|
|
|
end
|
|
|
|
|
2016-06-20 01:29:11 -05:00
|
|
|
if topic.private_message?
|
2017-12-13 20:53:21 -06:00
|
|
|
guardian.ensure_can_invite_group_to_private_message!(group, topic)
|
2016-06-20 01:29:11 -05:00
|
|
|
topic.invite_group(current_user, group)
|
2016-06-21 01:01:29 -05:00
|
|
|
render_json_dump BasicGroupSerializer.new(group, scope: guardian, root: 'group')
|
2016-06-20 01:29:11 -05:00
|
|
|
else
|
|
|
|
render json: failed_json, status: 422
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-02-05 13:16:51 -06:00
|
|
|
def invite
|
2014-05-06 08:41:59 -05:00
|
|
|
topic = Topic.find_by(id: params[:topic_id])
|
2018-02-25 20:42:06 -06:00
|
|
|
raise Discourse::InvalidParameters.new unless topic
|
|
|
|
|
|
|
|
username_or_email = params[:user] ? fetch_username : fetch_email
|
2013-02-05 13:16:51 -06:00
|
|
|
|
2017-07-21 01:12:24 -05:00
|
|
|
groups = Group.lookup_groups(
|
|
|
|
group_ids: params[:group_ids],
|
|
|
|
group_names: params[:group_names]
|
|
|
|
)
|
|
|
|
|
2018-08-22 23:36:49 -05:00
|
|
|
unless pm_has_slots?(topic)
|
2020-04-21 22:44:19 -05:00
|
|
|
return render_json_error(
|
|
|
|
I18n.t("pm_reached_recipients_limit", recipients_limit: SiteSetting.max_allowed_message_recipients)
|
|
|
|
)
|
2018-08-22 23:36:49 -05:00
|
|
|
end
|
|
|
|
|
2018-12-05 09:43:07 -06:00
|
|
|
guardian.ensure_can_invite_to!(topic)
|
2017-07-21 01:12:24 -05:00
|
|
|
group_ids = groups.map(&:id)
|
2014-05-08 20:45:18 -05:00
|
|
|
|
2015-12-14 10:02:23 -06:00
|
|
|
begin
|
2016-06-07 12:24:45 -05:00
|
|
|
if topic.invite(current_user, username_or_email, group_ids, params[:custom_message])
|
2015-12-14 10:02:23 -06:00
|
|
|
user = User.find_by_username_or_email(username_or_email)
|
2018-02-25 20:42:06 -06:00
|
|
|
|
2015-12-14 10:02:23 -06:00
|
|
|
if user
|
|
|
|
render_json_dump BasicUserSerializer.new(user, scope: guardian, root: 'user')
|
|
|
|
else
|
|
|
|
render json: success_json
|
|
|
|
end
|
2013-06-18 02:17:01 -05:00
|
|
|
else
|
2018-12-05 09:43:07 -06:00
|
|
|
json = failed_json
|
|
|
|
|
|
|
|
unless topic.private_message?
|
|
|
|
group_names = topic.category
|
|
|
|
.visible_group_names(current_user)
|
|
|
|
.where(automatic: false)
|
|
|
|
.pluck(:name)
|
|
|
|
.join(", ")
|
|
|
|
|
|
|
|
if group_names.present?
|
|
|
|
json.merge!(errors: [
|
2020-04-21 22:44:19 -05:00
|
|
|
I18n.t(
|
|
|
|
"topic_invite.failed_to_invite",
|
2018-12-05 09:43:07 -06:00
|
|
|
group_names: group_names
|
|
|
|
)
|
|
|
|
])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
render json: json, status: 422
|
2013-06-18 02:17:01 -05:00
|
|
|
end
|
2020-07-31 10:52:19 -05:00
|
|
|
rescue Topic::UserExists, Topic::NotAllowed => e
|
2017-07-27 20:20:09 -05:00
|
|
|
render json: { errors: [e.message] }, status: 422
|
2013-02-05 13:16:51 -06:00
|
|
|
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-16 14:55:14 -05:00
|
|
|
def merge_topic
|
2018-12-31 05:47:22 -06:00
|
|
|
topic_id = params.require(:topic_id)
|
|
|
|
destination_topic_id = params.require(:destination_topic_id)
|
|
|
|
params.permit(:participants)
|
|
|
|
params.permit(:archetype)
|
2013-05-16 14:55:14 -05:00
|
|
|
|
2018-12-31 05:47:22 -06:00
|
|
|
raise Discourse::InvalidAccess if params[:archetype] == "private_message" && !guardian.is_staff?
|
|
|
|
|
|
|
|
topic = Topic.find_by(id: topic_id)
|
2013-05-16 14:55:14 -05:00
|
|
|
guardian.ensure_can_move_posts!(topic)
|
|
|
|
|
2020-08-05 09:33:25 -05:00
|
|
|
destination_topic = Topic.find_by(id: destination_topic_id)
|
|
|
|
guardian.ensure_can_create_post_on_topic!(destination_topic)
|
|
|
|
|
2018-12-31 05:47:22 -06:00
|
|
|
args = {}
|
|
|
|
args[:destination_topic_id] = destination_topic_id.to_i
|
|
|
|
|
|
|
|
if params[:archetype].present?
|
|
|
|
args[:archetype] = params[:archetype]
|
|
|
|
args[:participants] = params[:participants] if params[:participants].present? && params[:archetype] == "private_message"
|
|
|
|
end
|
|
|
|
|
|
|
|
destination_topic = topic.move_posts(current_user, topic.posts.pluck(:id), args)
|
|
|
|
render_topic_changes(destination_topic)
|
2013-05-16 14:55:14 -05:00
|
|
|
end
|
|
|
|
|
2013-02-05 13:16:51 -06:00
|
|
|
def move_posts
|
2018-01-22 10:23:19 -06:00
|
|
|
post_ids = params.require(:post_ids)
|
|
|
|
topic_id = params.require(:topic_id)
|
2013-10-29 14:30:06 -05:00
|
|
|
params.permit(:category_id)
|
2018-07-06 11:21:32 -05:00
|
|
|
params.permit(:tags)
|
2018-12-31 05:47:22 -06:00
|
|
|
params.permit(:participants)
|
|
|
|
params.permit(:archetype)
|
|
|
|
|
|
|
|
raise Discourse::InvalidAccess if params[:archetype] == "private_message" && !guardian.is_staff?
|
2013-05-08 12:33:58 -05:00
|
|
|
|
2018-01-22 10:23:19 -06:00
|
|
|
topic = Topic.with_deleted.find_by(id: topic_id)
|
2013-02-05 13:16:51 -06:00
|
|
|
guardian.ensure_can_move_posts!(topic)
|
|
|
|
|
2020-08-05 09:33:25 -05:00
|
|
|
if params[:title].present?
|
|
|
|
# when creating a new topic, ensure the 1st post is a regular post
|
|
|
|
if Post.where(topic: topic, id: post_ids).order(:post_number).pluck_first(:post_type) != Post.types[:regular]
|
|
|
|
return render_json_error("When moving posts to a new topic, the first post must be a regular post.")
|
|
|
|
end
|
|
|
|
|
|
|
|
if params[:category_id].present?
|
|
|
|
guardian.ensure_can_create_topic_on_category!(params[:category_id])
|
|
|
|
end
|
2018-01-22 10:23:19 -06:00
|
|
|
end
|
|
|
|
|
2018-12-31 05:47:22 -06:00
|
|
|
destination_topic = move_posts_to_destination(topic)
|
|
|
|
render_topic_changes(destination_topic)
|
2014-08-11 13:42:50 -05:00
|
|
|
rescue ActiveRecord::RecordInvalid => ex
|
|
|
|
render_json_error(ex)
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
|
|
|
|
2014-03-27 20:28:14 -05:00
|
|
|
def change_post_owners
|
|
|
|
params.require(:post_ids)
|
|
|
|
params.require(:topic_id)
|
|
|
|
params.require(:username)
|
|
|
|
|
|
|
|
guardian.ensure_can_change_post_owner!
|
|
|
|
|
2015-03-02 10:17:11 -06:00
|
|
|
begin
|
2017-07-27 20:20:09 -05:00
|
|
|
PostOwnerChanger.new(post_ids: params[:post_ids].to_a,
|
|
|
|
topic_id: params[:topic_id].to_i,
|
|
|
|
new_owner: User.find_by(username: params[:username]),
|
|
|
|
acting_user: current_user).change_owner!
|
2015-03-02 10:17:11 -06:00
|
|
|
render json: success_json
|
|
|
|
rescue ArgumentError
|
|
|
|
render json: failed_json, status: 422
|
2014-03-27 20:28:14 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-07-25 11:06:49 -05:00
|
|
|
def change_timestamps
|
2019-02-22 03:03:52 -06:00
|
|
|
topic_id = params.require(:topic_id).to_i
|
|
|
|
timestamp = params.require(:timestamp).to_f
|
2015-07-25 11:06:49 -05:00
|
|
|
|
2016-11-06 13:14:09 -06:00
|
|
|
guardian.ensure_can_change_post_timestamps!
|
2015-07-25 11:06:49 -05:00
|
|
|
|
2019-02-22 03:03:52 -06:00
|
|
|
topic = Topic.with_deleted.find(topic_id)
|
|
|
|
previous_timestamp = topic.first_post.created_at
|
|
|
|
|
2015-07-25 11:06:49 -05:00
|
|
|
begin
|
2017-05-22 02:01:33 -05:00
|
|
|
TopicTimestampChanger.new(
|
2019-02-22 03:03:52 -06:00
|
|
|
topic: topic,
|
|
|
|
timestamp: timestamp
|
2017-05-22 02:01:33 -05:00
|
|
|
).change!
|
2019-02-22 03:03:52 -06:00
|
|
|
|
|
|
|
StaffActionLogger.new(current_user).log_topic_timestamps_changed(topic, Time.zone.at(timestamp), previous_timestamp)
|
2015-07-25 11:06:49 -05:00
|
|
|
|
|
|
|
render json: success_json
|
2017-05-22 03:03:49 -05:00
|
|
|
rescue ActiveRecord::RecordInvalid, TopicTimestampChanger::InvalidTimestampError
|
2015-07-25 11:06:49 -05:00
|
|
|
render json: failed_json, status: 422
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-03-06 14:17:07 -06:00
|
|
|
def clear_pin
|
2014-05-06 08:41:59 -05:00
|
|
|
topic = Topic.find_by(id: params[:topic_id].to_i)
|
2013-03-06 14:17:07 -06:00
|
|
|
guardian.ensure_can_see!(topic)
|
|
|
|
topic.clear_pin_for(current_user)
|
2017-08-30 23:06:56 -05:00
|
|
|
render body: nil
|
2013-03-06 14:17:07 -06:00
|
|
|
end
|
2013-02-25 17:38:11 -06:00
|
|
|
|
2014-04-09 19:56:56 -05:00
|
|
|
def re_pin
|
2014-05-06 08:41:59 -05:00
|
|
|
topic = Topic.find_by(id: params[:topic_id].to_i)
|
2014-04-09 19:56:56 -05:00
|
|
|
guardian.ensure_can_see!(topic)
|
|
|
|
topic.re_pin_for(current_user)
|
2017-08-30 23:06:56 -05:00
|
|
|
render body: nil
|
2014-04-09 19:56:56 -05:00
|
|
|
end
|
|
|
|
|
2013-03-06 14:17:07 -06:00
|
|
|
def timings
|
2018-01-18 15:26:18 -06:00
|
|
|
allowed_params = topic_params
|
|
|
|
|
|
|
|
topic_id = allowed_params[:topic_id].to_i
|
|
|
|
topic_time = allowed_params[:topic_time].to_i
|
|
|
|
timings = allowed_params[:timings].to_h || {}
|
|
|
|
|
2018-05-24 00:38:33 -05:00
|
|
|
# ensure we capture current user for the block
|
|
|
|
user = current_user
|
|
|
|
|
2018-01-18 15:26:18 -06:00
|
|
|
hijack do
|
|
|
|
PostTiming.process_timings(
|
2018-05-24 00:38:33 -05:00
|
|
|
user,
|
2018-01-18 15:26:18 -06:00
|
|
|
topic_id,
|
|
|
|
topic_time,
|
|
|
|
timings.map { |post_number, t| [post_number.to_i, t.to_i] },
|
|
|
|
mobile: view_context.mobile_view?
|
|
|
|
)
|
|
|
|
render body: nil
|
|
|
|
end
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|
|
|
|
|
2013-02-21 12:20:00 -06:00
|
|
|
def feed
|
|
|
|
@topic_view = TopicView.new(params[:topic_id])
|
2013-10-16 00:39:18 -05:00
|
|
|
discourse_expires_in 1.minute
|
|
|
|
render 'topics/show', formats: [:rss]
|
2013-02-21 12:20:00 -06:00
|
|
|
end
|
|
|
|
|
2014-01-29 14:48:52 -06:00
|
|
|
def bulk
|
2014-02-21 13:17:45 -06:00
|
|
|
if params[:topic_ids].present?
|
2017-07-27 20:20:09 -05:00
|
|
|
topic_ids = params[:topic_ids].map { |t| t.to_i }
|
2014-02-21 13:17:45 -06:00
|
|
|
elsif params[:filter] == 'unread'
|
|
|
|
tq = TopicQuery.new(current_user)
|
2017-05-25 14:07:12 -05:00
|
|
|
topics = TopicQuery.unread_filter(tq.joined_topic_user, current_user.id, staff: guardian.is_staff?).listable_topics
|
2020-09-25 14:39:37 -05:00
|
|
|
topics = TopicQuery.tracked_filter(topics, current_user.id) if params[:tracked].to_s == "true"
|
2019-06-26 22:26:07 -05:00
|
|
|
|
|
|
|
if params[:category_id]
|
|
|
|
if params[:include_subcategories]
|
|
|
|
topics = topics.where(<<~SQL, category_id: params[:category_id])
|
|
|
|
category_id in (select id FROM categories WHERE parent_category_id = :category_id) OR
|
|
|
|
category_id = :category_id
|
|
|
|
SQL
|
|
|
|
else
|
|
|
|
topics = topics.where('category_id = ?', params[:category_id])
|
|
|
|
end
|
|
|
|
end
|
2020-08-27 12:34:45 -05:00
|
|
|
|
|
|
|
if params[:tag_name].present?
|
|
|
|
topics = topics.joins(:tags).where("tags.name": params[:tag_name])
|
|
|
|
end
|
|
|
|
|
2014-10-30 09:19:49 -05:00
|
|
|
topic_ids = topics.pluck(:id)
|
2014-02-21 13:17:45 -06:00
|
|
|
else
|
|
|
|
raise ActionController::ParameterMissing.new(:topic_ids)
|
|
|
|
end
|
|
|
|
|
2018-03-23 11:59:31 -05:00
|
|
|
operation = params
|
|
|
|
.require(:operation)
|
2020-09-10 11:18:45 -05:00
|
|
|
.permit(:type, :group, :category_id, :notification_level_id, *DiscoursePluginRegistry.permitted_bulk_action_parameters, tags: [])
|
2018-03-23 11:59:31 -05:00
|
|
|
.to_h.symbolize_keys
|
2017-08-30 23:06:56 -05:00
|
|
|
|
2014-01-30 10:15:49 -06:00
|
|
|
raise ActionController::ParameterMissing.new(:operation_type) if operation[:type].blank?
|
2015-12-22 18:09:17 -06:00
|
|
|
operator = TopicsBulkAction.new(current_user, topic_ids, operation, group: operation[:group])
|
2014-01-30 10:15:49 -06:00
|
|
|
changed_topic_ids = operator.perform!
|
|
|
|
render_json_dump topic_ids: changed_topic_ids
|
2014-01-29 14:48:52 -06:00
|
|
|
end
|
|
|
|
|
2014-03-03 14:46:38 -06:00
|
|
|
def reset_new
|
2019-11-13 18:16:13 -06:00
|
|
|
if params[:category_id].present?
|
|
|
|
category_ids = [params[:category_id]]
|
|
|
|
if params[:include_subcategories] == 'true'
|
|
|
|
category_ids = category_ids.concat(Category.where(parent_category_id: params[:category_id]).pluck(:id))
|
|
|
|
end
|
|
|
|
category_ids.each do |category_id|
|
|
|
|
current_user
|
|
|
|
.category_users
|
|
|
|
.where(category_id: category_id)
|
|
|
|
.first_or_initialize
|
|
|
|
.update!(last_seen_at: Time.zone.now)
|
2019-11-24 13:17:31 -06:00
|
|
|
TopicTrackingState.publish_dismiss_new(current_user.id, category_id)
|
2019-11-13 18:16:13 -06:00
|
|
|
end
|
|
|
|
else
|
2020-09-25 14:39:37 -05:00
|
|
|
if params[:tracked].to_s == "true"
|
|
|
|
topics = TopicQuery.tracked_filter(TopicQuery.new(current_user).new_results, current_user.id)
|
|
|
|
topic_users = topics.map { |topic| { topic_id: topic.id, user_id: current_user.id, last_read_post_number: 0 } }
|
|
|
|
TopicUser.insert_all(topic_users) if !topic_users.empty?
|
|
|
|
else
|
|
|
|
current_user.user_stat.update_column(:new_since, Time.zone.now)
|
|
|
|
TopicTrackingState.publish_dismiss_new(current_user.id)
|
|
|
|
end
|
2019-11-13 18:16:13 -06:00
|
|
|
end
|
2017-08-30 23:06:56 -05:00
|
|
|
render body: nil
|
2014-03-03 14:46:38 -06:00
|
|
|
end
|
|
|
|
|
2016-05-01 06:48:43 -05:00
|
|
|
def convert_topic
|
|
|
|
params.require(:id)
|
|
|
|
params.require(:type)
|
|
|
|
topic = Topic.find_by(id: params[:id])
|
|
|
|
guardian.ensure_can_convert_topic!(topic)
|
|
|
|
|
|
|
|
if params[:type] == "public"
|
2019-07-19 10:52:50 -05:00
|
|
|
converted_topic = topic.convert_to_public_topic(current_user, category_id: params[:category_id])
|
2016-05-01 06:48:43 -05:00
|
|
|
else
|
|
|
|
converted_topic = topic.convert_to_private_message(current_user)
|
|
|
|
end
|
|
|
|
render_topic_changes(converted_topic)
|
|
|
|
rescue ActiveRecord::RecordInvalid => ex
|
|
|
|
render_json_error(ex)
|
|
|
|
end
|
|
|
|
|
2018-08-09 19:51:03 -05:00
|
|
|
def reset_bump_date
|
|
|
|
params.require(:id)
|
|
|
|
guardian.ensure_can_update_bumped_at!
|
|
|
|
|
|
|
|
topic = Topic.find_by(id: params[:id])
|
|
|
|
raise Discourse::NotFound.new unless topic
|
|
|
|
|
|
|
|
topic.reset_bumped_at
|
|
|
|
render body: nil
|
|
|
|
end
|
|
|
|
|
2020-10-16 14:24:38 -05:00
|
|
|
def set_slow_mode
|
|
|
|
topic = Topic.find(params[:topic_id])
|
2020-12-14 11:06:50 -06:00
|
|
|
slow_mode_type = TopicTimer.types[:clear_slow_mode]
|
|
|
|
timer = TopicTimer.find_by(topic: topic, status_type: slow_mode_type)
|
2020-10-16 14:24:38 -05:00
|
|
|
|
|
|
|
guardian.ensure_can_moderate!(topic)
|
|
|
|
topic.update!(slow_mode_seconds: params[:seconds])
|
2020-12-14 11:06:50 -06:00
|
|
|
enabled = params[:seconds].to_i > 0
|
|
|
|
|
|
|
|
time = enabled && params[:enabled_until].present? ? params[:enabled_until] : nil
|
|
|
|
|
|
|
|
topic.set_or_create_timer(
|
|
|
|
slow_mode_type,
|
|
|
|
time,
|
|
|
|
by_user: timer&.user
|
|
|
|
)
|
2020-10-16 14:24:38 -05:00
|
|
|
|
|
|
|
head :ok
|
|
|
|
end
|
|
|
|
|
2013-02-05 13:16:51 -06:00
|
|
|
private
|
|
|
|
|
2017-08-30 23:06:56 -05:00
|
|
|
def topic_params
|
|
|
|
params.permit(
|
|
|
|
:topic_id,
|
|
|
|
:topic_time,
|
|
|
|
timings: {}
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2018-06-28 01:54:54 -05:00
|
|
|
def fetch_topic_view(options)
|
|
|
|
if (username_filters = params[:username_filters]).present?
|
2018-07-13 02:34:28 -05:00
|
|
|
options[:username_filters] = username_filters.split(',')
|
2018-06-28 01:54:54 -05:00
|
|
|
end
|
2018-07-13 02:34:28 -05:00
|
|
|
|
|
|
|
@topic_view = TopicView.new(params[:topic_id], current_user, options)
|
2018-06-28 01:54:54 -05:00
|
|
|
end
|
|
|
|
|
2013-06-14 00:38:59 -05:00
|
|
|
def toggle_mute
|
2014-05-06 08:41:59 -05:00
|
|
|
@topic = Topic.find_by(id: params[:topic_id].to_i)
|
2013-02-10 12:50:26 -06:00
|
|
|
guardian.ensure_can_see!(@topic)
|
|
|
|
|
2013-05-24 01:06:38 -05:00
|
|
|
@topic.toggle_mute(current_user)
|
2017-08-30 23:06:56 -05:00
|
|
|
render body: nil
|
2013-02-10 12:50:26 -06:00
|
|
|
end
|
|
|
|
|
|
|
|
def consider_user_for_promotion
|
|
|
|
Promotion.new(current_user).review if current_user.present?
|
|
|
|
end
|
|
|
|
|
|
|
|
def slugs_do_not_match
|
2019-10-11 10:38:16 -05:00
|
|
|
if SiteSetting.slug_generation_method != "encoded"
|
|
|
|
params[:slug] && @topic_view.topic.slug != params[:slug]
|
|
|
|
else
|
|
|
|
params[:slug] && CGI.unescape(@topic_view.topic.slug) != params[:slug]
|
|
|
|
end
|
2013-02-10 12:50:26 -06:00
|
|
|
end
|
|
|
|
|
2017-07-27 20:20:09 -05:00
|
|
|
def redirect_to_correct_topic(topic, post_number = nil)
|
2020-05-27 13:28:38 -05:00
|
|
|
begin
|
|
|
|
guardian.ensure_can_see!(topic)
|
|
|
|
rescue Discourse::InvalidAccess => ex
|
2020-05-28 12:29:36 -05:00
|
|
|
raise(SiteSetting.detailed_404 ? ex : Discourse::NotFound)
|
2020-05-27 13:28:38 -05:00
|
|
|
end
|
2020-05-26 19:58:22 -05:00
|
|
|
|
2014-08-13 15:12:44 -05:00
|
|
|
url = topic.relative_url
|
2014-08-17 21:35:31 -05:00
|
|
|
url << "/#{post_number}" if post_number.to_i > 0
|
2014-08-13 15:12:44 -05:00
|
|
|
url << ".json" if request.format.json?
|
|
|
|
|
2016-02-24 21:32:16 -06:00
|
|
|
page = params[:page]
|
2015-05-20 09:16:17 -05:00
|
|
|
url << "?page=#{page}" if page != 0
|
|
|
|
|
2014-08-13 15:12:44 -05:00
|
|
|
redirect_to url, status: 301
|
2013-02-10 12:50:26 -06:00
|
|
|
end
|
|
|
|
|
|
|
|
def track_visit_to_topic
|
2014-06-11 20:29:29 -05:00
|
|
|
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?
|
|
|
|
|
2018-08-21 23:36:56 -05:00
|
|
|
if !request.format.json?
|
|
|
|
hash = {
|
2014-08-03 20:06:06 -05:00
|
|
|
referer: request.referer || flash[:referer],
|
|
|
|
host: request.host,
|
|
|
|
current_user: current_user,
|
|
|
|
topic_id: @topic_view.topic.id,
|
2018-11-20 18:58:47 -06:00
|
|
|
post_number: @topic_view.current_post_number,
|
2014-08-03 20:06:06 -05:00
|
|
|
username: request['u'],
|
|
|
|
ip_address: request.remote_ip
|
2018-08-21 23:36:56 -05:00
|
|
|
}
|
|
|
|
# defer this way so we do not capture the whole controller
|
|
|
|
# in the closure
|
|
|
|
TopicsController.defer_add_incoming_link(hash)
|
|
|
|
end
|
|
|
|
|
|
|
|
TopicsController.defer_track_visit(topic_id, ip, user_id, track_visit)
|
|
|
|
end
|
2014-08-03 20:06:06 -05:00
|
|
|
|
2018-08-21 23:36:56 -05:00
|
|
|
def self.defer_track_visit(topic_id, ip, user_id, track_visit)
|
2014-07-17 15:22:46 -05:00
|
|
|
Scheduler::Defer.later "Track Visit" do
|
2014-08-04 04:07:55 -05:00
|
|
|
TopicViewItem.add(topic_id, ip, user_id)
|
2016-05-14 03:06:29 -05:00
|
|
|
TopicUser.track_visit!(topic_id, user_id) if track_visit
|
2014-06-11 20:29:29 -05:00
|
|
|
end
|
2018-08-21 23:36:56 -05:00
|
|
|
end
|
2013-10-04 02:00:23 -05:00
|
|
|
|
2018-08-21 23:36:56 -05:00
|
|
|
def self.defer_add_incoming_link(hash)
|
|
|
|
Scheduler::Defer.later "Track Link" do
|
|
|
|
IncomingLink.add(hash)
|
|
|
|
end
|
2013-02-10 12:50:26 -06:00
|
|
|
end
|
|
|
|
|
|
|
|
def should_track_visit_to_topic?
|
2014-08-29 12:39:02 -05:00
|
|
|
!!((!request.format.json? || params[:track_visit]) && current_user)
|
2013-02-10 12:50:26 -06:00
|
|
|
end
|
|
|
|
|
|
|
|
def perform_show_response
|
2016-08-16 19:04:23 -05:00
|
|
|
|
|
|
|
if request.head?
|
|
|
|
head :ok
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2020-04-21 22:44:19 -05:00
|
|
|
topic_view_serializer = TopicViewSerializer.new(
|
|
|
|
@topic_view,
|
2017-09-26 01:42:27 -05:00
|
|
|
scope: guardian,
|
|
|
|
root: false,
|
2020-12-10 11:02:07 -06:00
|
|
|
include_raw: !!params[:include_raw],
|
|
|
|
exclude_suggested_and_related: !!params[:replies_to_post_number] || !!params[:filter_upwards_post_id]
|
2017-09-26 01:42:27 -05:00
|
|
|
)
|
2013-06-28 12:55:34 -05:00
|
|
|
|
2013-02-10 12:50:26 -06:00
|
|
|
respond_to do |format|
|
|
|
|
format.html do
|
2020-05-11 09:45:28 -05:00
|
|
|
@tags = SiteSetting.tagging_enabled ? @topic_view.topic.tags : []
|
|
|
|
@breadcrumbs = helpers.categories_breadcrumb(@topic_view.topic) || []
|
2018-10-17 03:19:32 -05:00
|
|
|
@description_meta = @topic_view.topic.excerpt.present? ? @topic_view.topic.excerpt : @topic_view.summary
|
2013-02-10 12:50:26 -06:00
|
|
|
store_preloaded("topic_#{@topic_view.topic.id}", MultiJson.dump(topic_view_serializer))
|
2015-08-12 16:00:16 -05:00
|
|
|
render :show
|
2013-02-10 12:50:26 -06:00
|
|
|
end
|
|
|
|
|
|
|
|
format.json do
|
|
|
|
render_json_dump(topic_view_serializer)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2013-06-18 00:52:09 -05:00
|
|
|
|
|
|
|
def render_topic_changes(dest_topic)
|
|
|
|
if dest_topic.present?
|
2017-07-27 20:20:09 -05:00
|
|
|
render json: { success: true, url: dest_topic.relative_url }
|
2013-06-18 00:52:09 -05:00
|
|
|
else
|
2017-07-27 20:20:09 -05:00
|
|
|
render json: { success: false }
|
2013-06-18 00:52:09 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-09-04 10:53:00 -05:00
|
|
|
def move_posts_to_destination(topic)
|
2013-06-18 00:52:09 -05: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?
|
2018-07-06 11:21:32 -05:00
|
|
|
args[:tags] = params[:tags] if params[:tags].present?
|
2013-06-18 00:52:09 -05:00
|
|
|
|
2018-12-31 05:47:22 -06:00
|
|
|
if params[:archetype].present?
|
|
|
|
args[:archetype] = params[:archetype]
|
|
|
|
args[:participants] = params[:participants] if params[:participants].present? && params[:archetype] == "private_message"
|
|
|
|
else
|
|
|
|
args[:category_id] = params[:category_id].to_i if params[:category_id].present?
|
|
|
|
end
|
|
|
|
|
2013-09-04 10:53:00 -05:00
|
|
|
topic.move_posts(current_user, post_ids_including_replies, args)
|
2013-06-18 00:52:09 -05:00
|
|
|
end
|
|
|
|
|
2013-10-28 01:12:07 -05:00
|
|
|
def check_for_status_presence(key, attr)
|
2015-03-13 19:18:05 -05:00
|
|
|
invalid_param(key) unless %w(pinned pinned_globally visible closed archived).include?(attr)
|
2013-10-28 01:12:07 -05:00
|
|
|
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
|
|
|
|
|
2018-08-22 23:36:49 -05:00
|
|
|
def pm_has_slots?(pm)
|
|
|
|
guardian.is_staff? || !pm.reached_recipients_limit?
|
|
|
|
end
|
2013-02-05 13:16:51 -06:00
|
|
|
end
|