From f23d70594faeccb73cedf26033588c99f6c5c8e3 Mon Sep 17 00:00:00 2001 From: Alexander Meindl Date: Sun, 10 Apr 2022 13:45:24 +0200 Subject: [PATCH] Switch from gemoji to tanuki_emoji --- .eslintrc.yml | 2 + .rubocop.yml | 2 +- CHANGELOG.rst | 1 + additionals.gemspec | 2 +- .../additionals/settings/_general.html.slim | 4 + assets/images/smilies.png | Bin 0 -> 9399 bytes assets/javascripts/additionals.js | 6 + assets/stylesheets/additionals.css | 119 +++++------------- config/locales/cs.yml | 4 +- config/locales/de.yml | 4 +- config/locales/en.yml | 4 +- config/locales/es.yml | 4 +- config/locales/fr.yml | 4 +- config/locales/it.yml | 4 +- config/locales/ja.yml | 4 +- config/locales/ko.yml | 4 +- config/locales/pl.yml | 4 +- config/locales/pt-BR.yml | 4 +- config/locales/ru.yml | 4 +- config/locales/zh-TW.yml | 4 +- config/locales/zh.yml | 4 +- config/settings.yml | 10 +- docs/index.rst | 8 -- lib/additionals.rb | 9 +- lib/additionals/formatter.rb | 70 ++++++++--- lib/additionals/gemify.rb | 22 ++++ .../patches/formatter_common_mark_patch.rb | 27 ++++ .../patches/formatter_markdown_patch.rb | 4 +- .../patches/formatter_textile_patch.rb | 15 ++- .../common_mark/emoji_filter.rb | 65 ++++++++++ .../common_mark/smiley_filter.rb | 26 ++++ test/unit/wiki_formating/common_mark_test.rb | 61 +++++++++ test/unit/wiki_formating/markdown_test.rb | 53 ++++++++ test/unit/wiki_formating/textile_test.rb | 75 +++++++++++ 34 files changed, 496 insertions(+), 137 deletions(-) create mode 100644 assets/images/smilies.png create mode 100644 lib/additionals/patches/formatter_common_mark_patch.rb create mode 100644 lib/additionals/wiki_formatting/common_mark/emoji_filter.rb create mode 100644 lib/additionals/wiki_formatting/common_mark/smiley_filter.rb create mode 100644 test/unit/wiki_formating/common_mark_test.rb create mode 100644 test/unit/wiki_formating/markdown_test.rb create mode 100644 test/unit/wiki_formating/textile_test.rb diff --git a/.eslintrc.yml b/.eslintrc.yml index f0f5725e..d88d2bbe 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -2,6 +2,8 @@ env: browser: true jquery: true extends: 'eslint:recommended' +parserOptions: + ecmaVersion: 2019 rules: indent: - error diff --git a/.rubocop.yml b/.rubocop.yml index 6a478dfe..2da4c060 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -7,7 +7,7 @@ Rails: AllCops: TargetRubyVersion: 2.7 - TargetRailsVersion: 6.1.4 + TargetRailsVersion: 6.1 NewCops: enable Metrics/AbcSize: diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6044ac86..3ab72935 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,6 +10,7 @@ Changelog - Drop feature "remove help" from top menu - Add project filter for new_issue_message type - Mermaid 9.0.0 support +- Switch from gemoji to tanuki_emoji 3.0.5.2 +++++++ diff --git a/additionals.gemspec b/additionals.gemspec index 8e18a984..6b0d72d6 100644 --- a/additionals.gemspec +++ b/additionals.gemspec @@ -20,9 +20,9 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.required_ruby_version = '>= 2.7' - spec.add_runtime_dependency 'gemoji', '~> 3.0.0' spec.add_runtime_dependency 'redmine_plugin_kit' spec.add_runtime_dependency 'render_async' spec.add_runtime_dependency 'rss' spec.add_runtime_dependency 'slim-rails' + spec.add_runtime_dependency 'tanuki_emoji', '~> 0.6' end diff --git a/app/views/additionals/settings/_general.html.slim b/app/views/additionals/settings/_general.html.slim index 4fc6ab24..6363e66c 100644 --- a/app/views/additionals/settings/_general.html.slim +++ b/app/views/additionals/settings/_general.html.slim @@ -25,6 +25,10 @@ fieldset.settings = additionals_settings_checkbox :add_go_to_top em.info = t :add_go_to_top_info + p + = additionals_settings_checkbox :emoji_support + em.info + = t :emoji_support_info_html p = additionals_settings_checkbox :legacy_smiley_support em.info diff --git a/assets/images/smilies.png b/assets/images/smilies.png new file mode 100644 index 0000000000000000000000000000000000000000..9c1112ac871efa97f9f847138ec2329496b09ed8 GIT binary patch literal 9399 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 cheat sheet the available Emoji codes can be used. Restart the application server, in order to activate the settings.' + label_emoji_support: Emoji support + emoji_support_info_html: 'Emojis im Text konvertieren. Aktivere diese Option, wenn :heart:, :+1: etc in Emojis konvertiert werden sollen. Im Emoji cheat sheet können die zur Verfügung stehenden Emoji-Codes eingesetzt werden.' + legacy_smiley_support_info_html: 'Smileys im Text konvertieren. Aktiviere diese Option, wenn :), :-) etc in Smileys konvertiert werden sollen. Wird diese Einstellung verändert, muss der Anwendungsserver neu gestartet werden.' max_live_search_results_info: "Počet maximálních výsledků vyhledávání pro službu LiveSearch. Poznámka: Nastavení se týká pouze zásuvných modulů, které podporují službu LiveSearch." menu_roles_info: "Tuto položku nabídky uvidí pouze členové vybraných rolí." new_ticket_message_info: "Všem, kteří chtějí přidat nové vydání, se zobrazí poznámka. Například zde můžete zadat akceptační testy nebo vydat pravidla. Tato nastavení se používají v celém projektu." diff --git a/config/locales/de.yml b/config/locales/de.yml index 4268ed2c..b5c42e90 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -166,7 +166,9 @@ de: label_without_value: "Ohne %{value}" label_yearly: jährlich latest_entries_changes: "Die letzten %{value} Änderungen" - legacy_smiley_support_info_html: 'Smileys im Text konvertieren. Diese Option sollte nur aktiviert werden, wenn der Code für Smileys (z.B. :) ) manuell eingetragen wurde und als Smiley Gesicht angezeigt werden soll. Im Emoji cheat sheet können die zur Verfügung stehenden Emoji-Codes eingesetzt werden. Wird diese Einstellung aktiviert, muss der Anwendungsserver neu gestartet werden.' + label_emoji_support: Emoji Support + emoji_support_info_html: 'Emojis im Text konvertieren. Aktivere diese Option, wenn :heart:, :+1: etc in Emojis konvertiert werden sollen. Im Emoji cheat sheet können die zur Verfügung stehenden Emoji-Codes eingesetzt werden.' + legacy_smiley_support_info_html: 'Smileys im Text konvertieren. Aktiviere diese Option, wenn :), :-) etc in Smileys konvertiert werden sollen. Wird diese Einstellung verändert, muss der Anwendungsserver neu gestartet werden.' max_live_search_results_info: "Anzahl der maximalen Suchergebnisse für die LiveSearch. Hinweis: Einstellung wirkt sich nur auf Plugins aus, welche die LiveSearch unterstützen." menu_roles_info: "Nur den ausgewählten Rollen wird der Menüpunkt angezeigt. Sobald ein Mitglied in einem Projekt die Rolle besitzt, wird der Menüpunkt angezeigt." new_ticket_message_info: "Ein Hinweis, der beim Erstellen neuer Tickets angezeigt wird. Es kann beispielsweise auf Akzeptanztests oder Richtlinien zum Erstellen der Tickets hingewiesen werden. Dieser Einstellung ist projektübergreifend und ist in allen Projekten aktiv." diff --git a/config/locales/en.yml b/config/locales/en.yml index 54d206f2..9bebd894 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -166,7 +166,9 @@ en: label_without_value: "No %{value}" label_yearly: yearly latest_entries_changes: "Latest %{value} changes" - legacy_smiley_support_info_html: 'Convert smileys in text. This option should only be activated if the code for smileys (e.g. :) ) was entered manually and should be displayed as smiley face. In the Emoji cheat sheet the available Emoji codes can be used. Restart the application server, in order to activate the settings.' + label_emoji_support: Emoji support + emoji_support_info_html: 'Emojis im Text konvertieren. Aktivere diese Option, wenn :heart:, :+1: etc in Emojis konvertiert werden sollen. Im Emoji cheat sheet können die zur Verfügung stehenden Emoji-Codes eingesetzt werden.' + legacy_smiley_support_info_html: 'Smileys im Text konvertieren. Aktiviere diese Option, wenn :), :-) etc in Smileys konvertiert werden sollen. Wird diese Einstellung verändert, muss der Anwendungsserver neu gestartet werden.' max_live_search_results_info: "Number of maximum search results for LiveSearch. Note: Setting only affects plugins that support LiveSearch." menu_roles_info: "Only members of selected roles will see this menu entry." new_ticket_message_info: "A note will be shown to everyone who wants to add a new issue. For example you can enter acceptance tests or issue rules here. These settings are used project wide." diff --git a/config/locales/es.yml b/config/locales/es.yml index 71f7e919..98526168 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -166,7 +166,9 @@ es: label_without_value: "No %{value}" label_yearly: anualmente latest_entries_changes: "Últimos %{value} cambios" - legacy_smiley_support_info_html: 'Convierte los smileys en texto. Esta opción sólo debería activarse si el código para los smileys (por ejemplo :) ) se introdujo manualmente y debería mostrarse como carita sonriente. En la hoja de trucos de Emoji se pueden usar los códigos Emoji disponibles. Para activar esta configuración debe Reiniciar el servidor de aplicación.' + label_emoji_support: Emoji support + emoji_support_info_html: 'Emojis im Text konvertieren. Aktivere diese Option, wenn :heart:, :+1: etc in Emojis konvertiert werden sollen. Im Emoji cheat sheet können die zur Verfügung stehenden Emoji-Codes eingesetzt werden.' + legacy_smiley_support_info_html: 'Smileys im Text konvertieren. Aktiviere diese Option, wenn :), :-) etc in Smileys konvertiert werden sollen. Wird diese Einstellung verändert, muss der Anwendungsserver neu gestartet werden.' max_live_search_results_info: "Número de resultados máximos de búsqueda para LiveSearch. Nota: La configuración sólo afecta a los plugins que soportan LiveSearch." menu_roles_info: "Solo los miembros con los roles selccionado podrán ver esta entrada de Menú." new_ticket_message_info: "Se mostrará una nota a todos aquellos que quieran agregar una nueva petición. Puede por ejemplo agregar las condiciones de aceptación o las plantillas. Estas configuraciones afectan a todos los proyectos." diff --git a/config/locales/fr.yml b/config/locales/fr.yml index e4698c6a..a5af3107 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -166,7 +166,9 @@ fr: label_without_value: "Aucune %{value}" label_yearly: annuellement latest_entries_changes: "Derniers %{value} changements" - legacy_smiley_support_info_html: 'Convertissez les smileys en texte. Cette option ne doit être activée que si le code des smileys (par exemple :) ) a été saisi manuellement et doit être affiché sous forme de smiley. Dans la fiche Emoji les codes Emoji disponibles peuvent être utilisés. Restart the application server, in order to activate the settings.' + label_emoji_support: Emoji support + emoji_support_info_html: 'Emojis im Text konvertieren. Aktivere diese Option, wenn :heart:, :+1: etc in Emojis konvertiert werden sollen. Im Emoji cheat sheet können die zur Verfügung stehenden Emoji-Codes eingesetzt werden.' + legacy_smiley_support_info_html: 'Smileys im Text konvertieren. Aktiviere diese Option, wenn :), :-) etc in Smileys konvertiert werden sollen. Wird diese Einstellung verändert, muss der Anwendungsserver neu gestartet werden.' max_live_search_results_info: "Nombre maximal de résultats de recherche pour la LiveSearch. Remarque : le paramètre n'a d'effet que sur les plugins qui supportent LiveSearch." menu_roles_info: "Seuls les membres des rôles sélectionnés verront cette entrée de menu." new_ticket_message_info: "Une note sera montrée à tous ceux qui veulent ajouter un nouveau numéro. Par exemple, vous pouvez saisir des tests d'acceptation ou édicter des règles ici. Ces paramètres sont utilisés à l'échelle du projet." diff --git a/config/locales/it.yml b/config/locales/it.yml index ca7e0cde..2a51ea67 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -166,7 +166,9 @@ it: label_without_value: "Nessun %{value}" label_yearly: per anno latest_entries_changes: "Ultime %{value} modifiche" - legacy_smiley_support_info_html: 'Convertire gli smileys in testo. Questa opzione dovrebbe essere attivata solo se il codice per gli smileys (ad es. :) ) è stato inserito manualmente e dovrebbe essere visualizzato come faccina sorridente. Nel Emoji cheat sheet si possono utilizzare i codici Emoji disponibili. Restart the application server, in order to activate the settings.' + label_emoji_support: Emoji support + emoji_support_info_html: 'Emojis im Text konvertieren. Aktivere diese Option, wenn :heart:, :+1: etc in Emojis konvertiert werden sollen. Im Emoji cheat sheet können die zur Verfügung stehenden Emoji-Codes eingesetzt werden.' + legacy_smiley_support_info_html: 'Smileys im Text konvertieren. Aktiviere diese Option, wenn :), :-) etc in Smileys konvertiert werden sollen. Wird diese Einstellung verändert, muss der Anwendungsserver neu gestartet werden.' max_live_search_results_info: "Numero di risultati massimi di ricerca per LiveSearch. Nota: l'impostazione riguarda solo i plugin che supportano LiveSearch." menu_roles_info: "Only members of selected roles will see this menu entry." new_ticket_message_info: "Una nota verrà mostrata a tutti coloro che vogliono aggiungere un nuovo numero. Per esempio puoi inserire qui i test di accettazione o le regole di emissione. Queste impostazioni vengono utilizzate in tutto il progetto." diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 811a74cb..eac3072e 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -166,7 +166,9 @@ ja: label_without_value: "%{value} なし" label_yearly: '毎年' latest_entries_changes: "最新の %{value} の変更点" - legacy_smiley_support_info_html: 'Convert smileys in text. This option should only be activated if the code for smileys (e.g. :) ) was entered manually and should be displayed as smiley face. In the Emoji cheat sheet the available Emoji codes can be used. Restart the application server, in order to activate the settings.' + label_emoji_support: Emoji support + emoji_support_info_html: 'Emojis im Text konvertieren. Aktivere diese Option, wenn :heart:, :+1: etc in Emojis konvertiert werden sollen. Im Emoji cheat sheet können die zur Verfügung stehenden Emoji-Codes eingesetzt werden.' + legacy_smiley_support_info_html: 'Smileys im Text konvertieren. Aktiviere diese Option, wenn :), :-) etc in Smileys konvertiert werden sollen. Wird diese Einstellung verändert, muss der Anwendungsserver neu gestartet werden.' max_live_search_results_info: "LiveSearchの最大検索結果数です。注意:この設定は、LiveSearchに対応したプラグインにのみ影響します。" menu_roles_info: "選択された役割のメンバーだけがこのメニュー項目を表示します。" new_ticket_message_info: "新規チケットを登録したいときに表示されるメモ書き。例えば、チケット入力のルールや承認規程を掲載する目的にでも使えます。これらの設定は全プロジェクトで有効です。" diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 9eeeeb4d..64eb17ae 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -166,7 +166,9 @@ ko: label_without_value: "아니요 %{value}" label_yearly: 매년 latest_entries_changes: "최근 %{value} 개의 변경 사항" - legacy_smiley_support_info_html: 'Convert smileys in text. This option should only be activated if the code for smileys (e.g. :) ) was entered manually and should be displayed as smiley face. In the Emoji cheat sheet the available Emoji codes can be used. Restart the application server, in order to activate the settings.' + label_emoji_support: Emoji support + emoji_support_info_html: 'Emojis im Text konvertieren. Aktivere diese Option, wenn :heart:, :+1: etc in Emojis konvertiert werden sollen. Im Emoji cheat sheet können die zur Verfügung stehenden Emoji-Codes eingesetzt werden.' + legacy_smiley_support_info_html: 'Smileys im Text konvertieren. Aktiviere diese Option, wenn :), :-) etc in Smileys konvertiert werden sollen. Wird diese Einstellung verändert, muss der Anwendungsserver neu gestartet werden.' max_live_search_results_info: "LiveSearch의 최대 검색 결과 수입니다. 참고: 설정은 LiveSearch를 지원하는 플러그인에만 영향을 미칩니다." menu_roles_info: "선택한 역할의 멤버 만이 메뉴 항목을 볼 수 있습니다." new_ticket_message_info: "새로운 호를 추가하려는 모든 사람에게 메모가 표시됩니다. 예를 들어 수락 테스트를 입력하거나 규칙을 발행 할 수 있습니다. 이 설정은 프로젝트 전체에서 사용됩니다. " diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 92174ac8..8ff529f4 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -166,7 +166,9 @@ pl: label_without_value: "Nie %{value}" label_yearly: coroczny latest_entries_changes: "Ostatnie %{value} zmian" - legacy_smiley_support_info_html: 'Convert smileys in text. This option should only be activated if the code for smileys (e.g. :) ) was entered manually and should be displayed as smiley face. In the Emoji cheat sheet the available Emoji codes can be used. Restart the application server, in order to activate the settings.' + label_emoji_support: Emoji support + emoji_support_info_html: 'Emojis im Text konvertieren. Aktivere diese Option, wenn :heart:, :+1: etc in Emojis konvertiert werden sollen. Im Emoji cheat sheet können die zur Verfügung stehenden Emoji-Codes eingesetzt werden.' + legacy_smiley_support_info_html: 'Smileys im Text konvertieren. Aktiviere diese Option, wenn :), :-) etc in Smileys konvertiert werden sollen. Wird diese Einstellung verändert, muss der Anwendungsserver neu gestartet werden.' max_live_search_results_info: "Liczba maksymalnych wyników wyszukiwania dla LiveSearch. Uwaga: Ustawienie ma wpływ tylko na wtyczki obsługujące LiveSearch." menu_roles_info: "Tylko członkowie wybranych ról będą widzieć ten wpis w menu." new_ticket_message_info: "Notatka zostanie wyświetlona każdemu, kto chce dodać nową issue. Na przykład, tutaj możesz wprowadzić testy akceptacyjne lub zasady issue. Ustawienia te są używane w całym projekcie." diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index c905909b..6bb658ee 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -166,7 +166,9 @@ pt-BR: label_without_value: "Não %{value}" label_yearly: "anual" latest_entries_changes: "Últimas %{value} mudanças" - legacy_smiley_support_info_html: 'Convert smileys in text. This option should only be activated if the code for smileys (e.g. :) ) was entered manually and should be displayed as smiley face. In the Emoji cheat sheet the available Emoji codes can be used. Restart the application server, in order to activate the settings.' + label_emoji_support: Emoji support + emoji_support_info_html: 'Emojis im Text konvertieren. Aktivere diese Option, wenn :heart:, :+1: etc in Emojis konvertiert werden sollen. Im Emoji cheat sheet können die zur Verfügung stehenden Emoji-Codes eingesetzt werden.' + legacy_smiley_support_info_html: 'Smileys im Text konvertieren. Aktiviere diese Option, wenn :), :-) etc in Smileys konvertiert werden sollen. Wird diese Einstellung verändert, muss der Anwendungsserver neu gestartet werden.' max_live_search_results_info: "Número máximo de resultados de busca para LiveSearch. Nota: A configuração só afeta os plugins que suportam o LiveSearch." menu_roles_info: "Somente membros de funções selecionadas verão esta entrada de menu." new_ticket_message_info: "Uma nota será mostrada a todos que quiserem acrescentar uma nova tarefa. Por exemplo, você pode inserir aqui testes de aceitação ou regras de tarefa. Estas configurações são usadas em todo o projeto." diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 91de4885..7979d783 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -166,7 +166,9 @@ ru: label_without_value: "Нет %{value}" label_yearly: "раз в год" latest_entries_changes: "Последние изменения %{value}" - legacy_smiley_support_info_html: 'Convert smileys in text. This option should only be activated if the code for smileys (e.g. :) ) was entered manually and should be displayed as smiley face. In the Emoji cheat sheet the available Emoji codes can be used. Restart the application server, in order to activate the settings.' + label_emoji_support: Emoji support + emoji_support_info_html: 'Emojis im Text konvertieren. Aktivere diese Option, wenn :heart:, :+1: etc in Emojis konvertiert werden sollen. Im Emoji cheat sheet können die zur Verfügung stehenden Emoji-Codes eingesetzt werden.' + legacy_smiley_support_info_html: 'Smileys im Text konvertieren. Aktiviere diese Option, wenn :), :-) etc in Smileys konvertiert werden sollen. Wird diese Einstellung verändert, muss der Anwendungsserver neu gestartet werden.' max_live_search_results_info: "Количество максимальных результатов поиска для LiveSearch. Примечание: Настройка влияет только на плагины, поддерживающие LiveSearch." menu_roles_info: "Этот пункт меню будет доступен только пользователям с выбранными ролями." new_ticket_message_info: "Всем, кто хочет добавить новое задание, будет показана заметка. Например, здесь вы можете ввести правила приемочных испытаний или заданий. Эти настройки используются для всего проекта." diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 0078ef98..7746c308 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -166,7 +166,9 @@ label_without_value: "沒有 %{value}" label_yearly: "每年" latest_entries_changes: "Latest %{value} changes" - legacy_smiley_support_info_html: "Convert smileys in text. This option should only be activated if the code for smileys (e.g. :) ) was entered manually and should be displayed as smiley face. In the Emoji cheat sheet the available Emoji codes can be used. 修改後必須重啟Redmine。" + label_emoji_support: Emoji support + emoji_support_info_html: 'Emojis im Text konvertieren. Aktivere diese Option, wenn :heart:, :+1: etc in Emojis konvertiert werden sollen. Im Emoji cheat sheet können die zur Verfügung stehenden Emoji-Codes eingesetzt werden.' + legacy_smiley_support_info_html: 'Smileys im Text konvertieren. Aktiviere diese Option, wenn :), :-) etc in Smileys konvertiert werden sollen. Wird diese Einstellung verändert, muss der Anwendungsserver neu gestartet werden.' max_live_search_results_info: "LiveSearch的最大搜索结果的数量。注意:设置只影响到支持LiveSearch的插件。" menu_roles_info: "只有所選的角色權限才能看到選單。" new_ticket_message_info: "每個新建立議題用戶都會看到提示訊息。" diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 0a812b74..4122b231 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -166,7 +166,9 @@ zh: label_without_value: "没有 %{value}" label_yearly: 每年 latest_entries_changes: "最新的 %{value} 变更" - legacy_smiley_support_info_html: '转换文字中的颜文字。此选项应当仅在希望将手动输入的颜文字代码(例如 :))展示为表情时选中。Emoji cheat sheet 提供了可用的颜文字代码。为激活该配置,必须 重启 Redmine 服务 。' + label_emoji_support: Emoji support + emoji_support_info_html: 'Emojis im Text konvertieren. Aktivere diese Option, wenn :heart:, :+1: etc in Emojis konvertiert werden sollen. Im Emoji cheat sheet können die zur Verfügung stehenden Emoji-Codes eingesetzt werden.' + legacy_smiley_support_info_html: 'Smileys im Text konvertieren. Aktiviere diese Option, wenn :), :-) etc in Smileys konvertiert werden sollen. Wird diese Einstellung verändert, muss der Anwendungsserver neu gestartet werden.' max_live_search_results_info: "LiveSearch的最大搜索结果的数量。注意:设置只影响到支持LiveSearch的插件。" menu_roles_info: "只有所选角色对应的用户才能看到菜单项。" new_ticket_message_info: "每个新建问题的用户都会看到提示信息。例如,您可以输入可接受性测试或问题处理规则。这些配置在某个具体的项目范围内适用。" diff --git a/config/settings.yml b/config/settings.yml index dcc7279c..c2fef8d1 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -2,30 +2,32 @@ account_login_bottom: '' add_go_to_top: 0 disabled_modules: -open_external_urls: 1 +emoji_support: 0 +disable_emoji_native_support: 0 global_footer: '' global_sidebar: '' global_wiki_sidebar: '' google_maps_api_key: '' hidden_macros_in_toolbar: -max_live_search_results: 50 issue_assign_to_me: 0 issue_assign_to_x: '' +issue_auto_assign: 0 issue_auto_assign_role: '' issue_auto_assign_status: '' -issue_auto_assign: 0 issue_change_status_in_sidebar: 0 issue_current_user_status: 0 issue_freezed_with_close: 0 issue_status_change: 0 issue_status_x: '' issue_status_y: '' +issue_timelog_required: 0 issue_timelog_required_status: '' issue_timelog_required_tracker: '' -issue_timelog_required: 0 legacy_smiley_support: 0 +max_live_search_results: 50 new_issue_on_profile: 0 new_ticket_message: "Don't forget to define acceptance criteria!" +open_external_urls: 1 remove_help: 0 wiki_pdf_remove_attachments: 0 wiki_pdf_remove_title: 0 diff --git a/docs/index.rst b/docs/index.rst index a706f4ea..3aba0ad6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -61,14 +61,6 @@ Install ``additionals`` plugin for `Redmine`_. $ bundle config set --local without 'development test' $ bundle install $ bundle exec rake redmine:plugins:migrate RAILS_ENV=production - $ - $ # if you want to use smiley/emoji legacy support, you have to put emoji icons to - $ # $REDMINE_ROOT/public/images/emoji - $ # To obtain image files, run the gemoji extract command on macOS Sierra or later: - $ bundle exec gemoji extract public/images/emoji - $ - $ # if you to not have macOS, you can put these files manually to $REDMINE_ROOT/public/images/emoji - $ # see https://github.com/github/gemoji for more infos Restart your application server (apache with passenger, nginx with passenger, unicorn, puma, etc.) and ``Additionals`` is ready to use. diff --git a/lib/additionals.rb b/lib/additionals.rb index 9a7cc3f8..d7024cdb 100644 --- a/lib/additionals.rb +++ b/lib/additionals.rb @@ -9,6 +9,7 @@ module Additionals DEFAULT_MODAL_WIDTH = '350px' GOTO_LIST = " \xc2\xbb" LIST_SEPARATOR = "#{GOTO_LIST} " + EMOJI_ASSERT_PATH = 'images/emojis' include RedminePluginKit::PluginBase @@ -135,6 +136,7 @@ module Additionals loader.add_patch [{ target: Redmine::WikiFormatting::Markdown::HTML, patch: 'FormatterMarkdown' }, { target: Redmine::WikiFormatting::Markdown::Helper, patch: 'FormattingHelper' }] when 'common_mark' + loader.add_patch [{ target: Redmine::WikiFormatting::CommonMark::Formatter, patch: 'FormatterCommonMark' }] loader.add_patch [{ target: Redmine::WikiFormatting::CommonMark::Helper, patch: 'FormattingHelper' }] when 'textile' loader.add_patch [{ target: Redmine::WikiFormatting::Textile::Formatter, patch: 'FormatterTextile' }, @@ -142,6 +144,9 @@ module Additionals end end + # Clients + loader.require_files File.join('wiki_formatting', 'common_mark', '**/*_filter.rb') + # Apply patches and helper loader.apply! @@ -155,7 +160,7 @@ module Additionals # Run the classic redmine plugin initializer after rails boot class Plugin < ::Rails::Engine - require 'emoji' + require 'tanuki_emoji' require 'render_async' require 'rss' require 'slim' @@ -165,6 +170,8 @@ module Additionals # create some side effencts plugin_id = 'additionals' + Additionals::Gemify.install_emoji_assets + # if plugin is already in plugins directory, use this and leave here next if Redmine::Plugin.installed? plugin_id diff --git a/lib/additionals/formatter.rb b/lib/additionals/formatter.rb index 62da370a..04362b65 100644 --- a/lib/additionals/formatter.rb +++ b/lib/additionals/formatter.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'emoji' - module Additionals module Formatter SMILEYS = { 'smiley' => ':-?\)', # :) @@ -30,8 +28,9 @@ module Additionals def render_inline_smileys(text) return text if text.blank? - inline_smileys text - text + content = text.dup + inline_smileys content + content end def inline_smileys(text) @@ -41,34 +40,67 @@ module Additionals esc = Regexp.last_match 2 smiley = Regexp.last_match 3 if esc.nil? - leading + tag.span(class: "additionals smiley smiley-#{name}", - title: smiley) + leading.to_s + ActionController::Base.helpers.tag.span(class: "additionals smiley smiley-#{name}", + title: smiley) else - leading + smiley + leading.to_s + smiley end end end end + def emoji_tag(emoji, emoji_code) + if Additionals.setting? :disable_emoji_native_support + emoji_tag_fallback emoji, emoji_code + else + emoji_tag_native emoji, emoji_code + end + end + + def emoji_tag_native(emoji, _emoji_code) + return unless emoji + + data = { + name: emoji.name, + unicode_version: emoji.unicode_version + } + options = { title: emoji.description, data: data } + + ActionController::Base.helpers.content_tag 'additionals-emoji', emoji.codepoints, options + end + + def emoji_tag_fallback(emoji, _emoji_code) + ActionController::Base.helpers.image_tag emoji_image_path(emoji), + title: emoji.description, + class: 'inline_emojify' + end + + def emoji_image_path(emoji, local: false) + base_url = local ? '/' : Additionals.full_url + File.join base_url, Additionals::EMOJI_ASSERT_PATH, emoji.image_name + end + + def with_emoji?(text) + text.match? emoji_pattern + end + + def emoji_pattern + @emoji_pattern ||= TanukiEmoji.index.alpha_code_pattern + end + def inline_emojify(text) - text.gsub!(/:([\w+-]+):/) do |match| + return text unless with_emoji? text + + text.gsub! emoji_pattern do |match| emoji_code = Regexp.last_match 1 - emoji = Emoji.find_by_alias emoji_code # rubocop:disable Rails/DynamicFindBy - if emoji.present? - tag.img src: inline_emojify_image_path(emoji.image_filename), - title: ":#{emoji_code}:", - class: 'inline_emojify' + emoji = TanukiEmoji.find_by_alpha_code emoji_code # rubocop: disable Rails/DynamicFindBy + if emoji + emoji_tag emoji, emoji_code else match end end text end - - # TODO: use relative path, if not for mailer - def inline_emojify_image_path(image_filename) - # path = '/' + Rails.public_path.relative_path_from Rails.root.join('public') - "#{Additionals.full_url '/images/emoji/'}#{image_filename}" - end end end diff --git a/lib/additionals/gemify.rb b/lib/additionals/gemify.rb index 3c518646..4ecce73e 100644 --- a/lib/additionals/gemify.rb +++ b/lib/additionals/gemify.rb @@ -3,6 +3,28 @@ module Additionals class Gemify class << self + # install emoji fallback assets from gem (without asset pipline) + def install_emoji_assets + Additionals.debug 'install_emoji_assets' + return Rails.logger.error 'TanukiEmoji class for emoji not found' unless defined? TanukiEmoji + + source_image_path = TanukiEmoji.images_path + target_image_path = File.join Dir.pwd, 'public', Additionals::EMOJI_ASSERT_PATH + + begin + FileUtils.mkdir_p target_image_path + rescue StandardError => e + raise "Could not create directory #{target_image_path}: " + e.message + end + + Dir["#{source_image_path}/*"].each do |file| + target = File.join target_image_path, file.gsub(source_image_path, '') + FileUtils.cp file, target unless File.exist?(target) && FileUtils.identical?(file, target) + rescue StandardError => e + raise "Could not copy #{file} to #{target}: " + e.message + end + end + # install assets from gem (without asset pipline) def install_assets(plugin_id = 'additionals') return unless Gem.loaded_specs[plugin_id] diff --git a/lib/additionals/patches/formatter_common_mark_patch.rb b/lib/additionals/patches/formatter_common_mark_patch.rb new file mode 100644 index 00000000..32343526 --- /dev/null +++ b/lib/additionals/patches/formatter_common_mark_patch.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Additionals + module Patches + module FormatterCommonMarkPatch + extend ActiveSupport::Concern + + included do + prepend InstanceOverwriteMethods + end + + module InstanceOverwriteMethods + def to_html(*_args) + return super unless Additionals.setting?(:legacy_smiley_support) || Additionals.setting?(:emoji_support) + + filters = Redmine::WikiFormatting::CommonMark::MarkdownPipeline.filters.dup + filters << Additionals::WikiFormatting::CommonMark::SmileyFilter if Additionals.setting? :legacy_smiley_support + filters << Additionals::WikiFormatting::CommonMark::EmojiFilter if Additionals.setting? :emoji_support + pipeline = HTML::Pipeline.new filters, Redmine::WikiFormatting::CommonMark::PIPELINE_CONFIG + + result = pipeline.call @text + result[:output].to_s + end + end + end + end +end diff --git a/lib/additionals/patches/formatter_markdown_patch.rb b/lib/additionals/patches/formatter_markdown_patch.rb index 520f9bb6..5230c724 100644 --- a/lib/additionals/patches/formatter_markdown_patch.rb +++ b/lib/additionals/patches/formatter_markdown_patch.rb @@ -10,8 +10,10 @@ module Additionals # Add a postprocess hook to redcarpet's html formatter def postprocess(text) + inline_emojify text if Additionals.setting? :emoji_support + if Additionals.setting? :legacy_smiley_support - render_inline_smileys(inline_emojify(text)) + render_inline_smileys text else text end diff --git a/lib/additionals/patches/formatter_textile_patch.rb b/lib/additionals/patches/formatter_textile_patch.rb index d5678520..6103be6f 100644 --- a/lib/additionals/patches/formatter_textile_patch.rb +++ b/lib/additionals/patches/formatter_textile_patch.rb @@ -7,9 +7,20 @@ module Additionals included do include Additionals::Formatter + prepend InstanceOverwriteMethods + end - # emojify are always enabled - Redmine::WikiFormatting::Textile::Formatter::RULES << :inline_emojify + module InstanceOverwriteMethods + def to_html(*_rules) + if Additionals.setting? :emoji_support + Redmine::WikiFormatting::Textile::Formatter::RULES << :inline_emojify + else + Redmine::WikiFormatting::Textile::Formatter::RULES.delete :inline_emojify + end + + @toc = [] + super(*Redmine::WikiFormatting::Textile::Formatter::RULES).to_s + end end end end diff --git a/lib/additionals/wiki_formatting/common_mark/emoji_filter.rb b/lib/additionals/wiki_formatting/common_mark/emoji_filter.rb new file mode 100644 index 00000000..6dfe14a0 --- /dev/null +++ b/lib/additionals/wiki_formatting/common_mark/emoji_filter.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +module Additionals + module WikiFormatting + module CommonMark + class EmojiFilter < HTML::Pipeline::Filter + IGNORED_ANCESTOR_TAGS = %w[pre code].to_set + + include Additionals::Formatter + + def call + doc.xpath('descendant-or-self::text()').each do |node| + content = node.to_html + next if has_ancestor? node, IGNORED_ANCESTOR_TAGS + next unless with_emoji?(content) || node.text.match(emoji_unicode_pattern) + + html = emoji_unicode_element_unicode_filter content + html = emoji_name_element_unicode_filter html + + next if html == content + + node.replace html + end + doc + end + + # Replace :emoji: with corresponding gl-emoji unicode. + # + # text - String text to replace :emoji: in. + # + # Returns a String with :emoji: replaced with gl-emoji unicode. + def emoji_name_element_unicode_filter(text) + text.gsub emoji_pattern do + name = Regexp.last_match 1 + emoji = TanukiEmoji.find_by_alpha_code name # rubocop: disable Rails/DynamicFindBy + emoji_tag emoji, name + end + end + + # Replace unicode emoji with corresponding gl-emoji unicode. + # + # text - String text to replace unicode emoji in. + # + # Returns a String with unicode emoji replaced with gl-emoji unicode. + def emoji_unicode_element_unicode_filter(text) + text.gsub emoji_unicode_pattern do |moji| + emoji = TanukiEmoji.find_by_codepoints moji # rubocop: disable Rails/DynamicFindBy + emoji_tag_native emoji, name + end + end + + # Build a regexp that matches all valid unicode emojis names. + def self.emoji_unicode_pattern + @emoji_unicode_pattern ||= TanukiEmoji.index.codepoints_pattern + end + + private + + def emoji_unicode_pattern + self.class.emoji_unicode_pattern + end + end + end + end +end diff --git a/lib/additionals/wiki_formatting/common_mark/smiley_filter.rb b/lib/additionals/wiki_formatting/common_mark/smiley_filter.rb new file mode 100644 index 00000000..d3a3d871 --- /dev/null +++ b/lib/additionals/wiki_formatting/common_mark/smiley_filter.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Additionals + module WikiFormatting + module CommonMark + class SmileyFilter < HTML::Pipeline::Filter + IGNORED_ANCESTOR_TAGS = %w[pre code].to_set + + include Additionals::Formatter + + def call + doc.xpath('descendant-or-self::text()').each do |node| + content = node.to_html + next if has_ancestor? node, IGNORED_ANCESTOR_TAGS + + html = render_inline_smileys content + next if html == content + + node.replace html + end + doc + end + end + end + end +end diff --git a/test/unit/wiki_formating/common_mark_test.rb b/test/unit/wiki_formating/common_mark_test.rb new file mode 100644 index 00000000..4b05d441 --- /dev/null +++ b/test/unit/wiki_formating/common_mark_test.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require File.expand_path '../../../test_helper', __FILE__ + +module WikiFormatting + class CommonMarkTest < ActiveSupport::TestCase + include Additionals::TestHelper + + def setup + @options = {} + end + + def test_smilies + with_plugin_settings 'additionals', legacy_smiley_support: 1, + emoji_support: 0 do + input = <<~HTML + A small test :) with an smiley + HTML + expected = <<~HTML + A small test with an smiley + HTML + assert_equal expected, smiley_filter(input) + end + end + + def test_emojis + input = <<~HTML + :heart: +

+        def foo
+          :heart:
+        end
+        
+ HTML + expected = <<~HTML + +

+        def foo
+          :heart:
+        end
+        
+ HTML + + with_plugin_settings 'additionals', legacy_smiley_support: 0, + emoji_support: 1, + disable_emoji_native_support: 1 do + assert_equal expected, emoji_filter(input) + end + end + + private + + def smiley_filter(html) + Additionals::WikiFormatting::CommonMark::SmileyFilter.to_html html, @options + end + + def emoji_filter(html) + Additionals::WikiFormatting::CommonMark::EmojiFilter.to_html html, @options + end + end +end diff --git a/test/unit/wiki_formating/markdown_test.rb b/test/unit/wiki_formating/markdown_test.rb new file mode 100644 index 00000000..65e02cc4 --- /dev/null +++ b/test/unit/wiki_formating/markdown_test.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require File.expand_path '../../../test_helper', __FILE__ + +module WikiFormatting + class MarkdownTest < ActionView::TestCase + include Additionals::TestHelper + + def setup + skip 'Redcarpet is not installed' unless Object.const_defined? :Redcarpet + + @formatter = Redmine::WikiFormatting::Markdown::Formatter + end + + def test_smilies + with_plugin_settings 'additionals', legacy_smiley_support: 1, + emoji_support: 0 do + text = 'A small test :) with an smilie' + assert_equal '

A small test with an smilie

', + @formatter.new(text).to_html.strip + end + end + + def test_emojies + with_plugin_settings 'additionals', legacy_smiley_support: 0, + emoji_support: 1, + disable_emoji_native_support: 1 do + text = 'A test with a :heart: emoji' + assert_equal '

A test with a emoji

', + @formatter.new(text).to_html.strip + end + end + + def test_smilies_and_emojies + with_plugin_settings 'additionals', legacy_smiley_support: 1, + emoji_support: 1, + disable_emoji_native_support: 1 do + text = ':heart: and :)' + assert_equal '

' \ + ' and

', + @formatter.new(text).to_html.strip + + text = ' :) and :heart:' + assert_equal '

and' \ + '

', + @formatter.new(text).to_html.strip + end + end + end +end diff --git a/test/unit/wiki_formating/textile_test.rb b/test/unit/wiki_formating/textile_test.rb new file mode 100644 index 00000000..2578d6ae --- /dev/null +++ b/test/unit/wiki_formating/textile_test.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require File.expand_path '../../../test_helper', __FILE__ + +require 'digest/md5' + +module WikiFormatting + class TextileTest < ActionView::TestCase + include Additionals::TestHelper + + def setup + @formatter = Redmine::WikiFormatting::Textile::Formatter + @to_test = {} + end + + def test_smilies + with_plugin_settings 'additionals', legacy_smiley_support: 1, + emoji_support: 0 do + # this is required, because inline_smileys are activated with controller action + @formatter::RULES << :inline_smileys + + @to_test['A test with a :) smiley'] = 'A test with a smiley' + @to_test[':) :)'] = '' \ + ' ' + assert_html_output @to_test + end + end + + def test_emojies + with_plugin_settings 'additionals', legacy_smiley_support: 0, + emoji_support: 1, + disable_emoji_native_support: 1 do + @formatter::RULES.delete :inline_smileys + + str = 'A test with a :heart: emoji and a :) smiley' + @to_test[str] = 'A test with a emoji and a :) smiley' + assert_html_output @to_test + end + end + + def test_smilies_and_emojies + with_plugin_settings 'additionals', legacy_smiley_support: 1, + emoji_support: 1, + disable_emoji_native_support: 1 do + # this is required, because inline_smileys are activated with controller action + @formatter::RULES << :inline_smileys + + @to_test[':heart: and :)'] = '' \ + ' and ' + @to_test[':) and :heart:'] = ' and' \ + ' ' + assert_html_output @to_test + end + end + + private + + def assert_html_output(to_test, expect_paragraph: true) + to_test.each do |text, expected| + assert_equal( + (expect_paragraph ? "

#{expected}

" : expected), + @formatter.new(text).to_html, + "Formatting the following text failed:\n===\n#{text}\n===\n" + ) + end + end + + def to_html(text) + @formatter.new(text).to_html + end + end +end