diff --git a/app/assets/javascripts/admin/addon/templates/components/watched-word-uploader.hbs b/app/assets/javascripts/admin/addon/templates/components/watched-word-uploader.hbs index 87c192ed67e..41c4920d637 100644 --- a/app/assets/javascripts/admin/addon/templates/components/watched-word-uploader.hbs +++ b/app/assets/javascripts/admin/addon/templates/components/watched-word-uploader.hbs @@ -1,5 +1,5 @@ diff --git a/app/controllers/admin/watched_words_controller.rb b/app/controllers/admin/watched_words_controller.rb index b356a765eec..bfc039441f1 100644 --- a/app/controllers/admin/watched_words_controller.rb +++ b/app/controllers/admin/watched_words_controller.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'csv' + class Admin::WatchedWordsController < Admin::AdminController skip_before_action :check_xhr, only: [:download] @@ -26,12 +28,20 @@ class Admin::WatchedWordsController < Admin::AdminController def upload file = params[:file] || params[:files].first action_key = params[:action_key].to_sym + has_replacement = WatchedWord.has_replacement?(action_key) Scheduler::Defer.later("Upload watched words") do begin - File.open(file.tempfile, encoding: "bom|utf-8").each_line do |line| - WatchedWord.create_or_update_word(word: line, action_key: action_key) unless line.empty? + CSV.foreach(file.tempfile, encoding: "bom|utf-8") do |row| + if row[0].present? && (!has_replacement || row[1].present?) + WatchedWord.create_or_update_word( + word: row[0], + replacement: has_replacement ? row[1] : nil, + action_key: action_key + ) + end end + data = { url: '/ok' } rescue => e data = failed_json.merge(errors: [e.message]) @@ -48,11 +58,17 @@ class Admin::WatchedWordsController < Admin::AdminController action = WatchedWord.actions[name] raise Discourse::NotFound if !action - content = WatchedWord.where(action: action).pluck(:word).join("\n") + 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}.txt", - content_type: "text/plain" + filename: "#{Discourse.current_hostname}-watched-words-#{name}.csv", + content_type: "text/csv" end def clear_all diff --git a/app/models/watched_word.rb b/app/models/watched_word.rb index 2cbe61dcc12..6c9c0e07a3b 100644 --- a/app/models/watched_word.rb +++ b/app/models/watched_word.rb @@ -46,6 +46,10 @@ class WatchedWord < ActiveRecord::Base w end + def self.has_replacement?(action) + action == :replace || action == :tag + end + def action_key=(arg) self.action = self.class.actions[arg.to_sym] end diff --git a/app/serializers/watched_word_serializer.rb b/app/serializers/watched_word_serializer.rb index f96fbde53fd..0a73b4a2a32 100644 --- a/app/serializers/watched_word_serializer.rb +++ b/app/serializers/watched_word_serializer.rb @@ -8,6 +8,6 @@ class WatchedWordSerializer < ApplicationSerializer end def include_replacement? - action == :replace || action == :tag + WatchedWord.has_replacement?(action) end end diff --git a/spec/fixtures/csv/words_tag.csv b/spec/fixtures/csv/words_tag.csv new file mode 100644 index 00000000000..eb95e96c6b5 --- /dev/null +++ b/spec/fixtures/csv/words_tag.csv @@ -0,0 +1,11 @@ +hello,"tag1,tag2" + +,"tag1,tag3" + + +world,"tag2,tag3" + + + +test, + diff --git a/spec/requests/admin/watched_words_controller_spec.rb b/spec/requests/admin/watched_words_controller_spec.rb index 97cac195f94..50017ff4e57 100644 --- a/spec/requests/admin/watched_words_controller_spec.rb +++ b/spec/requests/admin/watched_words_controller_spec.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'csv' require 'rails_helper' RSpec.describe Admin::WatchedWordsController do @@ -47,6 +48,23 @@ RSpec.describe Admin::WatchedWordsController do expect(WatchedWord.pluck(:action).uniq).to eq([WatchedWord.actions[:flag]]) end + + it 'creates the words from the file' do + post '/admin/logs/watched_words/upload.json', params: { + action_key: 'tag', + file: Rack::Test::UploadedFile.new(file_from_fixtures("words_tag.csv", "csv")) + } + + expect(response.status).to eq(200) + expect(WatchedWord.count).to eq(2) + + expect(WatchedWord.pluck(:word, :replacement)).to contain_exactly( + ['hello', 'tag1,tag2'], + ['world', 'tag2,tag3'] + ) + + expect(WatchedWord.pluck(:action).uniq).to eq([WatchedWord.actions[:tag]]) + end end end @@ -67,6 +85,8 @@ RSpec.describe Admin::WatchedWordsController do block_word_1 = Fabricate(:watched_word, action: WatchedWord.actions[:block]) block_word_2 = Fabricate(:watched_word, action: WatchedWord.actions[:block]) censor_word_1 = Fabricate(:watched_word, action: WatchedWord.actions[:censor]) + autotag_1 = Fabricate(:watched_word, action: WatchedWord.actions[:tag], replacement: "tag1,tag2") + autotag_2 = Fabricate(:watched_word, action: WatchedWord.actions[:tag], replacement: "tag3,tag2") get "/admin/logs/watched_words/action/block/download" expect(response.status).to eq(200) @@ -76,7 +96,15 @@ RSpec.describe Admin::WatchedWordsController do get "/admin/logs/watched_words/action/censor/download" expect(response.status).to eq(200) censor_words = response.body.split("\n") - expect(censor_words).to eq([censor_word_1.word]) + expect(censor_words).to contain_exactly(censor_word_1.word) + + get "/admin/logs/watched_words/action/tag/download" + expect(response.status).to eq(200) + tag_words = response.body.split("\n").map(&:parse_csv) + expect(tag_words).to contain_exactly( + [autotag_1.word, autotag_1.replacement], + [autotag_2.word, autotag_2.replacement] + ) end end end