Skip to content

Commit

Permalink
FEATURE: add HTML replacements
Browse files Browse the repository at this point in the history
This adds support for Watched Words to allow replacement with HTML content rather than always replacing with text.

Can be useful when automatically replacing with the abbr tag for example.
  • Loading branch information
ZogStriP committed May 6, 2024
1 parent 22db0b5 commit 637e926
Show file tree
Hide file tree
Showing 13 changed files with 63 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
{{/each}}
{{/if}}
{{#if this.isCaseSensitive}}
<span class="case-sensitive">{{i18n
"admin.watched_words.case_sensitive"
}}</span>
<span class="case-sensitive">{{i18n "admin.watched_words.case_sensitive"}}</span>
{{/if}}
{{#if this.isHtml}}
<span class="html">{{i18n "admin.watched_words.html"}}</span>
{{/if}}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ export default class AdminWatchedWord extends Component {
@service dialog;

@equal("actionKey", "replace") isReplace;

@equal("actionKey", "tag") isTag;

@equal("actionKey", "link") isLink;

@alias("word.case_sensitive") isCaseSensitive;
@alias("word.html") isHtml;

@discourseComputed("word.replacement")
tags(replacement) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@
</label>
</div>

<div class="watched-word-input">
<label for="watched-html">{{i18n "admin.watched_words.form.html_label"}}</label>
<label class="html-checkbox checkbox-label">
<Input
@type="checkbox"
@checked={{this.isHtml}}
disabled={{this.formSubmitted}}
/>
{{i18n "admin.watched_words.form.html_description"}}
</label>
</div>

<DButton
@action={{this.submitForm}}
@disabled={{this.submitDisabled}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@ export default class WatchedWordForm extends Component {
actionKey = null;
showMessage = false;
isCaseSensitive = false;
isHtml = false;
selectedTags = [];
words = [];

@empty("words") submitDisabled;

@equal("actionKey", "replace") canReplace;

@equal("actionKey", "tag") canTag;

@equal("actionKey", "link") canLink;

@discourseComputed("siteSettings.watched_words_regular_expressions")
Expand Down Expand Up @@ -102,6 +100,7 @@ export default class WatchedWordForm extends Component {
: null,
action: this.actionKey,
isCaseSensitive: this.isCaseSensitive,
isHtml: this.isHtml,
});

watchedWord
Expand All @@ -114,6 +113,7 @@ export default class WatchedWordForm extends Component {
showMessage: true,
message: I18n.t("admin.watched_words.form.success"),
isCaseSensitive: false,
isHtml: false,
});
if (result.words) {
result.words.forEach((word) => {
Expand Down
1 change: 1 addition & 0 deletions app/assets/javascripts/admin/addon/models/watched-word.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export default class WatchedWord extends EmberObject {
replacement: this.replacement,
action_key: this.action,
case_sensitive: this.isCaseSensitive,
html: this.isHtml,
},
dataType: "json",
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function isLinkClose(str) {
function findAllMatches(text, matchers) {
const matches = [];

for (const { word, pattern, replacement, link } of matchers) {
for (const { word, pattern, replacement, link, html } of matchers) {
if (matches.length >= MAX_MATCHES) {
break;
}
Expand All @@ -28,6 +28,7 @@ function findAllMatches(text, matchers) {
text: match[1],
replacement,
link,
html,
});

if (matches.length >= MAX_MATCHES) {
Expand Down Expand Up @@ -65,6 +66,7 @@ export function setup(helper) {
pattern: createWatchedWordRegExp(word),
replacement: options.replacement,
link: false,
html: options.html,
});
}
);
Expand Down Expand Up @@ -239,7 +241,8 @@ export function setup(helper) {
nodes.push(token);
}
} else {
token = new state.Token("text", "", 0);
let tokenType = matches[ln].html ? "html_inline" : "text";
token = new state.Token(tokenType, "", 0);
token.content = matches[ln].replacement;
token.level = level;
nodes.push(token);
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/admin/watched_words_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,6 @@ def clear_all

def watched_words_params
@watched_words_params ||=
params.permit(:id, :replacement, :action_key, :case_sensitive, words: [])
params.permit(:id, :replacement, :action_key, :case_sensitive, :html, words: [])
end
end
12 changes: 7 additions & 5 deletions app/models/watched_word.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class WatchedWord < ActiveRecord::Base
validates :action, presence: true
validate :replacement_is_url, if: -> { action == WatchedWord.actions[:link] }
validate :replacement_is_tag_list, if: -> { action == WatchedWord.actions[:tag] }
validate :replacement_is_html, if: -> { replacement.present? && html? }

validates_each :word do |record, attr, val|
if WatchedWord.where(action: record.action).count >= MAX_WORDS_PER_ACTION
Expand Down Expand Up @@ -65,6 +66,7 @@ def self.create_or_update_word(params)
word.action_key = params[:action_key] if params[:action_key]
word.action = params[:action] if params[:action]
word.case_sensitive = params[:case_sensitive] if !params[:case_sensitive].nil?
word.html = params[:html] if params[:html]
word.watched_word_group_id = params[:watched_word_group_id]
word.save
word
Expand All @@ -79,11 +81,7 @@ def action_key=(arg)
end

def action_log_details
if replacement.present?
"#{word}#{replacement}"
else
word
end
replacement.present? ? "#{word}#{replacement}" : word
end

private
Expand All @@ -107,6 +105,10 @@ def replacement_is_tag_list
errors.add(:base, :invalid_tag_list)
end
end

def replacement_is_html
errors.add(:base, :invalid_html) if action != WatchedWord.actions[:replace]
end
end

# == Schema Information
Expand Down
13 changes: 12 additions & 1 deletion app/serializers/watched_word_serializer.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
# frozen_string_literal: true

class WatchedWordSerializer < ApplicationSerializer
attributes :id, :word, :regexp, :replacement, :action, :case_sensitive, :watched_word_group_id
attributes :id,
:word,
:regexp,
:replacement,
:action,
:case_sensitive,
:watched_word_group_id,
:html

def regexp
WordWatcher.word_to_regexp(word, engine: :js)
Expand All @@ -14,4 +21,8 @@ def action
def include_replacement?
WatchedWord.has_replacement?(action)
end

def include_html?
object.action == WatchedWord.actions[:replace] && object.html
end
end
11 changes: 5 additions & 6 deletions app/services/word_watcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@ def self.words_for_action(action)
.where(action: WatchedWord.actions[action.to_sym])
.limit(WatchedWord::MAX_WORDS_PER_ACTION)
.order(:id)
.pluck(:word, :replacement, :case_sensitive)
.to_h do |w, r, c|
[
word_to_regexp(w, match_word: false),
{ word: w, replacement: r, case_sensitive: c }.compact,
]
.pluck(:word, :replacement, :case_sensitive, :html)
.to_h do |w, r, c, h|
opts = { word: w, replacement: r, case_sensitive: c }.compact
opts[:html] = true if h
[word_to_regexp(w, match_word: false), opts]
end
end

Expand Down
5 changes: 4 additions & 1 deletion config/locales/client.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4765,7 +4765,7 @@ en:
clear_filter: "Clear filter"
no_results:
title: "No results"
description: "We couldn’t find anything matching ‘%{filter}’.<br><br>Did you want to <a class=\"sidebar-additional-filter-settings\" href=\"%{settings_filter_url}\">search site settings</a> or the <a class=\"sidebar-additional-filter-users\" href=\"%{user_list_filter_url}\">admin user list?</a>"
description: 'We couldn’t find anything matching ‘%{filter}’.<br><br>Did you want to <a class="sidebar-additional-filter-settings" href="%{settings_filter_url}">search site settings</a> or the <a class="sidebar-additional-filter-users" href="%{user_list_filter_url}">admin user list?</a>'

welcome_topic_banner:
title: "Create your Welcome Topic"
Expand Down Expand Up @@ -6098,6 +6098,7 @@ en:
one: "show %{count} word"
other: "show %{count} words"
case_sensitive: "(case-sensitive)"
html: "(html)"
download: Download
clear_all: Clear All
clear_all_confirm: "Are you sure you want to clear all watched words for the %{action} action?"
Expand Down Expand Up @@ -6137,6 +6138,8 @@ en:
upload_successful: "Upload successful. Words have been added."
case_sensitivity_label: "Is case-sensitive"
case_sensitivity_description: "Only words with matching character casing"
html_label: "HTML"
html_description: "Outputs HTML in the replacement"
words_or_phrases: "words or phrases"
test:
button_label: "Test"
Expand Down
1 change: 1 addition & 0 deletions config/locales/server.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,7 @@ en:
base:
invalid_url: "Replacement URL is invalid"
invalid_tag_list: "Replacement tag list is invalid"
invalid_html: "HTML can only be used for replacement"
sidebar_section_link:
attributes:
linkable_type:
Expand Down
7 changes: 7 additions & 0 deletions db/migrate/20240506125839_add_html_to_watched_words.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class AddHtmlToWatchedWords < ActiveRecord::Migration[7.0]
def change
add_column :watched_words, :html, :boolean, default: false, null: false
end
end

0 comments on commit 637e926

Please sign in to comment.