[feature] Ability to like a document, comment or communication, see notifications about it and view it on activity feed
This commit is contained in:
parent
e3a2b1265c
commit
3241a0969f
72 changed files with 1839 additions and 999 deletions
4
frappe/change_log/current/like.md
Normal file
4
frappe/change_log/current/like.md
Normal file
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
@ -182,3 +184,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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
98
frappe/desk/like.py
Normal file
98
frappe/desk/like.py
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
# 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=False, user=None):
|
||||
"""Same as toggle_like but hides param `user` from API"""
|
||||
|
||||
if not user:
|
||||
user = frappe.session.user
|
||||
|
||||
try:
|
||||
liked_by = frappe.db.get_value(doctype, name, "_liked_by")
|
||||
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 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"
|
||||
}
|
||||
)])
|
||||
|
||||
# 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("<b>" + doc.comment + "</b>", 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("<b>" + doc.subject + "</b>", link),
|
||||
reference_doctype=doc.reference_doctype, reference_name=doc.reference_name)
|
||||
|
||||
else:
|
||||
doc.add_comment("Like", _("Liked"))
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: $("<div></div>").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: $("<div></div>").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";
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
<div class="row activity-row" data-creation="{%= creation.split(" ")[0] + " 00:00:00" %}">
|
||||
<div class="col-xs-3 text-right activity-date"><span class="{%= date_class %}">
|
||||
{%= date_sep || "" %}</span></div>
|
||||
<div class="col-xs-9 activity-message" data-doctype="{%= doc_type %}" data-docname="{%= doc_name %}"
|
||||
<div class="col-xs-9 activity-message"
|
||||
data-doctype="{%= doc_type %}"
|
||||
data-docname="{%= doc_name %}"
|
||||
data-reference-doctype="{{ reference_doctype }}"
|
||||
data-reference-name="{{ reference_name }}"
|
||||
title="{%= by %} / {%= dateutil.str_to_user(creation) %}">
|
||||
<span class="avatar avatar-small">
|
||||
<img src="{%= imgsrc %}">
|
||||
<div class="avatar-frame" style="background-image: url({{ imgsrc }});"></div>
|
||||
<!-- <img src="{%= imgsrc %}"> -->
|
||||
</span>
|
||||
<span class="small">
|
||||
{% if (feed_type==="Login") { %}
|
||||
|
|
@ -15,6 +20,13 @@
|
|||
{%= __("Commented on {0}: {1}", [link, "<strong>" + subject + "</strong>"]) %}
|
||||
{% } else if (doc_type && !feed_type) { %}
|
||||
{%= __("Updated {0}: {1}", [link, "<strong>" + subject + "</strong>"]) %}
|
||||
{% } else if (feed_type==="Like" && doc_type) { %}
|
||||
{%= by %} <i class="octicon octicon-heart"></i>
|
||||
{% if (in_list(["Comment", "Communication"], doc_type)) { %}
|
||||
{%= subject %}
|
||||
{% } else { %}
|
||||
{%= link %}
|
||||
{% } %}
|
||||
{% } else if (doc_type) { %}
|
||||
{%= __("{0}: {1}", [link, "<strong>" + subject + "</strong>"]) %}
|
||||
{% } else { %}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ frappe.pages.messages.on_page_load = function(parent) {
|
|||
frappe.pages.messages.on_page_show = function() {
|
||||
// clear title prefix
|
||||
frappe.utils.set_title_prefix("");
|
||||
|
||||
frappe.breadcrumbs.add("Desk");
|
||||
}
|
||||
|
||||
frappe.desk.pages.Messages = Class.extend({
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
"""Allow adding of stars to documents"""
|
||||
|
||||
import frappe, json
|
||||
from frappe.model.db_schema import add_column
|
||||
|
||||
@frappe.whitelist()
|
||||
def toggle_star(doctype, name, add=False):
|
||||
"""Adds / removes the current user in the `__starred_by` property of the given document.
|
||||
If column does not exist, will add it in the database.
|
||||
|
||||
The `_starred_by` property is always set from this function and is ignored if set via
|
||||
Document API
|
||||
|
||||
:param doctype: DocType of the document to star
|
||||
:param name: Name of the document to star
|
||||
:param add: `Yes` if star is to be added. If not `Yes` the star will be removed."""
|
||||
|
||||
_toggle_star(doctype, name, add)
|
||||
|
||||
def _toggle_star(doctype, name, add=False, user=None):
|
||||
"""Same as toggle_star but hides param `user` from API"""
|
||||
|
||||
if not user:
|
||||
user = frappe.session.user
|
||||
|
||||
try:
|
||||
starred_by = frappe.db.get_value(doctype, name, "_starred_by")
|
||||
if starred_by:
|
||||
starred_by = json.loads(starred_by)
|
||||
else:
|
||||
starred_by = []
|
||||
|
||||
if add=="Yes":
|
||||
if user not in starred_by:
|
||||
starred_by.append(user)
|
||||
else:
|
||||
if user in starred_by:
|
||||
starred_by.remove(user)
|
||||
|
||||
frappe.db.sql("""update `tab{0}` set `_starred_by`=%s where name=%s""".format(doctype),
|
||||
(json.dumps(starred_by), name))
|
||||
|
||||
except Exception, e:
|
||||
if e.args[0]==1054:
|
||||
add_column(doctype, "_starred_by", "Text")
|
||||
_toggle_star(doctype, name, add, user)
|
||||
else:
|
||||
raise
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
<!-- title: frappe.desk.star --><div class="dev-header">
|
||||
|
||||
<a class="btn btn-default btn-sm" disabled style="margin-bottom: 10px;">
|
||||
Version 6.x.x</a>
|
||||
|
||||
|
||||
<a class="btn btn-default btn-sm" href="https://github.com/frappe/frappe/blob/develop/frappe/desk/star.py"
|
||||
target="_blank" style="margin-left: 10px; margin-bottom: 10px;"><i class="octicon octicon-mark-github"></i> Source</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p class="docs-attr-name">
|
||||
<a name="frappe.desk.star._toggle_star" href="#frappe.desk.star._toggle_star" class="text-muted small">
|
||||
<i class="icon-link small" style="color: #ccc;"></i></a>
|
||||
frappe.desk.star.<b>_toggle_star</b>
|
||||
<i class="text-muted">(doctype, name, add=False, user=None)</i>
|
||||
</p>
|
||||
<div class="docs-attr-desc"><p>Same as toggle_star but hides param <code>user</code> from API</p>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p><span class="label label-info">Public API</span>
|
||||
<br><code>/api/method/frappe.desk.star.toggle_star</code>
|
||||
</p>
|
||||
<p class="docs-attr-name">
|
||||
<a name="frappe.desk.star.toggle_star" href="#frappe.desk.star.toggle_star" class="text-muted small">
|
||||
<i class="icon-link small" style="color: #ccc;"></i></a>
|
||||
frappe.desk.star.<b>toggle_star</b>
|
||||
<i class="text-muted">(doctype, name, add=False)</i>
|
||||
</p>
|
||||
<div class="docs-attr-desc"><p>Adds / removes the current user in the <code>__starred_by</code> property of the given document.
|
||||
If column does not exist, will add it in the database.</p>
|
||||
|
||||
<p>The <code>_starred_by</code> property is always set from this function and is ignored if set via
|
||||
Document API</p>
|
||||
|
||||
<p><strong>Parameters:</strong></p>
|
||||
|
||||
<ul>
|
||||
<li><strong><code>doctype</code></strong> - DocType of the document to star</li>
|
||||
<li><strong><code>name</code></strong> - Name of the document to star</li>
|
||||
<li><strong><code>add</code></strong> - <code>Yes</code> if star is to be added. If not <code>Yes</code> the star will be removed.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- autodoc -->
|
||||
|
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
|||
import frappe
|
||||
from frappe.utils.pdf import get_pdf
|
||||
from frappe.email.smtp import get_outgoing_email_account
|
||||
from frappe.utils import get_url, scrub_urls, strip, expand_relative_urls, cint, split_emails
|
||||
from frappe.utils import get_url, scrub_urls, strip, expand_relative_urls, cint, split_emails, to_markdown
|
||||
import email.utils
|
||||
from markdown2 import markdown
|
||||
|
||||
|
|
@ -89,12 +89,7 @@ class EMail:
|
|||
|
||||
def set_html_as_text(self, html):
|
||||
"""return html2text"""
|
||||
import HTMLParser
|
||||
from html2text import html2text
|
||||
try:
|
||||
self.set_text(html2text(html))
|
||||
except HTMLParser.HTMLParseError:
|
||||
pass
|
||||
self.set_text(to_markdown(html))
|
||||
|
||||
def set_message(self, message, mime_type='text/html', as_attachment=0, filename='attachment.html'):
|
||||
"""Append the message with MIME content to the root node (as attachment)"""
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ default_fields = ('doctype','name','owner','creation','modified','modified_by',
|
|||
integer_docfield_properties = ("reqd", "search_index", "in_list_view", "permlevel",
|
||||
"hidden", "read_only", "ignore_user_permissions", "allow_on_submit", "report_hide",
|
||||
"in_filter", "no_copy", "print_hide", "unique")
|
||||
optional_fields = ("_user_tags", "_comments", "_assign", "_starred_by")
|
||||
optional_fields = ("_user_tags", "_comments", "_assign", "_liked_by")
|
||||
|
||||
def rename(doctype, old, new, debug=False):
|
||||
import frappe.model.rename_doc
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ class BaseDocument(object):
|
|||
if k in default_fields:
|
||||
del doc[k]
|
||||
|
||||
for key in ("_user_tags", "__islocal", "__onload", "_starred_by", "__run_link_triggers"):
|
||||
for key in ("_user_tags", "__islocal", "__onload", "_liked_by", "__run_link_triggers"):
|
||||
if self.get(key):
|
||||
doc[key] = self.get(key)
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class DatabaseQuery(object):
|
|||
def execute(self, query=None, fields=None, filters=None, or_filters=None,
|
||||
docstatus=None, group_by=None, order_by=None, limit_start=False,
|
||||
limit_page_length=None, as_list=False, with_childnames=False, debug=False,
|
||||
ignore_permissions=False, user=None):
|
||||
ignore_permissions=False, user=None, with_comment_count=False):
|
||||
if not ignore_permissions and not frappe.has_permission(self.doctype, "read", user=user):
|
||||
raise frappe.PermissionError, self.doctype
|
||||
|
||||
|
|
@ -45,9 +45,14 @@ class DatabaseQuery(object):
|
|||
self.user = user or frappe.session.user
|
||||
|
||||
if query:
|
||||
return self.run_custom_query(query)
|
||||
result = self.run_custom_query(query)
|
||||
else:
|
||||
return self.build_and_run()
|
||||
result = self.build_and_run()
|
||||
|
||||
if with_comment_count and not as_list and self.doctype:
|
||||
self.add_comment_count(result)
|
||||
|
||||
return result
|
||||
|
||||
def build_and_run(self):
|
||||
args = self.prepare_args()
|
||||
|
|
@ -444,3 +449,21 @@ class DatabaseQuery(object):
|
|||
return 'limit %s, %s' % (self.limit_start, self.limit_page_length)
|
||||
else:
|
||||
return ''
|
||||
|
||||
def add_comment_count(self, result):
|
||||
for r in result:
|
||||
if not r.name:
|
||||
continue
|
||||
|
||||
if "_comments" in r:
|
||||
comment_count = len(json.loads(r._comments or "[]"))
|
||||
else:
|
||||
comment_count = cint(frappe.db.get_value("Comment",
|
||||
filters={"comment_doctype": self.doctype, "comment_docname": r.name, "comment_type": "Comment"},
|
||||
fieldname="count(name)"))
|
||||
|
||||
communication_count = cint(frappe.db.get_value("Communication",
|
||||
filters={"reference_doctype": self.doctype, "reference_name": r.name},
|
||||
fieldname="count(name)"))
|
||||
|
||||
r._comment_count = comment_count + communication_count
|
||||
|
|
|
|||
|
|
@ -764,7 +764,7 @@ class Document(BaseDocument):
|
|||
"""Returns Desk URL for this document. `/desk#Form/{doctype}/{name}`"""
|
||||
return "/desk#Form/{doctype}/{name}".format(doctype=self.doctype, name=self.name)
|
||||
|
||||
def add_comment(self, comment_type, text=None, comment_by=None):
|
||||
def add_comment(self, comment_type, text=None, comment_by=None, reference_doctype=None, reference_name=None):
|
||||
"""Add a comment to this document.
|
||||
|
||||
:param comment_type: e.g. `Comment`. See Comment for more info."""
|
||||
|
|
@ -774,7 +774,9 @@ class Document(BaseDocument):
|
|||
"comment_type": comment_type,
|
||||
"comment_doctype": self.doctype,
|
||||
"comment_docname": self.name,
|
||||
"comment": text or _(comment_type)
|
||||
"comment": text or _(comment_type),
|
||||
"reference_doctype": reference_doctype,
|
||||
"reference_name": reference_name
|
||||
}).insert(ignore_permissions=True)
|
||||
return comment
|
||||
|
||||
|
|
@ -782,10 +784,10 @@ class Document(BaseDocument):
|
|||
"""Returns signature (hash) for private URL."""
|
||||
return hashlib.sha224(get_datetime_str(self.creation)).hexdigest()
|
||||
|
||||
def get_starred_by(self):
|
||||
starred_by = getattr(self, "_starred_by", None)
|
||||
if starred_by:
|
||||
return json.loads(starred_by)
|
||||
def get_liked_by(self):
|
||||
liked_by = getattr(self, "_liked_by", None)
|
||||
if liked_by:
|
||||
return json.loads(liked_by)
|
||||
else:
|
||||
return []
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ execute:frappe.db.sql("""update tabComment set comment = substr(comment, 6, loca
|
|||
frappe.patches.v5_0.fix_feed
|
||||
frappe.patches.v5_0.update_shared
|
||||
execute:frappe.reload_doc("core", "doctype", "docshare") #2015-07-21
|
||||
frappe.patches.v6_16.star_to_like
|
||||
frappe.patches.v5_0.bookmarks_to_stars
|
||||
frappe.patches.v5_0.style_settings_to_website_theme
|
||||
frappe.patches.v5_0.rename_ref_type_fieldnames
|
||||
|
|
@ -112,3 +113,5 @@ execute:frappe.create_folder(os.path.join(frappe.local.site_path, 'private', 'fi
|
|||
frappe.patches.v6_15.remove_property_setter_for_previous_field #2015-12-29
|
||||
frappe.patches.v6_15.set_username
|
||||
execute:frappe.permissions.reset_perms("Error Snapshot")
|
||||
frappe.patches.v6_16.feed_doc_owner
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from __future__ import unicode_literals
|
|||
import json
|
||||
import frappe
|
||||
import frappe.defaults
|
||||
from frappe.desk.star import _toggle_star
|
||||
from frappe.desk.like import _toggle_like
|
||||
|
||||
def execute():
|
||||
for user in frappe.get_all("User"):
|
||||
|
|
@ -29,4 +29,4 @@ def execute():
|
|||
or int(frappe.db.get_value("DocType", doctype, "issingle") or 0)
|
||||
or not frappe.db.table_exists(doctype)):
|
||||
continue
|
||||
_toggle_star(doctype, docname, add="Yes", user=username)
|
||||
_toggle_like(doctype, docname, add="Yes", user=username)
|
||||
|
|
|
|||
0
frappe/patches/v6_16/__init__.py
Normal file
0
frappe/patches/v6_16/__init__.py
Normal file
30
frappe/patches/v6_16/feed_doc_owner.py
Normal file
30
frappe/patches/v6_16/feed_doc_owner.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doctype("Feed")
|
||||
|
||||
frappe.db.sql("update `tabFeed` set seen=1")
|
||||
|
||||
for doctype, name in frappe.db.sql("""select distinct doc_type, doc_name from `tabFeed`
|
||||
where
|
||||
(doc_type is not null and doc_type != '')
|
||||
and (doc_name is not null and doc_name != '')
|
||||
and doc_type != 'Feed'
|
||||
for update"""):
|
||||
|
||||
owner = frappe.db.get_value(doctype, name, "owner")
|
||||
|
||||
if not owner:
|
||||
continue
|
||||
|
||||
frappe.db.sql("""update `tabFeed`
|
||||
set doc_owner=%(owner)s
|
||||
where
|
||||
doc_type=%(doctype)s
|
||||
and doc_name=%(name)s
|
||||
and (doc_owner is null or doc_owner = '')""".format(doctype=doctype), {
|
||||
"doctype": doctype,
|
||||
"name": name,
|
||||
"owner": owner
|
||||
})
|
||||
18
frappe/patches/v6_16/star_to_like.py
Normal file
18
frappe/patches/v6_16/star_to_like.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.db_schema import add_column
|
||||
|
||||
def execute():
|
||||
frappe.reload_doctype("Feed")
|
||||
|
||||
frappe.db.sql("""update `tabSingles` set field='_liked_by' where field='_starred_by'""")
|
||||
frappe.db.commit()
|
||||
|
||||
for table in frappe.db.get_tables():
|
||||
columns = [r[0] for r in frappe.db.sql("DESC `{0}`".format(table))]
|
||||
if "_starred_by" in columns:
|
||||
frappe.db.sql_ddl("""alter table `{0}` change `_starred_by` `_liked_by` Text """.format(table))
|
||||
|
||||
for doctype in ("Comment", "Communication"):
|
||||
if not frappe.db.has_column(doctype, "_liked_by"):
|
||||
add_column(doctype, "_liked_by", "Text")
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
"public/js/lib/highlight.pack.js",
|
||||
"public/js/frappe/class.js",
|
||||
"public/js/lib/microtemplate.js",
|
||||
"public/js/frappe/query_string.js",
|
||||
"website/js/website.js",
|
||||
"public/js/lib/socket.io.min.js"
|
||||
],
|
||||
|
|
@ -121,6 +122,7 @@
|
|||
|
||||
"public/js/frappe/change_log.html",
|
||||
"public/js/frappe/desk.js",
|
||||
"public/js/frappe/query_string.js",
|
||||
|
||||
"public/html/error_object.html",
|
||||
"public/html/error_snapshot.html"
|
||||
|
|
@ -180,7 +182,8 @@
|
|||
"public/js/frappe/ui/filters/filters.js",
|
||||
"public/js/frappe/ui/filters/edit_filter.html",
|
||||
"public/js/frappe/ui/tags.js",
|
||||
"public/js/frappe/ui/star.js",
|
||||
"public/js/frappe/ui/like.js",
|
||||
"public/js/frappe/ui/liked_by.html",
|
||||
"public/html/print_template.html",
|
||||
"public/js/frappe/list/doclistview.js",
|
||||
"public/js/frappe/list/list_sidebar.js",
|
||||
|
|
|
|||
|
|
@ -43,3 +43,12 @@
|
|||
height: 0;
|
||||
padding-bottom: 100%;
|
||||
}
|
||||
.avatar-frame {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding: 50% 0px;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -546,3 +546,11 @@ ul.linked-with-list li {
|
|||
border-bottom: 1px solid #d1d8dd;
|
||||
border-radius: 0px;
|
||||
}
|
||||
.liked-by-popover {
|
||||
min-width: 200px;
|
||||
margin-top: -10px;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
.liked-by-popover li {
|
||||
margin: 15px 0px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,8 +61,7 @@
|
|||
.empty-section {
|
||||
display: none !important;
|
||||
}
|
||||
.shaded-section,
|
||||
.timeline-item:nth-child(even) {
|
||||
.shaded-section {
|
||||
background-color: #fafbfc;
|
||||
}
|
||||
.modal .form-layout {
|
||||
|
|
@ -87,37 +86,165 @@
|
|||
background-color: #e74c3c;
|
||||
}
|
||||
.timeline {
|
||||
border: 1px solid #d1d8dd;
|
||||
margin: 30px 0px;
|
||||
}
|
||||
.timeline .timeline-head .comment-input {
|
||||
height: auto;
|
||||
}
|
||||
.timeline-item {
|
||||
margin-top: 0px;
|
||||
padding: 15px 30px 7px;
|
||||
border-bottom: 1px solid #d1d8dd;
|
||||
}
|
||||
.timeline-item .icon-fixed-width {
|
||||
text-align: center;
|
||||
}
|
||||
.timeline-item blockquote {
|
||||
font-size: inherit;
|
||||
}
|
||||
.timeline-item:last-child {
|
||||
border-bottom: 0px;
|
||||
.timeline-items {
|
||||
position: relative;
|
||||
}
|
||||
.timeline-item .reply {
|
||||
margin-top: 5px;
|
||||
.timeline {
|
||||
position: relative;
|
||||
}
|
||||
.timeline::before {
|
||||
content: " ";
|
||||
border-left: 1px solid #d1d8dd;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
bottom: -124px;
|
||||
left: 43px;
|
||||
z-index: -1;
|
||||
}
|
||||
@media (max-width: 991px) {
|
||||
.timeline::before {
|
||||
bottom: -64px;
|
||||
}
|
||||
}
|
||||
.timeline-item.user-content {
|
||||
margin: 30px 0px 30px 30px;
|
||||
}
|
||||
.timeline-item.user-content .media-body {
|
||||
border: 1px solid #d1d8dd;
|
||||
border-radius: 2px;
|
||||
margin-left: -7px;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
}
|
||||
.timeline-item.user-content .comment-header {
|
||||
background-color: #fafbfc;
|
||||
padding: 7px 15px;
|
||||
margin: 0px;
|
||||
color: #8D99A6;
|
||||
border-bottom: 1px solid #EBEFF2;
|
||||
}
|
||||
.timeline-item.user-content .comment-header .octicon-heart {
|
||||
color: #ff5858;
|
||||
cursor: pointer;
|
||||
}
|
||||
.timeline-item.user-content .reply {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
.timeline-item.user-content .reply > div > p:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
.timeline-item.user-content .reply > div > p:last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.timeline-item.user-content .close-btn-container {
|
||||
padding: 2px 15px;
|
||||
}
|
||||
.timeline-item.user-content .comment-likes {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.timeline-item.user-content .media-body:after,
|
||||
.timeline-item.user-content .media-body:before {
|
||||
right: 100%;
|
||||
top: 15px;
|
||||
border: solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
.timeline-item.user-content .media-body:after {
|
||||
border-color: rgba(136, 183, 213, 0);
|
||||
border-right-color: #fafbfc;
|
||||
border-width: 6px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
.timeline-item.user-content .media-body:before {
|
||||
border-color: rgba(194, 225, 245, 0);
|
||||
border-right-color: #d1d8dd;
|
||||
border-width: 7px;
|
||||
margin-top: -7px;
|
||||
}
|
||||
.timeline-item.notification-content {
|
||||
padding-left: 30px;
|
||||
margin: 30px 0px;
|
||||
position: relative;
|
||||
color: #8D99A6;
|
||||
}
|
||||
.timeline-item.notification-content * {
|
||||
color: #8D99A6;
|
||||
}
|
||||
.timeline-item.notification-content .icon-fixed-width {
|
||||
margin-left: 36px;
|
||||
}
|
||||
.timeline-item.notification-content .octicon-heart {
|
||||
color: #ff5858 !important;
|
||||
}
|
||||
.timeline-item.notification-content::before {
|
||||
content: " ";
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
background-color: #d1d8dd;
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
border-radius: 50%;
|
||||
top: 5px;
|
||||
}
|
||||
.timeline-item .reply-link {
|
||||
padding: 0px 7px;
|
||||
}
|
||||
.timeline-item h6,
|
||||
.timeline-head h6 {
|
||||
margin-top: 6px;
|
||||
padding-left: 7px;
|
||||
}
|
||||
.timeline-head {
|
||||
background-color: white;
|
||||
border: 1px solid #d1d8dd;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.timeline-head .comment-input-header {
|
||||
background-color: #fafbfc;
|
||||
padding: 15px 30px;
|
||||
border-bottom: 1px solid #d1d8dd;
|
||||
padding: 7px 15px;
|
||||
border-bottom: 1px solid #EBEFF2;
|
||||
}
|
||||
.timeline-head .comment-input-container {
|
||||
padding: 15px;
|
||||
}
|
||||
.timeline-head .comment-input {
|
||||
border-color: #EBEFF2;
|
||||
max-width: 100%;
|
||||
}
|
||||
.timeline-head .comment-input:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.timeline-head {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-radius: 0px;
|
||||
}
|
||||
}
|
||||
.timeline-new-email {
|
||||
margin: 30px 0px;
|
||||
padding: 0px 65px;
|
||||
position: relative;
|
||||
}
|
||||
.timeline-new-email::before {
|
||||
content: " ";
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
background-color: #d1d8dd;
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
border-radius: 50%;
|
||||
top: 5px;
|
||||
}
|
||||
.form-footer h5 {
|
||||
margin: 15px 0px;
|
||||
|
|
|
|||
|
|
@ -48,3 +48,11 @@
|
|||
.indicator-right.darkgrey::after {
|
||||
background: #b8c2cc;
|
||||
}
|
||||
.indicator.yellow::before,
|
||||
.indicator-right.yellow::after {
|
||||
background: #FEEF72;
|
||||
}
|
||||
.indicator.light-blue::before,
|
||||
.indicator-right.light-blue::after {
|
||||
background: #7CD6FD;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,6 +98,13 @@
|
|||
.doclist-row {
|
||||
font-size: 12px;
|
||||
}
|
||||
.doclist-row .likes-count {
|
||||
display: inline-block;
|
||||
width: 15px;
|
||||
margin-left: -5px;
|
||||
color: #8D99A6;
|
||||
font-size: 12px;
|
||||
}
|
||||
.doclist-row .docstatus .octicon {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
|
@ -134,14 +141,14 @@
|
|||
margin: 0px -15px;
|
||||
padding: 5px 15px;
|
||||
}
|
||||
.listview-main-section .icon-star {
|
||||
.listview-main-section .octicon-heart {
|
||||
cursor: pointer;
|
||||
}
|
||||
.list-row-head .icon-star {
|
||||
vertical-align: middle;
|
||||
.list-row-head .octicon-heart {
|
||||
margin-right: 13px;
|
||||
}
|
||||
.star-action.icon-star {
|
||||
color: #ffdb4c;
|
||||
.like-action.octicon-heart {
|
||||
color: #ff5858;
|
||||
}
|
||||
.list-id {
|
||||
font-weight: bold;
|
||||
|
|
@ -163,3 +170,8 @@
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
.list-comment-count {
|
||||
display: inline-block;
|
||||
width: 37px;
|
||||
text-align: left;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -330,20 +330,6 @@ body {
|
|||
border-left-color: transparent !important;
|
||||
border-right-color: transparent !important;
|
||||
}
|
||||
.timeline {
|
||||
border-left: none !important;
|
||||
border-right: none !important;
|
||||
}
|
||||
.timeline .timeline-head {
|
||||
padding: 7px 15px;
|
||||
}
|
||||
.timeline .timeline-item {
|
||||
padding: 15px;
|
||||
border-bottom: 1px dashed #d1d8dd;
|
||||
}
|
||||
.timeline .timeline-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.list-row {
|
||||
padding: 13px 15px !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -161,6 +161,10 @@ body[data-route^="Module"] .main-menu .form-sidebar {
|
|||
.form-sidebar .form-viewers .shared-with-everyone .octicon {
|
||||
color: #36414C !important;
|
||||
}
|
||||
.form-sidebar .liked-by .octicon-heart {
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.form-sidebar .form-shared .share-doc-btn:hover,
|
||||
.form-sidebar .form-shared .share-doc-btn:focus,
|
||||
.form-sidebar .form-shared .share-doc-btn:active {
|
||||
|
|
@ -174,6 +178,7 @@ body[data-route^="Module"] .main-menu .form-sidebar {
|
|||
.sidebar-left .form-sidebar .form-tags,
|
||||
.sidebar-left .form-sidebar .assignment-row,
|
||||
.sidebar-left .form-sidebar .form-shared,
|
||||
.sidebar-left .form-sidebar .liked-by,
|
||||
.sidebar-left .form-sidebar .modified-by,
|
||||
.sidebar-left .form-sidebar .created-by,
|
||||
.sidebar-left .form-sidebar .tags-label,
|
||||
|
|
|
|||
|
|
@ -264,6 +264,16 @@ a.no-decoration:active {
|
|||
height: 0;
|
||||
padding-bottom: 100%;
|
||||
}
|
||||
.avatar-frame {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding: 50% 0px;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center top;
|
||||
border-radius: 0.5em;
|
||||
border: 1px solid #EBEFF2;
|
||||
}
|
||||
.indicator,
|
||||
.indicator-right {
|
||||
background: none;
|
||||
|
|
@ -314,6 +324,14 @@ a.no-decoration:active {
|
|||
.indicator-right.darkgrey::after {
|
||||
background: #b8c2cc;
|
||||
}
|
||||
.indicator.yellow::before,
|
||||
.indicator-right.yellow::after {
|
||||
background: #FEEF72;
|
||||
}
|
||||
.indicator.light-blue::before,
|
||||
.indicator-right.light-blue::after {
|
||||
background: #7CD6FD;
|
||||
}
|
||||
.navbar-brand {
|
||||
max-width: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +1,21 @@
|
|||
<div class="timeline">
|
||||
<div class="timeline-head">
|
||||
<h6 class="text-muted">{%= __("Add a comment") %}</h6>
|
||||
<div>
|
||||
<textarea style="height: 80px" style="margin-top: 10px;"
|
||||
class="form-control"></textarea>
|
||||
<div class="comment-input-header">
|
||||
<span class="small text-muted">{%= __("Add a comment") %}</span>
|
||||
<button class="btn btn-default btn-comment btn-xs pull-right">
|
||||
{%= __("Comment") %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="comment-input-container">
|
||||
<textarea class="form-control comment-input"></textarea>
|
||||
<input type="data" class="hidden mention-input">
|
||||
</div>
|
||||
<div class="media">
|
||||
<span class="pull-left avatar avatar-medium">
|
||||
<img class="media-object" src="{%= image %}">
|
||||
</span>
|
||||
<div class="media-body">
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<h6>{%= __("You") %}</h6>
|
||||
</div>
|
||||
<div class="col-xs-8 text-right" style="margin-top: 2px;">
|
||||
<button class="btn btn-primary btn-go btn-xs pull-right">
|
||||
{%= __("Comment") %}
|
||||
</button>
|
||||
<div class="checkbox text-muted small pull-right"
|
||||
style="margin-top: 3px; margin-right: 15px;">
|
||||
<label>
|
||||
<input type="checkbox" class="is-email" style="margin-top: 1px;">
|
||||
{%= __("Email") %}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="timeline-new-email">
|
||||
<button class="btn btn-default btn-new-email btn-xs">
|
||||
{%= __("New Email") %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="timeline-items">
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -14,33 +14,31 @@ frappe.ui.form.Comments = Class.extend({
|
|||
this.list = this.wrapper.find(".timeline-items");
|
||||
this.input = this.wrapper.find(".form-control");
|
||||
|
||||
this.button = this.wrapper.find(".btn-go")
|
||||
this.comment_button = this.wrapper.find(".btn-comment")
|
||||
.on("click", function() {
|
||||
if(me.wrapper.find(".is-email").prop("checked")) {
|
||||
new frappe.views.CommunicationComposer({
|
||||
doc: me.frm.doc,
|
||||
txt: frappe.markdown(me.input.val()),
|
||||
frm: me.frm,
|
||||
recipients: me.get_recipient()
|
||||
})
|
||||
} else {
|
||||
me.add_comment(this);
|
||||
}
|
||||
me.add_comment(this);
|
||||
});
|
||||
|
||||
this.input.keydown("meta+return ctrl+return", function(e) {
|
||||
me.button.trigger("click");
|
||||
me.comment_button.trigger("click");
|
||||
});
|
||||
|
||||
this.email_check = this.wrapper.find(".timeline-head input[type='checkbox']")
|
||||
.on("change", function() {
|
||||
me.button.html($(this).prop("checked") ? __("Compose") : __("Comment"));
|
||||
this.email_button = this.wrapper.find(".btn-new-email")
|
||||
.on("click", function() {
|
||||
new frappe.views.CommunicationComposer({
|
||||
doc: me.frm.doc,
|
||||
txt: frappe.markdown(me.input.val()),
|
||||
frm: me.frm,
|
||||
recipients: me.get_recipient()
|
||||
})
|
||||
});
|
||||
|
||||
this.list.on("click", ".toggle-blockquote", function() {
|
||||
$(this).parent().siblings("blockquote").toggleClass("hidden");
|
||||
});
|
||||
|
||||
this.setup_comment_like();
|
||||
|
||||
this.setup_mentions();
|
||||
},
|
||||
|
||||
|
|
@ -116,7 +114,7 @@ frappe.ui.form.Comments = Class.extend({
|
|||
|
||||
prepare_comment: function(c) {
|
||||
if((c.comment_type || "Comment") === "Comment" && frappe.model.can_delete("Comment")) {
|
||||
c["delete"] = '<a class="close" href="#">×</a>';
|
||||
c["delete"] = '<a class="close" href="#"><i class="octicon octicon-trashcan"></i></a>';
|
||||
} else {
|
||||
c["delete"] = "";
|
||||
}
|
||||
|
|
@ -130,7 +128,7 @@ frappe.ui.form.Comments = Class.extend({
|
|||
c.image = frappe.user_info(c.comment_by).image
|
||||
|| frappe.get_gravatar(c.comment_by);
|
||||
c.comment_on = comment_when(c.creation);
|
||||
c.fullname = frappe.user_info(c.comment_by).fullname;
|
||||
c.fullname = frappe.user.full_name(c.comment_by);
|
||||
|
||||
if(c.attachments && typeof c.attachments==="string")
|
||||
c.attachments = JSON.parse(c.attachments);
|
||||
|
|
@ -167,6 +165,15 @@ frappe.ui.form.Comments = Class.extend({
|
|||
if(c.comment_type==="Comment") {
|
||||
c.comment_html = c.comment_html.replace(/(^|\W)(@\w+)/g, "$1<b>$2</b>");
|
||||
}
|
||||
|
||||
if (in_list(["Comment", "Email"], c.comment_type)) {
|
||||
c.user_content = true;
|
||||
if (!$.isArray(c._liked_by)) {
|
||||
c._liked_by = JSON.parse(c._liked_by || "[]");
|
||||
}
|
||||
|
||||
c.liked_by_user = c._liked_by.indexOf(user)!==-1;
|
||||
}
|
||||
}
|
||||
},
|
||||
set_icon_and_color: function(c) {
|
||||
|
|
@ -186,7 +193,8 @@ frappe.ui.form.Comments = Class.extend({
|
|||
"Attachment": "octicon octicon-cloud-upload",
|
||||
"Attachment Removed": "octicon octicon-trashcan",
|
||||
"Shared": "octicon octicon-eye",
|
||||
"Unshared": "octicon octicon-circle-slash"
|
||||
"Unshared": "octicon octicon-circle-slash",
|
||||
"Like": "octicon octicon-heart"
|
||||
}[c.comment_type]
|
||||
|
||||
c.color = {
|
||||
|
|
@ -348,10 +356,13 @@ frappe.ui.form.Comments = Class.extend({
|
|||
|
||||
this.mention_input = this.wrapper.find(".mention-input");
|
||||
|
||||
var source = Object.keys(username_user_map);
|
||||
source.sort();
|
||||
|
||||
this.mention_input.autocomplete({
|
||||
minLength: 0,
|
||||
autoFocus: true,
|
||||
source: Object.keys(username_user_map),
|
||||
source: source,
|
||||
select: function(event, ui) {
|
||||
var value = ui.item.value;
|
||||
var textarea_value = me.input.val();
|
||||
|
|
@ -467,9 +478,21 @@ frappe.ui.form.Comments = Class.extend({
|
|||
me.mention_widget.trigger(me.enter);
|
||||
|
||||
// prevent default
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (e.which==me.codes.TAB) {
|
||||
me.comment_button.focus();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setup_comment_like: function() {
|
||||
this.wrapper.on("click", ".comment-likes .octicon-heart", frappe.ui.click_toggle_like);
|
||||
|
||||
frappe.ui.setup_like_popover(this.wrapper, ".comment-likes");
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
<div class="media timeline-item" data-name="{%= data.name %}">
|
||||
<div class="media timeline-item {% if (data.user_content) { %} user-content {% } else { %} notification-content {% } %}" data-doctype="{{ data.doctype }}" data-name="{%= data.name %}">
|
||||
{% if (data.user_content) { %}
|
||||
<span class="pull-left avatar avatar-medium">
|
||||
<img class="media-object" src="{%= data.image %}">
|
||||
<div class="avatar-frame" style="background-image: url({%= data.image %})"></div>
|
||||
</span>
|
||||
<div class="pull-left media-body" style="max-width: calc(100% - 41px); padding-right: 0px;">
|
||||
<div>
|
||||
<div class="pull-right">
|
||||
{% } %}
|
||||
|
||||
<div class="pull-left media-body" style="max-width: calc(100% - 50px); padding-right: 0px;">
|
||||
<div class="media-content-wrapper">
|
||||
<div class="pull-right close-btn-container">
|
||||
<span class="small text-muted">
|
||||
{%= data.delete %}
|
||||
</span>
|
||||
</div>
|
||||
{% if(data.doctype=="Communication" || data.comment_type=="Comment") { %}
|
||||
<h6>
|
||||
<div class="comment-header small">
|
||||
<i class="{%= data.icon %} icon-fixed-width"></i>
|
||||
<span title="{%= data.comment_by %}">{%= data.fullname %}</span>
|
||||
<span class="text-muted" style="font-weight: normal;">
|
||||
|
|
@ -47,14 +50,23 @@
|
|||
<a class="text-muted reply-link pull-right"
|
||||
data-name="{%= data.name %}">{%= __("Reply") %}</a>
|
||||
{% } %}
|
||||
</h6>
|
||||
<span class="comment-likes" data-liked-by=\'{{ JSON.stringify(data._liked_by) }}\'>
|
||||
<i class="octicon octicon-heart like-action
|
||||
{% if (!data.liked_by_user) { %}
|
||||
text-extra-muted not-liked
|
||||
{% } %} "
|
||||
data-doctype="{%= data.doctype %}"
|
||||
data-name="{%= data.name %}"></i>
|
||||
<span class="likes-count text-muted">{{ data._liked_by.length }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="reply">
|
||||
<div>
|
||||
{%= data.comment_html %}
|
||||
</div>
|
||||
</div>
|
||||
{% } else if(in_list(["Assignment Completed", "Assigned", "Shared", "Unshared"], data.comment_type)) { %}
|
||||
<h6>
|
||||
<div class="small">
|
||||
<i class="{%= data.icon %} icon-fixed-width"></i>
|
||||
{% if(data.reference_doctype && data.reference_name) { %}
|
||||
<a href="#Form/{%= data.reference_doctype %}/{%= data.reference_name %}">
|
||||
|
|
@ -65,14 +77,19 @@
|
|||
{% } %}
|
||||
<span class="text-muted" style="font-weight: normal;">
|
||||
– {%= data.comment_on %}</span>
|
||||
</h6>
|
||||
</div>
|
||||
{% } else { %}
|
||||
<h6>
|
||||
<div class="small">
|
||||
<i class="{%= data.icon %} icon-fixed-width"></i>
|
||||
<span title="{%= data.comment_by %}">{%= data.fullname %}</span> {%= data.comment %}
|
||||
<span class="text-muted" style="font-weight: normal;">
|
||||
– {%= data.comment_on %}</span>
|
||||
</h6>
|
||||
{% if (data.comment_type == "Like") { %}
|
||||
<span title="{%= data.comment_by %}">{%= __("Liked by {0}", [data.fullname]) %}</span>
|
||||
{% } else { %}
|
||||
<span title="{%= data.comment_by %}">{%= data.fullname %}</span>
|
||||
{%= data.comment %}
|
||||
{% } %}
|
||||
<span class="text-muted" style="font-weight: normal;">
|
||||
– {%= data.comment_on %}</span>
|
||||
</div>
|
||||
{% } %}
|
||||
{% if(data.attachments && data.attachments.length) { %}
|
||||
<div style="margin: 10px 0px">
|
||||
|
|
|
|||
|
|
@ -45,6 +45,12 @@
|
|||
<li class="form-viewers"></li>
|
||||
</ul>
|
||||
<ul class="list-unstyled sidebar-menu text-muted">
|
||||
<li class="liked-by-parent">
|
||||
<span class="liked-by">
|
||||
<i class="octicon octicon-heart like-action text-extra-muted"></i>
|
||||
<span class="like-count"></span>
|
||||
</span>
|
||||
</li>
|
||||
<li class="modified-by"></li>
|
||||
<li class="created-by"></li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ frappe.form.formatters = {
|
|||
|
||||
return frappe.form.formatters.Data(value);
|
||||
},
|
||||
StarredBy: function(value) {
|
||||
LikedBy: function(value) {
|
||||
var html = "";
|
||||
$.each(JSON.parse(value || "[]"), function(i, v) {
|
||||
if(v) html+= '<span class="avatar avatar-small" \
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ frappe.ui.form.Sidebar = Class.extend({
|
|||
this.make_shared();
|
||||
this.make_viewers();
|
||||
this.make_tags();
|
||||
this.make_like();
|
||||
|
||||
this.bind_events();
|
||||
|
||||
|
|
@ -37,6 +38,12 @@ frappe.ui.form.Sidebar = Class.extend({
|
|||
$(".offcanvas").removeClass("active-left active-right");
|
||||
frappe.ui.scroll(me.frm.footer.wrapper.find(".form-comments"), true);
|
||||
});
|
||||
|
||||
this.like_icon.on("click", function() {
|
||||
frappe.ui.toggle_like(me.like_icon, me.frm.doctype, me.frm.doc.name, function() {
|
||||
me.refresh_like();
|
||||
});
|
||||
})
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
|
|
@ -55,6 +62,8 @@ frappe.ui.form.Sidebar = Class.extend({
|
|||
this.sidebar.find(".created-by").html(__("{0} created this {1}",
|
||||
["<strong>" + frappe.user.full_name(this.frm.doc.owner) + "</strong>",
|
||||
"<br>" + comment_when(this.frm.doc.creation)]));
|
||||
|
||||
this.refresh_like();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -113,4 +122,26 @@ frappe.ui.form.Sidebar = Class.extend({
|
|||
this.user_actions.addClass("hide")
|
||||
this.user_actions.find(".user-action-row").remove();
|
||||
},
|
||||
|
||||
make_like: function() {
|
||||
this.like_wrapper = this.sidebar.find(".liked-by");
|
||||
this.like_icon = this.sidebar.find(".liked-by .octicon-heart");
|
||||
this.like_count = this.sidebar.find(".liked-by .like-count");
|
||||
frappe.ui.setup_like_popover(this.sidebar.find(".liked-by-parent"), ".liked-by");
|
||||
},
|
||||
|
||||
refresh_like: function() {
|
||||
if (!this.like_icon) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.like_wrapper.attr("data-liked-by", this.frm.doc._liked_by);
|
||||
|
||||
this.like_icon.toggleClass("text-extra-muted not-liked",
|
||||
!frappe.ui.is_liked(this.frm.doc))
|
||||
.attr("data-doctype", this.frm.doctype)
|
||||
.attr("data-name", this.frm.doc.name);
|
||||
|
||||
this.like_count.text(JSON.parse(this.frm.doc._liked_by || "[]").length);
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ frappe.ui.form.Toolbar = Class.extend({
|
|||
this.page.clear_user_actions();
|
||||
this.show_title_as_dirty();
|
||||
this.set_primary_action();
|
||||
this.refresh_star();
|
||||
|
||||
if(this.frm.meta.hide_toolbar) {
|
||||
this.page.hide_menu();
|
||||
|
|
@ -23,11 +22,9 @@ frappe.ui.form.Toolbar = Class.extend({
|
|||
if(this.frm.doc.__islocal) {
|
||||
this.page.hide_menu();
|
||||
this.print_icon && this.print_icon.addClass("hide");
|
||||
this.star_icon && this.star_icon.addClass("hide");
|
||||
} else {
|
||||
this.page.show_menu();
|
||||
this.print_icon && this.print_icon.removeClass("hide");
|
||||
this.star_icon && this.star_icon.removeClass("hide");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -97,13 +94,6 @@ frappe.ui.form.Toolbar = Class.extend({
|
|||
this.page.clear_indicator();
|
||||
}
|
||||
},
|
||||
refresh_star: function() {
|
||||
this.star_icon &&
|
||||
this.star_icon.toggleClass("text-extra-muted not-starred",
|
||||
!frappe.ui.is_starred(this.frm.doc))
|
||||
.attr("data-doctype", this.frm.doctype)
|
||||
.attr("data-name", this.frm.doc.name);
|
||||
},
|
||||
make_menu: function() {
|
||||
var me = this;
|
||||
var p = this.frm.perm[0];
|
||||
|
|
@ -117,13 +107,6 @@ frappe.ui.form.Toolbar = Class.extend({
|
|||
me.frm.print_doc();});
|
||||
}
|
||||
|
||||
// star
|
||||
if(!this.frm.meta.issingle) {
|
||||
this.star_icon = this.page.add_action_icon("icon-star", function() {
|
||||
frappe.ui.toggle_star(me.star_icon, me.frm.doctype, me.frm.doc.name);
|
||||
}).removeClass("text-muted").find(".icon-star").addClass("star-action");
|
||||
}
|
||||
|
||||
// email
|
||||
if(frappe.model.can_email(null, me.frm) && me.frm.doc.docstatus < 2) {
|
||||
this.page.add_menu_item(__("Email"), function() {
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
this.setup_filterable();
|
||||
this.init_filters();
|
||||
this.init_headers();
|
||||
this.init_star();
|
||||
this.init_like();
|
||||
this.init_select_all();
|
||||
},
|
||||
|
||||
|
|
@ -168,9 +168,9 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
}
|
||||
});
|
||||
this.$page.find(".result-list").on("click", ".list-row-left", function(e) {
|
||||
// don't open in case of checkbox, star, filterable
|
||||
// don't open in case of checkbox, like, filterable
|
||||
if ((e.target.className || "").indexOf("filterable")!==-1
|
||||
|| (e.target.className || "").indexOf("icon-star")!==-1
|
||||
|| (e.target.className || "").indexOf("octicon-heart")!==-1
|
||||
|| e.target.type==="checkbox") {
|
||||
return;
|
||||
}
|
||||
|
|
@ -307,8 +307,8 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
}
|
||||
}
|
||||
|
||||
this.list_header.find(".list-starred-by-me")
|
||||
.toggleClass("text-extra-muted not-starred", !this.is_star_filtered());
|
||||
this.list_header.find(".list-liked-by-me")
|
||||
.toggleClass("text-extra-muted not-liked", !this.is_star_filtered());
|
||||
|
||||
this.last_updated_on = new Date();
|
||||
this.dirty = false;
|
||||
|
|
@ -355,6 +355,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
filters: this.filter_list.get_filters(),
|
||||
order_by: this.listview.order_by || undefined,
|
||||
group_by: this.listview.group_by || undefined,
|
||||
with_comment_count: true
|
||||
}
|
||||
|
||||
// apply default filters, if specified for a listing
|
||||
|
|
@ -368,15 +369,15 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
this.filter_list.add_filter(this.doctype, "_assign", 'like', '%' + user + '%');
|
||||
this.run();
|
||||
},
|
||||
starred_by_me: function() {
|
||||
this.filter_list.add_filter(this.doctype, "_starred_by", 'like', '%' + user + '%');
|
||||
liked_by_me: function() {
|
||||
this.filter_list.add_filter(this.doctype, "_liked_by", 'like', '%' + user + '%');
|
||||
this.run();
|
||||
},
|
||||
remove_starred_by_me: function() {
|
||||
this.filter_list.get_filter("_starred_by").remove();
|
||||
remove_liked_by_me: function() {
|
||||
this.filter_list.get_filter("_liked_by").remove();
|
||||
},
|
||||
is_star_filtered: function() {
|
||||
return this.filter_list.filter_exists(this.doctype, "_starred_by", 'like', '%' + user + '%');
|
||||
return this.filter_list.filter_exists(this.doctype, "_liked_by", 'like', '%' + user + '%');
|
||||
},
|
||||
init_menu: function() {
|
||||
var me = this;
|
||||
|
|
@ -453,19 +454,20 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
}, true)
|
||||
},
|
||||
|
||||
init_star: function() {
|
||||
init_like: function() {
|
||||
var me = this;
|
||||
this.$page.find(".result-list").on("click", ".star-action", function() {
|
||||
frappe.ui.toggle_star($(this), me.doctype, $(this).attr("data-name"));
|
||||
return false;
|
||||
});
|
||||
this.list_header.find(".list-starred-by-me").on("click", function() {
|
||||
this.$page.find(".result-list").on("click", ".like-action", frappe.ui.click_toggle_like);
|
||||
this.list_header.find(".list-liked-by-me").on("click", function() {
|
||||
if (me.is_star_filtered()) {
|
||||
me.remove_starred_by_me();
|
||||
me.remove_liked_by_me();
|
||||
} else {
|
||||
me.starred_by_me();
|
||||
me.liked_by_me();
|
||||
}
|
||||
});
|
||||
|
||||
if (!frappe.dom.is_touchscreen()) {
|
||||
frappe.ui.setup_like_popover(this.$page.find(".result-list"), ".liked-by");
|
||||
}
|
||||
},
|
||||
|
||||
init_select_all: function() {
|
||||
|
|
@ -576,7 +578,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
return false;
|
||||
} else {
|
||||
// second filter set for this field
|
||||
if(fieldname=='_user_tags' || fieldname=="_starred_by") {
|
||||
if(fieldname=='_user_tags' || fieldname=="_liked_by") {
|
||||
// and for tags
|
||||
this.filter_list.add_filter(this.doctype, fieldname, 'like', '%' + label);
|
||||
} else {
|
||||
|
|
@ -587,7 +589,7 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
|
|||
} else {
|
||||
// no filter for this item,
|
||||
// setup one
|
||||
if(fieldname=='_user_tags' || fieldname=="_starred_by") {
|
||||
if(fieldname=='_user_tags' || fieldname=="_liked_by") {
|
||||
this.filter_list.add_filter(this.doctype, fieldname, 'like', '%' + label);
|
||||
} else {
|
||||
this.filter_list.add_filter(this.doctype, fieldname, '=', label);
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@
|
|||
title="{%= __("Select All") %}">
|
||||
{% } %}
|
||||
|
||||
<i class="icon-fixed-width icon-star text-extra-muted not-starred star-action list-starred-by-me"
|
||||
title="{%= __("Starred By Me") %}"></i>
|
||||
<i class="icon-fixed-width octicon octicon-heart text-extra-muted not-liked like-action list-liked-by-me"
|
||||
title="{%= __("Likes") %}"></i>
|
||||
|
||||
{% } %}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,9 +33,10 @@
|
|||
{% } else { %}
|
||||
<span class="avatar avatar-small avatar-empty"></span>
|
||||
{% } %}
|
||||
<span class="h6 {% if(!data._comments_list.length) { %}text-extra-muted{% } %}">
|
||||
<span class="list-comment-count small
|
||||
{% if(!data._comment_count) { %} text-extra-muted {% } else { %} text-muted {% } %}">
|
||||
<i class="octicon octicon-comment-discussion"></i>
|
||||
{%= data._comments_list.length || 0 %}
|
||||
{%= (data._comment_count > 99 ? "99+" : data._comment_count) || 0 %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="pull-right list-row-modified">
|
||||
|
|
|
|||
|
|
@ -2,13 +2,16 @@
|
|||
<input class="list-delete" type="checkbox"
|
||||
style="margin: 0 7px 0 0; vertical-align: middle;">
|
||||
{% } %}
|
||||
<i class="icon-star
|
||||
{% if (_starred_by.indexOf(_user)===-1) { %}
|
||||
text-extra-muted not-starred
|
||||
{% }%}
|
||||
icon-fixed-width star-action"
|
||||
data-name="{{ _name }}" data-doctype="{{ doctype }}">
|
||||
</i>
|
||||
<span class="liked-by" data-liked-by=\'{{ JSON.stringify(_liked_by) }}\'>
|
||||
<i class="octicon octicon-heart
|
||||
{% if (_liked_by.indexOf(_user)===-1) { %}
|
||||
text-extra-muted not-liked
|
||||
{% }%}
|
||||
icon-fixed-width like-action"
|
||||
data-name="{{ _name }}" data-doctype="{{ doctype }}">
|
||||
</i>
|
||||
<span class="likes-count">{{ (_liked_by.length > 9 ? "9+" : _liked_by.length) || "" }}</span>
|
||||
</span>
|
||||
<a class="grey list-id"
|
||||
style="margin-right: 7px;"
|
||||
href="#Form/{{ _doctype_encoded }}/{{ _name_encoded }}"
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ frappe.views.ListView = Class.extend({
|
|||
}
|
||||
|
||||
$.each(['name', 'owner', 'docstatus', '_user_tags', '_comments', 'modified',
|
||||
'modified_by', '_assign', '_starred_by'],
|
||||
'modified_by', '_assign', '_liked_by'],
|
||||
function(i, fieldname) { add_field(fieldname); })
|
||||
|
||||
// add title field
|
||||
|
|
@ -284,8 +284,8 @@ frappe.views.ListView = Class.extend({
|
|||
if(data.modified)
|
||||
this.prepare_when(data, data.modified);
|
||||
|
||||
data._starred_by = data._starred_by ?
|
||||
JSON.parse(data._starred_by) : [];
|
||||
data._liked_by = data._liked_by ?
|
||||
JSON.parse(data._liked_by) : [];
|
||||
|
||||
data._checkbox = (frappe.model.can_delete(this.doctype) || this.settings.selectable) && !this.no_delete
|
||||
|
||||
|
|
@ -311,7 +311,6 @@ frappe.views.ListView = Class.extend({
|
|||
}
|
||||
data._user = user;
|
||||
|
||||
data._comments_list = data._comments ? JSON.parse(data._comments) : [];
|
||||
data._tags = $.map((data._user_tags || "").split(","),
|
||||
function(v) { return v ? v : null; });
|
||||
data._assign_list = data._assign ? JSON.parse(data._assign) : [];
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ frappe.get_indicator = function(doc, doctype) {
|
|||
}
|
||||
|
||||
if(is_submittable && doc.docstatus==0) {
|
||||
return [__("Draft"), "red", "docstatus,=,0"];
|
||||
return [__("Draft"), "light-blue", "docstatus,=,0"];
|
||||
}
|
||||
|
||||
if(is_submittable && doc.docstatus==2) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ $.extend(frappe.model, {
|
|||
layout_fields: ['Section Break', 'Column Break', 'Fold'],
|
||||
|
||||
std_fields_list: ['name', 'owner', 'creation', 'modified', 'modified_by',
|
||||
'_user_tags', '_comments', '_assign', '_starred_by', 'docstatus',
|
||||
'_user_tags', '_comments', '_assign', '_liked_by', 'docstatus',
|
||||
'parent', 'parenttype', 'parentfield', 'idx'],
|
||||
|
||||
std_fields: [
|
||||
|
|
@ -21,7 +21,7 @@ $.extend(frappe.model, {
|
|||
{fieldname:'modified', fieldtype:'Date', label:__('Last Updated On')},
|
||||
{fieldname:'modified_by', fieldtype:'Data', label:__('Last Updated By')},
|
||||
{fieldname:'_user_tags', fieldtype:'Data', label:__('Tags')},
|
||||
{fieldname:'_starred_by', fieldtype:'Data', label:__('Starred By')},
|
||||
{fieldname:'_liked_by', fieldtype:'Data', label:__('Liked By')},
|
||||
{fieldname:'_comments', fieldtype:'Text', label:__('Comments')},
|
||||
{fieldname:'_assign', fieldtype:'Text', label:__('Assigned To')},
|
||||
{fieldname:'docstatus', fieldtype:'Int', label:__('Document Status')},
|
||||
|
|
|
|||
42
frappe/public/js/frappe/query_string.js
Normal file
42
frappe/public/js/frappe/query_string.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
function get_url_arg(name) {
|
||||
return get_query_params()[name] || "";
|
||||
}
|
||||
|
||||
function get_query_params(query_string) {
|
||||
var query_params = {};
|
||||
if (!query_string) {
|
||||
query_string = location.search.substring(1);
|
||||
}
|
||||
|
||||
var query_list = query_string.split("&");
|
||||
for (var i=0, l=query_list.length; i < l; i++ ){
|
||||
var pair = query_list[i].split("=");
|
||||
var key = pair[0];
|
||||
if (!key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var value = pair[1];
|
||||
if (typeof value === "string") {
|
||||
value = decodeURIComponent(value);
|
||||
}
|
||||
|
||||
if (key in query_params) {
|
||||
if (typeof query_params[key] === undefined) {
|
||||
query_params[key] = [];
|
||||
} else if (typeof query_params[key] === "string") {
|
||||
query_params[key] = [query_params[key]];
|
||||
}
|
||||
query_params[key].push(value);
|
||||
} else {
|
||||
query_params[key] = value;
|
||||
}
|
||||
}
|
||||
return query_params;
|
||||
}
|
||||
|
||||
function make_query_string(obj) {
|
||||
var query_params = [];
|
||||
$.each(obj, function(k, v) { query_params.push(encodeURIComponent(k) + "=" + encodeURIComponent(v)); });
|
||||
return "?" + query_params.join("&");
|
||||
}
|
||||
|
|
@ -53,7 +53,15 @@ frappe.route = function() {
|
|||
|
||||
frappe.get_route = function(route) {
|
||||
// for app
|
||||
return frappe.get_route_str(route).split('/')
|
||||
var route = frappe.get_route_str(route).split('/')
|
||||
var parts = route[route.length - 1].split("?");
|
||||
route[route.length - 1] = parts[0];
|
||||
if (parts.length > 1) {
|
||||
var query_params = get_query_params(parts[1]);
|
||||
frappe.route_options = $.extend(frappe.route_options || {}, query_params);
|
||||
}
|
||||
|
||||
return route;
|
||||
}
|
||||
|
||||
frappe.get_prev_route = function() {
|
||||
|
|
|
|||
128
frappe/public/js/frappe/ui/like.js
Normal file
128
frappe/public/js/frappe/ui/like.js
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// MIT License. See license.txt
|
||||
|
||||
frappe.ui.is_liked = function(doc) {
|
||||
var liked = frappe.ui.get_liked_by(doc);
|
||||
return liked.indexOf(user)===-1 ? false : true;
|
||||
}
|
||||
|
||||
frappe.ui.get_liked_by = function(doc) {
|
||||
var liked = doc._liked_by;
|
||||
if(liked) {
|
||||
liked = JSON.parse(liked);
|
||||
}
|
||||
|
||||
return liked || [];
|
||||
}
|
||||
|
||||
frappe.ui.toggle_like = function($btn, doctype, name, callback) {
|
||||
var add = $btn.hasClass("not-liked") ? "Yes" : "No";
|
||||
// disable click
|
||||
$btn.css('pointer-events', 'none');
|
||||
|
||||
frappe.call({
|
||||
method: "frappe.desk.like.toggle_like",
|
||||
quiet: true,
|
||||
args: {
|
||||
doctype: doctype,
|
||||
name: name,
|
||||
add: add,
|
||||
},
|
||||
callback: function(r) {
|
||||
// renable click
|
||||
$btn.css('pointer-events', 'auto');
|
||||
|
||||
if(!r.exc) {
|
||||
// update in all local-buttons
|
||||
var action_buttons = $('.like-action[data-name="'+ name.replace(/"/g, '\"')
|
||||
+'"][data-doctype="'+ doctype.replace(/"/g, '\"')+'"]');
|
||||
|
||||
if(add==="Yes") {
|
||||
action_buttons.removeClass("not-liked text-extra-muted");
|
||||
} else {
|
||||
action_buttons.addClass("not-liked text-extra-muted");
|
||||
}
|
||||
|
||||
// update in locals (form)
|
||||
var doc = locals[doctype] && locals[doctype][name];
|
||||
if(doc) {
|
||||
var liked_by = JSON.parse(doc._liked_by || "[]"),
|
||||
idx = liked_by.indexOf(user);
|
||||
if(add==="Yes") {
|
||||
if(idx===-1)
|
||||
liked_by.push(user);
|
||||
} else {
|
||||
if(idx!==-1) {
|
||||
liked_by = liked_by.slice(0,idx).concat(liked_by.slice(idx+1))
|
||||
}
|
||||
}
|
||||
doc._liked_by = JSON.stringify(liked_by);
|
||||
}
|
||||
|
||||
if(callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
frappe.ui.click_toggle_like = function() {
|
||||
var $btn = $(this);
|
||||
var $count = $btn.siblings(".likes-count");
|
||||
var not_liked = $btn.hasClass("not-liked");
|
||||
var doctype = $btn.attr("data-doctype");
|
||||
var name = $btn.attr("data-name");
|
||||
|
||||
frappe.ui.toggle_like($btn, doctype, name, function() {
|
||||
if (not_liked) {
|
||||
$count.text(cint($count.text()) + 1);
|
||||
} else {
|
||||
$count.text(cint($count.text()) - 1);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
frappe.ui.setup_like_popover = function($parent, selector) {
|
||||
if (frappe.dom.is_touchscreen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parent.on("mouseover", selector, function() {
|
||||
var $wrapper = $(this);
|
||||
|
||||
$wrapper.popover({
|
||||
animation: true,
|
||||
placement: "right",
|
||||
content: function() {
|
||||
var liked_by = JSON.parse($wrapper.attr('data-liked-by') || "[]");
|
||||
|
||||
// hack
|
||||
if ($wrapper.find(".not-liked").length) {
|
||||
if (liked_by.indexOf(user)!==-1) {
|
||||
liked_by.splice(liked_by.indexOf(user), 1);
|
||||
}
|
||||
} else {
|
||||
if (liked_by.indexOf(user)===-1) {
|
||||
liked_by.push(user);
|
||||
}
|
||||
}
|
||||
|
||||
if (!liked_by.length) {
|
||||
return "";
|
||||
}
|
||||
return frappe.render_template("liked_by", {"liked_by": liked_by});
|
||||
},
|
||||
html: true,
|
||||
container: 'body'
|
||||
});
|
||||
|
||||
$wrapper.popover('show');
|
||||
});
|
||||
|
||||
$parent.on("mouseout", selector, function() {
|
||||
$(this).popover('destroy');
|
||||
});
|
||||
}
|
||||
8
frappe/public/js/frappe/ui/liked_by.html
Normal file
8
frappe/public/js/frappe/ui/liked_by.html
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<ul class="list-unstyled liked-by-popover">
|
||||
{% for (var i in liked_by) { var liked_by_user = liked_by[i]; %}
|
||||
<li>
|
||||
{%= frappe.avatar(liked_by_user) %}
|
||||
<span>{%= frappe.user.full_name(liked_by_user) %}</span>
|
||||
</li>
|
||||
{% } %}
|
||||
</ul>
|
||||
|
|
@ -318,7 +318,7 @@ frappe.ui.Listing = Class.extend({
|
|||
} else {
|
||||
// no filter for this item,
|
||||
// setup one
|
||||
if(['_user_tags', '_comments', '_assign', '_starred_by'].indexOf(fieldname)!==-1) {
|
||||
if(['_user_tags', '_comments', '_assign', '_liked_by'].indexOf(fieldname)!==-1) {
|
||||
this.filter_list.add_filter(doctype, fieldname,
|
||||
'like', '%' + label + '%');
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// MIT License. See license.txt
|
||||
|
||||
frappe.ui.is_starred = function(doc) {
|
||||
var starred = frappe.ui.get_starred_by(doc);
|
||||
return starred.indexOf(user)===-1 ? false : true;
|
||||
}
|
||||
|
||||
frappe.ui.get_starred_by = function(doc) {
|
||||
var starred = doc._starred_by;
|
||||
if(starred) {
|
||||
starred = JSON.parse(starred);
|
||||
}
|
||||
|
||||
return starred || [];
|
||||
}
|
||||
|
||||
frappe.ui.toggle_star = function($btn, doctype, name) {
|
||||
var add = $btn.hasClass("not-starred") ? "Yes" : "No";
|
||||
frappe.call({
|
||||
method: "frappe.desk.star.toggle_star",
|
||||
quiet: true,
|
||||
args: {
|
||||
doctype: doctype,
|
||||
name: name,
|
||||
add: add,
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
// update in all local-buttons
|
||||
var action_buttons = $('.star-action[data-name="'+ name.replace(/"/g, '\"')
|
||||
+'"][data-doctype="'+ doctype.replace(/"/g, '\"')+'"]');
|
||||
|
||||
if(add==="Yes") {
|
||||
action_buttons.removeClass("not-starred").removeClass("text-extra-muted");
|
||||
} else {
|
||||
action_buttons.addClass("not-starred").addClass("text-extra-muted");
|
||||
}
|
||||
|
||||
// update in locals (form)
|
||||
var doc = locals[doctype] && locals[doctype][name];
|
||||
if(doc) {
|
||||
var starred_by = JSON.parse(doc._starred_by || "[]"),
|
||||
idx = starred_by.indexOf(user);
|
||||
if(add==="Yes") {
|
||||
if(idx===-1)
|
||||
starred_by.push(user);
|
||||
} else {
|
||||
if(idx!==-1) {
|
||||
starred_by = starred_by.slice(0,idx).concat(starred_by.slice(idx+1))
|
||||
}
|
||||
}
|
||||
doc._starred_by = JSON.stringify(starred_by);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -4,6 +4,7 @@ frappe.ui.notifications.update_notifications = function() {
|
|||
frappe.ui.notifications.total = 0;
|
||||
var doctypes = keys(frappe.boot.notification_info.open_count_doctype).sort();
|
||||
var modules = keys(frappe.boot.notification_info.open_count_module).sort();
|
||||
var other = keys(frappe.boot.notification_info.open_count_other).sort();
|
||||
|
||||
// clear toolbar / sidebar notifications
|
||||
frappe.ui.notifications.navbar_notification = $("#navbar-notification").empty();
|
||||
|
|
@ -14,6 +15,12 @@ frappe.ui.notifications.update_notifications = function() {
|
|||
frappe.ui.notifications.add_notification("ToDo");
|
||||
frappe.ui.notifications.add_notification("Event");
|
||||
|
||||
// add other
|
||||
$.each(other, function(i, name) {
|
||||
frappe.ui.notifications.add_notification(name, frappe.boot.notification_info.open_count_other);
|
||||
});
|
||||
|
||||
|
||||
// add a divider
|
||||
if(frappe.ui.notifications.total) {
|
||||
var divider = '<li class="divider"></li>';
|
||||
|
|
@ -34,6 +41,8 @@ frappe.ui.notifications.update_notifications = function() {
|
|||
var config = frappe.ui.notifications.config[doctype] || {};
|
||||
if (config.route) {
|
||||
frappe.set_route(config.route);
|
||||
} else if (config.click) {
|
||||
config.click();
|
||||
} else {
|
||||
frappe.views.show_open_count_list(this);
|
||||
}
|
||||
|
|
@ -46,8 +55,12 @@ frappe.ui.notifications.update_notifications = function() {
|
|||
|
||||
}
|
||||
|
||||
frappe.ui.notifications.add_notification = function(doctype) {
|
||||
var count = frappe.boot.notification_info.open_count_doctype[doctype];
|
||||
frappe.ui.notifications.add_notification = function(doctype, notifications_map) {
|
||||
if(!notifications_map) {
|
||||
notifications_map = frappe.boot.notification_info.open_count_doctype;
|
||||
}
|
||||
|
||||
var count = notifications_map[doctype];
|
||||
if(count) {
|
||||
var config = frappe.ui.notifications.config[doctype] || {};
|
||||
var label = config.label || doctype;
|
||||
|
|
@ -72,7 +85,21 @@ frappe.ui.notifications.add_notification = function(doctype) {
|
|||
frappe.ui.notifications.config = {
|
||||
"ToDo": { label: __("To Do") },
|
||||
"Comment": { label: __("Messages"), route: "messages"},
|
||||
"Event": { label: __("Calendar"), route: "Calendar/Event" }
|
||||
"Event": { label: __("Calendar"), route: "Calendar/Event" },
|
||||
"Likes": {
|
||||
label: __("Likes"),
|
||||
click: function() {
|
||||
frappe.route_options = {
|
||||
show_likes: true
|
||||
};
|
||||
|
||||
if (frappe.get_route()[0]=="activity") {
|
||||
frappe.pages['activity'].on_page_show();
|
||||
} else {
|
||||
frappe.set_route("activity");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
frappe.views.show_open_count_list = function(element) {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ frappe.breadcrumbs = {
|
|||
breadcrumbs.module = frappe.breadcrumbs.preferred[breadcrumbs.doctype];
|
||||
}
|
||||
|
||||
if(breadcrumbs.module && breadcrumbs.module != "Desk") {
|
||||
if(breadcrumbs.module) {
|
||||
if(in_list(["Core", "Email", "Custom", "Workflow", "Print"], breadcrumbs.module))
|
||||
breadcrumbs.module = "Setup";
|
||||
|
||||
|
|
|
|||
|
|
@ -42,9 +42,7 @@ frappe.views.Calendar = frappe.views.CalendarBase.extend({
|
|||
var module = locals.DocType[this.doctype].module;
|
||||
this.page.set_title(__("Calendar") + " - " + __(this.doctype));
|
||||
|
||||
if (module !== "Desk") {
|
||||
frappe.breadcrumbs.add(module, this.doctype)
|
||||
}
|
||||
frappe.breadcrumbs.add(module, this.doctype)
|
||||
|
||||
this.add_filters();
|
||||
|
||||
|
|
|
|||
|
|
@ -96,10 +96,10 @@ frappe.views.CommunicationComposer = Class.extend({
|
|||
|
||||
var cc = [ [this.frm.doc.owner, 1] ];
|
||||
|
||||
var starred_by = frappe.ui.get_starred_by(this.frm.doc);
|
||||
if (starred_by) {
|
||||
for ( var i=0, l=starred_by.length; i<l; i++ ) {
|
||||
cc.push( [starred_by[i], 1] );
|
||||
var liked_by = frappe.ui.get_liked_by(this.frm.doc);
|
||||
if (liked_by) {
|
||||
for ( var i=0, l=liked_by.length; i<l; i++ ) {
|
||||
cc.push( [liked_by[i], 1] );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
|
|||
"_user_tags": "Tag",
|
||||
"_comments": "Comment",
|
||||
"_assign": "Assign",
|
||||
"_starred_by": "StarredBy",
|
||||
"_liked_by": "LikedBy",
|
||||
}[docfield.fieldname] || docfield.fieldtype;
|
||||
|
||||
if(docfield.fieldtype==="Link" && docfield.fieldname!=="name") {
|
||||
|
|
|
|||
|
|
@ -472,6 +472,8 @@ _f.Frm.prototype.render_form = function(is_a_different_doc) {
|
|||
$(cur_frm.wrapper).trigger('render_complete');
|
||||
|
||||
this.layout.show_empty_form_message();
|
||||
|
||||
this.scroll_to_element();
|
||||
}
|
||||
|
||||
_f.Frm.prototype.refresh_field = function(fname) {
|
||||
|
|
@ -873,3 +875,21 @@ _f.Frm.prototype.get_handlers = function(fieldname, doctype, docname) {
|
|||
_f.Frm.prototype.has_perm = function(ptype) {
|
||||
return frappe.perm.has_perm(this.doctype, 0, ptype, this.doc);
|
||||
}
|
||||
|
||||
_f.Frm.prototype.scroll_to_element = function() {
|
||||
if (frappe.route_options && frappe.route_options.scroll_to) {
|
||||
var scroll_to = frappe.route_options.scroll_to;
|
||||
delete frappe.route_options.scroll_to;
|
||||
|
||||
var selector = [];
|
||||
for (var key in scroll_to) {
|
||||
var value = scroll_to[key];
|
||||
selector.push(repl('[data-%(key)s="%(value)s"]', {key: key, value: value}));
|
||||
}
|
||||
|
||||
selector = $(selector.join(" "));
|
||||
if (selector.length) {
|
||||
frappe.ui.scroll(selector, true, 30);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,3 +50,13 @@
|
|||
height: 0;
|
||||
padding-bottom: 100%;
|
||||
}
|
||||
|
||||
.avatar-frame {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding: 50% 0px;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -408,3 +408,14 @@ ul.linked-with-list li {
|
|||
border-bottom: 1px solid @border-color;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
// like pop-over
|
||||
.liked-by-popover {
|
||||
min-width: 200px;
|
||||
margin-top: -10px;
|
||||
margin-bottom: -10px;
|
||||
|
||||
li {
|
||||
margin: 15px 0px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@
|
|||
display: none !important;
|
||||
}
|
||||
|
||||
.shaded-section, .timeline-item:nth-child(even) {
|
||||
.shaded-section {
|
||||
background-color: @light-bg;
|
||||
}
|
||||
|
||||
|
|
@ -112,47 +112,200 @@
|
|||
}
|
||||
|
||||
.timeline {
|
||||
border: 1px solid @border-color;
|
||||
margin: 30px 0px;
|
||||
|
||||
.timeline-head {
|
||||
.comment-input {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
margin-top: 0px;
|
||||
padding: 15px 30px 7px;
|
||||
|
||||
.icon-fixed-width {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
border-bottom: 1px solid @border-color;
|
||||
|
||||
blockquote {
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-item:last-child {
|
||||
border-bottom: 0px;
|
||||
.timeline-items {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.timeline-item .reply {
|
||||
margin-top: 5px;
|
||||
// padding-left: 24px;
|
||||
// border-left: 8px solid #d8dfe6;
|
||||
.timeline {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.timeline::before {
|
||||
content: " ";
|
||||
border-left: 1px solid @border-color;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
bottom: -124px;
|
||||
left: 43px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@media(max-width: @screen-sm) {
|
||||
.timeline::before {
|
||||
bottom: -64px;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-item.user-content {
|
||||
margin: 30px 0px 30px 30px;
|
||||
|
||||
.media-body {
|
||||
border: 1px solid @border-color;
|
||||
border-radius: 2px;
|
||||
margin-left: -7px;
|
||||
position: relative;
|
||||
|
||||
// to display the triangle beside the box
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.comment-header {
|
||||
background-color: @light-bg;
|
||||
padding: 7px 15px;
|
||||
margin: 0px;
|
||||
color: @text-muted;
|
||||
border-bottom: 1px solid @light-border-color;
|
||||
|
||||
.octicon-heart {
|
||||
color: @heart-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.reply {
|
||||
padding: 10px 15px;
|
||||
|
||||
& > div > p:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
& > div > p:last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.close-btn-container {
|
||||
padding: 2px 15px;
|
||||
}
|
||||
|
||||
.comment-likes {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.media-body:after, .media-body:before {
|
||||
right: 100%;
|
||||
top: 15px;
|
||||
border: solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.media-body:after {
|
||||
border-color: rgba(136, 183, 213, 0);
|
||||
border-right-color: #fafbfc;
|
||||
border-width: 6px;
|
||||
margin-top: -6px;
|
||||
}
|
||||
.media-body:before {
|
||||
border-color: rgba(194, 225, 245, 0);
|
||||
border-right-color: @border-color;
|
||||
border-width: 7px;
|
||||
margin-top: -7px;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-item.notification-content {
|
||||
padding-left: 30px;
|
||||
margin: 30px 0px;
|
||||
position: relative;
|
||||
color: @text-muted;
|
||||
|
||||
* {
|
||||
color: @text-muted;
|
||||
}
|
||||
|
||||
.icon-fixed-width {
|
||||
margin-left: 36px;
|
||||
}
|
||||
|
||||
.octicon-heart {
|
||||
color: @heart-color !important;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-indicator() {
|
||||
content: " ";
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
background-color: @border-color;
|
||||
// background-color: white;
|
||||
// border: 1px solid @border-color;
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
border-radius: 50%;
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
.timeline-item.notification-content::before {
|
||||
.timeline-indicator();
|
||||
}
|
||||
|
||||
.timeline-item .reply-link {
|
||||
padding: 0px 7px;
|
||||
}
|
||||
|
||||
.timeline-item h6, .timeline-head h6 {
|
||||
margin-top: 6px;
|
||||
padding-left: 7px;
|
||||
}
|
||||
|
||||
.timeline-head {
|
||||
background-color: @light-bg;
|
||||
padding: 15px 30px;
|
||||
border-bottom: 1px solid @border-color;
|
||||
background-color: white;
|
||||
// padding: 15px 30px;
|
||||
border: 1px solid @border-color;
|
||||
border-radius: 2px;
|
||||
|
||||
.comment-input-header {
|
||||
background-color: @light-bg;
|
||||
padding: 7px 15px;
|
||||
border-bottom: 1px solid @light-border-color;
|
||||
}
|
||||
|
||||
.comment-input-container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.comment-input {
|
||||
border-color: @light-border-color;
|
||||
max-width: 100%;
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: @screen-xs) {
|
||||
.timeline-head {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-radius: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-new-email {
|
||||
margin: 30px 0px;
|
||||
padding: 0px 65px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.timeline-new-email::before {
|
||||
.timeline-indicator();
|
||||
}
|
||||
|
||||
.form-footer h5 {
|
||||
|
|
|
|||
|
|
@ -54,3 +54,13 @@
|
|||
.indicator-right.darkgrey::after {
|
||||
background: @indicator-darkgrey;
|
||||
}
|
||||
|
||||
.indicator.yellow::before,
|
||||
.indicator-right.yellow::after {
|
||||
background: @indicator-yellow;
|
||||
}
|
||||
|
||||
.indicator.light-blue::before,
|
||||
.indicator-right.light-blue::after {
|
||||
background: @indicator-light-blue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,6 +124,14 @@
|
|||
|
||||
.doclist-row {
|
||||
font-size: 12px;
|
||||
|
||||
.likes-count {
|
||||
display: inline-block;
|
||||
width: 15px;
|
||||
margin-left: -5px;
|
||||
color: @text-muted;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.doclist-row .docstatus .octicon {
|
||||
|
|
@ -172,16 +180,17 @@
|
|||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
.listview-main-section .icon-star {
|
||||
.listview-main-section .octicon-heart {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.list-row-head .icon-star {
|
||||
vertical-align: middle;
|
||||
.list-row-head .octicon-heart {
|
||||
margin-right: 13px;
|
||||
}
|
||||
|
||||
.star-action.icon-star {
|
||||
color: #ffdb4c;
|
||||
.like-action.octicon-heart {
|
||||
// color: #ffdb4c;
|
||||
color: @heart-color;
|
||||
}
|
||||
|
||||
.list-id {
|
||||
|
|
@ -209,3 +218,9 @@
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.list-comment-count {
|
||||
display: inline-block;
|
||||
width: 37px;
|
||||
text-align: left;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,26 +261,6 @@
|
|||
border-right-color: transparent !important;
|
||||
}
|
||||
|
||||
// timeline
|
||||
|
||||
.timeline {
|
||||
border-left: none !important;
|
||||
border-right: none !important;
|
||||
|
||||
.timeline-head {
|
||||
padding: 7px 15px;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
padding: 15px;
|
||||
border-bottom: 1px dashed @border-color;
|
||||
}
|
||||
|
||||
.timeline-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
// listviews
|
||||
.list-row {
|
||||
padding: 13px 15px !important;
|
||||
|
|
|
|||
|
|
@ -146,6 +146,11 @@ body[data-route^="Module"] .main-menu {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.liked-by .octicon-heart {
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.form-sidebar .form-shared .share-doc-btn& {
|
||||
|
|
@ -164,6 +169,7 @@ body[data-route^="Module"] .main-menu {
|
|||
.form-tags,
|
||||
.assignment-row,
|
||||
.form-shared,
|
||||
.liked-by,
|
||||
.modified-by,
|
||||
.created-by,
|
||||
.tags-label,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@
|
|||
@indicator-orange: #ffa00a;
|
||||
@indicator-purple: #743ee2;
|
||||
@indicator-darkgrey: #b8c2cc;
|
||||
@indicator-yellow: #FEEF72;
|
||||
@indicator-light-blue:#7CD6FD;
|
||||
|
||||
@heart-color: @indicator-red;
|
||||
|
||||
@navbar-default-color: #6C7680;
|
||||
@navbar-inverse-color: #9D9D9D;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ import re, urllib, datetime, math
|
|||
import babel.dates
|
||||
from dateutil import parser
|
||||
from num2words import num2words
|
||||
import HTMLParser
|
||||
from html2text import html2text
|
||||
|
||||
|
||||
DATE_FORMAT = "%Y-%m-%d"
|
||||
TIME_FORMAT = "%H:%M:%S.%f"
|
||||
|
|
@ -620,3 +623,12 @@ def unique(seq):
|
|||
def strip(val, chars=None):
|
||||
# \ufeff is no-width-break, \u200b is no-width-space
|
||||
return (val or "").replace("\ufeff", "").replace("\u200b", "").strip(chars)
|
||||
|
||||
def to_markdown(html):
|
||||
text = None
|
||||
try:
|
||||
text = html2text(html)
|
||||
except HTMLParser.HTMLParseError:
|
||||
pass
|
||||
|
||||
return text
|
||||
|
|
|
|||
|
|
@ -506,46 +506,6 @@ function valid_email(id) {
|
|||
|
||||
var validate_email = valid_email;
|
||||
|
||||
function get_url_arg(name) {
|
||||
return get_query_params()[name] || "";
|
||||
}
|
||||
|
||||
function get_query_params() {
|
||||
var query_params = {};
|
||||
var query_string = location.search.substring(1);
|
||||
var query_list = query_string.split("&");
|
||||
for (var i=0, l=query_list.length; i < l; i++ ){
|
||||
var pair = query_list[i].split("=");
|
||||
var key = pair[0];
|
||||
if (!key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var value = pair[1];
|
||||
if (typeof value === "string") {
|
||||
value = decodeURIComponent(value);
|
||||
}
|
||||
|
||||
if (key in query_params) {
|
||||
if (typeof query_params[key] === undefined) {
|
||||
query_params[key] = [];
|
||||
} else if (typeof query_params[key] === "string") {
|
||||
query_params[key] = [query_params[key]];
|
||||
}
|
||||
query_params[key].push(value);
|
||||
} else {
|
||||
query_params[key] = value;
|
||||
}
|
||||
}
|
||||
return query_params;
|
||||
}
|
||||
|
||||
function make_query_string(obj) {
|
||||
var query_params = [];
|
||||
$.each(obj, function(k, v) { query_params.push(encodeURIComponent(k) + "=" + encodeURIComponent(v)); });
|
||||
return "?" + query_params.join("&");
|
||||
}
|
||||
|
||||
function repl(s, dict) {
|
||||
if(s==null)return '';
|
||||
for(key in dict) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue