feat(Notifications): Add integrated notifications
This commit is contained in:
parent
e9e2a89dc8
commit
bae5049683
27 changed files with 1039 additions and 191 deletions
|
|
@ -8,6 +8,7 @@ from frappe import _
|
|||
import json
|
||||
from frappe.model.document import Document
|
||||
from frappe.core.doctype.user.user import extract_mentions
|
||||
from frappe.core.doctype.notification_log.notification_log import create_notification_log
|
||||
from frappe.utils import get_fullname, get_link_to_form
|
||||
from frappe.website.render import clear_cache
|
||||
from frappe.database.schema import add_column
|
||||
|
|
@ -67,18 +68,32 @@ class Comment(Document):
|
|||
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']
|
||||
)
|
||||
# from frappe.core.doctype.notification_settings.notification_settings import is_notifications_enabled
|
||||
# if is_notifications_enabled:
|
||||
notification_message = _('''<b>{0}</b> mentioned you in a comment in <b>{1} {2}</b></span>''')\
|
||||
.format(sender_fullname, self.reference_doctype, title)
|
||||
|
||||
notification_doc = {
|
||||
'type': 'Mention',
|
||||
'reference_doctype': self.reference_doctype,
|
||||
'subject': notification_message,
|
||||
'reference_name': self.reference_name,
|
||||
'reference_user': frappe.session.user
|
||||
}
|
||||
create_notification_log(recipients, notification_doc)
|
||||
|
||||
# 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']
|
||||
# )
|
||||
|
||||
|
||||
def on_doctype_update():
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from frappe.model.document import Document
|
|||
from frappe.utils import validate_email_address, get_fullname, strip_html, cstr
|
||||
from frappe.core.doctype.communication.email import (validate_email,
|
||||
notify, _notify, update_parent_mins_to_first_response)
|
||||
from frappe.core.doctype.notification_log.notification_log import create_notification_log
|
||||
from frappe.core.utils import get_parent_doc
|
||||
from frappe.utils.bot import BotReply
|
||||
from frappe.utils import parse_addr
|
||||
|
|
@ -103,6 +104,10 @@ class Communication(Document):
|
|||
doctype=self.reference_doctype, docname=self.reference_name,
|
||||
after_commit=True)
|
||||
|
||||
# from frappe.core.doctype.notification_settings.notification_settings import is_notifications_enabled
|
||||
# if is_notifications_enabled:
|
||||
create_notification(self)
|
||||
|
||||
elif self.communication_type in ("Chat", "Notification", "Bot"):
|
||||
if self.reference_name == frappe.session.user:
|
||||
message = self.as_dict()
|
||||
|
|
@ -413,3 +418,28 @@ def get_email_without_link(email):
|
|||
email_host = email.split("@")[1]
|
||||
|
||||
return "{0}@{1}".format(email_id, email_host)
|
||||
|
||||
def create_notification(self):
|
||||
title_field = frappe.get_meta(self.reference_doctype).get_title_field()
|
||||
title = self.reference_name if title_field == "name" else \
|
||||
frappe.db.get_value(self.reference_doctype, self.reference_name, title_field)
|
||||
|
||||
if self.cc or self.bcc:
|
||||
names = self.cc or '' + self.bcc or ''
|
||||
notification_message = _('''<b>{0}</b> included you in an Email <b>{1}</b>''').format(self.sender_full_name, title)
|
||||
args = {
|
||||
'type': 'Communication',
|
||||
'reference_doctype': self.reference_doctype,
|
||||
'subject': notification_message,
|
||||
'reference_name': self.reference_name
|
||||
}
|
||||
create_notification_log(names, args)
|
||||
notification_message = _('''<b>{0}</b> replied to your Email <b>{1}</b>''').format(self.sender_full_name, title)
|
||||
args = {
|
||||
'type': 'Communication',
|
||||
'reference_doctype': self.reference_doctype,
|
||||
'subject': notification_message,
|
||||
'reference_name': self.reference_name,
|
||||
'reference_user': frappe.session.user
|
||||
}
|
||||
create_notification_log(self.recipients, args)
|
||||
0
frappe/core/doctype/notification_log/__init__.py
Normal file
0
frappe/core/doctype/notification_log/__init__.py
Normal file
10
frappe/core/doctype/notification_log/notification_log.js
Normal file
10
frappe/core/doctype/notification_log/notification_log.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// 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.reference_doctype;
|
||||
let dn = frm.doc.reference_name;
|
||||
frm.fields_dict.reference_name.$input_wrapper.find('.control-value').wrapInner(`<a href='#Form/${dt}/${dn}'></a>`);
|
||||
}
|
||||
});
|
||||
97
frappe/core/doctype/notification_log/notification_log.json
Normal file
97
frappe/core/doctype/notification_log/notification_log.json
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"creation": "2019-08-26 13:37:34.165254",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"user",
|
||||
"type",
|
||||
"subject",
|
||||
"column_break_4",
|
||||
"reference_doctype",
|
||||
"seen",
|
||||
"reference_name",
|
||||
"reference_user"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "type",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Type",
|
||||
"options": "Mention\nEnergy Point\nCommunication\nAssignment",
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "subject",
|
||||
"fieldtype": "Text",
|
||||
"label": "Subject",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "reference_doctype",
|
||||
"fieldtype": "Link",
|
||||
"label": "Reference 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": "user",
|
||||
"fieldtype": "Link",
|
||||
"label": "User",
|
||||
"options": "User",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Reference Name",
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "reference_user",
|
||||
"fieldtype": "Link",
|
||||
"label": "Reference User",
|
||||
"options": "User",
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
}
|
||||
],
|
||||
"in_create": 1,
|
||||
"modified": "2019-09-11 15:45:05.102753",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Notification Log",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
45
frappe/core/doctype/notification_log/notification_log.py
Normal file
45
frappe/core/doctype/notification_log/notification_log.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# -*- 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 NotificationLog(Document):
|
||||
|
||||
def after_insert(self):
|
||||
frappe.publish_realtime('notification', after_commit=True, user=self.user)
|
||||
|
||||
|
||||
def get_permission_query_conditions(user):
|
||||
if not user: user = frappe.session.user
|
||||
|
||||
if user == "Administrator":
|
||||
return ""
|
||||
|
||||
return """(`tabNotification Log`.user = "{user}")""".format(user=user)
|
||||
|
||||
def create_notification_log(names, doc):
|
||||
doc = frappe._dict(doc)
|
||||
if(not isinstance(names, list)):
|
||||
if names:
|
||||
names = filter(None, names.split(', '))
|
||||
for name in names:
|
||||
if frappe.db.exists('User', name.strip()):
|
||||
_doc = frappe.new_doc('Notification Log')
|
||||
_doc.type = doc.type
|
||||
_doc.user = name.strip()
|
||||
_doc.reference_doctype = doc.reference_doctype
|
||||
_doc.reference_name = doc.reference_name
|
||||
_doc.reference_user = doc.reference_user
|
||||
_doc.subject = doc.subject.replace('<div>','').replace('</div>','')
|
||||
_doc.insert(ignore_permissions=True)
|
||||
_doc.save()
|
||||
|
||||
@frappe.whitelist()
|
||||
def set_notification_as_seen(notification_log):
|
||||
notification_log = frappe.parse_json(notification_log)
|
||||
for log in notification_log:
|
||||
frappe.db.set_value('Notification Log', log['name'], 'seen', 1, update_modified=False)
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2019, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestNotificationLog(unittest.TestCase):
|
||||
pass
|
||||
0
frappe/core/doctype/notification_settings/__init__.py
Normal file
0
frappe/core/doctype/notification_settings/__init__.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) 2019, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Notification Settings', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"creation": "2019-09-11 22:15:44.851526",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"enable",
|
||||
"enable_energy_point_notifications",
|
||||
"enable_email_notifications",
|
||||
"subscribed_documents"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "enable",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "enable_energy_point_notifications",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Energy Point Notifications"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "enable_email_notifications",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Email Notifications"
|
||||
},
|
||||
{
|
||||
"fieldname": "subscribed_documents",
|
||||
"fieldtype": "Table MultiSelect",
|
||||
"label": "Subscribed Documents",
|
||||
"options": "Subscribed Documents"
|
||||
}
|
||||
],
|
||||
"in_create": 1,
|
||||
"modified": "2019-09-12 17:32:38.026945",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Notification Settings",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# -*- 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):
|
||||
pass
|
||||
# def validate(self):
|
||||
# create_notification_settings()
|
||||
|
||||
def is_notifications_enabled():
|
||||
return frappe.get_cached_value('Notification Settings', None, 'enable')
|
||||
|
||||
def is_energy_point_notifications_enabled():
|
||||
return frappe.get_cached_value('Notification Settings', None, 'enable_energy_point_notifications')
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_notification_settings():
|
||||
_doc = frappe.new_doc('Notification Settings')
|
||||
_doc.insert(ignore_permissions=True)
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2019, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestNotificationSettings(unittest.TestCase):
|
||||
pass
|
||||
0
frappe/core/doctype/subscribed_documents/__init__.py
Normal file
0
frappe/core/doctype/subscribed_documents/__init__.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"creation": "2019-09-11 20:14:22.433772",
|
||||
"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-09-11 20:14:22.433772",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Subscribed Documents",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -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 SubscribedDocuments(Document):
|
||||
pass
|
||||
|
|
@ -7,6 +7,8 @@ from __future__ import unicode_literals
|
|||
import frappe
|
||||
from frappe import _
|
||||
from frappe.desk.form.document_follow import follow_document
|
||||
from frappe.core.doctype.notification_log.notification_log import create_notification_log
|
||||
import frappe.utils
|
||||
from frappe.utils import cint
|
||||
import frappe.share
|
||||
|
||||
|
|
@ -78,9 +80,11 @@ def add(args=None):
|
|||
# make this document followed by assigned user
|
||||
follow_document(args['doctype'], args['name'], args['assign_to'])
|
||||
|
||||
# notify
|
||||
# from frappe.core.doctype.notification_settings.notification_settings import is_notifications_enabled
|
||||
# if is_notifications_enabled:
|
||||
# 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"), notify=args.get('notify'))
|
||||
|
||||
return get(args)
|
||||
|
||||
|
|
@ -162,7 +166,11 @@ def notify_assignment(assigned_by, owner, doc_type, doc_name, action='CLOSE',
|
|||
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)
|
||||
if action=='CLOSE':
|
||||
print('CLOSED!!!')
|
||||
if owner == frappe.session.get('user'):
|
||||
arg = {
|
||||
'contact': assigned_by,
|
||||
|
|
@ -175,6 +183,15 @@ def notify_assignment(assigned_by, owner, doc_type, doc_name, action='CLOSE',
|
|||
'txt': _("The task {0}, that you assigned to {1}, has been closed by {2}.").format(assignment,
|
||||
owner_name, user_name)
|
||||
}
|
||||
subject = _('<b>Your</b> assignment on <b>{0} {1}</b> has been removed').format(doc_type, title)
|
||||
notification_doc = {
|
||||
'type': 'Assignment',
|
||||
'reference_doctype': doc_type,
|
||||
'subject': subject,
|
||||
'reference_name': doc_name,
|
||||
'reference_user': frappe.session.user
|
||||
}
|
||||
create_notification_log(owner, notification_doc)
|
||||
else:
|
||||
description_html = "<p>{0}</p>".format(description)
|
||||
arg = {
|
||||
|
|
@ -183,6 +200,16 @@ def notify_assignment(assigned_by, owner, doc_type, doc_name, action='CLOSE',
|
|||
user_name, description_html),
|
||||
'notify': notify
|
||||
}
|
||||
user = arg['contact']
|
||||
subject = '''<b>{0}</b> assigned a new task <b>{1} {2}</b> to you'''.format(user_name, doc_type, title)
|
||||
notification_doc = {
|
||||
'type': 'Assignment',
|
||||
'reference_doctype': doc_type,
|
||||
'subject': subject,
|
||||
'reference_name': doc_name,
|
||||
'reference_user': frappe.session.user
|
||||
}
|
||||
create_notification_log(user, notification_doc)
|
||||
|
||||
if arg and cint(arg.get("notify")):
|
||||
_notify(arg)
|
||||
|
|
|
|||
|
|
@ -37,57 +37,57 @@ 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),
|
||||
# "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()
|
||||
# "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)
|
||||
# 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 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)
|
||||
# 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)
|
||||
# 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_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_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])()
|
||||
# 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))
|
||||
# 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
|
||||
# return open_count
|
||||
|
||||
def get_notifications_for_doctypes(config, notification_count):
|
||||
"""Notifications for DocTypes"""
|
||||
|
|
@ -200,7 +200,8 @@ def clear_doctype_notifications(doc, method=None, *args, **kwargs):
|
|||
delete_notification_count_for(doctype)
|
||||
return
|
||||
|
||||
def get_notification_info_for_boot():
|
||||
@frappe.whitelist()
|
||||
def get_notification_info():
|
||||
out = get_notifications()
|
||||
config = get_notification_config()
|
||||
can_read = frappe.get_user().get_can_read()
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ 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.core.doctype.notification_log.notification_log.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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -272,49 +272,49 @@ frappe.Application = Class.extend({
|
|||
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);
|
||||
// this.refresh_notifications = frappe.utils.debounce(this.refresh_notifications.bind(this), 1000);
|
||||
|
||||
// kickoff
|
||||
this.refresh_notifications();
|
||||
// this.refresh_notifications();
|
||||
|
||||
frappe.realtime.on('clear_notifications', () => {
|
||||
me.refresh_notifications();
|
||||
});
|
||||
// frappe.realtime.on('clear_notifications', () => {
|
||||
// me.refresh_notifications();
|
||||
// });
|
||||
|
||||
// first time loaded in boot
|
||||
$(document).trigger("notification-update");
|
||||
// $(document).trigger("notification-update");
|
||||
|
||||
// refresh notifications if user is back after sometime
|
||||
$(document).on("session_alive", function() {
|
||||
me.refresh_notifications();
|
||||
});
|
||||
// $(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");
|
||||
// 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
|
||||
});
|
||||
}
|
||||
},
|
||||
// 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;
|
||||
|
|
|
|||
292
frappe/public/js/frappe/ui/notifications/notifications.js
Normal file
292
frappe/public/js/frappe/ui/notifications/notifications.js
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
|
||||
frappe.ui.Notifications = class Notifications {
|
||||
|
||||
constructor() {
|
||||
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.categories = ['Notifications', 'Todays Events', 'Open Documents'];
|
||||
|
||||
this.render_dropdown_headers();
|
||||
this.$notifications = this.$dropdown_list.find('#notifications');
|
||||
this.$open_docs = this.$dropdown_list.find('#open-documents');
|
||||
this.$todays_events = this.$dropdown_list.find('#todays-events');
|
||||
|
||||
this.setup_notifications();
|
||||
this.setup_todays_events();
|
||||
this.setup_open_document_count();
|
||||
this.bind_events();
|
||||
}
|
||||
|
||||
setup_notifications() {
|
||||
this.get_notifications_list(this.max_length).then(list => {
|
||||
this.dropdown_items = list;
|
||||
this.render_notifications_dropdown();
|
||||
this.setup_view_full_log();
|
||||
if (this.$notifications.find('.unseen').length) {
|
||||
this.$notification_indicator.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setup_todays_events() {
|
||||
this.$dropdown_list.on('click', '.todays-events-header', (e) => {
|
||||
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 `<li class="recent-item event">
|
||||
<a href="#Form/Event/${event.name}">
|
||||
<span class="event-time">${time}</span>
|
||||
<span class="event-subject"> ${event.subject}</span>
|
||||
</a>
|
||||
</li>`;
|
||||
}
|
||||
html = event_list.map(get_event_html).join('');
|
||||
} else {
|
||||
html = `<li class="recent-item text-center">
|
||||
<span class="text-muted">${__('No Events Today')}</span>
|
||||
</li>`;
|
||||
}
|
||||
this.$todays_events.html(html);
|
||||
}
|
||||
|
||||
setup_open_document_count() {
|
||||
this.open_docs_config = {
|
||||
"ToDo": { label: __("To Do") },
|
||||
"Event": { label: __("Calendar"), route: "List/Event/Calendar" },
|
||||
"Email": { label: __("Email"), route: "List/Communication/Inbox" }
|
||||
};
|
||||
this.$dropdown_list.on('click', '.open-documents-header', (e) => {
|
||||
frappe.xcall('frappe.desk.notifications.get_notification_info').then((r) => {
|
||||
this.open_document_list = r;
|
||||
this.render_open_document_count();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render_open_document_count() {
|
||||
let defaults = ["Comment", "ToDo", "Event"];
|
||||
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_notification(key, map[key], doc_dt, target);
|
||||
empty_map = 0;
|
||||
}
|
||||
});
|
||||
|
||||
if(divide && !empty_map) {
|
||||
this.$open_docs.append($('<li class="divider"></li>'));
|
||||
}
|
||||
}
|
||||
|
||||
add_notification(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
|
||||
? $(`<li><a class="badge-hover" href="#" onclick="return false;" data-doctype="${name}" ${title}>${__(label)}
|
||||
<span class="badge pull-right">${value}</span>
|
||||
</a></li>`)
|
||||
: $(`<li><a class="progress-small" href="#" onclick="return false;" ${title} data-doctype="${doc_dt}"
|
||||
data-doc="${name}"><span class="dropdown-item-label">${__(label)}<span>
|
||||
<div class="progress-chart"><div class="progress">
|
||||
<div class="progress-bar" style="width: ${value}%"></div>
|
||||
</div></div>
|
||||
</a></li>`);
|
||||
|
||||
this.$open_docs.append($list_item);
|
||||
if(!target) this.total += value;
|
||||
}
|
||||
|
||||
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('#notifications'));
|
||||
this.$dropdown_list.find('#notifications').collapse('show')
|
||||
}
|
||||
|
||||
|
||||
check_seen() {
|
||||
let unseen_logs = this.dropdown_items.filter(item => item.seen === 0);
|
||||
frappe.call(
|
||||
'frappe.core.doctype.notification_log.notification_log.set_notification_as_seen',
|
||||
{notification_log: unseen_logs});
|
||||
}
|
||||
|
||||
get_notifications_list(limit) {
|
||||
return frappe.db.get_list('Notification Log', {
|
||||
fields:
|
||||
['*'],
|
||||
limit: limit,
|
||||
order_by: 'creation desc'
|
||||
}).then((notifications_list) => {
|
||||
return notifications_list;
|
||||
});
|
||||
}
|
||||
|
||||
render_notifications_dropdown() {
|
||||
let body_html = '';
|
||||
let view_full_log_html = '';
|
||||
|
||||
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 = `<li class="recent-item text-center"><a class="text-muted full-log-btn">${__('View Full Log')}</a></li>`;
|
||||
} else {
|
||||
body_html += `<li class="recent-item text-center"><a href="#" onclick = "return false" class="text-muted">${__('No activity')}</a></li>`;
|
||||
}
|
||||
let dropdown_html = body_html + view_full_log_html;
|
||||
this.$notifications.append(dropdown_html);
|
||||
}
|
||||
|
||||
get_dropdown_item_html(field) {
|
||||
let doc_link = frappe.utils.get_form_link(field.reference_doctype, field.reference_name);
|
||||
let seen_class = field.seen? '': 'unseen';
|
||||
let message = field.subject;
|
||||
let message_html = `<div class="message">${message}</div>`;
|
||||
let user = field.reference_user;
|
||||
let user_avatar = frappe.avatar(user, 'avatar-small user-avatar');
|
||||
let timestamp = frappe.datetime.comment_when(field.creation, true);
|
||||
let item_html = `<li class="recent-item ${seen_class}">
|
||||
${user_avatar}
|
||||
<a class= "message-link" href = "${doc_link}">
|
||||
${message_html}
|
||||
</a>
|
||||
<div class="notification-timestamp text-muted">
|
||||
${timestamp}
|
||||
</div>
|
||||
</li>`;
|
||||
|
||||
return item_html;
|
||||
}
|
||||
|
||||
|
||||
setup_view_full_log() {
|
||||
this.$dropdown_list.find('.full-log-btn').on('click', () => {
|
||||
frappe.set_route('List', 'Notification Log');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
render_dropdown_headers() {
|
||||
let get_headers_html = (category) => {
|
||||
let category_id = frappe.scrub(category, '-');
|
||||
let category_header_class = frappe.scrub(category, '-') + '-header';
|
||||
return `<li class="notifications-category">
|
||||
<li class="text-muted header h6 uppercase ${category_header_class}"
|
||||
href="#${category_id}"
|
||||
data-toggle="collapse">
|
||||
${category}
|
||||
<span class="octicon octicon-chevron-down collapse-indicator"></span>
|
||||
</li>
|
||||
<div id = "${category_id}" class="collapse"></div>
|
||||
</li>`;
|
||||
}
|
||||
|
||||
let html = this.categories.map(get_headers_html).join('<li class="divider"></li>');
|
||||
this.$dropdown_list.append(html);
|
||||
}
|
||||
|
||||
bind_events() {
|
||||
frappe.realtime.on('notification', () => {
|
||||
this.$dropdown.find('.notifications-indicator').show();
|
||||
this.update_dropdown();
|
||||
});
|
||||
|
||||
let me = this;
|
||||
this.$dropdown.on('hide.bs.dropdown', function(e) {
|
||||
me.$notification_indicator.hide();
|
||||
let hide = $(this).data('closable');
|
||||
me.$dropdown_list.find('.unseen').removeClass('unseen');
|
||||
$(this).data('closable', true);
|
||||
return hide;
|
||||
});
|
||||
|
||||
this.$dropdown.on('show.bs.dropdown', () => {
|
||||
this.check_seen();
|
||||
});
|
||||
|
||||
this.$dropdown.on('click', function(e) {
|
||||
if ($(e.target).closest('.dropdown-toggle').length) {
|
||||
$(this).data('closable', true);
|
||||
} else {
|
||||
$(this).data('closable', false);
|
||||
}
|
||||
});
|
||||
|
||||
this.$dropdown_list.on('click', '.recent-item', (e) => {
|
||||
this.$dropdown.removeClass('open');
|
||||
});
|
||||
|
||||
this.$dropdown.find(".header").on('click', function(e) {
|
||||
let hide = me.$dropdown.find('.header').next().hasClass("in");
|
||||
$(this).find('.collapse-indicator').toggleClass("octicon-chevron-down", hide);
|
||||
$(this).find('.collapse-indicator').toggleClass("octicon-chevron-up", !hide);
|
||||
});
|
||||
|
||||
this.$open_docs.on('click', 'li a', function() {
|
||||
var doctype = $(this).attr('data-doctype');
|
||||
var doc = $(this).attr('data-doc');
|
||||
if(!doc) {
|
||||
var config = me.open_docs_config[doctype] || {};
|
||||
console.log('config', config)
|
||||
if (config.route) {
|
||||
frappe.set_route(config.route);
|
||||
} else if (config.click) {
|
||||
config.click();
|
||||
} else {
|
||||
me.show_open_count_list(doctype);
|
||||
}
|
||||
} else {
|
||||
frappe.set_route("Form", doctype, doc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -86,7 +86,24 @@
|
|||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="dropdown dropdown-navbar-new-comments dropdown-mobile">
|
||||
<li class="dropdown dropdown-notifications dropdown-mobile">
|
||||
<span class="notifications-indicator"><i class="fa fa-circle"></i></span>
|
||||
<a
|
||||
class="dropdown-toggle notifications-icon"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="true"
|
||||
href="#"
|
||||
onclick="return false;">
|
||||
<span><i class="fa fa-bell" aria-hidden="true"></i></span>
|
||||
</a>
|
||||
<ul
|
||||
class="dropdown-menu notifications-list"
|
||||
role="menu">
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<!-- <li class="dropdown dropdown-navbar-new-comments dropdown-mobile">
|
||||
<a
|
||||
class="btn dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
|
|
@ -101,7 +118,7 @@
|
|||
id="dropdown-notification"
|
||||
role="menu">
|
||||
</ul>
|
||||
</li>
|
||||
</li> -->
|
||||
</ul>
|
||||
|
||||
<div id="search-modal" class="modal fade" role="dialog">
|
||||
|
|
|
|||
|
|
@ -1,103 +1,103 @@
|
|||
frappe.provide("frappe.ui.notifications");
|
||||
// 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" }
|
||||
},
|
||||
// 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"];
|
||||
// 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);
|
||||
// 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);
|
||||
// // 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();
|
||||
// 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);
|
||||
},
|
||||
// // 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($('<li class="divider"></li>'));
|
||||
}
|
||||
},
|
||||
// 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($('<li class="divider"></li>'));
|
||||
// }
|
||||
// },
|
||||
|
||||
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
|
||||
? $(`<li><a class="badge-hover" href="#" onclick="return false;" data-doctype="${name}" ${title}>${__(label)}
|
||||
<span class="badge pull-right">${value}</span>
|
||||
</a></li>`)
|
||||
: $(`<li><a class="progress-small" href="#" onclick="return false;" ${title} data-doctype="${doc_dt}"
|
||||
data-doc="${name}"><span class="dropdown-item-label">${__(label)}<span>
|
||||
<div class="progress-chart"><div class="progress">
|
||||
<div class="progress-bar" style="width: ${value}%"></div>
|
||||
</div></div>
|
||||
</a></li>`);
|
||||
this.dropdown.append($list_item);
|
||||
if(!target) this.total += value;
|
||||
},
|
||||
// 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
|
||||
// ? $(`<li><a class="badge-hover" href="#" onclick="return false;" data-doctype="${name}" ${title}>${__(label)}
|
||||
// <span class="badge pull-right">${value}</span>
|
||||
// </a></li>`)
|
||||
// : $(`<li><a class="progress-small" href="#" onclick="return false;" ${title} data-doctype="${doc_dt}"
|
||||
// data-doc="${name}"><span class="dropdown-item-label">${__(label)}<span>
|
||||
// <div class="progress-chart"><div class="progress">
|
||||
// <div class="progress-bar" style="width: ${value}%"></div>
|
||||
// </div></div>
|
||||
// </a></li>`);
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
},
|
||||
// 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);
|
||||
},
|
||||
};
|
||||
// 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);
|
||||
// },
|
||||
// };
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ frappe.ui.toolbar.Toolbar = Class.extend({
|
|||
awesome_bar.setup("#modal-search");
|
||||
|
||||
this.setup_energy_point_notifications();
|
||||
|
||||
this.setup_notifications();
|
||||
this.make();
|
||||
},
|
||||
|
||||
|
|
@ -30,9 +30,9 @@ frappe.ui.toolbar.Toolbar = Class.extend({
|
|||
},
|
||||
|
||||
bind_events: function() {
|
||||
$(document).on("notification-update", function() {
|
||||
frappe.ui.notifications.update_notifications();
|
||||
});
|
||||
// $(document).on("notification-update", function() {
|
||||
// frappe.ui.notifications.update_notifications();
|
||||
// });
|
||||
|
||||
// clear all custom menus on page change
|
||||
$(document).on("page-change", function() {
|
||||
|
|
@ -168,6 +168,17 @@ frappe.ui.toolbar.Toolbar = Class.extend({
|
|||
} else {
|
||||
$('.dropdown-energy-points').hide();
|
||||
}
|
||||
},
|
||||
|
||||
setup_notifications: function() {
|
||||
// if (frappe.boot.notifications_enabled) {
|
||||
$('.dropdown-notifications').show();
|
||||
console.log('called');
|
||||
this.notifications = new frappe.ui.Notifications();
|
||||
console.log(this.notifications)
|
||||
// } else {
|
||||
// $('.dropdown-notifications').hide();
|
||||
// }
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
|||
99
frappe/public/less/notifications.less
Normal file
99
frappe/public/less/notifications.less
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
@import "variables.less";
|
||||
@import "mixins.less";
|
||||
|
||||
|
||||
.dropdown-notifications .header {
|
||||
margin: 8px 15px;
|
||||
cursor: pointer;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.collapse-indicator {
|
||||
padding: 0px 5px;
|
||||
color: #d1d8dd;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
.recent-item {
|
||||
padding: 14px;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.recent-item > a {
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.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: 5px 0 0px 34px;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.message-link {
|
||||
display: flow-root;
|
||||
}
|
||||
|
|
@ -113,8 +113,8 @@ def clear_expired_sessions():
|
|||
|
||||
def get():
|
||||
"""get session boot info"""
|
||||
from frappe.desk.notifications import \
|
||||
get_notification_info_for_boot, get_notifications
|
||||
# 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 +123,14 @@ def get():
|
|||
bootinfo = frappe.cache().hget("bootinfo", frappe.session.user)
|
||||
if bootinfo:
|
||||
bootinfo['from_cache'] = 1
|
||||
bootinfo["notification_info"].update(get_notifications())
|
||||
# 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()
|
||||
# bootinfo["notification_info"] = get_notification_info_for_boot()
|
||||
frappe.cache().hset("bootinfo", frappe.session.user, bootinfo)
|
||||
try:
|
||||
frappe.cache().ping()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import frappe
|
|||
from frappe import _
|
||||
import json
|
||||
from frappe.model.document import Document
|
||||
from frappe.core.doctype.notification_log.notification_log import create_notification_log
|
||||
from frappe.utils import cint, get_fullname, getdate, get_link_to_form
|
||||
|
||||
class EnergyPointLog(Document):
|
||||
|
|
@ -22,6 +23,9 @@ class EnergyPointLog(Document):
|
|||
['reference_type', 'reference_name'])
|
||||
|
||||
def after_insert(self):
|
||||
# from frappe.core.doctype.notification_settings.notification_settings import is_notifications_enabled,\
|
||||
# is_energy_point_notifications_enabled
|
||||
# if is_notifications_enabled and is_energy_point_notifications_enabled:
|
||||
alert_dict = get_alert_dict(self)
|
||||
if alert_dict:
|
||||
frappe.publish_realtime('energy_point_alert', message=alert_dict, user=self.user)
|
||||
|
|
@ -31,7 +35,51 @@ class EnergyPointLog(Document):
|
|||
frappe.publish_realtime('update_points', after_commit=True)
|
||||
|
||||
if self.type != 'Review':
|
||||
reference_user = self.user if self.type == 'Auto' else self.owner
|
||||
frappe.publish_realtime('energy_points_notification', after_commit=True, user=self.user)
|
||||
notification_doc = {
|
||||
'type': 'Energy Point',
|
||||
'reference_doctype': self.reference_doctype,
|
||||
'reference_name': self.reference_name,
|
||||
'subject': get_notifications_message(self),
|
||||
'reference_user': reference_user
|
||||
}
|
||||
create_notification_log(self.user, notification_doc)
|
||||
|
||||
def get_notifications_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)
|
||||
print(title, 'titlee')
|
||||
if doc.type == 'Auto':
|
||||
if points == 1:
|
||||
message = _('<b>You</b> gained <b>{0}</b> point for {1} <b>{2}</b>')
|
||||
else:
|
||||
message = _('<b>You</b> gained <b>{0}</b> points for {1} <b>{2}</b>')
|
||||
message = message.format(points, doc.rule, title)
|
||||
elif doc.type == 'Appreciation':
|
||||
if points == 1:
|
||||
message = _('<b>{0}</b> appreciated your work on <b>{1}</b> with <b>{2}</b> point')
|
||||
else:
|
||||
message = _('<b>{0}</b> appreciated your work on <b>{1}</b> with <b>{2}</b> points')
|
||||
message = message.format(owner_name, title, points)
|
||||
elif doc.type == 'Criticism':
|
||||
if points == 1:
|
||||
message = _('<b>{0}</b> criticized your work on <b>{1}</b> with <b>{2}</b> point')
|
||||
else:
|
||||
message = _('<b>{0}</b> criticized your work on <b>{1}</b> with <b>{2}</b> points')
|
||||
|
||||
message = message.format(owner_name, title, points)
|
||||
elif doc.type == 'Revert':
|
||||
if points == 1:
|
||||
message = _('<b>{0}</b> reverted your point on <b>{1}</b>')
|
||||
else:
|
||||
message = _('<b>{0}</b> reverted your points on <b>{1}</b>')
|
||||
message = message.format(owner_name, title)
|
||||
|
||||
return message
|
||||
|
||||
def get_alert_dict(doc):
|
||||
alert_dict = frappe._dict()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue