UX: Add notice when watched words are regexes (#13493)

UX: Add notice when watched words are regexes (#13493)

There is a big difference between regular watched words and regular expressions and this has been confusing in the past. This notice adds an explanation.

This commit also reorganizes the code of the test modal.

diff --git a/app/assets/javascripts/admin/addon/controllers/modals/admin-watched-word-test.js b/app/assets/javascripts/admin/addon/controllers/modals/admin-watched-word-test.js
index 3ce1204..3ea2618 100644
--- a/app/assets/javascripts/admin/addon/controllers/modals/admin-watched-word-test.js
+++ b/app/assets/javascripts/admin/addon/controllers/modals/admin-watched-word-test.js
@@ -18,33 +18,45 @@ export default Controller.extend(ModalFunctionality, {
   )
   matches(value, regexpString, words, isReplace, isTag, isLink) {
     if (!value || !regexpString) {
-      return;
+      return [];
     }
 
-    const regexp = new RegExp(regexpString, "ig");
-    const matches = value.match(regexp) || [];
-
     if (isReplace || isLink) {
-      return matches.map((match) => ({
-        match,
-        replacement: words.find((word) =>
-          new RegExp(word.regexp, "ig").test(match)
-        ).replacement,
-      }));
+      const matches = [];
+      words.forEach((word) => {
+        const regexp = new RegExp(word.regexp, "gi");
+        let match;
+        while ((match = regexp.exec(value)) !== null) {
+          matches.push({
+            match: match[1],
+            replacement: word.replacement,
+          });
+        }
+      });
+      return matches;
     } else if (isTag) {
-      return matches.map((match) => {
-        const tags = new Set();
-
-        words.forEach((word) => {
-          if (new RegExp(word.regexp, "ig").test(match)) {
-            word.replacement.split(",").forEach((tag) => tags.add(tag));
+      const matches = {};
+      words.forEach((word) => {
+        const regexp = new RegExp(word.regexp, "gi");
+        let match;
+        while ((match = regexp.exec(value)) !== null) {
+          if (!matches[match[1]]) {
+            matches[match[1]] = new Set();
           }
-        });
 
-        return { match, tags: Array.from(tags) };
+          let tags = matches[match[1]];
+          word.replacement.split(",").forEach((tag) => {
+            tags.add(tag);
+          });
+        }
       });
-    }
 
-    return matches;
+      return Object.entries(matches).map((entry) => ({
+        match: entry[0],
+        tags: Array.from(entry[1]),
+      }));
+    } else {
+      return value.match(new RegExp(regexpString, "ig")) || [];
+    }
   },
 });
diff --git a/app/assets/javascripts/admin/addon/templates/watched-words-action.hbs b/app/assets/javascripts/admin/addon/templates/watched-words-action.hbs
index 005e4b7..6860ac1 100644
--- a/app/assets/javascripts/admin/addon/templates/watched-words-action.hbs
+++ b/app/assets/javascripts/admin/addon/templates/watched-words-action.hbs
@@ -26,6 +26,10 @@
 
 <p class="about">{{actionDescription}}</p>
 
+{{#if siteSettings.watched_words_regular_expressions}}
+  <p>{{html-safe (i18n "admin.watched_words.regex_warning" basePath=(base-path))}}</p>
+{{/if}}
+
 {{watched-word-form
   actionKey=actionNameKey
   action=(action "recordAdded")
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index c4a1837..ca725af 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -4752,6 +4752,7 @@ en:
         clear_all: Clear All
         clear_all_confirm: "Are you sure you want to clear all watched words for the %{action} action?"
         invalid_regex: 'The watched word "%{word}" is an invalid regular expression.'
+        regex_warning: '<a href="%{basePath}/admin/site_settings/category/all_results?filter=watched%20words%20regular%20expressions%20">Watched words are regular expressions</a> and they do not automatically include word boundaries. If you want the regular expression to match whole words, include <code>\b</code> at the start and end of your regular expression.'
         actions:
           block: "Block"
           censor: "Censor"

GitHub sha: 8ab6fd88ef231ed782c9ce85c4424f81ec5a50a2

This commit appears in #13493 which was approved by ZogStriP. It was merged by nbianca.