diff --git a/frappe/cache_manager.py b/frappe/cache_manager.py
index a3b0271c13..39ddc993ca 100644
--- a/frappe/cache_manager.py
+++ b/frappe/cache_manager.py
@@ -11,7 +11,7 @@ from frappe.desk.notifications import (delete_notification_count_for,
common_default_keys = ["__default", "__global"]
global_cache_keys = ("app_hooks", "installed_apps",
- "app_modules", "module_app", "notification_config", 'system_settings',
+ "app_modules", "module_app", "system_settings",
'scheduler_events', 'time_zone', 'webhooks', 'active_domains',
'active_modules', 'assignment_rule', 'server_script_map')
@@ -114,4 +114,4 @@ def get_doctype_map(doctype, name, filters, order_by=None):
def clear_doctype_map(doctype, name):
cache_key = frappe.scrub(doctype) + '_map'
- frappe.cache().hdel(cache_key, name)
\ No newline at end of file
+ frappe.cache().hdel(cache_key, name)
diff --git a/frappe/core/doctype/comment/comment.py b/frappe/core/doctype/comment/comment.py
index 21d924bb7f..fcd32c52c9 100644
--- a/frappe/core/doctype/comment/comment.py
+++ b/frappe/core/doctype/comment/comment.py
@@ -8,7 +8,8 @@ from frappe import _
import json
from frappe.model.document import Document
from frappe.core.doctype.user.user import extract_mentions
-from frappe.utils import get_fullname, get_link_to_form
+from frappe.desk.doctype.notification_log.notification_log import enqueue_create_notification
+from frappe.utils import get_fullname
from frappe.website.render import clear_cache
from frappe.database.schema import add_column
from frappe.exceptions import ImplicitCommitError
@@ -54,31 +55,22 @@ class Comment(Document):
title = self.reference_name if title_field == "name" else \
frappe.db.get_value(self.reference_doctype, self.reference_name, title_field)
- if title != self.reference_name:
- parent_doc_label = "{0}: {1} (#{2})".format(_(self.reference_doctype),
- title, self.reference_name)
- else:
- parent_doc_label = "{0}: {1}".format(_(self.reference_doctype),
- self.reference_name)
-
- subject = _("{0} mentioned you in a comment in {1}").format(sender_fullname, parent_doc_label)
-
recipients = [frappe.db.get_value("User", {"enabled": 1, "name": name, "user_type": "System User", "allowed_in_mentions": 1}, "email")
for name in mentions]
- link = get_link_to_form(self.reference_doctype, self.reference_name, label=parent_doc_label)
- frappe.sendmail(
- recipients = recipients,
- sender = frappe.session.user,
- subject = subject,
- template = "mentioned_in_comment",
- args = {
- "body_content": _("{0} mentioned you in a comment in {1}").format(sender_fullname, link),
- "comment": self,
- "link": link
- },
- header = [_('New Mention'), 'orange']
- )
+ notification_message = _('''{0} mentioned you in a comment in {1} {2}''')\
+ .format(frappe.bold(sender_fullname), frappe.bold(self.reference_doctype), frappe.bold(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():
diff --git a/frappe/core/notifications.py b/frappe/core/notifications.py
index 26e2049273..771a15a2e7 100644
--- a/frappe/core/notifications.py
+++ b/frappe/core/notifications.py
@@ -14,13 +14,6 @@ def get_notification_config():
"Error Snapshot": {"seen": 0, "parent_error_snapshot": None},
"Workflow Action": {"status": 'Open'}
},
- "for_other": {
- "Likes": "frappe.core.notifications.get_unseen_likes",
- "Email": "frappe.core.notifications.get_unread_emails",
- },
- "for_module": {
- "Social": "frappe.social.doctype.post.post.get_unseen_post_count"
- }
}
def get_things_todo(as_list=False):
diff --git a/frappe/desk/doctype/notification_log/__init__.py b/frappe/desk/doctype/notification_log/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/desk/doctype/notification_log/notification_log.js b/frappe/desk/doctype/notification_log/notification_log.js
new file mode 100644
index 0000000000..654b2b2b06
--- /dev/null
+++ b/frappe/desk/doctype/notification_log/notification_log.js
@@ -0,0 +1,12 @@
+// Copyright (c) 2019, Frappe Technologies and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Notification Log', {
+ refresh: function(frm) {
+ let dt = frm.doc.document_type;
+ let dn = frm.doc.document_name;
+ frm.fields_dict.document_name.$input_wrapper
+ .find('.control-value')
+ .wrapInner(` `);
+ }
+});
diff --git a/frappe/desk/doctype/notification_log/notification_log.json b/frappe/desk/doctype/notification_log/notification_log.json
new file mode 100644
index 0000000000..11037a83c0
--- /dev/null
+++ b/frappe/desk/doctype/notification_log/notification_log.json
@@ -0,0 +1,106 @@
+{
+ "creation": "2019-08-26 13:37:34.165254",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "subject",
+ "for_user",
+ "type",
+ "email_content",
+ "column_break_4",
+ "document_type",
+ "seen",
+ "document_name",
+ "from_user"
+ ],
+ "fields": [
+ {
+ "fieldname": "subject",
+ "fieldtype": "Text",
+ "in_list_view": 1,
+ "label": "Subject",
+ "read_only": 1
+ },
+ {
+ "fieldname": "for_user",
+ "fieldtype": "Link",
+ "label": "For User",
+ "options": "User",
+ "read_only": 1
+ },
+ {
+ "fieldname": "type",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Type",
+ "options": "Mention\nEnergy Point\nAssignment\nShare",
+ "read_only": 1,
+ "search_index": 1
+ },
+ {
+ "fieldname": "email_content",
+ "fieldtype": "Text",
+ "label": "Email Content",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "document_type",
+ "fieldtype": "Link",
+ "label": "Document Type",
+ "options": "DocType",
+ "read_only": 1,
+ "search_index": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "seen",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "ignore_user_permissions": 1,
+ "label": "Seen"
+ },
+ {
+ "fieldname": "document_name",
+ "fieldtype": "Data",
+ "label": "Document Name",
+ "read_only": 1,
+ "search_index": 1
+ },
+ {
+ "fieldname": "from_user",
+ "fieldtype": "Link",
+ "label": "From User",
+ "options": "User",
+ "read_only": 1,
+ "search_index": 1
+ }
+ ],
+ "in_create": 1,
+ "modified": "2019-10-09 15:03:56.682093",
+ "modified_by": "Administrator",
+ "module": "Desk",
+ "name": "Notification Log",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "subject",
+ "track_changes": 1,
+ "track_seen": 1
+}
\ No newline at end of file
diff --git a/frappe/desk/doctype/notification_log/notification_log.py b/frappe/desk/doctype/notification_log/notification_log.py
new file mode 100644
index 0000000000..ca699bbcbd
--- /dev/null
+++ b/frappe/desk/doctype/notification_log/notification_log.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from frappe.desk.doctype.notification_settings.notification_settings import (is_notifications_enabled,
+ is_email_notifications_enabled, is_email_notifications_enabled_for_type)
+
+class NotificationLog(Document):
+ def after_insert(self):
+ frappe.publish_realtime('notification', after_commit=True, user=self.for_user)
+ if is_email_notifications_enabled(self.for_user):
+ send_notification_email(self)
+
+
+def get_permission_query_conditions(for_user):
+ if not for_user:
+ for_user = frappe.session.user
+
+ if for_user == 'Administrator':
+ return
+
+ return '''(`tabNotification Log`.for_user = '{user}')'''.format(user=for_user)
+
+def enqueue_create_notification(users, doc):
+ doc = frappe._dict(doc)
+
+ if isinstance(users, frappe.string_types):
+ users = [user.strip() for user in users.split(',') if user.strip()]
+
+ frappe.enqueue(
+ 'frappe.desk.doctype.notification_log.notification_log.make_notification_logs',
+ doc=doc,
+ users=users,
+ now=frappe.flags.in_test
+ )
+
+def make_notification_logs(doc, users):
+ from frappe.social.doctype.energy_point_settings.energy_point_settings import is_energy_point_enabled
+ for user in users:
+ if frappe.db.exists('User', user):
+ if is_notifications_enabled(user):
+ if doc.type == 'Energy Point' and not is_energy_point_enabled():
+ return
+ else:
+ _doc = frappe.new_doc('Notification Log')
+ _doc.update(doc)
+ _doc.for_user = user
+ _doc.subject = _doc.subject.replace('
', '').replace('
', '')
+ _doc.insert(ignore_permissions=True)
+
+def send_notification_email(doc):
+ is_type_enabled = is_email_notifications_enabled_for_type(doc.for_user, doc.type)
+ if not is_type_enabled:
+ return
+
+ from frappe.utils import get_url_to_form, strip_html
+
+ doc_link = get_url_to_form(doc.document_type, doc.document_name)
+ header = get_email_header(doc)
+ email_subject = strip_html(doc.subject)
+
+ frappe.sendmail(
+ recipients = doc.for_user,
+ subject = email_subject,
+ template = "new_notification",
+ args = {
+ 'body_content': doc.subject,
+ 'description': doc.email_content,
+ 'document_type': doc.document_type,
+ 'document_name': doc.document_name,
+ 'doc_link': doc_link
+ },
+ header = [header, 'orange'],
+ now=frappe.flags.in_test
+ )
+
+def get_email_header(doc):
+ return {
+ 'Default': _('New Notification'),
+ 'Mention': _('New Mention'),
+ 'Assignment': _('New Assignment'),
+ 'Share': _('New Document Shared'),
+ 'Energy Point': _('Energy Point Update'),
+ }[doc.type or 'Default']
+
+
+@frappe.whitelist()
+def mark_as_seen(docnames):
+ docnames = frappe.parse_json(docnames)
+ if docnames:
+ filters = {'name': ['in', docnames]}
+ frappe.db.set_value('Notification Log', filters, 'seen', 1, update_modified=False)
diff --git a/frappe/desk/doctype/notification_log/test_notification_log.py b/frappe/desk/doctype/notification_log/test_notification_log.py
new file mode 100644
index 0000000000..9431336aad
--- /dev/null
+++ b/frappe/desk/doctype/notification_log/test_notification_log.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+from frappe.desk.form.assign_to import add as assign_task
+import unittest
+
+class TestNotificationLog(unittest.TestCase):
+ def test_assignment(self):
+ todo = get_todo()
+ user = get_user()
+
+ assign_task({
+ "assign_to": user,
+ "doctype": 'ToDo',
+ "name": todo.name,
+ "description": todo.description
+ })
+ log_type = frappe.db.get_value('Notification Log', {
+ 'document_type': 'ToDo',
+ 'document_name': todo.name
+ }, 'type')
+ self.assertEqual(log_type, 'Assignment')
+
+ def test_share(self):
+ todo = get_todo()
+ user = get_user()
+
+ frappe.share.add('ToDo', todo.name, user)
+ log_type = frappe.db.get_value('Notification Log', {
+ 'document_type': 'ToDo',
+ 'document_name': todo.name
+ }, 'type')
+ self.assertEqual(log_type, 'Share')
+
+ email = get_last_email_queue()
+ content = 'Subject: {} shared a document ToDo'.format(frappe.utils.get_fullname(frappe.session.user))
+ self.assertTrue(content in email.message)
+
+
+def get_last_email_queue():
+ res = frappe.db.get_all('Email Queue',
+ fields=['message'],
+ order_by='creation desc',
+ limit=1
+ )
+ return res[0]
+
+def get_todo():
+ if not frappe.get_all('ToDo'):
+ return frappe.get_doc({ 'doctype': 'ToDo', 'description': 'Test for Notification' }).insert()
+
+ res = frappe.get_all('ToDo', limit=1)
+ return frappe.get_cached_doc('ToDo', res[0].name)
+
+def get_user():
+ users = frappe.db.get_all('User',
+ filters={'name': ('not in', ['Administrator', 'Guest'])},
+ fields='name', limit=1)
+ return users[0].name
diff --git a/frappe/desk/doctype/notification_settings/__init__.py b/frappe/desk/doctype/notification_settings/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/desk/doctype/notification_settings/notification_settings.json b/frappe/desk/doctype/notification_settings/notification_settings.json
new file mode 100644
index 0000000000..4998edb42e
--- /dev/null
+++ b/frappe/desk/doctype/notification_settings/notification_settings.json
@@ -0,0 +1,103 @@
+{
+ "autoname": "Prompt",
+ "creation": "2019-09-11 22:15:44.851526",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "enabled",
+ "subscribed_documents",
+ "column_break_3",
+ "enable_email_notifications",
+ "enable_email_mention",
+ "enable_email_assignment",
+ "enable_email_energy_point",
+ "enable_email_share",
+ "user"
+ ],
+ "fields": [
+ {
+ "default": "1",
+ "fieldname": "enabled",
+ "fieldtype": "Check",
+ "label": "Enabled"
+ },
+ {
+ "fieldname": "subscribed_documents",
+ "fieldtype": "Table MultiSelect",
+ "label": "Subscribed Documents",
+ "options": "Notification Subscribed Document"
+ },
+ {
+ "fieldname": "column_break_3",
+ "fieldtype": "Section Break",
+ "label": "Email Settings"
+ },
+ {
+ "default": "1",
+ "fieldname": "enable_email_notifications",
+ "fieldtype": "Check",
+ "label": "Enable Email Notifications"
+ },
+ {
+ "default": "1",
+ "depends_on": "enable_email_notifications",
+ "fieldname": "enable_email_mention",
+ "fieldtype": "Check",
+ "label": "Mentions"
+ },
+ {
+ "default": "1",
+ "depends_on": "enable_email_notifications",
+ "fieldname": "enable_email_assignment",
+ "fieldtype": "Check",
+ "label": "Assignments"
+ },
+ {
+ "default": "1",
+ "depends_on": "enable_email_notifications",
+ "fieldname": "enable_email_energy_point",
+ "fieldtype": "Check",
+ "label": "Energy Points"
+ },
+ {
+ "default": "1",
+ "depends_on": "enable_email_notifications",
+ "fieldname": "enable_email_share",
+ "fieldtype": "Check",
+ "label": "Document Share"
+ },
+ {
+ "default": "__user",
+ "fieldname": "user",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "in_list_view": 1,
+ "label": "User",
+ "options": "User",
+ "read_only": 1
+ }
+ ],
+ "in_create": 1,
+ "modified": "2019-10-09 15:58:16.746610",
+ "modified_by": "Administrator",
+ "module": "Desk",
+ "name": "Notification Settings",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "All",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "read_only": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/frappe/desk/doctype/notification_settings/notification_settings.py b/frappe/desk/doctype/notification_settings/notification_settings.py
new file mode 100644
index 0000000000..b14c2fd219
--- /dev/null
+++ b/frappe/desk/doctype/notification_settings/notification_settings.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class NotificationSettings(Document):
+ def on_update(self):
+ from frappe.desk.notifications import clear_notification_config
+ clear_notification_config(frappe.session.user)
+
+
+def is_notifications_enabled(user):
+ enabled = frappe.db.get_value('Notification Settings', user, 'enabled')
+ if enabled is None:
+ return True
+ return enabled
+
+def is_email_notifications_enabled(user):
+ enabled = frappe.db.get_value('Notification Settings', user, 'enable_email_notifications')
+ if enabled is None:
+ return True
+ return enabled
+
+def is_email_notifications_enabled_for_type(user, notification_type):
+ fieldname = 'enable_email_' + frappe.scrub(notification_type)
+ enabled = frappe.db.get_value('Notification Settings', user, fieldname)
+ if enabled is None:
+ return True
+ return enabled
+
+@frappe.whitelist()
+def create_notification_settings():
+ _doc = frappe.new_doc('Notification Settings')
+ _doc.name = frappe.session.user
+ _doc.insert(ignore_permissions=True)
+ frappe.db.commit()
+
+
+@frappe.whitelist()
+def get_subscribed_documents():
+ try:
+ doc = frappe.get_doc('Notification Settings', frappe.session.user)
+ subscribed_documents = [item.document for item in doc.subscribed_documents]
+ except frappe.DoesNotExistError:
+ subscribed_documents = []
+
+ return subscribed_documents
+
+
+def get_permission_query_conditions(user):
+ if not user: user = frappe.session.user
+
+ return '''(`tabNotification Settings`.user = '{user}')'''.format(user=user)
diff --git a/frappe/desk/doctype/notification_subscribed_document/__init__.py b/frappe/desk/doctype/notification_subscribed_document/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/desk/doctype/notification_subscribed_document/notification_subscribed_document.json b/frappe/desk/doctype/notification_subscribed_document/notification_subscribed_document.json
new file mode 100644
index 0000000000..b3f4046163
--- /dev/null
+++ b/frappe/desk/doctype/notification_subscribed_document/notification_subscribed_document.json
@@ -0,0 +1,30 @@
+{
+ "creation": "2019-10-09 15:04:39.504787",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "document"
+ ],
+ "fields": [
+ {
+ "fieldname": "document",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Document",
+ "options": "DocType",
+ "reqd": 1
+ }
+ ],
+ "istable": 1,
+ "modified": "2019-10-09 16:02:00.049237",
+ "modified_by": "Administrator",
+ "module": "Desk",
+ "name": "Notification Subscribed Document",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/frappe/desk/doctype/notification_subscribed_document/notification_subscribed_document.py b/frappe/desk/doctype/notification_subscribed_document/notification_subscribed_document.py
new file mode 100644
index 0000000000..f005efae76
--- /dev/null
+++ b/frappe/desk/doctype/notification_subscribed_document/notification_subscribed_document.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class NotificationSubscribedDocument(Document):
+ pass
diff --git a/frappe/desk/doctype/todo/todo.json b/frappe/desk/doctype/todo/todo.json
index 9e0598b128..508720a488 100644
--- a/frappe/desk/doctype/todo/todo.json
+++ b/frappe/desk/doctype/todo/todo.json
@@ -190,4 +190,4 @@
"title_field": "description",
"track_changes": 1,
"track_seen": 1
-}
\ No newline at end of file
+}
diff --git a/frappe/desk/form/assign_to.py b/frappe/desk/form/assign_to.py
index 959eee7e60..5841f9474a 100644
--- a/frappe/desk/form/assign_to.py
+++ b/frappe/desk/form/assign_to.py
@@ -7,7 +7,8 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.desk.form.document_follow import follow_document
-from frappe.utils import cint
+from frappe.desk.doctype.notification_log.notification_log import enqueue_create_notification
+import frappe.utils
import frappe.share
class DuplicateToDoError(frappe.ValidationError): pass
@@ -80,7 +81,7 @@ def add(args=None):
# notify
notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN',\
- description=args.get("description"), notify=args.get('notify'))
+ description=args.get("description"))
return get(args)
@@ -147,7 +148,7 @@ def clear(doctype, name):
return True
def notify_assignment(assigned_by, owner, doc_type, doc_name, action='CLOSE',
- description=None, notify=0):
+ description=None):
"""
Notify assignee that there is a change in assignment
"""
@@ -158,56 +159,28 @@ def notify_assignment(assigned_by, owner, doc_type, doc_name, action='CLOSE',
return
# Search for email address in description -- i.e. assignee
- from frappe.utils import get_link_to_form
- assignment = get_link_to_form(doc_type, doc_name, label="%s: %s" % (doc_type, doc_name))
- owner_name = frappe.get_cached_value('User', owner, 'full_name')
user_name = frappe.get_cached_value('User', frappe.session.user, 'full_name')
+ title_field = frappe.get_meta(doc_type).get_title_field()
+ title = doc_name if title_field == "name" else \
+ frappe.db.get_value(doc_type, doc_name, title_field)
+ description_html = "{0}
".format(description) if description else None
+
if action=='CLOSE':
- if owner == frappe.session.get('user'):
- arg = {
- 'contact': assigned_by,
- 'txt': _("The task {0}, that you assigned to {1}, has been closed.").format(assignment,
- owner_name)
- }
- else:
- arg = {
- 'contact': assigned_by,
- 'txt': _("The task {0}, that you assigned to {1}, has been closed by {2}.").format(assignment,
- owner_name, user_name)
- }
+ subject = _('Your assignment on {0} {1} has been removed').format(frappe.bold(doc_type), frappe.bold(title))
else:
- description_html = "{0}
".format(description)
- arg = {
- 'contact': owner,
- 'txt': _("A new task, {0}, has been assigned to you by {1}. {2}").format(assignment,
- user_name, description_html),
- 'notify': notify
- }
+ user_name = frappe.bold(user_name)
+ document_type = frappe.bold(doc_type)
+ title = frappe.bold(title)
+ subject = _('{0} assigned a new task {1} {2} to you').format(user_name, document_type, title)
- if arg and cint(arg.get("notify")):
- _notify(arg)
+ notification_doc = {
+ 'type': 'Assignment',
+ 'document_type': doc_type,
+ 'subject': subject,
+ 'document_name': doc_name,
+ 'from_user': frappe.session.user,
+ 'email_content': description_html
+ }
-def _notify(args):
- from frappe.utils import get_fullname, get_url
+ enqueue_create_notification(owner, notification_doc)
- args = frappe._dict(args)
- contact = args.contact
- txt = args.txt
-
- try:
- if not isinstance(contact, list):
- contact = [frappe.db.get_value("User", contact, "email") or contact]
-
- frappe.sendmail(\
- recipients=contact,
- sender= frappe.db.get_value("User", frappe.session.user, "email"),
- subject=_("New message from {0}").format(get_fullname(frappe.session.user)),
- template="new_message",
- args={
- "from": get_fullname(frappe.session.user),
- "message": txt,
- "link": get_url()
- },
- header=[_('New Message'), 'orange'])
- except frappe.OutgoingEmailError:
- pass
diff --git a/frappe/desk/notifications.py b/frappe/desk/notifications.py
index 8b88af60b0..3a00330a55 100644
--- a/frappe/desk/notifications.py
+++ b/frappe/desk/notifications.py
@@ -4,8 +4,7 @@
from __future__ import unicode_literals
import frappe
-from frappe.utils import time_diff_in_seconds, now, now_datetime, DATETIME_FORMAT
-from dateutil.relativedelta import relativedelta
+from frappe.desk.doctype.notification_settings.notification_settings import get_subscribed_documents
from six import string_types
import json
@@ -16,10 +15,7 @@ def get_notifications():
not frappe.db.get_single_value('System Settings', 'setup_complete')):
return {
"open_count_doctype": {},
- "open_count_module": {},
- "open_count_other": {},
"targets": {},
- "new_messages": []
}
config = get_notification_config()
@@ -37,58 +33,9 @@ def get_notifications():
return {
"open_count_doctype": get_notifications_for_doctypes(config, notification_count),
- "open_count_module": get_notifications_for_modules(config, notification_count),
- "open_count_other": get_notifications_for_other(config, notification_count),
"targets": get_notifications_for_targets(config, notification_percent),
- "new_messages": get_new_messages()
}
-def get_new_messages():
- last_update = frappe.cache().hget("notifications_last_update", frappe.session.user)
- now_timestamp = now()
- frappe.cache().hset("notifications_last_update", frappe.session.user, now_timestamp)
-
- if not last_update:
- return []
-
- if last_update and time_diff_in_seconds(now_timestamp, last_update) > 1800:
- # no update for 30 mins, consider only the last 30 mins
- last_update = (now_datetime() - relativedelta(seconds=1800)).strftime(DATETIME_FORMAT)
-
- return frappe.db.sql("""select sender_full_name, content
- from `tabCommunication`
- where communication_type in ('Chat', 'Notification')
- and reference_doctype='user'
- and reference_name = %s
- and creation > %s
- order by creation desc""", (frappe.session.user, last_update), as_dict=1)
-
-def get_notifications_for_modules(config, notification_count):
- """Notifications for modules"""
- return get_notifications_for("for_module", config, notification_count)
-
-def get_notifications_for_other(config, notification_count):
- """Notifications for other items"""
- return get_notifications_for("for_other", config, notification_count)
-
-def get_notifications_for(notification_type, config, notification_count):
- open_count = {}
- notification_map = config.get(notification_type) or {}
- for m in notification_map:
- try:
- if m in notification_count:
- open_count[m] = notification_count[m]
- else:
- open_count[m] = frappe.get_attr(notification_map[m])()
-
- frappe.cache().hset("notification_count:" + m, frappe.session.user, open_count[m])
- except frappe.PermissionError:
- frappe.clear_messages()
- pass
- # frappe.msgprint("Permission Error in notifications for {0}".format(m))
-
- return open_count
-
def get_notifications_for_doctypes(config, notification_count):
"""Notifications for DocTypes"""
can_read = frappe.get_user().get_can_read()
@@ -170,12 +117,11 @@ def get_notifications_for_targets(config, notification_percent):
def clear_notifications(user=None):
if frappe.flags.in_install:
return
-
+ cache = frappe.cache()
config = get_notification_config()
for_doctype = list(config.get('for_doctype')) if config.get('for_doctype') else []
for_module = list(config.get('for_module')) if config.get('for_module') else []
groups = for_doctype + for_module
- cache = frappe.cache()
for name in groups:
if user:
@@ -185,6 +131,9 @@ def clear_notifications(user=None):
frappe.publish_realtime('clear_notifications')
+def clear_notification_config(user):
+ frappe.cache().hdel('notification_config', user)
+
def delete_notification_count_for(doctype):
frappe.cache().delete_key("notification_count:" + doctype)
frappe.publish_realtime('clear_notifications')
@@ -200,9 +149,10 @@ def clear_doctype_notifications(doc, method=None, *args, **kwargs):
delete_notification_count_for(doctype)
return
-def get_notification_info_for_boot():
- out = get_notifications()
+@frappe.whitelist()
+def get_notification_info():
config = get_notification_config()
+ out = get_notifications()
can_read = frappe.get_user().get_can_read()
conditions = {}
module_doctypes = {}
@@ -224,6 +174,7 @@ def get_notification_info_for_boot():
def get_notification_config():
def _get():
+ subscribed_documents = get_subscribed_documents()
config = frappe._dict()
hooks = frappe.get_hooks()
if hooks:
@@ -231,15 +182,26 @@ def get_notification_config():
nc = frappe.get_attr(notification_config)()
for key in ("for_doctype", "for_module", "for_other", "targets"):
config.setdefault(key, {})
- config[key].update(nc.get(key, {}))
+ if key == "for_doctype":
+ if len(subscribed_documents) > 0:
+ key_config = nc.get(key, {})
+ subscribed_docs_config = frappe._dict()
+ for document in subscribed_documents:
+ if key_config.get(document):
+ subscribed_docs_config[document] = key_config.get(document)
+ config[key].update(subscribed_docs_config)
+ else:
+ config[key].update(nc.get(key, {}))
+ else:
+ config[key].update(nc.get(key, {}))
return config
- return frappe.cache().get_value("notification_config", _get)
+ return frappe.cache().hget("notification_config", frappe.session.user, _get)
def get_filters_for(doctype):
'''get open filters for doctype'''
config = get_notification_config()
- return config.get('for_doctype').get(doctype, {})
+ return config.get("for_doctype").get(doctype, {})
@frappe.whitelist()
@frappe.read_only()
@@ -253,7 +215,7 @@ def get_open_count(doctype, name, items=[]):
if frappe.flags.in_migrate or frappe.flags.in_install:
return {
- 'count': []
+ "count": []
}
frappe.has_permission(doc=frappe.get_doc(doctype, name), throw=True)
@@ -264,39 +226,39 @@ def get_open_count(doctype, name, items=[]):
# compile all items in a list
if not items:
for group in links.transactions:
- items.extend(group.get('items'))
+ items.extend(group.get("items"))
if not isinstance(items, list):
items = json.loads(items)
out = []
for d in items:
- if d in links.get('internal_links', {}):
+ if d in links.get("internal_links", {}):
# internal link
continue
filters = get_filters_for(d)
- fieldname = links.get('non_standard_fieldnames', {}).get(d, links.fieldname)
- data = {'name': d}
+ fieldname = links.get("non_standard_fieldnames", {}).get(d, links.fieldname)
+ data = {"name": d}
if filters:
# get the fieldname for the current document
# we only need open documents related to the current document
filters[fieldname] = name
- total = len(frappe.get_all(d, fields='name',
+ total = len(frappe.get_all(d, fields="name",
filters=filters, limit=100, distinct=True, ignore_ifnull=True))
- data['open_count'] = total
+ data["open_count"] = total
- total = len(frappe.get_all(d, fields='name',
+ total = len(frappe.get_all(d, fields="name",
filters={fieldname: name}, limit=100, distinct=True, ignore_ifnull=True))
- data['count'] = total
+ data["count"] = total
out.append(data)
out = {
- 'count': out,
+ "count": out,
}
module = frappe.get_meta_module(doctype)
- if hasattr(module, 'get_timeline_data'):
- out['timeline_data'] = module.get_timeline_data(doctype, name)
+ if hasattr(module, "get_timeline_data"):
+ out["timeline_data"] = module.get_timeline_data(doctype, name)
return out
diff --git a/frappe/hooks.py b/frappe/hooks.py
index aeba3c7445..a35fd78ebb 100644
--- a/frappe/hooks.py
+++ b/frappe/hooks.py
@@ -88,6 +88,8 @@ permission_query_conditions = {
"Event": "frappe.desk.doctype.event.event.get_permission_query_conditions",
"ToDo": "frappe.desk.doctype.todo.todo.get_permission_query_conditions",
"User": "frappe.core.doctype.user.user.get_permission_query_conditions",
+ "Notification Log": "frappe.desk.doctype.notification_log.notification_log.get_permission_query_conditions",
+ "Notification Settings": "frappe.desk.doctype.notification_settings.notification_settings.get_permission_query_conditions",
"Note": "frappe.desk.doctype.note.note.get_permission_query_conditions",
"Kanban Board": "frappe.desk.doctype.kanban_board.kanban_board.get_permission_query_conditions",
"Contact": "frappe.contacts.address_and_contact.get_permission_query_conditions_for_contact",
diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py
index 33d7f8e0af..af67350ab6 100644
--- a/frappe/model/delete_doc.py
+++ b/frappe/model/delete_doc.py
@@ -20,7 +20,8 @@ from frappe.desk.doctype.tag.tag import delete_tags_for_document
from frappe.exceptions import FileNotFoundError
-doctypes_to_skip = ("Communication", "ToDo", "DocShare", "Email Unsubscribe", "Activity Log", "File", "Version", "Document Follow", "Comment" , "View Log", "Tag Link")
+doctypes_to_skip = ("Communication", "ToDo", "DocShare", "Email Unsubscribe", "Activity Log", "File",
+ "Version", "Document Follow", "Comment" , "View Log", "Tag Link", "Notification Log")
def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reload=False,
ignore_permissions=False, flags=None, ignore_on_trash=False, ignore_missing=True):
@@ -305,6 +306,7 @@ def delete_dynamic_links(doctype, name):
delete_references('Comment', doctype, name)
delete_references('View Log', doctype, name)
delete_references('Document Follow', doctype, name, 'ref_doctype', 'ref_docname')
+ delete_references('Notification Log', doctype, name, 'document_type', 'document_name')
# unlink communications
clear_timeline_references(doctype, name)
diff --git a/frappe/patches.txt b/frappe/patches.txt
index 36aa390d65..8c3a176811 100644
--- a/frappe/patches.txt
+++ b/frappe/patches.txt
@@ -252,3 +252,5 @@ frappe.patches.v12_0.move_email_and_phone_to_child_table
frappe.patches.v12_0.delete_duplicate_indexes
frappe.patches.v12_0.set_default_incoming_email_port
frappe.patches.v12_0.update_global_search
+frappe.patches.v12_0.setup_tags
+execute:frappe.reload_doc('desk', 'doctype', 'notification_settings')
diff --git a/frappe/public/build.json b/frappe/public/build.json
index c59df8034c..6c67c17374 100755
--- a/frappe/public/build.json
+++ b/frappe/public/build.json
@@ -70,6 +70,7 @@
"public/less/indicator.less",
"public/less/avatar.less",
"public/less/navbar.less",
+ "public/less/notifications.less",
"public/less/sidebar.less",
"public/less/page.less",
"public/less/tree.less",
@@ -185,6 +186,7 @@
"public/js/frappe/ui/toolbar/awesome_bar.js",
"public/js/frappe/ui/toolbar/energy_points_notifications.js",
+ "public/js/frappe/ui/notifications/notifications.js",
"public/js/frappe/ui/toolbar/search.js",
"public/js/frappe/ui/toolbar/tag_utils.js",
"public/js/frappe/ui/toolbar/search.html",
diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js
index 88a3ba9803..5a972e0dbe 100644
--- a/frappe/public/js/frappe/desk.js
+++ b/frappe/public/js/frappe/desk.js
@@ -74,8 +74,6 @@ frappe.Application = Class.extend({
// trigger app startup
$(document).trigger('startup');
- this.start_notification_updates();
-
$(document).trigger('app_ready');
if (frappe.boot.messages) {
@@ -268,54 +266,6 @@ frappe.Application = Class.extend({
}
},
- start_notification_updates: function() {
- var me = this;
-
- // refresh_notifications will be called only once during a 1 second window
- this.refresh_notifications = frappe.utils.debounce(this.refresh_notifications.bind(this), 1000);
-
- // kickoff
- this.refresh_notifications();
-
- frappe.realtime.on('clear_notifications', () => {
- me.refresh_notifications();
- });
-
- // first time loaded in boot
- $(document).trigger("notification-update");
-
- // refresh notifications if user is back after sometime
- $(document).on("session_alive", function() {
- me.refresh_notifications();
- });
- },
-
- refresh_notifications: function() {
- var me = this;
- if(frappe.session_alive && frappe.boot && frappe.boot.home_page !== 'setup-wizard') {
- if (this._refresh_notifications) {
- this._refresh_notifications.abort();
- }
- this._refresh_notifications = frappe.call({
- type: 'GET',
- method: "frappe.desk.notifications.get_notifications",
- callback: function(r) {
- if(r.message) {
- $.extend(frappe.boot.notification_info, r.message);
- $(document).trigger("notification-update");
-
- if(frappe.get_route()[0] != "messages") {
- if(r.message.new_messages.length) {
- frappe.utils.set_title_prefix("(" + r.message.new_messages.length + ")");
- }
- }
- }
- },
- freeze: false
- });
- }
- },
-
set_globals: function() {
frappe.session.user = frappe.boot.user.name;
frappe.session.user_email = frappe.boot.user.email;
diff --git a/frappe/public/js/frappe/form/sidebar/assign_to.js b/frappe/public/js/frappe/form/sidebar/assign_to.js
index 2f8c6469b3..61d1789518 100644
--- a/frappe/public/js/frappe/form/sidebar/assign_to.js
+++ b/frappe/public/js/frappe/form/sidebar/assign_to.js
@@ -140,7 +140,6 @@ frappe.ui.form.AssignToDialog = Class.extend({
{ fieldtype: 'Section Break' },
{ fieldtype: 'Column Break' },
{ fieldtype: 'Date', fieldname: 'date', label: __("Complete By") },
- { fieldtype: 'Check', fieldname: 'notify', label: __("Notify by Email"), default: 1},
{ fieldtype: 'Column Break' },
{ fieldtype: 'Select', fieldname: 'priority', label: __("Priority"),
options: [
@@ -171,12 +170,10 @@ frappe.ui.form.AssignToDialog = Class.extend({
var me = this;
if($(myself).prop("checked")) {
me.dialog.set_value("assign_to", frappe.session.user);
- me.dialog.set_value("notify", 0);
me.dialog.get_field("notify").$wrapper.toggle(false);
me.dialog.get_field("assign_to").$wrapper.toggle(false);
} else {
me.dialog.set_value("assign_to", "");
- me.dialog.get_field("notify").$wrapper.toggle(true);
me.dialog.get_field("assign_to").$wrapper.toggle(true);
}
},
diff --git a/frappe/public/js/frappe/form/sidebar/share.js b/frappe/public/js/frappe/form/sidebar/share.js
index 0a7cc2b831..3cb5257e78 100644
--- a/frappe/public/js/frappe/form/sidebar/share.js
+++ b/frappe/public/js/frappe/form/sidebar/share.js
@@ -141,7 +141,6 @@ frappe.ui.form.Share = Class.extend({
read: $(d.body).find(".add-share-read").prop("checked") ? 1 : 0,
write: $(d.body).find(".add-share-write").prop("checked") ? 1 : 0,
share: $(d.body).find(".add-share-share").prop("checked") ? 1 : 0,
- notify: $(d.body).find(".add-share-notify").prop("checked") ? 1 : 0
},
btn: this,
callback: function(r) {
diff --git a/frappe/public/js/frappe/form/templates/set_sharing.html b/frappe/public/js/frappe/form/templates/set_sharing.html
index 617cb07848..1fb744d7d4 100644
--- a/frappe/public/js/frappe/form/templates/set_sharing.html
+++ b/frappe/public/js/frappe/form/templates/set_sharing.html
@@ -51,17 +51,5 @@
{{ __("Add") }}
-
-
-
-
-
-
- {{ __("Notify by email") }}
-
-
-
-
{% endif %}
\ No newline at end of file
diff --git a/frappe/public/js/frappe/ui/notifications/notifications.js b/frappe/public/js/frappe/ui/notifications/notifications.js
new file mode 100644
index 0000000000..a41e931402
--- /dev/null
+++ b/frappe/public/js/frappe/ui/notifications/notifications.js
@@ -0,0 +1,450 @@
+frappe.ui.Notifications = class Notifications {
+ constructor() {
+ frappe.model
+ .with_doc('Notification Settings', frappe.session.user)
+ .then(doc => {
+ this.notifications_settings = doc;
+ this.make();
+ });
+ }
+
+ make() {
+ this.$dropdown = $('.navbar').find('.dropdown-notifications');
+ this.$dropdown_list = this.$dropdown.find('.notifications-list');
+ this.$notification_indicator = this.$dropdown.find(
+ '.notifications-indicator'
+ );
+ this.user = frappe.session.user;
+ this.max_length = 20;
+
+ this.render_dropdown_headers();
+ this.$notifications = this.$dropdown_list.find(
+ '.category-list[data-category="Notifications"]'
+ );
+ this.$open_docs = this.$dropdown_list.find(
+ '.category-list[data-category="Open Documents"]'
+ );
+ this.$upcoming_events = this.$dropdown_list.find(
+ '.category-list[data-category="Upcoming Events"]'
+ );
+
+ frappe.utils.bind_actions_with_object(this.$dropdown_list, this);
+ this.setup_notifications();
+ this.bind_events();
+ }
+
+ setup_notifications() {
+ this.get_notifications_list(this.max_length).then(list => {
+ this.dropdown_items = list;
+ this.render_notifications_dropdown();
+
+ if (this.$notifications.find('.unseen').length) {
+ this.$notification_indicator.show();
+ }
+ });
+ }
+
+ render_upcoming_events(e, $target) {
+ let hide = $target.next().hasClass('in');
+ if (!hide) {
+ let today = frappe.datetime.now_date();
+
+ frappe
+ .xcall('frappe.desk.doctype.event.event.get_events', {
+ start: today,
+ end: today
+ })
+ .then(event_list => {
+ this.render_events_html(event_list);
+ });
+ }
+ }
+
+ render_events_html(event_list) {
+ let html = '';
+ if (event_list.length) {
+ let get_event_html = event => {
+ let time = frappe.datetime.get_time(event.starts_on);
+ return `
+ ${time}
+ ${event.subject}
+ `;
+ };
+ html = event_list.map(get_event_html).join('');
+ } else {
+ html = `
+ ${__('No Upcoming Events')}
+ `;
+ }
+
+ this.$upcoming_events.html(html);
+ }
+
+ get_open_document_config(e) {
+ this.open_docs_config = {
+ ToDo: { label: __('To Do') },
+ Event: { label: __('Calendar'), route: 'List/Event/Calendar' }
+ };
+
+ let hide = $(e.currentTarget)
+ .next()
+ .hasClass('in');
+ if (!hide) {
+ frappe
+ .xcall('frappe.desk.notifications.get_notification_info')
+ .then(r => {
+ this.open_document_list = r;
+ this.render_open_document_count();
+ });
+ }
+ }
+
+ render_open_document_count() {
+ this.$open_docs.html('');
+ let defaults = ['ToDo'];
+ this.get_counts(this.open_document_list['open_count_doctype'], 1, defaults);
+ let targets = { doctypes: {} },
+ map = this.open_document_list['targets'];
+
+ Object.keys(map).map(doctype => {
+ Object.keys(map[doctype]).map(doc => {
+ targets[doc] = map[doctype][doc];
+ targets.doctypes[doc] = doctype;
+ });
+ });
+
+ this.get_counts(targets, 1, null, ['doctypes'], true);
+ this.get_counts(
+ this.open_document_list['open_count_doctype'],
+ 0,
+ null,
+ defaults
+ );
+ }
+
+ get_counts(map, divide, keys, excluded = [], target = false) {
+ let empty_map = 1;
+ keys = keys
+ ? keys
+ : Object.keys(map).sort().filter(e => !excluded.includes(e));
+ keys.map(key => {
+ let doc_dt = map.doctypes ? map.doctypes[key] : undefined;
+ if (map[key] > 0 || target) {
+ this.add_open_document_html(key, map[key], doc_dt, target);
+ empty_map = 0;
+ }
+ });
+
+ if (divide && !empty_map) {
+ this.$open_docs.append($(' '));
+ }
+ }
+
+ add_open_document_html(name, value, doc_dt, target = false) {
+ let label = this.open_docs_config[name]
+ ? this.open_docs_config[name].label
+ : name;
+ let title = target ? `title="${__('Your Target')}"` : '';
+ let $list_item = !target
+ ? $(`
+ ${label}
+ ${value}
+ `)
+ : $(`
+ ${label}
+
+ `);
+
+ this.$open_docs.append($list_item);
+ if (!target) this.total += value;
+ }
+
+ route_to_list_with_filters(doctype) {
+ let filters = this.open_document_list['conditions'][doctype];
+ if (filters && $.isPlainObject(filters)) {
+ if (!frappe.route_options) {
+ frappe.route_options = {};
+ }
+ $.extend(frappe.route_options, filters);
+ }
+ frappe.set_route('List', doctype);
+ }
+
+ route_to_document_type(e) {
+ this.$dropdown.removeClass('open');
+ this.$dropdown.trigger('hide.bs.dropdown');
+ let doctype = $(e.currentTarget).attr('data-doctype');
+ let docname = $(e.currentTarget).attr('data-docname');
+ if (!docname) {
+ let config = this.open_docs_config[doctype] || {};
+ if (config.route) {
+ frappe.set_route(config.route);
+ } else if (config.click) {
+ config.click();
+ } else {
+ this.route_to_list_with_filters(doctype);
+ }
+ } else {
+ frappe.set_route('Form', doctype, docname);
+ }
+ }
+
+ update_dropdown() {
+ this.get_notifications_list(1).then(r => {
+ let new_item = r[0];
+ this.dropdown_items.unshift(new_item);
+ if (this.dropdown_items.length > this.max_length) {
+ this.$dropdown_list
+ .find('.recent-notification')
+ .last()
+ .remove();
+ this.dropdown_items.pop();
+ }
+
+ this.insert_into_dropdown();
+ });
+ }
+
+ insert_into_dropdown() {
+ let new_item = this.dropdown_items[0];
+ let new_item_html = this.get_dropdown_item_html(new_item);
+ $(new_item_html).prependTo(this.$dropdown_list.find(this.$notifications));
+ this.change_activity_status();
+ }
+
+ change_activity_status() {
+ if (this.$dropdown_list.find('.activity-status')) {
+ this.$dropdown_list.find('.activity-status').replaceWith(
+ `
+ ${__('View Full Log')}
+ `
+ );
+ }
+ }
+
+ mark_as_seen() {
+ let unseen_docnames = this.dropdown_items
+ .filter(item => item.seen === 0)
+ .map(d => d.name);
+ if (!unseen_docnames.length) return;
+ frappe.call(
+ 'frappe.desk.doctype.notification_log.notification_log.mark_as_seen',
+ { docnames: unseen_docnames }
+ );
+ }
+
+ get_notifications_list(limit) {
+ return frappe.db.get_list('Notification Log', {
+ fields: ['*'],
+ limit: limit,
+ order_by: 'creation desc'
+ });
+ }
+
+ render_notifications_dropdown() {
+ let body_html = '';
+ let view_full_log_html = '';
+ let dropdown_html;
+
+ if (this.notifications_settings && !this.notifications_settings.enabled) {
+ dropdown_html = `
+
+ ${__('Notifications Disabled')}
+ `;
+ } else {
+ if (this.dropdown_items.length) {
+ this.dropdown_items.forEach(field => {
+ let item_html = this.get_dropdown_item_html(field);
+ if (item_html) body_html += item_html;
+ });
+ view_full_log_html = `
+ ${__('View Full Log')}
+ `;
+ } else {
+ body_html += `
+
+ ${__('No activity')}
+ `;
+ }
+ dropdown_html = body_html + view_full_log_html;
+ }
+
+ this.$notifications.html(dropdown_html);
+ }
+
+ get_dropdown_item_html(field) {
+ let doc_link = frappe.utils.get_form_link(
+ field.document_type,
+ field.document_name
+ );
+ let seen_class = field.seen ? '' : 'unseen';
+ let message = field.subject;
+ let message_html = `${message}
`;
+ let user = field.from_user;
+ let user_avatar = frappe.avatar(user, 'avatar-small user-avatar');
+ let timestamp = frappe.datetime.comment_when(field.creation, true);
+ let item_html = `
+ ${user_avatar}
+ ${message_html}
+
+ ${timestamp}
+
+ `;
+
+ return item_html;
+ }
+
+ render_dropdown_headers() {
+ this.categories = [
+ {
+ label: __('Notifications'),
+ value: 'Notifications'
+ },
+ {
+ label: __('Upcoming Events'),
+ value: 'Upcoming Events',
+ action: 'render_upcoming_events'
+ },
+ {
+ label: __('Open Documents'),
+ value: 'Open Documents',
+ action: 'get_open_document_config'
+ }
+ ];
+
+ let get_headers_html = category => {
+ let category_id = frappe.dom.get_unique_id();
+ let settings_html =
+ category.value === 'Notifications'
+ ? `
+ ${__('Settings')}
+ `
+ : '';
+ let html = `
+
+
+
+ ${__('Loading...')}
+
+
+ `;
+
+ return html;
+ };
+
+ let html = this.categories
+ .map(get_headers_html)
+ .join(' ');
+ this.$dropdown_list.append(html);
+ this.$dropdown_list
+ .find('.category-list[data-category="Notifications"]')
+ .collapse('show');
+ this.toggle_collapse_indicator(
+ this.$dropdown_list.find('.category-list[data-category="Notifications"]')
+ );
+ }
+
+ make_and_route_to_settings(e) {
+ e.stopImmediatePropagation();
+ this.$dropdown.removeClass('open');
+ this.$dropdown.trigger('hide.bs.dropdown');
+ let method =
+ 'frappe.desk.doctype.notification_settings.notification_settings.create_notification_settings';
+
+ return Promise.resolve()
+ .then(() => {
+ if (!this.notifications_settings) return frappe.call(method);
+ })
+ .then(() => {
+ frappe.set_route(`#Form/Notification Settings/${frappe.session.user}`);
+ });
+ }
+
+ bind_events() {
+ this.setup_notification_listener();
+ this.setup_dropdown_events();
+
+ this.$dropdown_list.on('click', '.recent-item', () => {
+ this.$dropdown.removeClass('open');
+ });
+
+ $('.category-list').on('hide.bs.collapse', e => {
+ this.toggle_collapse_indicator($(e.currentTarget));
+ });
+
+ $('.category-list').on('show.bs.collapse', e => {
+ this.toggle_collapse_indicator($(e.currentTarget));
+ });
+ }
+
+ setup_notification_listener() {
+ frappe.realtime.on('notification', () => {
+ this.$dropdown.find('.notifications-indicator').show();
+ this.update_dropdown();
+ });
+ }
+
+ setup_dropdown_events() {
+ this.$dropdown_list
+ .find(
+ '[data-category="Notifications"], [data-category="Upcoming Events"], [data-category="Open Documents"]'
+ )
+ .collapse({
+ toggle: false
+ });
+ this.$dropdown.on('hide.bs.dropdown', e => {
+ this.$notification_indicator.hide();
+ let hide = $(e.currentTarget).data('closable');
+ if (hide) {
+ this.$dropdown_list
+ .find('[data-category="Notifications"]')
+ .collapse('show');
+ this.$dropdown_list
+ .find(
+ '[data-category="Upcoming Events"], [data-category="Open Documents"]'
+ )
+ .collapse('hide');
+ }
+ this.$dropdown_list.find('.unseen').removeClass('unseen');
+ $(e.currentTarget).data('closable', true);
+ return hide;
+ });
+
+ this.$dropdown.on('show.bs.dropdown', () => {
+ this.mark_as_seen();
+ });
+
+ this.$dropdown.on('click', e => {
+ if ($(e.target).closest('.dropdown-toggle').length) {
+ $(e.currentTarget).data('closable', true);
+ } else {
+ $(e.currentTarget).data('closable', false);
+ }
+ });
+ }
+
+ toggle_collapse_indicator($el) {
+ $el
+ .prev()
+ .find('.collapse-indicator')
+ .toggleClass('octicon-chevron-down');
+ $el
+ .prev()
+ .find('.collapse-indicator')
+ .toggleClass('octicon-chevron-up');
+ }
+};
diff --git a/frappe/public/js/frappe/ui/toolbar/energy_points_notifications.js b/frappe/public/js/frappe/ui/toolbar/energy_points_notifications.js
deleted file mode 100644
index 5d166f41fd..0000000000
--- a/frappe/public/js/frappe/ui/toolbar/energy_points_notifications.js
+++ /dev/null
@@ -1,186 +0,0 @@
-
-frappe.ui.EnergyPointsNotifications = class {
-
- constructor() {
- this.$dropdown = $('.navbar').find('.dropdown-energy-points');
- this.$dropdown_list = this.$dropdown.find('.recent-points-list');
- this.$notification_indicator = this.$dropdown.find('.energy-points-notification');
- this.max_length = 20;
- this.setup_energy_points_notifications();
- }
-
- setup_energy_points_notifications() {
- this.get_energy_points_list(this.max_length).then(user_points_list => {
- this.dropdown_items = user_points_list;
- this.render_energy_points_dropdown();
- this.setup_view_full_log();
- if (this.$dropdown_list.find('.unseen').length) {
- this.$notification_indicator.show();
- }
- });
-
- this.bind_events();
- }
-
- bind_events() {
- frappe.realtime.on('energy_points_notification', () => {
- this.$dropdown.find('.energy-points-notification').show();
- this.update_dropdown();
- });
-
- this.$dropdown.on('hide.bs.dropdown', () => {
- this.$notification_indicator.hide();
- this.$dropdown_list.find('.unseen').removeClass('unseen');
- });
-
- this.$dropdown.on('show.bs.dropdown', () => {
- this.check_seen();
- });
- }
-
- update_dropdown() {
- this.get_energy_points_list(1).then(r => {
- let new_item = r[0];
- this.dropdown_items.unshift(new_item);
- if (this.dropdown_items.length > this.max_length) {
- this.$dropdown_list.find('.recent-points-item').last().remove();
- this.dropdown_items.pop();
- }
- this.insert_into_dropdown();
- });
- }
-
- insert_into_dropdown() {
- let new_item = this.dropdown_items[0];
- let new_item_html = this.get_dropdown_item_html(new_item);
- let new_item_date_range = this.get_date_range_title(new_item.creation);
- let current_date_range = this.get_date_range_title(this.dropdown_items[1].creation);
- if (current_date_range !== new_item_date_range) {
- let $date_range = $(`${new_item_date_range} `);
- $date_range.insertAfter(this.$dropdown_list.find('.points-updates-header'));
- $(new_item_html).insertAfter($date_range);
- } else {
- $(new_item_html).insertAfter(this.$dropdown_list.find('.points-date-range').eq(0));
- }
- }
-
- check_seen() {
- let unseen_logs = this.dropdown_items.filter(item => item.seen === 0);
- frappe.call('frappe.social.doctype.energy_point_log.energy_point_log.set_notification_as_seen', {point_logs: unseen_logs});
- }
-
- get_date_range_title(date) {
- let current_date = frappe.datetime.now_date();
- let prev_week = frappe.datetime.add_days(current_date, -7);
- let prev_month = frappe.datetime.add_months(frappe.datetime.now_date(), -1);
- if (date >= current_date) {
- return __('Today');
- } else if (date > prev_week) {
- return __('Last 7 days');
- } else if (date > prev_month) {
- return __('Last 30 days');
- } else {
- return __('Older');
- }
- }
-
- get_energy_points_list(limit) {
- return frappe.db.get_list('Energy Point Log', {
- filters: {
- user: frappe.session.user,
- type: ['not in', ['Review']],
- },
- fields:
- ['name', 'user', 'points', 'reference_doctype', 'reference_name', 'reason', 'type', 'seen', 'rule', 'owner', 'creation'],
- limit: limit,
- order_by: 'creation desc'
- }).then((energy_points_list) => {
- return energy_points_list;
- });
- }
-
- render_energy_points_dropdown() {
- let header_html =
- ``;
- let body_html = '';
- let view_full_log_html = '';
-
- if (this.dropdown_items.length) {
- let date_range = this.get_date_range_title(this.dropdown_items[0].creation);
- body_html += `${date_range} `;
- this.dropdown_items.forEach(field => {
- let current_field_date_range = this.get_date_range_title(field.creation);
- if (date_range !== current_field_date_range) {
- body_html += `${current_field_date_range} `;
- date_range = current_field_date_range;
- }
- let item_html = this.get_dropdown_item_html(field);
- if (item_html) body_html += item_html;
- });
- view_full_log_html = `${__('View Full Log')} `;
- } else {
- body_html += `${__('No activity')} `;
- }
- let dropdown_html = header_html + body_html + view_full_log_html;
- this.$dropdown_list.html(dropdown_html);
- }
-
- get_dropdown_item_html(field) {
- let doc_link = frappe.utils.get_form_link(field.reference_doctype, field.reference_name);
- let link_html_string = field.seen ? ``: ` `;
- let points_html = `${frappe.energy_points.get_points(field.points)}
`;
- let message_html = this.get_message_html(field);
-
- let item_html = `
- ${link_html_string}
- ${points_html}
-
- ${message_html}
-
-
- `;
- return item_html;
- }
-
- get_message_html(field) {
- let owner_name = frappe.user.full_name(field.owner).trim();
- owner_name = frappe.ellipsis(owner_name, 50);
- let message_html = '';
- let reference_doc = `
-
- ${field.reference_name}
-
- `;
- let reason_string = `
-
- - "${frappe.ellipsis(field.reason, 50)}"
-
- `;
- if (field.type === 'Auto' ) {
- message_html = __('For {0} {1}',
- [field.rule, reference_doc]);
- } else {
- if (field.type === 'Appreciation') {
- message_html = __('{0} appreciated your work on {1} {2}',
- [owner_name, reference_doc, reason_string]);
- } else if (field.type === 'Criticism') {
- message_html = __('{0} criticized your work on {1} {2}',
- [owner_name, reference_doc, reason_string]);
- } else if (field.type === 'Revert') {
- message_html = __('{0} reverted your points on {1} {2}',
- [owner_name, reference_doc, reason_string]);
- }
- }
- return message_html;
- }
-
- setup_view_full_log() {
- this.$dropdown_list.find('.full-log-btn').on('click', () => {
- frappe.set_route('List', 'Energy Point Log', {user: frappe.session.user});
- });
- }
-
-};
diff --git a/frappe/public/js/frappe/ui/toolbar/navbar.html b/frappe/public/js/frappe/ui/toolbar/navbar.html
index 51c40a7d46..119e43e0e8 100644
--- a/frappe/public/js/frappe/ui/toolbar/navbar.html
+++ b/frappe/public/js/frappe/ui/toolbar/navbar.html
@@ -68,36 +68,19 @@
-
-
+
+
-
+
-
-
-
diff --git a/frappe/public/js/frappe/ui/toolbar/notifications.js b/frappe/public/js/frappe/ui/toolbar/notifications.js
deleted file mode 100644
index 432c232dd8..0000000000
--- a/frappe/public/js/frappe/ui/toolbar/notifications.js
+++ /dev/null
@@ -1,103 +0,0 @@
-frappe.provide("frappe.ui.notifications");
-
-frappe.ui.notifications = {
- config: {
- "ToDo": { label: __("To Do") },
- "Event": { label: __("Calendar"), route: "List/Event/Calendar" },
- "Email": { label: __("Email"), route: "List/Communication/Inbox" }
- },
-
- update_notifications: function() {
- this.total = 0;
- this.dropdown = $("#dropdown-notification").empty();
- this.boot_info = frappe.boot.notification_info;
- let defaults = ["Comment", "ToDo", "Event"];
-
- this.get_counts(this.boot_info.open_count_doctype, 1, defaults);
- this.get_counts(this.boot_info.open_count_other, 1);
-
- // Target counts are stored for docs per doctype
- let targets = { doctypes : {} }, map = this.boot_info.targets;
- Object.keys(map).map(doctype => {
- Object.keys(map[doctype]).map(doc => {
- targets[doc] = map[doctype][doc];
- targets.doctypes[doc] = doctype;
- });
- });
- this.get_counts(targets, 1, null, ["doctypes"], true);
- this.get_counts(this.boot_info.open_count_doctype,
- 0, null, defaults);
-
- this.bind_list();
-
- // switch colour on the navbar and disable if no notifications
- $(".navbar-new-comments")
- .html(this.total > 99 ? '99+' : this.total)
- .toggleClass("navbar-new-comments-true", this.total ? true : false)
- .parent().toggleClass("disabled", this.total ? false : true);
- },
-
- get_counts: function(map, divide, keys, excluded = [], target = false) {
- let empty_map = 1;
- keys = keys ? keys
- : Object.keys(map).sort().filter(e => !excluded.includes(e));
- keys.map(key => {
- let doc_dt = (map.doctypes) ? map.doctypes[key] : undefined;
- if(map[key] > 0 || target) {
- this.add_notification(key, map[key], doc_dt, target);
- empty_map = 0;
- }
- });
- if(divide && !empty_map) {
- this.dropdown.append($(' '));
- }
- },
-
- add_notification: function(name, value, doc_dt, target = false) {
- let label = this.config[name] ? this.config[name].label : name;
- let title = target ? `title="Your Target"` : '';
- let $list_item = !target
- ? $(`${__(label)}
- ${value}
- `)
- : $(`${__(label)}
-
- `);
- this.dropdown.append($list_item);
- if(!target) this.total += value;
- },
-
- bind_list: function() {
- var me = this;
- $("#dropdown-notification a").on("click", function() {
- var doctype = $(this).attr("data-doctype");
- var doc = $(this).attr("data-doc");
- if(!doc) {
- var config = me.config[doctype] || {};
- if (config.route) {
- frappe.set_route(config.route);
- } else if (config.click) {
- config.click();
- } else {
- frappe.ui.notifications.show_open_count_list(doctype);
- }
- } else {
- frappe.set_route("Form", doctype, doc);
- }
- });
- },
-
- show_open_count_list: function(doctype) {
- let filters = this.boot_info.conditions[doctype];
- if(filters && $.isPlainObject(filters)) {
- if (!frappe.route_options) {
- frappe.route_options = {};
- }
- $.extend(frappe.route_options, filters);
- }
- frappe.set_route("List", doctype);
- },
-};
diff --git a/frappe/public/js/frappe/ui/toolbar/toolbar.js b/frappe/public/js/frappe/ui/toolbar/toolbar.js
index b98ef94887..a7a14f5476 100644
--- a/frappe/public/js/frappe/ui/toolbar/toolbar.js
+++ b/frappe/public/js/frappe/ui/toolbar/toolbar.js
@@ -15,8 +15,7 @@ frappe.ui.toolbar.Toolbar = Class.extend({
awesome_bar.setup("#navbar-search");
awesome_bar.setup("#modal-search");
- this.setup_energy_point_notifications();
-
+ this.setup_notifications();
this.make();
},
@@ -30,10 +29,6 @@ frappe.ui.toolbar.Toolbar = Class.extend({
},
bind_events: function() {
- $(document).on("notification-update", function() {
- frappe.ui.notifications.update_notifications();
- });
-
// clear all custom menus on page change
$(document).on("page-change", function() {
$("header .navbar .custom-menu").remove();
@@ -161,13 +156,8 @@ frappe.ui.toolbar.Toolbar = Class.extend({
}
},
- setup_energy_point_notifications: function() {
- if (frappe.boot.energy_points_enabled) {
- $('.dropdown-energy-points').show();
- this.energy_points_notifications = new frappe.ui.EnergyPointsNotifications();
- } else {
- $('.dropdown-energy-points').hide();
- }
+ setup_notifications: function() {
+ this.notifications = new frappe.ui.Notifications();
}
});
diff --git a/frappe/public/js/frappe/views/components/ModuleDetail.vue b/frappe/public/js/frappe/views/components/ModuleDetail.vue
index c0c7dc96e8..59d1034c1e 100644
--- a/frappe/public/js/frappe/views/components/ModuleDetail.vue
+++ b/frappe/public/js/frappe/views/components/ModuleDetail.vue
@@ -10,7 +10,6 @@
:key="section.label + item.label"
:data-youtube-id="item.type==='help' ? item.youtube_id : false"
v-bind="item"
- :open_count="item.type==='doctype' ? frappe.boot.notification_info.open_count_doctype[item.doctype] : false"
>
diff --git a/frappe/public/js/frappe/views/interaction.js b/frappe/public/js/frappe/views/interaction.js
index 458f4c8dc1..6f694801a6 100644
--- a/frappe/public/js/frappe/views/interaction.js
+++ b/frappe/public/js/frappe/views/interaction.js
@@ -262,7 +262,6 @@ frappe.views.InteractionComposer = class InteractionComposer {
doctype: doc.doctype,
name: doc.name,
assign_to: assignee,
- notify: 1
},
callback:function(r) {
if(!r.exc) {
diff --git a/frappe/public/less/navbar.less b/frappe/public/less/navbar.less
index 684aefabcd..f6145e77bb 100644
--- a/frappe/public/less/navbar.less
+++ b/frappe/public/less/navbar.less
@@ -276,56 +276,3 @@
.navbar-default .navbar-brand {
color: @text-muted;
}
-
-.dropdown-energy-points .energy-points-icon {
- height: 40px;
- font-size: 14px;
- text-align: center;
-}
-
-.recent-points-list {
- width: 300px;
- max-height: 480px;
- overflow-y: auto;
-}
-
-.energy-points-notification {
- font-size: 7px;
- position: absolute;
- top: 4px;
- right: 8px;
- color: @indicator-orange;
- display: none;
-}
-
-.points-update {
- float: left;
- text-align: right;
- margin-right: 15px;
- width: 7%;
-}
-
-.points-reason {
- display: flow-root;
-}
-
-.recent-points-list .points-updates-header .points-leaderboard {
- float: right;
- padding: 0;
-}
-
-.points-updates-header {
- background: @navbar-bg;
- padding: 10px;
- position: sticky;
- top: 0;
-}
-
-.points-date-range {
- padding: 10px 0 2px 10px;
- font-weight: 500;
-}
-
-.unseen {
- background: @light-yellow;
-}
\ No newline at end of file
diff --git a/frappe/public/less/notifications.less b/frappe/public/less/notifications.less
new file mode 100644
index 0000000000..adc15ec3ae
--- /dev/null
+++ b/frappe/public/less/notifications.less
@@ -0,0 +1,126 @@
+@import "variables.less";
+@import "mixins.less";
+
+
+.dropdown-notifications .header {
+ margin: 7px 15px 10px 15px;
+ cursor: pointer;
+}
+
+.notification-settings {
+ margin-top: 2px;
+}
+
+.collapse-indicator {
+ padding: 0px 5px;
+ color: #d1d8dd;
+}
+
+.category-list[data-category="Open Documents"] li a {
+ text-decoration: none;
+ display: block;
+ padding: 14px;
+ clear: both;
+ font-weight: normal;
+ line-height: 1.42857143;
+ color: #333;
+ white-space: normal;
+}
+
+.category-list[data-category="Open Documents"] li a:hover {
+ background-color: #f0f4f7;
+}
+
+.open-doc-count {
+ margin-left: 150px;
+}
+
+.notifications-indicator {
+ font-size: 7px;
+ position: absolute;
+ top: 4px;
+ right: 8px;
+ color: @indicator-orange;
+ display: none;
+}
+
+.dropdown-notifications .recent-item {
+ padding: 10px 14px;
+ white-space: normal;
+ text-decoration: none;
+ font-weight: normal;
+ display: flow-root;
+}
+
+a.recent-item:hover {
+ background-color: #f0f4f7;
+}
+
+.dropdown-energy-points .energy-points-icon {
+ height: 40px;
+ font-size: 14px;
+ text-align: center;
+}
+
+.notifications-list {
+ width: 450px;
+ max-height: 480px;
+ overflow-y: auto;
+}
+
+.energy-points-notification {
+ font-size: 7px;
+ position: absolute;
+ top: 4px;
+ right: 8px;
+ color: @indicator-orange;
+ display: none;
+}
+
+.date-range {
+ padding: 10px 0 2px 10px;
+ font-weight: 500;
+}
+
+.unseen {
+ background: @light-yellow;
+}
+
+.notification-timestamp {
+ margin-top: 5px;
+ font-size: 11px;
+}
+
+.user-avatar {
+ float: left;
+ margin-right: 10px;
+ margin-bottom: 30px;
+}
+
+.event-time {
+ margin-right: 10px;
+}
+
+.notifications-loading {
+ margin-bottom: 15px;
+}
+
+@media (max-width: 767px) {
+
+ .dropdown-notifications {
+ .notifications-list {
+ max-height: 100vh;
+ min-width: 100vw;
+ }
+
+ .category-list[data-category="Open Documents"] li a {
+ margin: 0 15px;
+ }
+
+ .recent-item .user-avatar {
+ margin-right: 10px;
+ margin-left: 5px;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/frappe/sessions.py b/frappe/sessions.py
index 8b5900fe51..ffad4b5e7b 100644
--- a/frappe/sessions.py
+++ b/frappe/sessions.py
@@ -112,9 +112,8 @@ def clear_expired_sessions():
delete_session(sid, reason="Session Expired")
def get():
+
"""get session boot info"""
- from frappe.desk.notifications import \
- get_notification_info_for_boot, get_notifications
from frappe.boot import get_bootinfo, get_unseen_notes
bootinfo = None
@@ -123,14 +122,12 @@ def get():
bootinfo = frappe.cache().hget("bootinfo", frappe.session.user)
if bootinfo:
bootinfo['from_cache'] = 1
- bootinfo["notification_info"].update(get_notifications())
bootinfo["user"]["recent"] = json.dumps(\
frappe.cache().hget("user_recent", frappe.session.user))
if not bootinfo:
# if not create it
bootinfo = get_bootinfo()
- bootinfo["notification_info"] = get_notification_info_for_boot()
frappe.cache().hset("bootinfo", frappe.session.user, bootinfo)
try:
frappe.cache().ping()
diff --git a/frappe/share.py b/frappe/share.py
index a03e02d551..c8471bfe2c 100644
--- a/frappe/share.py
+++ b/frappe/share.py
@@ -5,10 +5,11 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.desk.form.document_follow import follow_document
+from frappe.desk.doctype.notification_log.notification_log import enqueue_create_notification
from frappe.utils import cint
@frappe.whitelist()
-def add(doctype, name, user=None, read=1, write=0, share=0, everyone=0, flags=None, notify=0):
+def add(doctype, name, user=None, read=1, write=0, share=0, everyone=0, flags=None):
"""Share the given document with a user."""
if not user:
user = frappe.session.user
@@ -40,7 +41,7 @@ def add(doctype, name, user=None, read=1, write=0, share=0, everyone=0, flags=No
})
doc.save(ignore_permissions=True)
- notify_assignment(user, doctype, name, description=None, notify=notify)
+ notify_assignment(user, doctype, name, everyone)
follow_document(doctype, name, user)
@@ -145,16 +146,26 @@ def check_share_permission(doctype, name):
if not frappe.has_permission(doctype, ptype="share", doc=name):
frappe.throw(_("No permission to {0} {1} {2}".format("share", doctype, name)), frappe.PermissionError)
-def notify_assignment(shared_by, doc_type, doc_name, description=None, notify=0):
+def notify_assignment(shared_by, doctype, doc_name, everyone):
- if not (shared_by and doc_type and doc_name): return
+ if not (shared_by and doctype and doc_name) or everyone: return
- from frappe.utils import get_link_to_form
- document = get_link_to_form(doc_type, doc_name, label="%s: %s" % (doc_type, doc_name))
+ from frappe.utils import get_fullname
- arg = {
- 'contact': shared_by,
- 'txt': _("A new document {0} has been shared by with you {1}.").format(document,
- shared_by),
- 'notify': notify
- }
\ No newline at end of file
+ title_field = frappe.get_meta(doctype).get_title_field()
+ title = doc_name if title_field == "name" else \
+ frappe.db.get_value(doctype, doc_name, title_field)
+
+ reference_user = get_fullname(frappe.session.user)
+ notification_message = _('{0} shared a document {1} {2} with you').format(
+ frappe.bold(reference_user), frappe.bold(doctype), frappe.bold(title))
+
+ notification_doc = {
+ 'type': 'Share',
+ 'document_type': doctype,
+ 'subject': notification_message,
+ 'document_name': doc_name,
+ 'from_user': frappe.session.user
+ }
+
+ enqueue_create_notification(shared_by, notification_doc)
diff --git a/frappe/social/doctype/energy_point_log/energy_point_log.py b/frappe/social/doctype/energy_point_log/energy_point_log.py
index a612d6bdfa..c347c81b1e 100644
--- a/frappe/social/doctype/energy_point_log/energy_point_log.py
+++ b/frappe/social/doctype/energy_point_log/energy_point_log.py
@@ -7,6 +7,7 @@ import frappe
from frappe import _
import json
from frappe.model.document import Document
+from frappe.desk.doctype.notification_log.notification_log import enqueue_create_notification
from frappe.utils import cint, get_fullname, getdate, get_link_to_form
class EnergyPointLog(Document):
@@ -25,13 +26,58 @@ class EnergyPointLog(Document):
alert_dict = get_alert_dict(self)
if alert_dict:
frappe.publish_realtime('energy_point_alert', message=alert_dict, user=self.user)
- send_review_mail(self, alert_dict)
frappe.cache().hdel('energy_points', self.user)
frappe.publish_realtime('update_points', after_commit=True)
if self.type != 'Review':
- frappe.publish_realtime('energy_points_notification', after_commit=True, user=self.user)
+ reference_user = self.user if self.type == 'Auto' else self.owner
+ notification_doc = {
+ 'type': 'Energy Point',
+ 'document_type': self.reference_doctype,
+ 'document_name': self.reference_name,
+ 'subject': get_notification_message(self),
+ 'from_user': reference_user,
+ 'email_content': '{}
'.format(self.reason)
+ }
+
+ enqueue_create_notification(self.user, notification_doc)
+
+def get_notification_message(doc):
+ owner_name = get_fullname(doc.owner)
+ points = doc.points
+ title_field = frappe.get_meta(doc.reference_doctype).get_title_field()
+ title = doc.reference_name if title_field == "name" else \
+ frappe.db.get_value(doc.reference_doctype, doc.reference_name, title_field)
+
+ if doc.type == 'Auto':
+ owner_name = frappe.bold('You')
+ if points == 1:
+ message = _('{0} gained {1} point for {2} {3}')
+ else:
+ message = _('{0} gained {1} points for {2} {3}')
+ message = message.format(owner_name, frappe.bold(points), doc.rule, frappe.bold(title))
+ elif doc.type == 'Appreciation':
+ if points == 1:
+ message = _('{0} appreciated your work on {1} with {2} point')
+ else:
+ message = _('{0} appreciated your work on {1} with {2} points')
+ message = message.format(frappe.bold(owner_name), frappe.bold(title), frappe.bold(points))
+ elif doc.type == 'Criticism':
+ if points == 1:
+ message = _('{0} criticized your work on {1} with {2} point')
+ else:
+ message = _('{0} criticized your work on {1} with {2} points')
+
+ message = message.format(frappe.bold(owner_name), frappe.bold(title), frappe.bold(points))
+ elif doc.type == 'Revert':
+ if points == 1:
+ message = _('{0} reverted your point on {1}')
+ else:
+ message = _('{0} reverted your points on {1}')
+ message = message.format(frappe.bold(owner_name), frappe.bold(title))
+
+ return message
def get_alert_dict(doc):
alert_dict = frappe._dict()
@@ -83,13 +129,6 @@ def get_alert_dict(doc):
return alert_dict
-def send_review_mail(doc, message_dict):
- if doc.type in ['Appreciation', 'Criticism']:
- frappe.sendmail(recipients=doc.user,
- subject=_("You gained some energy points") if doc.points > 0 else _("You lost some energy points"),
- message=message_dict.message + '{}
'.format(doc.reason),
- header=[_('Energy point update'), message_dict.indicator])
-
def create_energy_points_log(ref_doctype, ref_name, doc):
doc = frappe._dict(doc)
log_exists = frappe.db.exists('Energy Point Log', {
@@ -173,13 +212,6 @@ def get_user_energy_and_review_points(user=None, from_date=None, as_dict=True):
dict_to_return[d.pop('user')] = d
return dict_to_return
-
-@frappe.whitelist()
-def set_notification_as_seen(point_logs):
- point_logs = frappe.parse_json(point_logs)
- for log in point_logs:
- frappe.db.set_value('Energy Point Log', log['name'], 'seen', 1, update_modified=False)
-
@frappe.whitelist()
def review(doc, points, to_user, reason, review_type='Appreciation'):
current_review_points = get_energy_points(frappe.session.user).review_points
diff --git a/frappe/templates/emails/mentioned_in_comment.html b/frappe/templates/emails/mentioned_in_comment.html
deleted file mode 100644
index 92bf15723a..0000000000
--- a/frappe/templates/emails/mentioned_in_comment.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
- {{ body_content }}
-
-
- {{ comment.content | markdown }}
-
diff --git a/frappe/templates/emails/new_notification.html b/frappe/templates/emails/new_notification.html
new file mode 100644
index 0000000000..fb1fc98901
--- /dev/null
+++ b/frappe/templates/emails/new_notification.html
@@ -0,0 +1,13 @@
+
+
{{ body_content }}
+
+{% if description %}
+
+ {{ description | markdown }}
+
+{% endif %}
+
+