From b7d7a62cec81551c6f6ea4c9281df6fef3158cfd Mon Sep 17 00:00:00 2001 From: Himanshu Gijawara Date: Sat, 13 Dec 2025 11:03:34 +0530 Subject: [PATCH 1/2] feat: add email to users_for_mentions and enhance dropdown rendering - Updated get_users_for_mentions to return email along with user id and full_name - Fixed normalize_values and renderItem in get_mention_options to display email in suggestions - Cleared cache dependency so updated values appear in mention dropdown - Improves usability when multiple users share the same name --- frappe/desk/search.py | 2 +- .../js/frappe/form/controls/text_editor.js | 45 ++++++++++++++----- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/frappe/desk/search.py b/frappe/desk/search.py index 4c9a92e698..b37c5fb01c 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -382,7 +382,7 @@ def get_names_for_mentions(search_term): def get_users_for_mentions(): return frappe.get_all( "User", - fields=["name as id", "full_name as value"], + fields=["name as id", "full_name as value", "email as email"], filters={ "name": ["not in", ("Administrator", "Guest")], "allowed_in_mentions": True, diff --git a/frappe/public/js/frappe/form/controls/text_editor.js b/frappe/public/js/frappe/form/controls/text_editor.js index 8edcb6d889..991995e0e9 100644 --- a/frappe/public/js/frappe/form/controls/text_editor.js +++ b/frappe/public/js/frappe/form/controls/text_editor.js @@ -252,24 +252,47 @@ frappe.ui.form.ControlTextEditor = class ControlTextEditor extends frappe.ui.for return null; } let me = this; + const esc = frappe.utils.escape_html; + + // FIXED normalize_values + function normalize_values(values) { + if (!Array.isArray(values)) return []; + + return values.map(v => { + return { + id: v.id, + value: v.value, + email: v.email || "", + is_group: v.is_group || false + }; + }); + } + return { allowedChars: /^[A-Za-z0-9_]*$/, mentionDenotationChars: ["@"], isolateCharacter: true, - source: frappe.utils.debounce(async function (search_term, renderList) { - let method = - me.mention_search_method || "frappe.desk.search.get_names_for_mentions"; - let values = await frappe.xcall(method, { - search_term, - }); - let sorted_values = me.prioritize_involved_users_in_mention(values); - renderList(sorted_values, search_term); + source: frappe.utils.debounce(async function(search_term, renderList) { + let method = me.mention_search_method || "frappe.desk.search.get_names_for_mentions"; + + let values = await frappe.xcall(method, { search_term }); + + // DEBUG + console.log("MENTION RAW VALUES:", values); + + let normalized = normalize_values(values); + + // DEBUG + console.log("MENTION NORMALIZED:", normalized); + + renderList(normalized, search_term); }, 300), renderItem(item) { - let value = item.value; - return `${value} ${item.is_group ? frappe.utils.icon("users") : ""}`; - }, + const name = esc(item.value); + const email = esc(item.email || ""); + return `${name} ${email ? " (" + email + ")" : ""}`; + } }; } From f19467aa6b2b908346c5e3eb086e71e6d835336c Mon Sep 17 00:00:00 2001 From: Ejaaz Khan Date: Thu, 22 Jan 2026 12:25:59 +0530 Subject: [PATCH 2/2] refactor: remove unwanted code and keep group metion functionality --- frappe/desk/search.py | 2 +- .../js/frappe/form/controls/text_editor.js | 44 +++++-------------- 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/frappe/desk/search.py b/frappe/desk/search.py index 8e73e48620..0f9c86cf8e 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -393,7 +393,7 @@ def get_names_for_mentions(search_term): def get_users_for_mentions(): return frappe.get_all( "User", - fields=["name as id", "full_name as value", "email as email"], + fields=["name as id", "full_name as value", "email"], filters={ "name": ["not in", ("Administrator", "Guest")], "allowed_in_mentions": True, diff --git a/frappe/public/js/frappe/form/controls/text_editor.js b/frappe/public/js/frappe/form/controls/text_editor.js index 991995e0e9..ce0e516f65 100644 --- a/frappe/public/js/frappe/form/controls/text_editor.js +++ b/frappe/public/js/frappe/form/controls/text_editor.js @@ -252,47 +252,27 @@ frappe.ui.form.ControlTextEditor = class ControlTextEditor extends frappe.ui.for return null; } let me = this; - const esc = frappe.utils.escape_html; - - // FIXED normalize_values - function normalize_values(values) { - if (!Array.isArray(values)) return []; - - return values.map(v => { - return { - id: v.id, - value: v.value, - email: v.email || "", - is_group: v.is_group || false - }; - }); - } return { allowedChars: /^[A-Za-z0-9_]*$/, mentionDenotationChars: ["@"], isolateCharacter: true, - source: frappe.utils.debounce(async function(search_term, renderList) { - let method = me.mention_search_method || "frappe.desk.search.get_names_for_mentions"; + source: frappe.utils.debounce(async function (search_term, renderList) { + let method = + me.mention_search_method || "frappe.desk.search.get_names_for_mentions"; + let values = await frappe.xcall(method, { + search_term, + }); - let values = await frappe.xcall(method, { search_term }); - - // DEBUG - console.log("MENTION RAW VALUES:", values); - - let normalized = normalize_values(values); - - // DEBUG - console.log("MENTION NORMALIZED:", normalized); - - renderList(normalized, search_term); + let sorted_values = me.prioritize_involved_users_in_mention(values); + renderList(sorted_values, search_term); }, 300), renderItem(item) { - const name = esc(item.value); - const email = esc(item.email || ""); - return `${name} ${email ? " (" + email + ")" : ""}`; - } + let value = item.value; + let email = item?.email ? `(${item?.email})` : ""; + return `${value} ${email} ${item.is_group ? frappe.utils.icon("users") : ""}`; + }, }; }