From 91c9a1eacaa653a072309ce2630e8fc227147a1e Mon Sep 17 00:00:00 2001 From: "Stephen M. Pallen" Date: Sun, 8 Apr 2018 16:01:33 -0400 Subject: [PATCH] Move message_replacement_patterns to ChatGeneral Settings --- CHANGELOG.md | 2 + assets/_scss/theme/imports/color_consts.scss | 2 +- assets/css/admin.scss | 18 +++ assets/js/admin.js | 30 +++++ assets/js/chan_user.js | 7 ++ .../controllers/site_avatar_controller.ex | 1 - .../lib/one_admin/services/admin_service.ex | 12 +- .../one_admin_web/channels/admin_channel.ex | 15 +++ plugins/one_chat/lib/one_chat/application.ex | 5 + .../one_chat/message_replacement_patterns.ex | 78 ++++++++++++ .../lib/one_chat/models/schema/channel.ex | 4 +- .../one_chat/settings/schema/chat_general.ex | 119 +++++++++++++++++- .../lib/one_chat_web/channels/user_channel.ex | 1 + .../templates/admin/chat_general.html.slime | 1 + .../message_replacement_patterns.html.slime | 11 ++ .../admin/replacement_pattern.html.slime | 17 +++ .../lib/one_chat_web/views/admin_view.ex | 14 +++ .../lib/one_chat_web/views/message_view.ex | 40 ++---- ...0112043010_add_patters_to_chat_general.exs | 9 ++ 19 files changed, 342 insertions(+), 44 deletions(-) create mode 100644 plugins/one_chat/lib/one_chat/message_replacement_patterns.ex create mode 100644 plugins/one_chat/lib/one_chat_web/templates/admin/message_replacement_patterns.html.slime create mode 100644 plugins/one_chat/lib/one_chat_web/templates/admin/replacement_pattern.html.slime create mode 100644 plugins/one_chat/priv/repo/migrations/04420180112043010_add_patters_to_chat_general.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index 97aef03..857ee31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ * Add site avatar to admin for customizing the desktop app icon * Add site_client_name to admin for customizing the desktop app tooltip * Add support for @@username and @@@username live status and status-message messages +* Move message_replacement_patterns to ChatGeneral Settings +* Optimize message_replacement_patterns processing with a run-time compiled module ### Bug Fixes diff --git a/assets/_scss/theme/imports/color_consts.scss b/assets/_scss/theme/imports/color_consts.scss index 321cedb..e00ae5c 100644 --- a/assets/_scss/theme/imports/color_consts.scss +++ b/assets/_scss/theme/imports/color_consts.scss @@ -21,7 +21,7 @@ $component-color: #EAEAEA; $success-color: #4dff4d; $pending-color: #1ec7f1; // $pending-color: #FCB316; -$error-color: #BC2031; +$error-color: rgb(244, 67, 54); $selection-color: #02ACEC; $attention-color: #9C27B0; $content-background-color: #FFFFFF; diff --git a/assets/css/admin.scss b/assets/css/admin.scss index fc6795a..1c2775d 100644 --- a/assets/css/admin.scss +++ b/assets/css/admin.scss @@ -63,3 +63,21 @@ form.backup { .reset-settings { margin-left: 20px; } + +label.message-pattern { + font-weight: bold; +} +input.message-pattern.cmd { + display: inline-block; + width: 50% +} +.input-line.double-col.message-pattern { + position: relative; + button.reset-settings { + display: none; + } +} +a.delete-pattern { + position: absolute; + bottom: 10px; +} diff --git a/assets/js/admin.js b/assets/js/admin.js index 552a96d..3a03b76 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -102,6 +102,8 @@ class Admin { }) .on('click', '.admin-settings button.save', function(e) { //console.log('saving form....', $('form').data('id')) + $('input.error').removeClass('error'); + $('.help-block').remove(); e.preventDefault() OneChat.userchan.push('admin:save:' + $('form').data('id'), $('form').serializeArray()) .receive("ok", resp => { @@ -132,6 +134,7 @@ class Admin { }) .on('click', '#showPassword', e => { let prefix = ""; + if ($('#user_password').length > 0) { prefix = "user_"; } @@ -240,6 +243,33 @@ class Admin { .on('change', 'section.page-container input.check', e => { this.enable_disable_batch_delete(); }) + .on('click', '.delete-pattern', e => { + let id = $(e.currentTarget).attr('data-id'); + let target = $(`.input-line.message-pattern[data-id="${id}"]`); + $(`input.pattern-deleted`).val("true"); + target.hide('slow', () => { + target.remove(); + this.enable_save_button(); + this.adjust_pattern_indexes(); + }); + }); + } + + adjust_pattern_indexes() { + // adjust pattern numbers and indexes + $.each($('.input-line.message-pattern'), (cnt, item) => { + let $item = $(item); + if (parseInt($item.attr('data-id')) !== cnt) { + $item.attr('data-id', cnt); + $item.find('[data-id]').attr('data-id', cnt); + $.each($item.find('[name]'), (_, item) => { + let $elem = $(item); + $elem.attr('name', $elem.attr('name').replace(/\[\d+\]/, `[${cnt}]`)); + }); + let label = $item.find('label.setting-label'); + label.text(label.text().replace(/[0-9]+/, cnt + 1)); + } + }); } enable_disable_batch_delete() { diff --git a/assets/js/chan_user.js b/assets/js/chan_user.js index 8111ed5..b885891 100644 --- a/assets/js/chan_user.js +++ b/assets/js/chan_user.js @@ -17,6 +17,13 @@ OneChat.on_connect(function(one_chat, socket) { console.log('userchan connect') let ucxchat = one_chat.ucxchat let chan = window.Rebel.channels.user.channel + + // TODO: This is a temporary work around to stop the duplicate messages. + // It however does not stop the duplicate channels on the server. + if (one_chat.userchan) { + console.warn('found an existing user channel', one_chat.userchan, chan); + return; + } one_chat.userchan = chan console.log('ucxchat', ucxchat) console.log('one_chat', one_chat) diff --git a/lib/infinity_one_web/controllers/site_avatar_controller.ex b/lib/infinity_one_web/controllers/site_avatar_controller.ex index 592626b..8ef7a54 100644 --- a/lib/infinity_one_web/controllers/site_avatar_controller.ex +++ b/lib/infinity_one_web/controllers/site_avatar_controller.ex @@ -5,7 +5,6 @@ defmodule InfinityOneWeb.SiteAvatarController do """ use InfinityOneWeb, :controller - alias InfinityOne.Accounts alias InfinityOne.Settings.General alias InfinityOne.SiteAvatar diff --git a/plugins/one_admin/lib/one_admin/services/admin_service.ex b/plugins/one_admin/lib/one_admin/services/admin_service.ex index 4b5de83..0ac844c 100644 --- a/plugins/one_admin/lib/one_admin/services/admin_service.ex +++ b/plugins/one_admin/lib/one_admin/services/admin_service.ex @@ -70,8 +70,13 @@ defmodule OneAdmin.AdminService do |> case do {:ok, _} -> {:ok, %{success: ~g"General settings updated successfully"}} - {:error, cs} -> - Logger.error "problem updating general: #{inspect cs}" + {:error, %{errors: errors} = cs} -> + Logger.debug fn -> "problem updating general: #{inspect cs.errors}" end + Enum.each(errors, fn {name, {error, _}} -> + error = String.replace(error, "'", "\\'") + js = ~s|$('input[name="#{name}"]').addClass('error').after('
#{error}
')| + Rebel.Core.exec_js(socket, js); + end) {:ok, %{error: ~g"There a problem updating your settings."}} end {:reply, resp, socket} @@ -317,14 +322,12 @@ defmodule OneAdmin.AdminService do def handle_in(ev = "permissions:role:new", _params, socket) do debug ev, "" - # Logger.warn "new role" Rebel.Core.async_js socket, ~s/$('.admin-link[data-id="admin_role"]').click();/ {:noreply, socket} end def handle_in(ev = "permissions:role:edit", params, socket) do debug ev, params - # Logger.warn "new role params: " <> inspect(params) AdminChannel.admin_link("admin_role", socket, Map.put(%{}, "edit-name", params["name"])) # {:noreply, socket} @@ -332,7 +335,6 @@ defmodule OneAdmin.AdminService do def handle_in(ev = "permissions:role:delete", params, socket) do debug ev, params - # Logger.warn "new role params: " <> inspect(params) role_name = params["name"] if role = InfinityOne.Accounts.get_by_role(name: role_name) do changeset = InfinityOne.Accounts.change_role(role) diff --git a/plugins/one_admin/lib/one_admin_web/channels/admin_channel.ex b/plugins/one_admin/lib/one_admin_web/channels/admin_channel.ex index ace54f7..4465f2f 100644 --- a/plugins/one_admin/lib/one_admin_web/channels/admin_channel.ex +++ b/plugins/one_admin/lib/one_admin_web/channels/admin_channel.ex @@ -482,4 +482,19 @@ defmodule OneAdminWeb.AdminChannel do async_js socket, ~s/$('.-autocomplete-container.rooms').removeClass('hidden')/ end + def admin_new_pattern(socket, sender) do + last_id_string = Rebel.Core.exec_js!(socket, ~s/$('.input-line.message-pattern').last().attr('data-id')/) + index = + case is_binary(last_id_string) && Integer.parse(last_id_string) do + {int, ""} -> int + 1 + _ -> 0 + end + + html = Phoenix.View.render_to_string(OneChatWeb.AdminView, "replacement_pattern.html", bindings: [hidden: true, index: index]) + + socket + |> Rebel.Query.insert(html, before: ~s/a.new-message-pattern/) + |> async_js(~s/$('a.new-message-pattern').prev().show('slow')/) + end + end diff --git a/plugins/one_chat/lib/one_chat/application.ex b/plugins/one_chat/lib/one_chat/application.ex index d4dbe61..ca1afaf 100644 --- a/plugins/one_chat/lib/one_chat/application.ex +++ b/plugins/one_chat/lib/one_chat/application.ex @@ -28,6 +28,11 @@ defmodule OneChat.Application do def start(_type, _args) do OneChatWeb.FlexBar.Defaults.add_buttons() OneChatWeb.RoomChannel.KeyStore.initialize() + spawn fn -> + # Need time for the app to fully load before compiling the Message Patterns + Process.sleep(1000) + OneChat.MessageReplacementPatterns.compile() + end end end diff --git a/plugins/one_chat/lib/one_chat/message_replacement_patterns.ex b/plugins/one_chat/lib/one_chat/message_replacement_patterns.ex new file mode 100644 index 0000000..932eb5b --- /dev/null +++ b/plugins/one_chat/lib/one_chat/message_replacement_patterns.ex @@ -0,0 +1,78 @@ +defmodule OneChat.MessageReplacementPatterns do + require Logger + + def compile do + compile_patterns() + |> compile_regex() + |> create_module() + |> compile_module() + end + + defp compile_patterns do + try do + {res, _} = Code.eval_string OneSettings.message_replacement_patterns() + res + rescue + _ -> [] + end + end + + defp compile_regex(patterns) do + Enum.reduce(patterns, [], fn tuple, acc -> + command = elem(tuple, 3) + command = + cond do + is_tuple(command) -> command + is_binary(command) && command =~ "." -> + case String.split(command, ".", trim: true) do + [_] -> "" + list when is_list(list) -> + [fun | modules] = Enum.reverse list + mod = modules |> Enum.reverse() |> Module.concat() + {mod, String.to_existing_atom(fun)} + _ -> "" + end + true -> "" + end + case Regex.compile(elem(tuple, 1)) do + {:ok, re} -> + [{re, elem(tuple, 2), command} | acc] + _ -> + Logger.warn "Failed to compile #{elem(tuple, 1)} for name: #{elem(tuple, 0)}" + acc + end + end) + end + + def create_module(patterns) do + """ + defmodule OneChat.RunPatterns do + require Logger + @patterns #{inspect patterns} + def get do + @patterns + end + def run(body) do + Enum.reduce(@patterns, body, fn {re, sub, command}, body -> + case command do + {mod, fun} -> + apply(mod, fun, [Regex.scan(re, body)]) + _ -> :ok + end + Regex.replace(re, body, sub) + end) + end + end + """ + end + + defp compile_module(module) do + try do + Code.eval_string(module) + rescue + e -> + Logger.warn "error: " <> inspect(e) + Logger.warn "Could not compile module: #{inspect module}" + end + end +end diff --git a/plugins/one_chat/lib/one_chat/models/schema/channel.ex b/plugins/one_chat/lib/one_chat/models/schema/channel.ex index 38f0870..ec28556 100644 --- a/plugins/one_chat/lib/one_chat/models/schema/channel.ex +++ b/plugins/one_chat/lib/one_chat/models/schema/channel.ex @@ -36,6 +36,8 @@ defmodule OneChat.Schema.Channel do @fields ~w(archived name type topic read_only blocked default user_id description active nway)a def model, do: OneChat.Channel + + def validate_name_re, do: ~r/^[a-z0-9\.\-_]+$/i @doc """ Builds a changeset based on the `struct` and `params`. """ @@ -68,7 +70,7 @@ defmodule OneChat.Schema.Channel do def validate(changeset, params \\ %{}) do changeset |> validate_required([:name, :user_id]) - |> validate_format(:name, ~r/^[a-z0-9\.\-_]+$/i) + |> validate_format(:name, validate_name_re()) |> validate_length(:name, min: 2, max: 55) |> handle_virtual_private(params) end diff --git a/plugins/one_chat/lib/one_chat/settings/schema/chat_general.ex b/plugins/one_chat/lib/one_chat/settings/schema/chat_general.ex index 9e5bc44..a2c40e4 100644 --- a/plugins/one_chat/lib/one_chat/settings/schema/chat_general.ex +++ b/plugins/one_chat/lib/one_chat/settings/schema/chat_general.ex @@ -1,5 +1,27 @@ defmodule OneChat.Settings.Schema.ChatGeneral do + @moduledoc """ + The Schema for the ChatGeneral Settings module. + + ## Message replacement patterns + + The `:message_replacement_patterns` field has very special handling here: + + * Input fields come in on the virtual `:patterns` field. + * The `:patterns` map is converted to a binary and put in the + `:message_replacement_patterns` field + * This binary contains an elixir array that is later dynamically evaluated. + * A dynamic module is then recompiled by `OneChat.MessageReplacementPatterns.compile/0` + after a brief spawned sleep. + + ### Example patterns + + "[{\"Jira\",\"\\\\[(UCX-[0-9]+)\\\\]([^\\\\(]|$|\\\\n)\",\"[\\\\1](https://emetrotel.atlassian.net/browse/\\\\1)\\\\2\",\"\"},{\"User Status\",\"@@([\\\\w\d0-9_]+)\",\"\\\\1\",\"Elixir.OneChat.refresh_users_status\"},{\"Status & Message\",\"@@@([\\\\w0-9_]+)\",\"\\\\1\",\"Elixir.OneChat.refresh_users_status\"},]" + + """ use OneSettings.Settings.Schema + use InfinityOneWeb.Gettext + + require Logger @all_slash_commands [ "join", "archive", "kick", "lennyface", "leave", "gimme", "create", "invite", @@ -29,21 +51,26 @@ defmodule OneChat.Settings.Schema.ChatGeneral do field :mobile_notifications_default_alert, :string, default: "mentions" field :max_members_disable_notifications, :integer, default: 100 field :first_channel_after_login, :string, default: "" + field :message_replacement_patterns, :string, default: "" + field :patterns, :map, default: %{}, virtual: true end - @fields [ + @required_fields [ :enable_favorite_rooms, :rooms_slash_commands, :chat_slash_commands, :unread_count, :unread_count_dm, :default_message_notification_audio, :audio_notifications_default_alert, :desktop_notifications_default_alert, - :mobile_notifications_default_alert, :max_members_disable_notifications, - :first_channel_after_login, + :mobile_notifications_default_alert, :max_members_disable_notifications ] + @fields [:patterns, :message_replacement_patterns, :first_channel_after_login | @required_fields] + def changeset(struct, params \\ %{}) do struct |> cast(params, @fields) |> first_channel_after_login(params) - |> validate_required(@fields -- [:first_channel_after_login]) + |> message_replacement_patterns() + |> validate_required(@required_fields) + |> prepare_changes(&prepare_patterns/1) end @doc """ @@ -61,10 +88,92 @@ defmodule OneChat.Settings.Schema.ChatGeneral do first_channel_after_login = params["first_channel_after_login"] || "" if params != %{} and not :first_channel_after_login in Map.keys(changeset.changes) and first_channel_after_login != changeset.data.first_channel_after_login do - put_change changeset, :first_channel_after_login, first_channel_after_login + + if first_channel_after_login == "" || first_channel_after_login =~ OneChat.Schema.Channel.validate_name_re() do + put_change changeset, :first_channel_after_login, first_channel_after_login + else + add_error changeset, :first_channel_after_login, ~g(Invalid format) + end else changeset end end + defp message_replacement_patterns(%{changes: %{patterns: patterns}} = changeset) do + {changeset, string} = + patterns + |> Map.values() + |> Enum.with_index() + |> Enum.reduce({changeset, ""}, fn + {%{"deleted" => "true"}, _}, acc -> + acc + {map, index}, {changeset, acc} -> + changeset = + changeset + |> validate_re(map["re"], index) + |> validate_name(map["name"], index) + |> validate_command(map["cmd"], index) + if changeset.valid? do + re = String.replace(map["re"], "\\", "\\\\") + sub = String.replace(map["sub"], "\\", "\\\\") + {changeset, acc <> ~s/{"#{map["name"]}","#{re}","#{sub}","#{map["cmd"] || ""}"},/} + else + {changeset, acc} + end + end) + new_value = "[#{string}]" + if changeset.valid? and changeset.data.message_replacement_patterns != new_value do + put_change changeset, :message_replacement_patterns, new_value + else + changeset + end + end + + defp message_replacement_patterns(changeset) do + # Logger.warn "changeset: " <> inspect(changeset) + changeset + end + + defp validate_re(changeset, re, index) do + case Regex.compile(re) do + {:ok, _} -> + changeset + {:error, error} -> + error = + error + |> inspect() + |> String.replace(~r/['\{\}]/, "") + add_error(changeset, pattern_field_name(:re, index), error) + end + end + + defp validate_command(changeset, command, index) do + if is_nil(command) or command =~ ~r/(^[A-Z][\w\.]+\.[a-z_]+$)|(^$)/ do + changeset + else + add_error(changeset, pattern_field_name(:cmd, index), ~g(invalid format)) + end + end + + defp validate_name(changeset, name, index) do + if is_nil(name) or name == "" do + add_error(changeset, pattern_field_name(:name, index), ~g(is required)) + else + changeset + end + end + + defp pattern_field_name(field, index), do: "chat_general[patterns[#{index}]#{field}]" + + defp prepare_patterns(%{action: action} = changeset) when action in ~w(insert update)a do + spawn fn -> + Process.sleep(250) + OneChat.MessageReplacementPatterns.compile() + end + changeset + end + + defp prepare_patterns(changeset) do + changeset + end end diff --git a/plugins/one_chat/lib/one_chat_web/channels/user_channel.ex b/plugins/one_chat/lib/one_chat_web/channels/user_channel.ex index 80bb24a..a3967a1 100644 --- a/plugins/one_chat/lib/one_chat_web/channels/user_channel.ex +++ b/plugins/one_chat/lib/one_chat_web/channels/user_channel.ex @@ -1778,5 +1778,6 @@ defmodule OneChatWeb.UserChannel do defdelegateadmin :admin_click_scoped_room defdelegateadmin :admin_autocomplete_mouseenter defdelegateadmin :admin_reset_setting_click + defdelegateadmin :admin_new_pattern end diff --git a/plugins/one_chat/lib/one_chat_web/templates/admin/chat_general.html.slime b/plugins/one_chat/lib/one_chat_web/templates/admin/chat_general.html.slime index 6feb474..242d523 100644 --- a/plugins/one_chat/lib/one_chat_web/templates/admin/chat_general.html.slime +++ b/plugins/one_chat/lib/one_chat_web/templates/admin/chat_general.html.slime @@ -25,6 +25,7 @@ section.page-container.page-home.page-static.page-settings.admin-settings = text_input_line f, item, :first_channel_after_login, ~g(First channel after login), disabled: "disabled", changed: @changed = unless defer do = radio_button_line(f, item, :enable_favorite_rooms, ~g"Enable Favorite Rooms", changed: @changed) + = render "message_replacement_patterns.html", f: f, item: item, field: :message_replacement_patterns, title: ~g"Message Replacement Patterns", changed: @changed = collapsable_section ~g"Notifications", fn _ -> - desc = ~g(Can be any custom sound or the default ones: beep, chelle, ding, droplet, highbell, seasons) = text_input_line f, item, :default_message_notification_audio, ~g"Default message notification audio", description: desc, changed: @changed diff --git a/plugins/one_chat/lib/one_chat_web/templates/admin/message_replacement_patterns.html.slime b/plugins/one_chat/lib/one_chat_web/templates/admin/message_replacement_patterns.html.slime new file mode 100644 index 0000000..e7eeea5 --- /dev/null +++ b/plugins/one_chat/lib/one_chat_web/templates/admin/message_replacement_patterns.html.slime @@ -0,0 +1,11 @@ +.section.section-collapsed + .section-title + .section-title-text= @title + .section-title-right + button.button.primary.expand + span= ~g"Expand" + .section-content.border-component-color.message-patterns + input.pattern-deleted type="hidden" name="chat_general[patterns[999999]deleted]" + = for {{name, re, sub, cmd}, index} <- format_message_replacement_patterns() do + = render("replacement_pattern.html", [bindings: [name: name, re: re, sub: sub, cmd: cmd, index: index]]) + a.button.primary.new-message-pattern href="#" style="margin-top: 10px;" rebel-click="admin_new_pattern" = ~g(NEW PATTERN) diff --git a/plugins/one_chat/lib/one_chat_web/templates/admin/replacement_pattern.html.slime b/plugins/one_chat/lib/one_chat_web/templates/admin/replacement_pattern.html.slime new file mode 100644 index 0000000..312697c --- /dev/null +++ b/plugins/one_chat/lib/one_chat_web/templates/admin/replacement_pattern.html.slime @@ -0,0 +1,17 @@ +- index = @bindings[:index] +- cmd = @bindings[:cmd] || "" +- style = if @bindings[:hidden], do: "display: none;", else: "" +.input-line.double-col.message-pattern data-id="#{index}" style="#{style}" + label.setting-label = ~g(Pattern) <> " #{index + 1}" + .setting-field + label.message-pattern = ~g(Name) + input.message-pattern.name type="text" value="#{@bindings[:name]}" name="chat_general[patterns[#{index}]name]" + label.message-pattern = ~g(Match Expression) + input.message-pattern.re type="text" value="#{escape_val(@bindings[:re])}" name="chat_general[patterns[#{index}]re]" + label.message-pattern = ~g(Substution Expression) + input.message-pattern.sub type="text" value="#{escape_val(@bindings[:sub])}" name="chat_general[patterns[#{index}]sub]" + label.message-pattern = ~g(Command Module and Function) + input.message-pattern.cmd type="text" value="#{get_command_value(cmd)}" placeholder="#{~g(MyModule.handle_pattern)}" name="chat_general[patterns[#{index}]cmd]" + a.button.danger.delete-pattern href="#" data-id="#{index}" title="#{~g(Delete this pattern)}" + = ~g(Delete) + i.icon-trash diff --git a/plugins/one_chat/lib/one_chat_web/views/admin_view.ex b/plugins/one_chat/lib/one_chat_web/views/admin_view.ex index ca2220a..ea90d4d 100644 --- a/plugins/one_chat/lib/one_chat_web/views/admin_view.ex +++ b/plugins/one_chat/lib/one_chat_web/views/admin_view.ex @@ -64,4 +64,18 @@ defmodule OneChatWeb.AdminView do def unauthorized_message do ~g(You are not authorized to view this page.) end + + def format_message_replacement_patterns do + OneSettings.message_replacement_patterns() + |> Code.eval_string() + |> elem(0) + |> Enum.with_index() + end + + def escape_val(value) do + value + end + + def get_command_value({mod, fun}), do: "#{mod}.#{fun}" + def get_command_value(command), do: command end diff --git a/plugins/one_chat/lib/one_chat_web/views/message_view.ex b/plugins/one_chat/lib/one_chat_web/views/message_view.ex index 3167608..e40732a 100644 --- a/plugins/one_chat/lib/one_chat_web/views/message_view.ex +++ b/plugins/one_chat/lib/one_chat_web/views/message_view.ex @@ -363,24 +363,7 @@ defmodule OneChatWeb.MessageView do * message_replacement_patterns - A compiled version of Regex translations """ def message_opts do - [md_key: md_key(), message_replacement_patterns: compile_message_replacement_patterns()] - end - - defp compile_message_replacement_patterns do - :infinity_one - |> Application.get_env(:message_replacement_patterns, []) - |> Enum.reduce([], fn - {re, sub}, acc -> - case Regex.compile re do - {:ok, re} -> [{re, sub} | acc] - _ -> acc - end - {re, sub, command}, acc -> - case Regex.compile re do - {:ok, re} -> [{re, sub, command} | acc] - _ -> acc - end - end) + [md_key: md_key()] end @doc """ @@ -400,19 +383,18 @@ defmodule OneChatWeb.MessageView do def format_message_body(message, user, opts \\ []) do body = message.body || "" md_key = Keyword.get(opts, :md_key, md_key()) - message_replacement_patterns = opts[:message_replacement_patterns] || compile_message_replacement_patterns() markdown? = md_key && String.contains?(body, md_key) quoted? = String.contains?(body, "```") && !markdown? body |> html_escape(!message.system) - |> autolink() |> encode_mentions(user) |> encode_room_links |> EmojiOne.shortname_to_image(single_class: "big") |> message_formats(markdown?) - |> run_message_replacement_patterns(message_replacement_patterns) + |> run_message_replacement_patterns() + |> autolink() |> run_markdown(markdown?, md_key) |> format_newlines(quoted? || markdown?, message.system) |> OneChatWeb.SharedView.format_quoted_code(quoted? && !markdown?, message.system) @@ -430,18 +412,14 @@ defmodule OneChatWeb.MessageView do Runs the configured message replacement patterns regex's against the message body. """ - def run_message_replacement_patterns(body, [_ | _] = patterns) do - Enum.reduce(patterns, body, fn - {re, sub}, body -> - Regex.replace(re, body, sub) - {re, sub, {mod, fun}}, body -> - apply(mod, fun, [Regex.scan(re, body)]) - Regex.replace(re, body, sub) - end) + def run_message_replacement_patterns(body) do + if function_exported?(OneChat.RunPatterns, :run, 1) do + apply(OneChat.RunPatterns, :run, [body]) + else + body + end end - def run_message_replacement_patterns(body, _), do: body - defp autolink(body, opts \\ []) defp autolink(body, false), do: body defp autolink(body, opts) do diff --git a/plugins/one_chat/priv/repo/migrations/04420180112043010_add_patters_to_chat_general.exs b/plugins/one_chat/priv/repo/migrations/04420180112043010_add_patters_to_chat_general.exs new file mode 100644 index 0000000..32cb574 --- /dev/null +++ b/plugins/one_chat/priv/repo/migrations/04420180112043010_add_patters_to_chat_general.exs @@ -0,0 +1,9 @@ +defmodule InfinityOne.Repo.Migrations.AddPatternsToChatGeneral do + use Ecto.Migration + + def change do + alter table(:settings_chat_general) do + add :message_replacement_patterns, :text + end + end +end