Bladeren bron

Merge pull request #17592 from nabinhait/notify-mentions

feat: Enable mentions and notify users from any text field
version-14
mergify[bot] 2 jaren geleden
committed by GitHub
bovenliggende
commit
249e647fc4
Geen bekende sleutel gevonden voor deze handtekening in de database GPG sleutel-ID: 4AEE18F83AFDEB23
7 gewijzigde bestanden met toevoegingen van 93 en 95 verwijderingen
  1. +3
    -44
      frappe/core/doctype/comment/comment.py
  2. +1
    -1
      frappe/core/doctype/user/test_user.py
  3. +0
    -20
      frappe/core/doctype/user/user.py
  4. +0
    -1
      frappe/desk/desktop.py
  5. +62
    -0
      frappe/desk/notifications.py
  6. +1
    -27
      frappe/public/js/frappe/form/controls/comment.js
  7. +26
    -2
      frappe/public/js/frappe/form/controls/text_editor.js

+ 3
- 44
frappe/core/doctype/comment/comment.py Bestand weergeven

@@ -3,24 +3,17 @@
import json

import frappe
from frappe import _
from frappe.core.doctype.user.user import extract_mentions
from frappe.database.schema import add_column
from frappe.desk.doctype.notification_log.notification_log import (
enqueue_create_notification,
get_title,
get_title_html,
)
from frappe.model.utils import is_virtual_doctype
from frappe.desk.notifications import notify_mentions
from frappe.exceptions import ImplicitCommitError
from frappe.model.document import Document
from frappe.utils import get_fullname
from frappe.model.utils import is_virtual_doctype
from frappe.website.utils import clear_cache


class Comment(Document):
def after_insert(self):
self.notify_mentions()
notify_mentions(self.reference_doctype, self.reference_name, self.content)
self.notify_change("add")

def validate(self):
@@ -64,40 +57,6 @@ class Comment(Document):

update_comments_in_parent(self.reference_doctype, self.reference_name, _comments)

def notify_mentions(self):
if self.reference_doctype and self.reference_name and self.content:
mentions = extract_mentions(self.content)

if not mentions:
return

sender_fullname = get_fullname(frappe.session.user)
title = get_title(self.reference_doctype, self.reference_name)

recipients = [
frappe.db.get_value(
"User",
{"enabled": 1, "name": name, "user_type": "System User", "allowed_in_mentions": 1},
"email",
)
for name in mentions
]

notification_message = _("""{0} mentioned you in a comment in {1} {2}""").format(
frappe.bold(sender_fullname), frappe.bold(self.reference_doctype), get_title_html(title)
)

notification_doc = {
"type": "Mention",
"document_type": self.reference_doctype,
"document_name": self.reference_name,
"subject": notification_message,
"from_user": frappe.session.user,
"email_content": self.content,
}

enqueue_create_notification(recipients, notification_doc)


def on_doctype_update():
frappe.db.add_index("Comment", ["reference_doctype", "reference_name"])


+ 1
- 1
frappe/core/doctype/user/test_user.py Bestand weergeven

@@ -8,13 +8,13 @@ from unittest.mock import patch
import frappe
import frappe.exceptions
from frappe.core.doctype.user.user import (
extract_mentions,
reset_password,
sign_up,
test_password_strength,
update_password,
verify_password,
)
from frappe.desk.notifications import extract_mentions
from frappe.frappeclient import FrappeClient
from frappe.model.delete_doc import delete_doc
from frappe.utils import get_url


+ 0
- 20
frappe/core/doctype/user/user.py Bestand weergeven

@@ -2,8 +2,6 @@
# License: MIT. See LICENSE
from datetime import timedelta

from bs4 import BeautifulSoup

import frappe
import frappe.defaults
import frappe.permissions
@@ -1044,24 +1042,6 @@ def notify_admin_access_to_system_manager(login_manager=None):
)


def extract_mentions(txt):
"""Find all instances of @mentions in the html."""
soup = BeautifulSoup(txt, "html.parser")
emails = []
for mention in soup.find_all(class_="mention"):
if mention.get("data-is-group") == "true":
try:
user_group = frappe.get_cached_doc("User Group", mention["data-id"])
emails += [d.user for d in user_group.user_group_members]
except frappe.DoesNotExistError:
pass
continue
email = mention["data-id"]
emails.append(email)

