diff --git a/frappe/core/doctype/comment/comment.py b/frappe/core/doctype/comment/comment.py
index fcd32c52c9..3d8dbc1ced 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.desk.doctype.notification_log.notification_log import enqueue_create_notification
+from frappe.desk.doctype.notification_log.notification_log import enqueue_create_notification,\
+ get_title, get_title_html
from frappe.utils import get_fullname
from frappe.website.render import clear_cache
from frappe.database.schema import add_column
@@ -51,15 +52,13 @@ class Comment(Document):
return
sender_fullname = get_fullname(frappe.session.user)
- 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)
+ title = get_title(self.reference_doctype, self.reference_name)
recipients = [frappe.db.get_value("User", {"enabled": 1, "name": name, "user_type": "System User", "allowed_in_mentions": 1}, "email")
for name in mentions]
notification_message = _('''{0} mentioned you in a comment in {1} {2}''')\
- .format(frappe.bold(sender_fullname), frappe.bold(self.reference_doctype), frappe.bold(title))
+ .format(frappe.bold(sender_fullname), frappe.bold(self.reference_doctype), get_title_html(title))
notification_doc = {
'type': 'Mention',
diff --git a/frappe/desk/doctype/notification_log/notification_log.py b/frappe/desk/doctype/notification_log/notification_log.py
index 8f43cdc2b9..c187a8a392 100644
--- a/frappe/desk/doctype/notification_log/notification_log.py
+++ b/frappe/desk/doctype/notification_log/notification_log.py
@@ -50,13 +50,17 @@ def make_notification_logs(doc, users):
_doc.update(doc)
_doc.for_user = user
_doc.subject = _doc.subject.replace('
', '').replace('
', '')
- _doc.insert(ignore_permissions=True)
+ if _doc.for_user != _doc.from_user or doc.type == 'Energy Point':
+ _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
+ if doc.type == 'Energy Point' and doc.email_content is None:
+ return
+
from frappe.utils import get_url_to_form, strip_html
doc_link = get_url_to_form(doc.document_type, doc.document_name)
@@ -88,10 +92,22 @@ def get_email_header(doc):
}[doc.type or 'Default']
+def get_title(doctype, docname, title_field=None):
+ if not title_field:
+ title_field = frappe.get_meta(doctype).get_title_field()
+ title = docname if title_field == "name" else \
+ frappe.db.get_value(doctype, docname, title_field)
+ return title
+
+def get_title_html(title):
+ return '{0}'.format(title)
+
@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)
- frappe.publish_realtime('seen_notification', after_commit=True, user=frappe.session.user)
+def mark_as_seen(docname):
+ if docname:
+ frappe.db.set_value('Notification Log', docname, 'seen', 1, update_modified=False)
+
+
+@frappe.whitelist()
+def trigger_indicator_hide():
+ frappe.publish_realtime('indicator_hide', user=frappe.session.user)
diff --git a/frappe/desk/form/assign_to.py b/frappe/desk/form/assign_to.py
index 5841f9474a..9714f31d1f 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.desk.doctype.notification_log.notification_log import enqueue_create_notification
+from frappe.desk.doctype.notification_log.notification_log import enqueue_create_notification,\
+ get_title, get_title_html
import frappe.utils
import frappe.share
@@ -160,17 +161,15 @@ def notify_assignment(assigned_by, owner, doc_type, doc_name, action='CLOSE',
# Search for email address in description -- i.e. assignee
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)
+ title = get_title(doc_type, doc_name)
description_html = "{0}
".format(description) if description else None
if action=='CLOSE':
- subject = _('Your assignment on {0} {1} has been removed').format(frappe.bold(doc_type), frappe.bold(title))
+ subject = _('Your assignment on {0} {1} has been removed').format(frappe.bold(doc_type), get_title_html(title))
else:
user_name = frappe.bold(user_name)
document_type = frappe.bold(doc_type)
- title = frappe.bold(title)
+ title = get_title_html(title)
subject = _('{0} assigned a new task {1} {2} to you').format(user_name, document_type, title)
notification_doc = {
diff --git a/frappe/public/js/frappe/ui/notifications/notifications.js b/frappe/public/js/frappe/ui/notifications/notifications.js
index e0915e927c..8c6697e350 100644
--- a/frappe/public/js/frappe/ui/notifications/notifications.js
+++ b/frappe/public/js/frappe/ui/notifications/notifications.js
@@ -24,8 +24,8 @@ frappe.ui.Notifications = class 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"]'
+ this.$today_events = this.$dropdown_list.find(
+ '.category-list[data-category="Todays Events"]'
);
frappe.utils.bind_actions_with_object(this.$dropdown_list, this);
@@ -44,18 +44,13 @@ frappe.ui.Notifications = class Notifications {
});
}
- render_upcoming_events(e, $target) {
+ render_todays_events(e, $target) {
let hide = $target.next().hasClass('in');
if (!hide) {
let today = frappe.datetime.get_today();
- let tomorrow = frappe.datetime.add_days(today, 1);
- frappe.db.get_list('Event', {
- fields: ['name', 'subject', 'starts_on'],
- filters: [
- {'starts_on': ['between', today, tomorrow]},
- {'ends_on': ['>=', frappe.datetime.now_datetime()]},
- {'owner': frappe.session.user}
- ]
+ frappe.xcall('frappe.desk.doctype.event.event.get_events', {
+ start: today,
+ end: today
}).then(event_list => {
this.render_events_html(event_list);
});
@@ -75,11 +70,11 @@ frappe.ui.Notifications = class Notifications {
html = event_list.map(get_event_html).join('');
} else {
html = `
- ${__('No Upcoming Events')}
+ ${__('No Events Today')}
`;
}
- this.$upcoming_events.html(html);
+ this.$today_events.html(html);
}
get_open_document_config(e) {
@@ -222,23 +217,34 @@ frappe.ui.Notifications = class Notifications {
change_activity_status() {
if (this.$dropdown_list.find('.activity-status')) {
this.$dropdown_list.find('.activity-status').replaceWith(
- `
- ${__('View Full Log')}
+ ${__('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;
+ set_field_as_seen(docname, $el) {
frappe.call(
'frappe.desk.doctype.notification_log.notification_log.mark_as_seen',
- { docnames: unseen_docnames }
- );
+ { docname: docname }
+ ).then(()=> {
+ $el.removeClass('unseen');
+ });
+ }
+
+ explicitly_mark_as_seen(e, $target) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ let docname = $target.parents('.unseen').attr('data-name');
+ this.set_field_as_seen(docname, $target.parents('.unseen'));
+ }
+
+ mark_as_seen(e, $target) {
+ let docname = $target.attr('data-name');
+ let df = this.dropdown_items.filter(f => docname.includes(f.name))[0];
+ this.set_field_as_seen(df.name, $target);
}
get_notifications_list(limit) {
@@ -265,9 +271,9 @@ frappe.ui.Notifications = class Notifications {
let item_html = this.get_dropdown_item_html(field);
if (item_html) body_html += item_html;
});
- view_full_log_html = `
- ${__('View Full Log')}
+ ${__('View Full Log')}
`;
} else {
body_html += `
@@ -287,18 +293,29 @@ frappe.ui.Notifications = class Notifications {
field.document_name
);
let seen_class = field.seen ? '' : 'unseen';
+ let mark_seen_action = field.seen ? '': 'data-action="mark_as_seen"';
let message = field.subject;
+ let title = message.match(/(.*?)<\/b>/);
+ message = title ? message.replace(title[1], frappe.ellipsis(title[1], 100)): message;
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 = `
+ let item_html =
+ `
${user_avatar}
${message_html}
${timestamp}
- `;
+
+ ${__('Mark as Read')}
+
+ `;
return item_html;
}
@@ -306,18 +323,18 @@ frappe.ui.Notifications = class Notifications {
render_dropdown_headers() {
this.categories = [
{
- label: __('Notifications'),
- value: 'Notifications'
+ label: __("Notifications"),
+ value: "Notifications"
},
{
- label: __('Upcoming Events'),
- value: 'Upcoming Events',
- action: 'render_upcoming_events'
+ label: __("Today's Events"),
+ value: "Todays Events",
+ action: "render_todays_events"
},
{
- label: __('Open Documents'),
- value: 'Open Documents',
- action: 'get_open_document_config'
+ label: __("Open Documents"),
+ value: "Open Documents",
+ action: "get_open_document_config"
}
];
@@ -377,8 +394,8 @@ frappe.ui.Notifications = class Notifications {
}
bind_events() {
- this.setup_notification_listeners();
this.setup_dropdown_events();
+ this.setup_notification_listeners();
this.$dropdown_list.on('click', '.recent-item', () => {
this.$dropdown.removeClass('open');
@@ -399,7 +416,7 @@ frappe.ui.Notifications = class Notifications {
this.update_dropdown();
});
- frappe.realtime.on('seen_notification', () => {
+ frappe.realtime.on('indicator_hide', () => {
this.$dropdown.find('.notifications-indicator').hide();
});
}
@@ -407,7 +424,7 @@ frappe.ui.Notifications = class Notifications {
setup_dropdown_events() {
this.$dropdown_list
.find(
- '[data-category="Notifications"], [data-category="Upcoming Events"], [data-category="Open Documents"]'
+ '[data-category="Notifications"], [data-category="Todays Events"], [data-category="Open Documents"]'
)
.collapse({
toggle: false
@@ -420,17 +437,20 @@ frappe.ui.Notifications = class Notifications {
.collapse('show');
this.$dropdown_list
.find(
- '[data-category="Upcoming Events"], [data-category="Open Documents"]'
+ '[data-category="Todays 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();
+ if (this.$notification_indicator.is(':visible')) {
+ frappe.call(
+ 'frappe.desk.doctype.notification_log.notification_log.trigger_indicator_hide'
+ );
+ }
});
this.$dropdown.on('click', e => {
diff --git a/frappe/public/less/notifications.less b/frappe/public/less/notifications.less
index adc15ec3ae..9c40a90a71 100644
--- a/frappe/public/less/notifications.less
+++ b/frappe/public/less/notifications.less
@@ -52,14 +52,30 @@
display: flow-root;
}
+.category-list[data-category="Notifications"] .recent-item {
+ padding: 15px 14px 0 14px;
+}
+
+.full-log-btn {
+ padding-bottom: 10px;
+}
+
a.recent-item:hover {
background-color: #f0f4f7;
}
-.dropdown-energy-points .energy-points-icon {
- height: 40px;
- font-size: 14px;
- text-align: center;
+a.unseen:hover .mark-read {
+ display: inline-block;
+}
+
+.mark-read {
+ display: none;
+ margin-left: 10px;
+ font-size: 11px;
+}
+
+.mark-read:hover {
+ text-decoration: underline;
}
.notifications-list {
@@ -68,15 +84,6 @@ a.recent-item:hover {
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;
@@ -89,6 +96,8 @@ a.recent-item:hover {
.notification-timestamp {
margin-top: 5px;
font-size: 11px;
+ display: inline-block;
+ margin-bottom: 10px;
}
.user-avatar {
diff --git a/frappe/share.py b/frappe/share.py
index c8471bfe2c..3875870949 100644
--- a/frappe/share.py
+++ b/frappe/share.py
@@ -5,7 +5,8 @@ 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.desk.doctype.notification_log.notification_log import enqueue_create_notification,\
+ get_title, get_title_html
from frappe.utils import cint
@frappe.whitelist()
@@ -152,13 +153,11 @@ def notify_assignment(shared_by, doctype, doc_name, everyone):
from frappe.utils import get_fullname
- 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)
+ title = get_title(doctype, doc_name)
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))
+ frappe.bold(reference_user), frappe.bold(doctype), get_title_html(title))
notification_doc = {
'type': 'Share',
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 c347c81b1e..724117df2e 100644
--- a/frappe/social/doctype/energy_point_log/energy_point_log.py
+++ b/frappe/social/doctype/energy_point_log/energy_point_log.py
@@ -7,7 +7,8 @@ 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.desk.doctype.notification_log.notification_log import enqueue_create_notification,\
+ get_title, get_title_html
from frappe.utils import cint, get_fullname, getdate, get_link_to_form
class EnergyPointLog(Document):
@@ -38,7 +39,7 @@ class EnergyPointLog(Document):
'document_name': self.reference_name,
'subject': get_notification_message(self),
'from_user': reference_user,
- 'email_content': '{}
'.format(self.reason)
+ 'email_content': '{}
'.format(self.reason) if self.reason else None
}
enqueue_create_notification(self.user, notification_doc)
@@ -46,9 +47,7 @@ class EnergyPointLog(Document):
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)
+ title = get_title(doc.reference_doctype, doc.reference_name)
if doc.type == 'Auto':
owner_name = frappe.bold('You')
@@ -56,26 +55,26 @@ def get_notification_message(doc):
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))
+ message = message.format(owner_name, frappe.bold(points), doc.rule, get_title_html(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))
+ message = message.format(frappe.bold(owner_name), get_title_html(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))
+ message = message.format(frappe.bold(owner_name), get_title_html(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))
+ message = message.format(frappe.bold(owner_name), get_title_html(title))
return message