diff --git a/frappe/__init__.py b/frappe/__init__.py index 30e4e1d3b3..3efa7b54b4 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -1052,7 +1052,9 @@ def publish_realtime(*args, **kwargs): :param room: Room in which to publish update (default entire site) :param user: Transmit to user :param doctype: Transmit to doctype, docname - :param docname: Transmit to doctype, docname""" + :param docname: Transmit to doctype, docname + :param after_commit: (default False) will emit after current transaction is committed + """ import frappe.async return frappe.async.publish_realtime(*args, **kwargs) diff --git a/frappe/__version__.py b/frappe/__version__.py index 08963105ce..43a4eeb246 100644 --- a/frappe/__version__.py +++ b/frappe/__version__.py @@ -1,2 +1,2 @@ from __future__ import unicode_literals -__version__ = "6.16.4" +__version__ = "6.17.6" diff --git a/frappe/async.py b/frappe/async.py index a27bb7ee0e..b965bd0b98 100644 --- a/frappe/async.py +++ b/frappe/async.py @@ -97,7 +97,7 @@ def is_file_old(file_path): return ((time.time() - os.stat(file_path).st_mtime) > TASK_LOG_MAX_AGE) -def publish_realtime(event=None, message=None, room=None, user=None, doctype=None, docname=None, now=False): +def publish_realtime(event=None, message=None, room=None, user=None, doctype=None, docname=None, after_commit=False): """Publish real-time updates :param event: Event name, like `task_progress` etc. that will be handled by the client (default is `task_progress` if within task or `global`) @@ -105,23 +105,24 @@ def publish_realtime(event=None, message=None, room=None, user=None, doctype=Non :param room: Room in which to publish update (default entire site) :param user: Transmit to user :param doctype: Transmit to doctype, docname - :param docname: Transmit to doctype, docname""" + :param docname: Transmit to doctype, docname + :param after_commit: (default False) will emit after current transaction is committed""" if message is None: message = {} if event is None: - if frappe.local.task_id: + if getattr(frappe.local, "task_id", None): event = "task_progress" else: event = "global" if not room: - if frappe.local.task_id: + if getattr(frappe.local, "task_id", None): room = get_task_progress_room() if not "task_id" in message: message["task_id"] = frappe.local.task_id - now = True + after_commit = False elif user: room = get_user_room(user) elif doctype and docname: @@ -129,10 +130,10 @@ def publish_realtime(event=None, message=None, room=None, user=None, doctype=Non else: room = get_site_room() - if now: - emit_via_redis(event, message, room) - else: + if after_commit: frappe.local.realtime_log.append([event, message, room]) + else: + emit_via_redis(event, message, room) def emit_via_redis(event, message, room): """Publish real-time updates via redis @@ -159,7 +160,7 @@ def put_log(line_no, line, task_id=None): "lines": {line_no: line} }, "task_id": task_id - }, room=task_progress_room, now=True) + }, room=task_progress_room) r.hset(task_log_key, line_no, line) r.expire(task_log_key, 3600) diff --git a/frappe/change_log/v6/v6_17_0.md b/frappe/change_log/v6/v6_17_0.md new file mode 100644 index 0000000000..f6606603f0 --- /dev/null +++ b/frappe/change_log/v6/v6_17_0.md @@ -0,0 +1,4 @@ +- Ability to **Like** a document, comment or communication + - See notifications about likes that you received + - View it on Activity feed + - *Stars* have been converted to Likes diff --git a/frappe/commands.py b/frappe/commands.py index 5537118355..0a19a220dd 100644 --- a/frappe/commands.py +++ b/frappe/commands.py @@ -218,6 +218,7 @@ def migrate(context, rebuild_website=False): clear_notifications() finally: + frappe.publish_realtime("version-update") frappe.destroy() if rebuild_website: @@ -225,6 +226,7 @@ def migrate(context, rebuild_website=False): else: call_command(sync_www, context) + def prepare_for_update(): from frappe.sessions import clear_global_cache clear_global_cache() diff --git a/frappe/core/doctype/comment/comment.json b/frappe/core/doctype/comment/comment.json index fd8d0e45f0..2405dcdd90 100644 --- a/frappe/core/doctype/comment/comment.json +++ b/frappe/core/doctype/comment/comment.json @@ -1,338 +1,338 @@ { - "allow_copy": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "hash", - "creation": "2012-08-08 10:40:11", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", + "allow_copy": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "hash", + "creation": "2012-08-08 10:40:11", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "comment", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Comment", - "length": 0, - "no_copy": 0, - "oldfieldname": "comment", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "comment", + "fieldtype": "Text", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Comment", + "length": 0, + "no_copy": 0, + "oldfieldname": "comment", + "oldfieldtype": "Text", + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "comment_type", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Comment Type", - "length": 0, - "no_copy": 0, - "options": "Email\nChat\nPhone\nSMS\nCreated\nSubmitted\nCancelled\nAssigned\nAssignment Completed\nComment\nWorkflow\nLabel\nAttachment\nAttachment Removed", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "comment_type", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Comment Type", + "length": 0, + "no_copy": 0, + "options": "Email\nChat\nPhone\nSMS\nCreated\nSubmitted\nCancelled\nAssigned\nAssignment Completed\nComment\nWorkflow\nLabel\nAttachment\nAttachment Removed\nLike", + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "comment_by", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Comment By", - "length": 0, - "no_copy": 0, - "oldfieldname": "comment_by", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "comment_by", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Comment By", + "length": 0, + "no_copy": 0, + "oldfieldname": "comment_by", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "comment_by_fullname", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Comment By Fullname", - "length": 0, - "no_copy": 0, - "oldfieldname": "comment_by_fullname", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "comment_by_fullname", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Comment By Fullname", + "length": 0, + "no_copy": 0, + "oldfieldname": "comment_by_fullname", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "comment_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Comment Date", - "length": 0, - "no_copy": 0, - "oldfieldname": "comment_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "comment_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Comment Date", + "length": 0, + "no_copy": 0, + "oldfieldname": "comment_date", + "oldfieldtype": "Date", + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "comment_time", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Comment Time", - "length": 0, - "no_copy": 0, - "oldfieldname": "comment_time", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "comment_time", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Comment Time", + "length": 0, + "no_copy": 0, + "oldfieldname": "comment_time", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "comment_doctype", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Comment Doctype", - "length": 0, - "no_copy": 0, - "oldfieldname": "comment_doctype", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "comment_doctype", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Comment Doctype", + "length": 0, + "no_copy": 0, + "oldfieldname": "comment_doctype", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "comment_docname", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Comment Docname", - "length": 0, - "no_copy": 0, - "oldfieldname": "comment_docname", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "comment_docname", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Comment Docname", + "length": 0, + "no_copy": 0, + "oldfieldname": "comment_docname", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "post_topic", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Post Topic", - "length": 0, - "no_copy": 0, - "oldfieldname": "post_topic", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "post_topic", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Post Topic", + "length": 0, + "no_copy": 0, + "oldfieldname": "post_topic", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "unsubscribed", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Unsubscribed", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "unsubscribed", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Unsubscribed", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "reference_doctype", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Reference DocType", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "reference_doctype", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Reference DocType", + "length": 0, + "no_copy": 0, + "options": "DocType", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "description": "Reference DocType and Reference Name are used to render a comment as a link (href) to a Doc.", - "fieldname": "reference_name", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Reference Name", - "length": 0, - "no_copy": 0, - "options": "reference_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "description": "Reference DocType and Reference Name are used to render a comment as a link (href) to a Doc.", + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Reference Name", + "length": 0, + "no_copy": 0, + "options": "reference_doctype", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "icon-comments", - "idx": 1, - "in_create": 0, - "in_dialog": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2015-11-16 06:29:43.314568", - "modified_by": "Administrator", - "module": "Core", - "name": "Comment", - "owner": "Administrator", + ], + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "icon-comments", + "idx": 1, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2015-12-26 06:29:43.314568", + "modified_by": "Administrator", + "module": "Core", + "name": "Comment", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 } - ], - "read_only": 0, - "read_only_onload": 0, + ], + "read_only": 0, + "read_only_onload": 0, "title_field": "comment" -} \ No newline at end of file +} diff --git a/frappe/core/doctype/comment/comment.py b/frappe/core/doctype/comment/comment.py index 70d71b8c0e..068687f55c 100644 --- a/frappe/core/doctype/comment/comment.py +++ b/frappe/core/doctype/comment/comment.py @@ -23,8 +23,8 @@ class Comment(Document): if self.comment_type in ("Created", "Submitted", "Cancelled", "Label"): comment_type = "Label" - elif self.comment_type == "Comment": - comment_type = "Comment" + elif self.comment_type in ("Comment", "Like"): + comment_type = self.comment_type else: comment_type = "Info" @@ -32,7 +32,9 @@ class Comment(Document): "subject": self.comment, "doctype": self.comment_doctype, "name": self.comment_docname, - "feed_type": comment_type + "feed_type": comment_type, + "reference_doctype": self.reference_doctype, + "reference_name": self.reference_name } def after_insert(self): @@ -44,13 +46,15 @@ class Comment(Document): if self.comment_docname == frappe.session.user: message = self.as_dict() message['broadcast'] = True - frappe.publish_realtime('new_message', message) + frappe.publish_realtime('new_message', message, after_commit=True) else: # comment_docname contains the user who is addressed in the messages' page comment - frappe.publish_realtime('new_message', self.as_dict(), user=self.comment_docname) + frappe.publish_realtime('new_message', self.as_dict(), + user=self.comment_docname, after_commit=True) else: - frappe.publish_realtime('new_comment', self.as_dict(), doctype= self.comment_doctype, - docname = self.comment_docname) + frappe.publish_realtime('new_comment', self.as_dict(), + doctype= self.comment_doctype, docname = self.comment_docname, + after_commit=True) self.notify_mentions() @@ -182,3 +186,6 @@ def on_doctype_update(): frappe.db.commit() frappe.db.sql("""alter table `tabComment` add index comment_doctype_docname_index(comment_doctype, comment_docname)""") + + if "_liked_by" not in frappe.db.get_table_columns("Comment"): + add_column("Comment", "_liked_by", "Text") diff --git a/frappe/core/doctype/communication/communication.js b/frappe/core/doctype/communication/communication.js index 3b8501cda0..b886bb5792 100644 --- a/frappe/core/doctype/communication/communication.js +++ b/frappe/core/doctype/communication/communication.js @@ -16,12 +16,12 @@ frappe.ui.form.on("Communication", "refresh", function(frm) { } if(frm.doc.status==="Open") { - frm.add_custom_button("Close", function() { + frm.add_custom_button(__("Close"), function() { frm.set_value("status", "Closed"); frm.save(); }); } else if (frm.doc.status !== "Linked") { - frm.add_custom_button("Reopen", function() { + frm.add_custom_button(__("Reopen"), function() { frm.set_value("status", "Open"); frm.save(); }); @@ -30,7 +30,7 @@ frappe.ui.form.on("Communication", "refresh", function(frm) { frappe.ui.form.on("Communication", "onload", function(frm) { if(frm.doc.content) { - frm.doc.content = frappe.utils.remove_script_and_style(frm.doc.content); + frm.doc.content = frappe.dom.remove_script_and_style(frm.doc.content); } frm.set_query("reference_doctype", function() { return { diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index a8053e672c..e45912111d 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -10,6 +10,7 @@ from frappe.utils.file_manager import get_file from frappe.email.bulk import check_bulk_limit import frappe.email.smtp from frappe import _ +from frappe.model.db_schema import add_column from frappe.model.document import Document @@ -50,7 +51,7 @@ class Communication(Document): comment["comment_type"] = comment["communication_medium"] frappe.publish_realtime('new_comment', comment, doctype = self.reference_doctype, - docname = self.reference_name) + docname = self.reference_name, after_commit=True) def on_update(self): """Update parent status as `Open` or `Replied`.""" @@ -246,7 +247,6 @@ class Communication(Document): # if it is a fetched email, add follows to CC cc.append(self.get_owner_email()) cc += self.get_assignees() - cc += self.get_starrers() if cc: # exclude email accounts, unfollows, recipients and unsubscribes @@ -306,10 +306,6 @@ class Communication(Document): return filtered - def get_starrers(self): - """Return list of users who have starred this document.""" - return [( get_formatted_email(user) or user ) for user in self.get_parent_doc().get_starred_by()] - def get_owner_email(self): owner = self.get_parent_doc().owner return get_formatted_email(owner) or owner @@ -337,6 +333,9 @@ def on_doctype_update(): """Add index in `tabCommunication` for `(reference_doctype, reference_name)`""" frappe.db.add_index("Communication", ["reference_doctype", "reference_name"]) + if "_liked_by" not in frappe.db.get_table_columns("Communication"): + add_column("Communication", "_liked_by", "Text") + @frappe.whitelist() def make(doctype=None, name=None, content=None, subject=None, sent_or_received = "Sent", sender=None, recipients=None, communication_medium="Email", send_email=False, diff --git a/frappe/core/doctype/docshare/docshare.py b/frappe/core/doctype/docshare/docshare.py index bbe31f9e84..047d24a9dd 100644 --- a/frappe/core/doctype/docshare/docshare.py +++ b/frappe/core/doctype/docshare/docshare.py @@ -42,8 +42,13 @@ class DocShare(Document): frappe.throw(_('You need to have "Share" permission'), frappe.PermissionError) def after_insert(self): - self.get_doc().add_comment("Shared", - _("{0} shared this document with {1}").format(get_fullname(self.owner), get_fullname(self.user))) + doc = self.get_doc() + owner = get_fullname(self.owner) + + if self.everyone: + doc.add_comment("Shared", _("{0} shared this document with everyone").format(owner)) + else: + doc.add_comment("Shared", _("{0} shared this document with {1}").format(owner, get_fullname(self.user))) def on_trash(self): if not self.flags.ignore_share_permission: diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 7acda02a74..99366781c1 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -26,6 +26,7 @@ "oldfieldtype": "Section Break", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -51,6 +52,7 @@ "options": "Module Def", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 1, @@ -76,6 +78,7 @@ "oldfieldtype": "Check", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -101,11 +104,12 @@ "oldfieldtype": "Check", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, "search_index": 0, - "set_only_once": 0, + "set_only_once": 1, "unique": 0 }, { @@ -122,6 +126,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -147,6 +152,7 @@ "options": "\nDocument\nSetup\nSystem\nOther", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -169,6 +175,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -191,6 +198,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -213,6 +221,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -236,6 +245,7 @@ "oldfieldtype": "Section Break", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -261,6 +271,7 @@ "options": "DocField", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -283,6 +294,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -308,6 +320,7 @@ "oldfieldtype": "Data", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -333,6 +346,7 @@ "options": "\nTitle Case\nUPPER CASE", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -357,6 +371,7 @@ "oldfieldtype": "Text", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -378,6 +393,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -401,6 +417,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -425,6 +442,7 @@ "oldfieldtype": "Data", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -449,6 +467,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -473,6 +492,7 @@ "options": "ASC\nDESC", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -496,6 +516,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -522,6 +543,7 @@ "options": "DocPerm", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -544,6 +566,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -566,6 +589,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -590,6 +614,7 @@ "oldfieldtype": "Check", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -614,6 +639,7 @@ "oldfieldtype": "Check", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -636,6 +662,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -659,6 +686,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -683,6 +711,7 @@ "oldfieldtype": "Check", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -707,6 +736,7 @@ "oldfieldtype": "Check", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -731,6 +761,7 @@ "oldfieldtype": "Check", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -755,6 +786,7 @@ "oldfieldtype": "Int", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -777,6 +809,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -801,6 +834,7 @@ "oldfieldtype": "Check", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -825,6 +859,7 @@ "oldfieldtype": "Check", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -849,6 +884,7 @@ "oldfieldtype": "Check", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -871,6 +907,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -889,7 +926,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2015-11-16 06:29:45.648699", + "modified": "2016-01-11 01:54:58.471041", "modified_by": "Administrator", "module": "Core", "name": "DocType", diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index a3ff7dd261..e150e84a9e 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -50,6 +50,9 @@ class DocType(Document): self.make_amendable() + if self.istable: + self.allow_import = 0 + def check_developer_mode(self): """Throw exception if not developer mode or via patch""" if frappe.flags.in_patch: @@ -274,7 +277,7 @@ def validate_fields(meta): frappe.throw(_("Max width for type Currency is 100px in row {0}").format(d.idx)) def check_in_list_view(d): - if d.in_list_view and d.fieldtype!="Image" and (d.fieldtype in no_value_fields): + if d.in_list_view and (d.fieldtype in no_value_fields): frappe.throw(_("'In List View' not allowed for type {0} in row {1}").format(d.fieldtype, d.idx)) def check_dynamic_link_options(d): diff --git a/frappe/core/doctype/user/user.json b/frappe/core/doctype/user/user.json index 9650b623fa..a213daf456 100644 --- a/frappe/core/doctype/user/user.json +++ b/frappe/core/doctype/user/user.json @@ -1385,7 +1385,7 @@ "istable": 0, "max_attachments": 5, "menu_index": 0, - "modified": "2015-12-23 02:45:19.261689", + "modified": "2016-01-08 04:50:48.919694", "modified_by": "Administrator", "module": "Core", "name": "User", @@ -1398,9 +1398,9 @@ "create": 1, "delete": 1, "email": 1, - "export": 0, + "export": 1, "if_owner": 0, - "import": 0, + "import": 1, "permlevel": 0, "print": 1, "read": 1, diff --git a/frappe/core/notifications.py b/frappe/core/notifications.py index 266884915e..626081f846 100644 --- a/frappe/core/notifications.py +++ b/frappe/core/notifications.py @@ -14,6 +14,9 @@ def get_notification_config(): "Comment": "frappe.core.notifications.get_unread_messages", "Error Snapshot": {"seen": 0, "parent_error_snapshot": None}, }, + "for_other": { + "Likes": "frappe.core.notifications.get_unseen_likes" + } } def get_things_todo(): @@ -41,3 +44,12 @@ def get_unread_messages(): AND comment_docname = %s AND docstatus=0 """, (frappe.session.user,))[0][0] + +def get_unseen_likes(): + """Returns count of unseen likes""" + return frappe.db.sql("""select count(*) from `tabFeed` + where + feed_type='Like' + and owner is not null and owner!=%(user)s + and doc_owner=%(user)s + and seen=0""", {"user": frappe.session.user})[0][0] diff --git a/frappe/core/page/data_import_tool/data_import_tool.js b/frappe/core/page/data_import_tool/data_import_tool.js index bc3917ee59..e3e01c6fec 100644 --- a/frappe/core/page/data_import_tool/data_import_tool.js +++ b/frappe/core/page/data_import_tool/data_import_tool.js @@ -110,7 +110,8 @@ frappe.DataImportTool = Class.extend({ me.write_messages(r.messages); } - } + }, + is_private: true }); frappe.realtime.on("data_import_progress", function(data) { diff --git a/frappe/core/page/data_import_tool/data_import_tool.py b/frappe/core/page/data_import_tool/data_import_tool.py index 6b3ec60f72..15376af248 100644 --- a/frappe/core/page/data_import_tool/data_import_tool.py +++ b/frappe/core/page/data_import_tool/data_import_tool.py @@ -19,11 +19,7 @@ def get_data_keys(): @frappe.whitelist() def get_doctypes(): - if "System Manager" in frappe.get_roles(): - return [r[0] for r in frappe.db.sql("""select name from `tabDocType` - where allow_import = 1""")] - else: - return frappe.get_user()._get("can_import") + return frappe.get_user()._get("can_import") @frappe.whitelist() def get_doctype_options(): diff --git a/frappe/core/page/data_import_tool/importer.py b/frappe/core/page/data_import_tool/importer.py index 1396174723..0a942510df 100644 --- a/frappe/core/page/data_import_tool/importer.py +++ b/frappe/core/page/data_import_tool/importer.py @@ -218,7 +218,7 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, # publish task_update frappe.publish_realtime("data_import_progress", {"progress": [i, total]}, - user=frappe.session.user, now=True) + user=frappe.session.user) try: doc = get_doc(row_idx) diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py index 3ef98260e2..c1d0346014 100644 --- a/frappe/custom/doctype/custom_field/custom_field.py +++ b/frappe/custom/doctype/custom_field/custom_field.py @@ -92,11 +92,9 @@ class CustomField(Document): # Create new peroperty setter if order changed if _idx and not existing_property_setter: field_idx = (_idx.index(self.insert_after) + 1) if (self.insert_after in _idx) else len(_idx) - if field_idx < len(_idx): - _idx[field_idx] = self.fieldname - else: - _idx.append(self.fieldname) - + + _idx.insert(field_idx, self.fieldname) + frappe.make_property_setter({ "doctype":self.dt, "doctype_or_field": "DocType", diff --git a/frappe/custom/doctype/customize_form/customize_form.js b/frappe/custom/doctype/customize_form/customize_form.js index d8c08dd289..5497ee7edb 100644 --- a/frappe/custom/doctype/customize_form/customize_form.js +++ b/frappe/custom/doctype/customize_form/customize_form.js @@ -12,7 +12,6 @@ frappe.ui.form.on("Customize Form", { filters: [ ['DocType', 'issingle', '=', 0], ['DocType', 'custom', '=', 0], - ['DocType', 'in_create', '=', 0], ['DocType', 'name', 'not in', 'DocType, DocField, DocPerm, User, Role, UserRole, \ Page, Page Role, Module Def, Print Format, Report, Customize Form, \ Customize Form Field'] diff --git a/frappe/defaults.py b/frappe/defaults.py index 38cd18b7b9..23dfa0be95 100644 --- a/frappe/defaults.py +++ b/frappe/defaults.py @@ -19,31 +19,34 @@ def add_user_default(key, value, user=None, parenttype=None): def get_user_default(key, user=None): user_defaults = get_defaults(user or frappe.session.user) d = user_defaults.get(key, None) - - if key != frappe.scrub(key): + + if is_a_user_permission_key(key): if d and isinstance(d, (list, tuple)) and len(d)==1: # Use User Permission value when only when it has a single value d = d[0] - + else: d = user_defaults.get(frappe.scrub(key), None) - + return isinstance(d, (list, tuple)) and d[0] or d def get_user_default_as_list(key, user=None): user_defaults = get_defaults(user or frappe.session.user) d = user_defaults.get(key, None) - - if key != frappe.scrub(key): + + if is_a_user_permission_key(key): if d and isinstance(d, (list, tuple)) and len(d)==1: # Use User Permission value when only when it has a single value d = [d[0]] - + else: d = user_defaults.get(frappe.scrub(key), None) return (not isinstance(d, (list, tuple))) and [d] or d +def is_a_user_permission_key(key): + return ":" not in key and key != frappe.scrub(key) + def get_user_permissions(user=None): if not user: user = frappe.session.user diff --git a/frappe/desk/doctype/event/event.json b/frappe/desk/doctype/event/event.json index 45313a838e..d846a16ef5 100644 --- a/frappe/desk/doctype/event/event.json +++ b/frappe/desk/doctype/event/event.json @@ -1,6 +1,6 @@ { "allow_copy": 0, - "allow_import": 0, + "allow_import": 1, "allow_rename": 0, "autoname": "EV.#####", "creation": "2013-06-10 13:17:47", @@ -24,6 +24,7 @@ "oldfieldtype": "Section Break", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -46,6 +47,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 1, @@ -71,6 +73,7 @@ "options": "Private\nPublic\nCancel", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 1, @@ -94,6 +97,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -115,6 +119,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -137,6 +142,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 1, @@ -159,6 +165,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -181,6 +188,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -202,6 +210,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -224,6 +233,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -246,6 +256,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -270,6 +281,7 @@ "options": "\nEvery Day\nEvery Week\nEvery Month\nEvery Year", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -294,6 +306,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -315,6 +328,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -338,6 +352,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -361,6 +376,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -384,6 +400,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -407,6 +424,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -430,6 +448,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -453,6 +472,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -476,6 +496,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -497,6 +518,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -521,6 +543,7 @@ "oldfieldtype": "Text", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "print_width": "300px", "read_only": 0, "report_hide": 0, @@ -546,6 +569,7 @@ "oldfieldtype": "Section Break", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -569,6 +593,7 @@ "oldfieldtype": "Column Break", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "print_width": "50%", "read_only": 0, "report_hide": 0, @@ -596,6 +621,7 @@ "options": "Event Role", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -621,6 +647,7 @@ "options": "DocType", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -646,6 +673,7 @@ "options": "ref_type", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 1, "report_hide": 0, "reqd": 0, @@ -664,7 +692,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2015-11-16 06:29:46.775883", + "modified": "2016-01-08 04:50:37.240223", "modified_by": "Administrator", "module": "Desk", "name": "Event", @@ -697,9 +725,9 @@ "create": 1, "delete": 1, "email": 1, - "export": 0, + "export": 1, "if_owner": 0, - "import": 0, + "import": 1, "permlevel": 0, "print": 1, "read": 1, diff --git a/frappe/desk/doctype/feed/feed.json b/frappe/desk/doctype/feed/feed.json index 8950f89490..5fda9adcaf 100644 --- a/frappe/desk/doctype/feed/feed.json +++ b/frappe/desk/doctype/feed/feed.json @@ -1,204 +1,309 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "hash", - "creation": "2012-07-03 13:29:42", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "hash", + "creation": "2012-07-03 13:29:42", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "feed_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Feed Type", - "length": 0, - "no_copy": 0, - "options": "\nComment\nLogin\nLabel\nInfo", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "feed_type", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Feed Type", + "length": 0, + "no_copy": 0, + "options": "\nComment\nLogin\nLabel\nInfo\nLike", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "doc_type", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Doc Type", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "doc_type", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Doc Type", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "doc_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Doc Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "doc_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Doc Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "subject", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Subject", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "subject", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Subject", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "color", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Color", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "color", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Color", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "full_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Full Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "full_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Full Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "doc_owner", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Doc Owner", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "seen", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Seen", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "description": "Use this to provide alternative link to a feed record", + "fieldname": "reference_doctype", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Reference DocType", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "reference_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Reference Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "icon-rss", - "idx": 1, - "in_create": 0, - "in_dialog": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2015-11-16 06:29:47.123186", - "modified_by": "Administrator", - "module": "Desk", - "name": "Feed", - "owner": "Administrator", + ], + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "icon-rss", + "idx": 1, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2015-12-30 02:48:03.860188", + "modified_by": "Administrator", + "module": "Desk", + "name": "Feed", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 0 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 1, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "All", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "amend": 0, + "apply_user_permissions": 1, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 1, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "All", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 0 } - ], - "read_only": 0, + ], + "read_only": 0, "read_only_onload": 0 -} +} \ No newline at end of file diff --git a/frappe/desk/doctype/feed/feed.py b/frappe/desk/doctype/feed/feed.py index c9a8ee02fc..f901a978b6 100644 --- a/frappe/desk/doctype/feed/feed.py +++ b/frappe/desk/doctype/feed/feed.py @@ -12,7 +12,13 @@ from frappe import _ exclude_from_linked_with = True class Feed(Document): - pass + no_feed_on_delete = True + + def validate(self): + if not (self.reference_doctype and self.reference_name): + # reset both if even one is missing + self.reference_doctype = self.reference_name = None + def on_doctype_update(): if not frappe.db.sql("""show index from `tabFeed` @@ -24,9 +30,12 @@ def on_doctype_update(): def get_permission_query_conditions(user): if not user: user = frappe.session.user - if not frappe.permissions.apply_user_permissions("Feed", "read", user): + use_user_permissions = frappe.permissions.apply_user_permissions("Feed", "read", user) + if not use_user_permissions: return "" + conditions = ['`tabFeed`.owner="{user}" or `tabFeed`.doc_owner="{user}"'.format(user=frappe.db.escape(user))] + user_permissions = frappe.defaults.get_user_permissions(user) can_read = frappe.get_user().get_can_read() @@ -34,19 +43,17 @@ def get_permission_query_conditions(user): list(set(can_read) - set(user_permissions.keys()))] if not can_read_doctypes: - return "" + conditions += ["tabFeed.doc_type in ({})".format(", ".join(can_read_doctypes))] - conditions = ["tabFeed.doc_type in ({})".format(", ".join(can_read_doctypes))] + if user_permissions: + can_read_docs = [] + for doctype, names in user_permissions.items(): + for n in names: + can_read_docs.append('"{}|{}"'.format(doctype, n)) - if user_permissions: - can_read_docs = [] - for doctype, names in user_permissions.items(): - for n in names: - can_read_docs.append('"{}|{}"'.format(doctype, n)) - - if can_read_docs: - conditions.append("concat_ws('|', tabFeed.doc_type, tabFeed.doc_name) in ({})".format( - ", ".join(can_read_docs))) + if can_read_docs: + conditions.append("concat_ws('|', tabFeed.doc_type, tabFeed.doc_name) in ({})".format( + ", ".join(can_read_docs))) return "(" + " or ".join(conditions) + ")" @@ -83,7 +90,10 @@ def update_feed(doc, method=None): "doc_type": doctype, "doc_name": name, "subject": feed.subject, - "full_name": get_fullname(doc.owner) + "full_name": get_fullname(doc.owner), + "doc_owner": frappe.db.get_value(doctype, name, "owner"), + "reference_doctype": feed.reference_doctype, + "reference_name": feed.reference_name }).insert(ignore_permissions=True) def login_feed(login_manager): diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py index 12860fa6a4..a15cae4e77 100644 --- a/frappe/desk/form/load.py +++ b/frappe/desk/form/load.py @@ -95,11 +95,11 @@ def get_docinfo(doc=None, doctype=None, name=None): def get_user_permissions(meta): out = {} all_user_permissions = frappe.defaults.get_user_permissions() - + for m in meta: for df in m.get_fields_to_check_permissions(all_user_permissions): out[df.options] = list(set(all_user_permissions[df.options])) - + return out def get_attachments(dt, dn): @@ -108,7 +108,7 @@ def get_attachments(dt, dn): def get_comments(dt, dn, limit=100): comments = frappe.db.sql("""select name, comment, comment_by, creation, - reference_doctype, reference_name, comment_type, "Comment" as doctype + reference_doctype, reference_name, comment_type, "Comment" as doctype, _liked_by from `tabComment` where comment_doctype=%s and comment_docname=%s order by creation desc limit %s""", @@ -116,7 +116,7 @@ def get_comments(dt, dn, limit=100): communications = frappe.db.sql("""select name, content as comment, sender as comment_by, creation, - communication_medium as comment_type, subject, delivery_status, + communication_medium as comment_type, subject, delivery_status, _liked_by, "Communication" as doctype from tabCommunication where reference_doctype=%s and reference_name=%s diff --git a/frappe/desk/like.py b/frappe/desk/like.py new file mode 100644 index 0000000000..d22d4edcbd --- /dev/null +++ b/frappe/desk/like.py @@ -0,0 +1,103 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals + +"""Allow adding of likes to documents""" + +import frappe, json +from frappe.model.db_schema import add_column +from frappe import _ +from frappe.utils import get_link_to_form + +@frappe.whitelist() +def toggle_like(doctype, name, add=False): + """Adds / removes the current user in the `__liked_by` property of the given document. + If column does not exist, will add it in the database. + + The `_liked_by` property is always set from this function and is ignored if set via + Document API + + :param doctype: DocType of the document to like + :param name: Name of the document to like + :param add: `Yes` if like is to be added. If not `Yes` the like will be removed.""" + + _toggle_like(doctype, name, add) + +def _toggle_like(doctype, name, add, user=None): + """Same as toggle_like but hides param `user` from API""" + + if not user: + user = frappe.session.user + + try: + liked_by, owner = frappe.db.get_value(doctype, name, ["_liked_by", "owner"]) + + # CHANGED: Allow someone to like their own documents as it also works as a bookmark + # if owner==frappe.session.user and add=="Yes": + # frappe.throw(_("You cannot like something that you created")) + + if liked_by: + liked_by = json.loads(liked_by) + else: + liked_by = [] + + if add=="Yes": + if user not in liked_by: + liked_by.append(user) + add_comment(doctype, name) + + else: + if user in liked_by: + liked_by.remove(user) + remove_like(doctype, name) + + frappe.db.set_value(doctype, name, "_liked_by", json.dumps(liked_by), update_modified=False) + + except Exception, e: + if isinstance(e.args, (tuple, list)) and e.args and e.args[0]==1054: + add_column(doctype, "_liked_by", "Text") + _toggle_like(doctype, name, add, user) + else: + raise + +def remove_like(doctype, name): + """Remove previous Like""" + # remove Comment + frappe.delete_doc("Comment", [c.name for c in frappe.get_all("Comment", + filters={ + "comment_doctype": doctype, + "comment_docname": name, + "comment_by": frappe.session.user, + "comment_type": "Like" + } + )], ignore_permissions=True) + + # remove Feed + frappe.delete_doc("Feed", [c.name for c in frappe.get_all("Feed", + filters={ + "doc_type": doctype, + "doc_name": name, + "owner": frappe.session.user, + "feed_type": "Like" + } + )], ignore_permissions=True) + +def add_comment(doctype, name): + doc = frappe.get_doc(doctype, name) + + if doctype=="Comment": + link = get_link_to_form(doc.comment_doctype, doc.comment_docname, + "{0} {1}".format(_(doc.comment_doctype), doc.comment_docname)) + doc.add_comment("Like", _("Comment: {0} in {1}").format("" + doc.comment + "", link), + reference_doctype=doc.comment_doctype, reference_name=doc.comment_docname) + + elif doctype=="Communication": + link = get_link_to_form(doc.reference_doctype, doc.reference_name, + "{0} {1}".format(_(doc.reference_doctype), doc.reference_name)) + + doc.add_comment("Like", _("Communication: {0} in {1}").format("" + doc.subject + "", link), + reference_doctype=doc.reference_doctype, reference_name=doc.reference_name) + + else: + doc.add_comment("Like", _("Liked")) diff --git a/frappe/desk/notifications.py b/frappe/desk/notifications.py index 29a7cbcbaa..b818b79bd4 100644 --- a/frappe/desk/notifications.py +++ b/frappe/desk/notifications.py @@ -24,7 +24,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), "new_messages": get_new_messages() + # "likes": get_count_of_new_likes() } def get_new_messages(): @@ -48,19 +50,27 @@ def get_new_messages(): def get_notifications_for_modules(config, notification_count): """Notifications for modules""" - open_count_module = {} - for m in config.for_module: + 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_module[m] = notification_count[m] + open_count[m] = notification_count[m] else: - open_count_module[m] = frappe.get_attr(config.for_module[m])() + open_count[m] = frappe.get_attr(notification_map[m])() - frappe.cache().hset("notification_count:" + m, frappe.session.user, open_count_module[m]) + frappe.cache().hset("notification_count:" + m, frappe.session.user, open_count[m]) except frappe.PermissionError: frappe.msgprint("Permission Error in notifications for {0}".format(m)) - return open_count_module + return open_count def get_notifications_for_doctypes(config, notification_count): """Notifications for DocTypes""" @@ -143,7 +153,7 @@ def get_notification_config(): config = frappe._dict() for notification_config in frappe.get_hooks().notification_config: nc = frappe.get_attr(notification_config)() - for key in ("for_doctype", "for_module", "for_module_doctypes"): + for key in ("for_doctype", "for_module", "for_module_doctypes", "for_other"): config.setdefault(key, {}) config[key].update(nc.get(key, {})) return config diff --git a/frappe/desk/page/activity/activity.css b/frappe/desk/page/activity/activity.css index 327533d3ce..b28d74ddc0 100644 --- a/frappe/desk/page/activity/activity.css +++ b/frappe/desk/page/activity/activity.css @@ -62,3 +62,12 @@ width: 97% !important; margin: auto; } + +#page-activity .list-filters { + display: none !important; +} + +#page-activity .octicon-heart { + color: #ff5858; + margin: 0px 5px; +} diff --git a/frappe/desk/page/activity/activity.js b/frappe/desk/page/activity/activity.js index 9a386e17af..89183c2acf 100644 --- a/frappe/desk/page/activity/activity.js +++ b/frappe/desk/page/activity/activity.js @@ -18,36 +18,73 @@ frappe.pages['activity'].on_page_load = function(wrapper) { this.page.set_title(__("Activity")); - this.page.list = new frappe.ui.Listing({ - hide_refresh: true, - page: this.page, - method: 'frappe.desk.page.activity.activity.get_feed', - parent: $("
").appendTo(this.page.main), - render_row: function(row, data) { - new frappe.activity.Feed(row, data); - } - }); + frappe.model.with_doctype("Feed", function() { + me.page.list = new frappe.ui.Listing({ + hide_refresh: true, + page: me.page, + method: 'frappe.desk.page.activity.activity.get_feed', + parent: $("").appendTo(me.page.main), + render_row: function(row, data) { + new frappe.activity.Feed(row, data); + }, + show_filters: true, + doctype: "Feed", + get_args: function() { + if (frappe.route_options && frappe.route_options.show_likes) { + delete frappe.route_options.show_likes; + return { + show_likes: true + } + } else { + return {} + } + } + }); - this.page.list.run(); + me.page.list.run(); + + me.page.set_primary_action(__("Refresh"), function() { + me.page.list.filter_list.clear_filters(); + me.page.list.run(); + }, "octicon octicon-sync"); + }); frappe.activity.render_plot(this.page); this.page.main.on("click", ".activity-message", function() { - var doctype = $(this).attr("data-doctype"), + var reference_doctype = $(this).attr("data-reference-doctype"), + reference_name = $(this).attr("data-reference-name"), + doctype = $(this).attr("data-doctype"), docname = $(this).attr("data-docname"); + if (doctype && docname) { - frappe.set_route(["Form", doctype, docname]); + frappe.set_route(["Form", reference_doctype || doctype, reference_name || docname]); + + if (reference_doctype && reference_name) { + frappe.route_options = { + scroll_to: { "doctype": doctype, "name": docname } + } + } } }); - this.page.set_primary_action(__("Refresh"), function() { me.page.list.run(); }, "octicon octicon-sync"); - // Build Report Button if(frappe.boot.user.can_get_report.indexOf("Feed")!=-1) { - this.page.set_secondary_action(__('Build Report'), function() { + this.page.add_menu_item(__('Build Report'), function() { frappe.set_route('Report', "Feed"); - }, 'icon-th'); + }, 'icon-th') } + + this.page.add_menu_item(__('Show Likes'), function() { + frappe.route_options = { + show_likes: true + }; + me.page.list.run(); + }, 'octicon octicon-heart'); +}; + +frappe.pages['activity'].on_page_show = function() { + frappe.breadcrumbs.add("Desk"); } frappe.activity.last_feed_date = false; @@ -69,7 +106,7 @@ frappe.activity.Feed = Class.extend({ .find("a").addClass("grey"); }, scrub_data: function(data) { - data.by = frappe.user_info(data.owner).fullname; + data.by = frappe.user.full_name(data.owner); data.imgsrc = frappe.utils.get_file_link(frappe.user_info(data.owner).image); data.icon = "icon-flag"; diff --git a/frappe/desk/page/activity/activity.py b/frappe/desk/page/activity/activity.py index 8298ddb503..ba02679559 100644 --- a/frappe/desk/page/activity/activity.py +++ b/frappe/desk/page/activity/activity.py @@ -3,14 +3,38 @@ from __future__ import unicode_literals import frappe +from frappe.utils import cint +from frappe.desk.doctype.feed.feed import get_permission_query_conditions @frappe.whitelist() -def get_feed(limit_start, limit_page_length): +def get_feed(limit_start, limit_page_length, show_likes=False): """get feed""" - return frappe.get_list("Feed", fields=["name", "feed_type", "doc_type", - "subject", "owner", "modified", "doc_name", "creation"], - limit_start = limit_start, limit_page_length = limit_page_length, - order_by="creation desc") + # directly use the permission query condition function of feed + match_conditions = get_permission_query_conditions(frappe.session.user) + + result = frappe.db.sql("""select name, feed_type, doc_type, doc_name, subject, + owner, modified, creation, seen, reference_doctype, reference_name + from `tabFeed` + where + ((feed_type='Like' and (owner=%(user)s or doc_owner=%(user)s)) or feed_type!='Like') + {match_conditions} + {show_likes} + order by creation desc + limit %(limit_start)s, %(limit_page_length)s""" + .format(match_conditions="and {0}".format(match_conditions) if match_conditions else "", + show_likes="and feed_type='Like'" if show_likes else ""), + { + "user": frappe.session.user, + "limit_start": cint(limit_start), + "limit_page_length": cint(limit_page_length) + }, as_dict=True) + + if show_likes: + # mark likes as seen! + frappe.db.sql("update `tabFeed` set seen=1 where feed_type='Like' and doc_owner=%s", frappe.session.user) + frappe.local.flags.commit = True + + return result @frappe.whitelist() def get_months_activity(): diff --git a/frappe/desk/page/activity/activity_row.html b/frappe/desk/page/activity/activity_row.html index ccbd37dd07..2b72a95b02 100644 --- a/frappe/desk/page/activity/activity_row.html +++ b/frappe/desk/page/activity/activity_row.html @@ -1,10 +1,15 @@