return emails


def handle_password_test_fail(result):
suggestions = result["feedback"]["suggestions"][0] if result["feedback"]["suggestions"] else ""
warning = result["feedback"]["warning"] if "warning" in result["feedback"] else ""


+ 0
- 1
frappe/desk/desktop.py Bestand weergeven

@@ -40,7 +40,6 @@ class Workspace:
self.allowed_modules = self.get_cached("user_allowed_modules", self.get_allowed_modules)

self.doc = frappe.get_cached_doc("Workspace", self.page_name)

if (
self.doc
and self.doc.module


+ 62
- 0
frappe/desk/notifications.py Bestand weergeven

@@ -3,10 +3,19 @@

import json

from bs4 import BeautifulSoup

import frappe
from frappe import _
from frappe.desk.doctype.notification_log.notification_log import (
enqueue_create_notification,
get_title,
get_title_html,
)
from frappe.desk.doctype.notification_settings.notification_settings import (
get_subscribed_documents,
)
from frappe.utils import get_fullname


@frappe.whitelist()
@@ -298,3 +307,56 @@ def get_open_count(doctype, name, items=None):
out["timeline_data"] = module.get_timeline_data(doctype, name)

return out


def notify_mentions(ref_doctype, ref_name, content):
if ref_doctype and ref_name and content:
mentions = extract_mentions(content)

if not mentions:
return

sender_fullname = get_fullname(frappe.session.user)
title = get_title(ref_doctype, ref_name)

recipients = [
frappe.db.get_value(
"User",
{"enabled": 1, "name": name, "user_type": "System User", "allowed_in_mentions": 1},
"email",
)
for name in mentions
]

notification_message = _("""{0} mentioned you in a comment in {1} {2}""").format(
frappe.bold(sender_fullname), frappe.bold(ref_doctype), get_title_html(title)
)

notification_doc = {
"type": "Mention",
"document_type": ref_doctype,
"document_name": ref_name,
"subject": notification_message,
"from_user": frappe.session.user,
"email_content": content,
}

enqueue_create_notification(recipients, notification_doc)


def extract_mentions(txt):
"""Find all instances of @mentions in the html."""
soup = BeautifulSoup(txt, "html.parser")
emails = []
for mention in soup.find_all(class_="mention"):
if mention.get("data-is-group") == "true":
try:
user_group = frappe.get_cached_doc("User Group", mention["data-id"])
emails += [d.user for d in user_group.user_group_members]
except frappe.DoesNotExistError:
pass
continue
email = mention["data-id"]
emails.append(email)

return emails

+ 1
- 27
frappe/public/js/frappe/form/controls/comment.js Bestand weergeven

@@ -71,36 +71,10 @@ frappe.ui.form.ControlComment = class ControlComment extends frappe.ui.form.Cont
const options = super.get_quill_options();
return Object.assign(options, {
theme: 'bubble',
bounds: this.quill_container[0],
modules: Object.assign(options.modules, {
mention: this.get_mention_options()
})
bounds: this.quill_container[0]
});
}

get_mention_options() {
if (!this.enable_mentions) {
return null;
}
let me = this;
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
});
renderList(values, search_term);
}, 300),
renderItem(item) {
let value = item.value;
return `${value} ${item.is_group ? frappe.utils.icon('users') : ''}`;
}
};
}

get_toolbar_options() {
return [
['bold', 'italic', 'underline', 'strike'],


+ 26
- 2
frappe/public/js/frappe/form/controls/text_editor.js Bestand weergeven

@@ -174,9 +174,33 @@ frappe.ui.form.ControlTextEditor = class ControlTextEditor extends frappe.ui.for
toolbar: this.get_toolbar_options(),
table: true,
imageResize: {},
magicUrl: true
magicUrl: true,
mention: this.get_mention_options()
},
theme: 'snow'
theme: 'snow',
};
}

get_mention_options() {
if (!this.enable_mentions && !this.df.enable_mentions) {
return null;
}
let me = this;
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
});
renderList(values, search_term);
}, 300),
renderItem(item) {
let value = item.value;
return `${value} ${item.is_group ? frappe.utils.icon('users') : ''}`;
}
};
}



Laden…
Annuleren
Opslaan