mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-25 07:58:07 +08:00
## Summary The `upload` action in `Admin::WatchedWordsController` reads the uploaded file's tempfile inside a `Scheduler::Defer.later` block, but Rails/Rack automatically deletes tempfiles after the request completes, causing `Errno::ENOENT` when the deferred block executes asynchronously in production.
160 lines
4.8 KiB
Ruby
Vendored
160 lines
4.8 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
require "csv"
|
|
|
|
class Admin::WatchedWordsController < Admin::StaffController
|
|
skip_before_action :check_xhr, only: [:download]
|
|
|
|
def index
|
|
watched_words = WatchedWord.order(:action, :word)
|
|
watched_words =
|
|
watched_words.where.not(action: WatchedWord.actions[:tag]) if !SiteSetting.tagging_enabled
|
|
render_json_dump WatchedWordListSerializer.new(watched_words, scope: guardian, root: false)
|
|
end
|
|
|
|
def create
|
|
opts = watched_words_params
|
|
action = WatchedWord.actions[opts[:action_key].to_sym]
|
|
words = opts.delete(:words)
|
|
|
|
if action == WatchedWord.actions[:tag] && watched_words_params[:replacement_tags].present?
|
|
opts = opts.merge(replacement: resolve_replacement_tags)
|
|
end
|
|
|
|
watched_word_group = WatchedWordGroup.new(action: action)
|
|
watched_word_group.create_or_update_members(words, opts)
|
|
|
|
if watched_word_group.valid?
|
|
StaffActionLogger.new(current_user).log_watched_words_creation(watched_word_group)
|
|
render_json_dump WatchedWordListSerializer.new(
|
|
watched_word_group.watched_words,
|
|
scope: guardian,
|
|
root: false,
|
|
)
|
|
else
|
|
render_json_error(watched_word_group)
|
|
end
|
|
end
|
|
|
|
def destroy
|
|
watched_word = WatchedWord.find_by(id: params[:id])
|
|
raise Discourse::InvalidParameters.new(:id) unless watched_word
|
|
|
|
watched_word_group = watched_word.watched_word_group
|
|
|
|
if watched_word_group&.watched_words&.count == 1
|
|
watched_word_group.destroy!
|
|
StaffActionLogger.new(current_user).log_watched_words_deletion(watched_word_group)
|
|
else
|
|
watched_word.destroy!
|
|
StaffActionLogger.new(current_user).log_watched_words_deletion(watched_word)
|
|
end
|
|
|
|
render json: success_json
|
|
end
|
|
|
|
def upload
|
|
file = params[:file] || params[:files].first
|
|
action_key = params[:action_key].to_sym
|
|
has_replacement = WatchedWord.has_replacement?(action_key)
|
|
|
|
content = Encodings.to_utf8(File.read(file.tempfile, mode: "rb"))
|
|
|
|
Scheduler::Defer.later("Upload watched words") do
|
|
begin
|
|
words_updated = 0
|
|
|
|
CSV.parse(content) do |row|
|
|
if row[0].present? && (!has_replacement || row[1].present?)
|
|
watched_word =
|
|
WatchedWord.create_or_update_word(
|
|
word: row[0],
|
|
replacement: has_replacement ? row[1] : nil,
|
|
action_key: action_key,
|
|
case_sensitive: "true" == row[2]&.strip&.downcase,
|
|
)
|
|
if watched_word.valid?
|
|
words_updated += 1
|
|
StaffActionLogger.new(current_user).log_watched_words_creation(watched_word)
|
|
end
|
|
end
|
|
end
|
|
|
|
data = { result: "ok", words_updated: words_updated }
|
|
rescue => e
|
|
data = failed_json.merge(errors: [e.message], words_updated: words_updated)
|
|
end
|
|
MessageBus.publish("/watched_words/upload", data.as_json, client_ids: [params[:client_id]])
|
|
end
|
|
|
|
render json: success_json
|
|
end
|
|
|
|
def download
|
|
params.require(:id)
|
|
name = watched_words_params[:id].to_sym
|
|
action = WatchedWord.actions[name]
|
|
raise Discourse::NotFound if !action
|
|
|
|
content = WatchedWord.where(action: action)
|
|
if WatchedWord.has_replacement?(name)
|
|
content = content.pluck(:word, :replacement).map(&:to_csv).join
|
|
else
|
|
content = content.pluck(:word).join("\n")
|
|
end
|
|
|
|
headers["Content-Length"] = content.bytesize.to_s
|
|
send_data content,
|
|
filename: "#{Discourse.current_hostname}-watched-words-#{name}.csv",
|
|
content_type: "text/csv"
|
|
end
|
|
|
|
def clear_all
|
|
params.require(:id)
|
|
name = watched_words_params[:id].to_sym
|
|
action = WatchedWord.actions[name]
|
|
raise Discourse::NotFound if !action
|
|
|
|
WatchedWord
|
|
.where(action: action)
|
|
.find_each do |watched_word|
|
|
watched_word.destroy!
|
|
StaffActionLogger.new(current_user).log_watched_words_deletion(watched_word)
|
|
end
|
|
WordWatcher.clear_cache!
|
|
render json: success_json
|
|
end
|
|
|
|
private
|
|
|
|
def watched_words_params
|
|
@watched_words_params ||=
|
|
params.permit(
|
|
:id,
|
|
:replacement,
|
|
:action_key,
|
|
:case_sensitive,
|
|
:html,
|
|
words: [],
|
|
replacement_tags: %i[id name],
|
|
)
|
|
end
|
|
|
|
def resolve_replacement_tags
|
|
tags_param = watched_words_params[:replacement_tags]
|
|
tags_param = tags_param.values if tags_param.is_a?(ActionController::Parameters)
|
|
return if tags_param.blank?
|
|
|
|
tag_ids = tags_param.filter_map { |t| t[:id]&.to_i }
|
|
new_tag_names = tags_param.select { |t| t[:id].blank? }.filter_map { |t| t[:name] }
|
|
|
|
tag_names = Tag.where(id: tag_ids).pluck(:name)
|
|
|
|
if new_tag_names.present?
|
|
new_tags = DiscourseTagging.find_or_create_tags!(new_tag_names, guardian)
|
|
tag_names.concat(new_tags.map(&:name))
|
|
end
|
|
|
|
tag_names.join(",")
|
|
end
|
|
end
|