mirror of
https://github.com/zulip/zulip.git
synced 2025-10-23 04:52:12 +00:00
markdown: Extend user mention syntax to support user_id for mentioning.
Extend our markdown system to support mentioning of users by id also. Following these changes, it would be possible to mention users with @**|user_id** and silently mention using @_**|user_id**. Main intention for extending the mention syntax is to make it convenient for bots to mention a users using their ids. It is to be noted that previous syntax are also supported. Documentation tweaked by tabbott for better readability. The changes were tested manually in development server, and also by adding some new backend and frontend tests. Fixes: #17487.
This commit is contained in:
committed by
Tim Abbott
parent
9c6d8d9d81
commit
2699048208
@@ -447,6 +447,16 @@ test("marked", () => {
|
||||
expected:
|
||||
'<p><span class="user-mention" data-user-id="106">@Brother of Bobby|123</span></p>',
|
||||
},
|
||||
{
|
||||
input: "@**|106** valid user id.",
|
||||
expected:
|
||||
'<p><span class="user-mention" data-user-id="106">@Brother of Bobby|123</span> valid user id.</p>',
|
||||
},
|
||||
{
|
||||
input: "@**|123|106** comes under user|id case.",
|
||||
expected: "<p>@**|123|106** comes under user|id case.</p>",
|
||||
},
|
||||
{input: "@**|1234** invalid id.", expected: "<p>@**|1234** invalid id.</p>"},
|
||||
{input: "T\n@hamletcharacters", expected: "<p>T<br>\n@hamletcharacters</p>"},
|
||||
{
|
||||
input: "T\n@*hamletcharacters*",
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 18 KiB |
@@ -109,7 +109,7 @@ export function apply_markdown(message) {
|
||||
let full_name;
|
||||
let user_id;
|
||||
|
||||
const id_regex = /(.+)\|(\d+)$/g; // For @**user|id** syntax
|
||||
const id_regex = /^(.+)?\|(\d+)$/; // For @**user|id** and @**|id** syntax
|
||||
const match = id_regex.exec(mention);
|
||||
|
||||
if (match) {
|
||||
@@ -131,9 +131,20 @@ export function apply_markdown(message) {
|
||||
full_name = match[1];
|
||||
user_id = Number.parseInt(match[2], 10);
|
||||
|
||||
if (!helpers.is_valid_full_name_and_user_id(full_name, user_id)) {
|
||||
user_id = undefined;
|
||||
full_name = undefined;
|
||||
if (full_name === undefined) {
|
||||
// For @**|id** syntax
|
||||
if (!helpers.is_valid_user_id(user_id)) {
|
||||
// silently ignore invalid user id.
|
||||
user_id = undefined;
|
||||
} else {
|
||||
full_name = helpers.get_actual_name_from_user_id(user_id);
|
||||
}
|
||||
} else {
|
||||
// For @**user|id** syntax
|
||||
if (!helpers.is_valid_full_name_and_user_id(full_name, user_id)) {
|
||||
user_id = undefined;
|
||||
full_name = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -31,6 +31,7 @@ export const get_helpers = () => ({
|
||||
get_user_id_from_name: people.get_user_id_from_name,
|
||||
is_valid_full_name_and_user_id: people.is_valid_full_name_and_user_id,
|
||||
my_user_id: people.my_current_user_id,
|
||||
is_valid_user_id: people.is_known_user_id,
|
||||
|
||||
// user groups
|
||||
get_user_group_from_name: user_groups.get_user_group_from_name,
|
||||
|
@@ -189,14 +189,17 @@ You can also [add custom emoji](/help/add-custom-emoji).
|
||||
## Mentions
|
||||
|
||||
Learn more about mentions [here](/help/mention-a-user-or-group).
|
||||
The numbers will be added automatically by the typeahead if needed for disambiguation.
|
||||
|
||||
```
|
||||
Users: @**Polonius** or @**Zoe|2132** (two asterisks)
|
||||
Users: @**Polonius** or @**aaron|26** or @**|26** (two asterisks)
|
||||
User group: @*support team* (one asterisk)
|
||||
Silent mention: @_**Polonius** (@_ instead of @)
|
||||
Silent mention: @_**Polonius** or @_**|26** (@_ instead of @)
|
||||
```
|
||||
|
||||
The variants with numbers use user IDs, and are intended for
|
||||
disambiguation (if multiple users have the same name) and bots (for
|
||||
the variant that only contains the user ID).
|
||||
|
||||

|
||||
|
||||
## Status Messages
|
||||
|
@@ -1884,7 +1884,7 @@ class UserMentionPattern(markdown.inlinepatterns.InlineProcessor):
|
||||
|
||||
wildcard = mention.user_mention_matches_wildcard(name)
|
||||
|
||||
id_syntax_match = re.match(r".+\|(?P<user_id>\d+)$", name)
|
||||
id_syntax_match = re.match(r"(.+)?\|(?P<user_id>\d+)$", name)
|
||||
if id_syntax_match:
|
||||
id = int(id_syntax_match.group("user_id"))
|
||||
user = db_data["mention_data"].get_user_by_id(id)
|
||||
@@ -2445,18 +2445,25 @@ def get_possible_mentions_info(realm_id: int, mention_texts: Set[str]) -> List[F
|
||||
if not mention_texts:
|
||||
return []
|
||||
|
||||
# Remove the trailing part of the `name|id` mention syntax,
|
||||
# thus storing only full names in full_names.
|
||||
full_names = set()
|
||||
name_re = r"(?P<full_name>.+)\|\d+$"
|
||||
mention_ids: Set[int] = set()
|
||||
|
||||
name_re = r"(?P<full_name>.+)?\|(?P<mention_id>\d+)$"
|
||||
for mention_text in mention_texts:
|
||||
name_syntax_match = re.match(name_re, mention_text)
|
||||
if name_syntax_match:
|
||||
full_names.add(name_syntax_match.group("full_name"))
|
||||
full_name = name_syntax_match.group("full_name")
|
||||
mention_id = name_syntax_match.group("mention_id")
|
||||
if full_name:
|
||||
full_names.add(name_syntax_match.group("full_name"))
|
||||
else:
|
||||
mention_ids.add(int(mention_id))
|
||||
else:
|
||||
full_names.add(mention_text)
|
||||
|
||||
q_list = {Q(full_name__iexact=full_name) for full_name in full_names}
|
||||
id_q_list = {Q(id=id) for id in mention_ids}
|
||||
q_list |= id_q_list
|
||||
|
||||
rows = (
|
||||
UserProfile.objects.filter(
|
||||
|
@@ -1830,6 +1830,13 @@ class MarkdownTest(ZulipTestCase):
|
||||
)
|
||||
self.assertEqual(msg.mentions_user_ids, {user_profile.id})
|
||||
|
||||
content = f"@**|{user_id}**"
|
||||
self.assertEqual(
|
||||
render_markdown(msg, content),
|
||||
'<p><span class="user-mention" ' f'data-user-id="{user_id}">' "@King Hamlet</span></p>",
|
||||
)
|
||||
self.assertEqual(msg.mentions_user_ids, {user_profile.id})
|
||||
|
||||
def test_mention_silent(self) -> None:
|
||||
sender_user_profile = self.example_user("othello")
|
||||
user_profile = self.example_user("hamlet")
|
||||
@@ -1875,18 +1882,30 @@ class MarkdownTest(ZulipTestCase):
|
||||
)
|
||||
self.assertEqual(msg.mentions_user_ids, set())
|
||||
|
||||
content = f"@_**|123456789** and @_**|{user_id}**"
|
||||
self.assertEqual(
|
||||
render_markdown(msg, content),
|
||||
"<p>@_<strong>|123456789</strong> and "
|
||||
'<span class="user-mention silent" '
|
||||
f'data-user-id="{user_id}">'
|
||||
"King Hamlet</span></p>",
|
||||
)
|
||||
self.assertEqual(msg.mentions_user_ids, set())
|
||||
|
||||
def test_possible_mentions(self) -> None:
|
||||
def assert_mentions(content: str, names: Set[str], has_wildcards: bool = False) -> None:
|
||||
self.assertEqual(possible_mentions(content), (names, has_wildcards))
|
||||
|
||||
aaron = self.example_user("aaron")
|
||||
|
||||
assert_mentions("", set())
|
||||
assert_mentions("boring", set())
|
||||
assert_mentions("@**all**", set(), True)
|
||||
assert_mentions("smush@**steve**smush", set())
|
||||
|
||||
assert_mentions(
|
||||
"Hello @**King Hamlet** and @**Cordelia Lear**\n@**Foo van Barson|1234** @**all**",
|
||||
{"King Hamlet", "Cordelia Lear", "Foo van Barson|1234"},
|
||||
f"Hello @**King Hamlet**, @**|{aaron.id}** and @**Cordelia Lear**\n@**Foo van Barson|1234** @**all**",
|
||||
{"King Hamlet", f"|{aaron.id}", "Cordelia Lear", "Foo van Barson|1234"},
|
||||
True,
|
||||
)
|
||||
|
||||
|
Reference in New Issue
Block a user