FIX: Allow to add the same watched word with a different case (#17799)

Currently we can’t add a case-sensitive watched word if another one
exists with a different case. For example, the existing watched word
`Meta` has been created and is case-sensitive. Now an admin tries to add
`metA` while marking it as case-sensitive too, this won’t work and the
word won’t be added.

This patch changes this behavior by allowing to add same words that have
different cases, so the example above will now work as expected.

We still check for uniqueness but case-sensitivy is now taken
into account. It means that if the watched word `meta` already exists
and is not case-sensitive then it will not be possible to add `Meta`
(case-sensitive or not) as `meta` already matches every possible
variations of this word.
This commit is contained in:
Loïc Guitaut 2022-08-05 12:18:17 +02:00 committed by GitHub
parent 39f12ddeca
commit 5c37a5d0f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 4 deletions

View File

@ -47,9 +47,12 @@ export default Component.extend({
const filtered = words.filter( const filtered = words.filter(
(content) => content.action === this.actionKey (content) => content.action === this.actionKey
); );
return filtered.every( return filtered.every((content) => {
(content) => content.word.toLowerCase() !== word.toLowerCase() if (content.case_sensitive === true) {
); return content.word !== word;
}
return content.word.toLowerCase() !== word.toLowerCase();
});
}, },
actions: { actions: {

View File

@ -86,6 +86,14 @@ acceptance("Admin - Watched Words", function (needs) {
assert assert
.dom(".watched-words-list .watched-word") .dom(".watched-words-list .watched-word")
.hasText(`Discourse ${I18n.t("admin.watched_words.case_sensitive")}`); .hasText(`Discourse ${I18n.t("admin.watched_words.case_sensitive")}`);
fillIn(".watched-word-form input", "discourse");
click(".case-sensitivity-checkbox");
await click(".watched-word-form button");
assert
.dom(".watched-words-list .watched-word")
.hasText(`discourse ${I18n.t("admin.watched_words.case_sensitive")}`);
}); });
test("remove words", async function (assert) { test("remove words", async function (assert) {

View File

@ -40,6 +40,9 @@ class WatchedWord < ActiveRecord::Base
after_destroy :clear_cache after_destroy :clear_cache
scope :by_action, -> { order("action ASC, word ASC") } scope :by_action, -> { order("action ASC, word ASC") }
scope :for, ->(word:) do
where("(word ILIKE :word AND case_sensitive = 'f') OR (word LIKE :word AND case_sensitive = 't')", word: word)
end
def self.normalize_word(w) def self.normalize_word(w)
w.strip.squeeze('*') w.strip.squeeze('*')
@ -61,7 +64,7 @@ class WatchedWord < ActiveRecord::Base
def self.create_or_update_word(params) def self.create_or_update_word(params)
new_word = normalize_word(params[:word]) new_word = normalize_word(params[:word])
w = WatchedWord.where("word ILIKE ?", new_word).first || WatchedWord.new(word: new_word) w = self.for(word: new_word).first_or_initialize(word: new_word)
w.replacement = params[:replacement] if params[:replacement] w.replacement = params[:replacement] if params[:replacement]
w.action_key = params[:action_key] if params[:action_key] w.action_key = params[:action_key] if params[:action_key]
w.action = params[:action] if params[:action] w.action = params[:action] if params[:action]

View File

@ -125,5 +125,36 @@ RSpec.describe WatchedWord do
expect(updated.case_sensitive?).to eq(false) expect(updated.case_sensitive?).to eq(false)
end end
context "when a case-sensitive word already exists" do
subject(:create_or_update) do
described_class.create_or_update_word(word: word, action_key: :block, case_sensitive: true)
end
fab!(:existing_word) { Fabricate(:watched_word, case_sensitive: true, word: "Meta") }
context "when providing the exact same word" do
let(:word) { existing_word.word }
it "doesn't create a new watched word" do
expect { create_or_update }.not_to change { described_class.count }
end
it "returns the existing watched word" do
expect(create_or_update).to eq(existing_word)
end
end
context "when providing the same word with a different case" do
let(:word) { "metA" }
it "creates a new watched word" do
expect(create_or_update).not_to eq(existing_word)
end
it "returns the new watched word" do
expect(create_or_update).to have_attributes word: "metA", case_sensitive: true, action: 1
end
end
end
end end
end end