diff --git a/help/add-a-custom-linkifier.md b/help/add-a-custom-linkifier.md index 4e7332b1beab03..414b60ce416643 100644 --- a/help/add-a-custom-linkifier.md +++ b/help/add-a-custom-linkifier.md @@ -100,6 +100,10 @@ variety of situations. Linkifier patterns are regular expressions, using the [re2](https://github.com/google/re2/wiki/Syntax) regular expression engine. +The linkifiers are ordered as they are displayed in the settings. When the +regular expressions have an overlapping match, only the earliest one will be used +to generate the link. You can reorder the linkifiers by dragging and dropping. + Linkifiers use [RFC 6570](https://www.rfc-editor.org/rfc/rfc6570.html) compliant URL templates to describe how links should be generated. These templates support several expression types. The default expression type (`{var}`) will URL-encode @@ -155,3 +159,37 @@ This example pattern allows linking to Google searches. This pattern uses the `{?var}` expression type. With the default expression type (`{q}`), there would be no way to only include the `?` in the URL if the optional `q` is present. + +### Overlapping patterns + +These example patterns link to arbitrary repositories in either +the organization `zulip-testing` or `zulip`. `zulip` is only used +when the repo name is not `lorem`. + +{start_tabs} + +* Linkifier #1 + * Pattern: `lorem#(?P[0-9]+)` + * URL template: `https://github.com/zulip-testing/lorem/pull/{id}` + +* Linkifier #2 + * Pattern: `(?P[a-zA-Z0-9_-]+)#(?P[0-9]+)` + * URL template: `https://github.com/zulip/{repo}/pull/{id}` + +* Example matching linkifier #1 and #2 + * Original text: `lorem#123` + * Automatically links to: `https://github.com/zulip-testing/lorem/pull/123` + +* Example matching linkifier #2 only + * Original text: `zulip-flutter#123` + * Automatically links to: `https://github.com/zulip/zulip-flutter/pull/123` + +{end_tabs} + +!!! tip "" + + This set of patterns have overlapping regular expressions. Note that + `(?P[a-zA-Z0-9_-]+)#(?P[0-9]+)` would match `lorem#123` too. + Linkifier #1 gets prioritized over linkifier #2 because it is + ordered before the linkifier #2. This order can be customized by + dragging and dropping the linkifiers. diff --git a/web/src/settings_linkifiers.js b/web/src/settings_linkifiers.js index 242cad2406141e..8309453afe9ddf 100644 --- a/web/src/settings_linkifiers.js +++ b/web/src/settings_linkifiers.js @@ -1,4 +1,5 @@ import $ from "jquery"; +import {Sortable} from "sortablejs"; import render_confirm_delete_linkifier from "../templates/confirm_dialog/confirm_delete_linkifier.hbs"; import render_admin_linkifier_edit_form from "../templates/settings/admin_linkifier_edit_form.hbs"; @@ -28,23 +29,6 @@ export function maybe_disable_widgets() { } } -function compare_values(x, y) { - if (x > y) { - return 1; - } else if (x === y) { - return 0; - } - return -1; -} - -function sort_pattern(a, b) { - return compare_values(a.pattern, b.pattern); -} - -function sort_url(a, b) { - return compare_values(a.url_template, b.url_template); -} - function open_linkifier_edit_form(linkifier_id) { const linkifiers_list = page_params.realm_linkifiers; const linkifier = linkifiers_list.find((linkifier) => linkifier.id === linkifier_id); @@ -108,6 +92,19 @@ function open_linkifier_edit_form(linkifier_id) { }); } +function update_linkifiers_order() { + const order = []; + $(".linkifier_row").each(function () { + order.push(Number.parseInt($(this).attr("data-linkifier-id"), 10)); + }); + settings_ui.do_settings_change( + channel.patch, + "/json/realm/linkifiers", + {ordered_linkifier_ids: JSON.stringify(order)}, + $("#linkifier-field-status").expectOne(), + ); +} + function handle_linkifier_api_error(xhr, pattern_status, template_status, linkifier_status) { // The endpoint uses the Django ValidationError system for error // handling, which returns somewhat complicated error @@ -159,13 +156,16 @@ export function populate_linkifiers(linkifiers_data) { }, }, $parent_container: $("#linkifier-settings").expectOne(), - init_sort: sort_pattern, - sort_fields: { - pattern: sort_pattern, - url: sort_url, - }, $simplebar_container: $("#linkifier-settings .progressive-table-wrapper"), }); + + if (page_params.is_admin) { + Sortable.create($linkifiers_table[0], { + onUpdate: update_linkifiers_order, + filter: "input", + preventOnFilter: false, + }); + } } export function set_up() { diff --git a/web/styles/settings.css b/web/styles/settings.css index 36528196d024d8..ab1827108d432d 100644 --- a/web/styles/settings.css +++ b/web/styles/settings.css @@ -726,6 +726,7 @@ input[type="checkbox"] { .admin_profile_fields_table, .edit_profile_field_choices_container, +.admin_linkifiers_table, .profile_field_choices_table { .movable-row { cursor: move; diff --git a/web/templates/settings/admin_linkifier_list.hbs b/web/templates/settings/admin_linkifier_list.hbs index d8ba6d4efe0957..e39a55506c4c6e 100644 --- a/web/templates/settings/admin_linkifier_list.hbs +++ b/web/templates/settings/admin_linkifier_list.hbs @@ -1,6 +1,10 @@ {{#with linkifier}} - + + {{#if ../can_modify}} + + + {{/if}} {{pattern}} diff --git a/web/templates/settings/linkifier_settings_admin.hbs b/web/templates/settings/linkifier_settings_admin.hbs index 860c40d68d1992..8cbfa796ee8083 100644 --- a/web/templates/settings/linkifier_settings_admin.hbs +++ b/web/templates/settings/linkifier_settings_admin.hbs @@ -21,6 +21,11 @@ {{t "URL template" }}: https://github.com/zulip/zulip/issues/{id} +

+ {{t "The linkifiers are ordered as they are displayed below. When the + regular expressions have an overlapping match, only the earliest one will be used + to generate the link. You can reorder the linkifiers by dragging and dropping." }} +

{{#tr}} For more examples, see the help center documentation @@ -65,8 +70,8 @@

- - + + {{#if is_admin}} {{/if}}
{{t "Pattern" }}{{t "URL template" }}{{t "Pattern" }}{{t "URL template" }}{{t "Actions" }}