FEATURE: Add a test facility to the watched words admin interface

FEATURE: Add a test facility to the watched words admin interface

diff --git a/app/assets/javascripts/admin/controllers/admin-watched-words-action.js.es6 b/app/assets/javascripts/admin/controllers/admin-watched-words-action.js.es6
index 2e12605..cf4a28e 100644
--- a/app/assets/javascripts/admin/controllers/admin-watched-words-action.js.es6
+++ b/app/assets/javascripts/admin/controllers/admin-watched-words-action.js.es6
@@ -2,6 +2,7 @@ import computed from "ember-addons/ember-computed-decorators";
 import WatchedWord from "admin/models/watched-word";
 import { ajax } from "discourse/lib/ajax";
 import { fmt } from "discourse/lib/computed";
+import showModal from "discourse/lib/show-modal";
 
 export default Ember.Controller.extend({
   actionNameKey: null,
@@ -101,6 +102,16 @@ export default Ember.Controller.extend({
           }
         }
       );
+    },
+
+    test() {
+      WatchedWord.findAll().then(data => {
+        this.set("adminWatchedWords.model", data);
+        showModal("admin-watched-word-test", {
+          admin: true,
+          model: this.currentAction
+        });
+      });
     }
   }
 });
diff --git a/app/assets/javascripts/admin/controllers/modals/admin-watched-word-test.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-watched-word-test.js.es6
new file mode 100644
index 0000000..7af6650
--- /dev/null
+++ b/app/assets/javascripts/admin/controllers/modals/admin-watched-word-test.js.es6
@@ -0,0 +1,11 @@
+import { default as computed } from "ember-addons/ember-computed-decorators";
+import ModalFunctionality from "discourse/mixins/modal-functionality";
+
+export default Ember.Controller.extend(ModalFunctionality, {
+  @computed("value", "model.compiledRegularExpression")
+  matches(value, regexpString) {
+    if (!value || !regexpString) return;
+    let censorRegexp = new RegExp(regexpString, "ig");
+    return value.match(censorRegexp);
+  }
+});
diff --git a/app/assets/javascripts/admin/models/watched-word.js.es6 b/app/assets/javascripts/admin/models/watched-word.js.es6
index 03404e7..9f33503 100644
--- a/app/assets/javascripts/admin/models/watched-word.js.es6
+++ b/app/assets/javascripts/admin/models/watched-word.js.es6
@@ -42,7 +42,8 @@ WatchedWord.reopenClass({
           name: I18n.t("admin.watched_words.actions." + n),
           words: actions[n],
           count: actions[n].length,
-          regularExpressions: list.regular_expressions
+          regularExpressions: list.regular_expressions,
+          compiledRegularExpression: list.compiled_regular_expressions[n]
         });
       });
     });
diff --git a/app/assets/javascripts/admin/templates/modal/admin-watched-word-test.hbs b/app/assets/javascripts/admin/templates/modal/admin-watched-word-test.hbs
new file mode 100644
index 0000000..a46f4dd
--- /dev/null
+++ b/app/assets/javascripts/admin/templates/modal/admin-watched-word-test.hbs
@@ -0,0 +1,16 @@
+{{#d-modal-body rawTitle=(i18n "admin.watched_words.test.modal_title" action=model.name) class="watched-words-test-modal"}}
+    <p>{{i18n "admin.watched_words.test.description"}}</p>
+    {{textarea name="test_value" value=value autofocus="autofocus"}}
+    {{#if matches}}
+        <p>
+            {{i18n "admin.watched_words.test.found_matches"}}
+            <ul>
+                {{#each matches as |match|}}
+                    <li>{{match}}</li>
+                {{/each}}
+            </ul>
+        </p>
+    {{else}}
+        <p>{{i18n "admin.watched_words.test.no_matches"}}</p>
+    {{/if}}
+{{/d-modal-body}}
diff --git a/app/assets/javascripts/admin/templates/watched-words-action.hbs b/app/assets/javascripts/admin/templates/watched-words-action.hbs
index 9e81157..68222cc 100644
--- a/app/assets/javascripts/admin/templates/watched-words-action.hbs
+++ b/app/assets/javascripts/admin/templates/watched-words-action.hbs
@@ -39,6 +39,10 @@
 
 <div class="clear-all-row">
   {{d-button
+    label="admin.watched_words.test.button_label"
+    icon="far-eye"
+    action=(action "test")}}
+  {{d-button
     class="btn-danger clear-all"
     label="admin.watched_words.clear_all"
     icon="trash-alt"
diff --git a/app/assets/stylesheets/common/admin/staff_logs.scss b/app/assets/stylesheets/common/admin/staff_logs.scss
index 349480b..f28b454 100644
--- a/app/assets/stylesheets/common/admin/staff_logs.scss
+++ b/app/assets/stylesheets/common/admin/staff_logs.scss
@@ -370,6 +370,9 @@ table.screened-ip-addresses {
     display: flex;
     margin-top: 10px;
     justify-content: flex-end;
+    .clear-all {
+      margin-left: 5px;
+    }
   }
 }
 
@@ -431,6 +434,10 @@ table.screened-ip-addresses {
   }
 }
 
+.watched-words-test-modal p {
+  margin-top: 0;
+}
+
 // Search logs
 
 table.search-logs-list {
diff --git a/app/serializers/watched_word_list_serializer.rb b/app/serializers/watched_word_list_serializer.rb
index d51f43e..5cc151c 100644
--- a/app/serializers/watched_word_list_serializer.rb
+++ b/app/serializers/watched_word_list_serializer.rb
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class WatchedWordListSerializer < ApplicationSerializer
-  attributes :actions, :words, :regular_expressions
+  attributes :actions, :words, :regular_expressions, :compiled_regular_expressions
 
   def actions
     WatchedWord.actions.keys
@@ -18,4 +18,12 @@ class WatchedWordListSerializer < ApplicationSerializer
   def regular_expressions
     SiteSetting.watched_words_regular_expressions?
   end
+
+  def compiled_regular_expressions
+    expressions = {}
+    WatchedWord.actions.keys.each do |action|
+      expressions[action] = WordWatcher.word_matcher_regexp(action)&.source
+    end
+    expressions
+  end
 end
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 3f8b850..b5e71fc 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -3920,6 +3920,12 @@ en:
           exists: "Already exists"
           upload: "Add from file"
           upload_successful: "Upload successful. Words have been added."
+        test:
+          button_label: "Test"
+          modal_title: "Test '%{action}' Watched Words"
+          description: "Enter text below to check for matches with watched words"
+          found_matches: "Found matches:"
+          no_matches: "No matches found"
 
       impersonate:
         title: "Impersonate"
diff --git a/test/javascripts/fixtures/watched-words-fixtures.js.es6 b/test/javascripts/fixtures/watched-words-fixtures.js.es6
index fb9869e..6ac2eaa 100644
--- a/test/javascripts/fixtures/watched-words-fixtures.js.es6
+++ b/test/javascripts/fixtures/watched-words-fixtures.js.es6
@@ -8,6 +8,7 @@ export default {
       { id: 4, word: "scheme", action: "flag" },
       { id: 5, word: "coupon", action: "require_approval" },
       { id: 6, word: '<img src="x">', action: "block" }
-    ]
+    ],
+    compiled_regular_expressions: {}
   }
 };

GitHub sha: 06e75724

1 Like