diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json
index 04377ec510..0bfa848a46 100644
--- a/frappe/core/doctype/doctype/doctype.json
+++ b/frappe/core/doctype/doctype/doctype.json
@@ -18,6 +18,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "",
@@ -42,6 +43,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Module",
@@ -69,6 +71,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Is Child Table",
@@ -95,6 +98,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Is Single",
@@ -120,6 +124,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -142,6 +147,7 @@
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Document Type",
@@ -168,6 +174,7 @@
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Icon",
@@ -191,6 +198,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Custom?",
@@ -214,6 +222,7 @@
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Plugin",
@@ -237,6 +246,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Fields",
@@ -261,6 +271,7 @@
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Fields",
@@ -287,6 +298,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Naming",
@@ -306,11 +318,12 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "description": "\\\nfield:[fieldname] - By Field\\\nnaming_series: - By Naming Series (field called naming_series must be present\\\nPrompt - Prompt user for a name\\\n[series] - Series by prefix (separated by a dot); for example PRE.#####\\\n')\">Naming Options",
+ "description": "Naming Options",
"fieldname": "autoname",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Auto Name",
@@ -336,6 +349,7 @@
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Name Case",
@@ -362,6 +376,7 @@
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Description",
@@ -388,6 +403,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -412,6 +428,7 @@
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Title Field",
@@ -437,6 +454,7 @@
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Timeline Field",
@@ -462,6 +480,7 @@
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Search Fields",
@@ -490,6 +509,7 @@
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Sort Field",
@@ -515,6 +535,7 @@
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Sort Order",
@@ -540,6 +561,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Permission Rules",
@@ -564,6 +586,7 @@
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Permissions",
@@ -591,6 +614,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -613,6 +637,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Permissions Settings",
@@ -636,6 +661,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "User Cannot Create",
@@ -661,6 +687,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "User Cannot Search",
@@ -686,6 +713,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Is Submittable",
@@ -710,6 +738,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Allow Import",
@@ -733,6 +762,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Allow Rename",
@@ -758,6 +788,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "In Dialog",
@@ -783,6 +814,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Show Print First",
@@ -808,6 +840,7 @@
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Max Attachments",
@@ -833,9 +866,10 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Hide Actions",
+ "label": "Other Settings",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -856,6 +890,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Hide Heading",
@@ -881,6 +916,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Hide Toolbar",
@@ -906,6 +942,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Hide Copy",
@@ -923,6 +960,31 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "track_seen",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Track 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,
@@ -931,6 +993,7 @@
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Default Print Format",
@@ -957,7 +1020,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-02-01 07:55:35.810722",
+ "modified": "2016-03-25 05:25:12.265558",
"modified_by": "Administrator",
"module": "Core",
"name": "DocType",
@@ -1008,5 +1071,6 @@
"read_only_onload": 0,
"search_fields": "module",
"sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/frappe/core/doctype/user/user.js b/frappe/core/doctype/user/user.js
index 9bc055836e..707c6f4b32 100644
--- a/frappe/core/doctype/user/user.js
+++ b/frappe/core/doctype/user/user.js
@@ -20,7 +20,7 @@ frappe.ui.form.on('User', {
},
onload: function(frm) {
- if(has_common(user_roles, ["Administrator", "System Manager"])) {
+ if(has_common(user_roles, ["Administrator", "System Manager"]) && !frm.doc.__islocal) {
if(!frm.roles_editor) {
var role_area = $('
')
.appendTo(frm.fields_dict.roles_html.wrapper);
diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py
index 0358194691..f1e99f27b7 100644
--- a/frappe/core/doctype/user/user.py
+++ b/frappe/core/doctype/user/user.py
@@ -452,7 +452,6 @@ def user_query(doctype, txt, searchfield, start, page_len, filters):
where enabled=1
and docstatus < 2
and name not in ({standard_users})
- and user_type != 'Website User'
and ({key} like %s
or concat_ws(' ', first_name, middle_name, last_name) like %s)
{mcond}
diff --git a/frappe/data/Framework.sql b/frappe/data/Framework.sql
index 98e039b3a0..3e541bcd43 100644
--- a/frappe/data/Framework.sql
+++ b/frappe/data/Framework.sql
@@ -132,6 +132,7 @@ CREATE TABLE `tabDocType` (
`allow_import` int(1) NOT NULL DEFAULT 0,
`hide_toolbar` int(1) NOT NULL DEFAULT 0,
`hide_heading` int(1) NOT NULL DEFAULT 0,
+ `track_seen` int(1) NOT NULL DEFAULT 0,
`max_attachments` int(11) NOT NULL DEFAULT 0,
`print_outline` varchar(255) DEFAULT NULL,
`read_only_onload` int(1) NOT NULL DEFAULT 0,
diff --git a/frappe/desk/doctype/event/event.json b/frappe/desk/doctype/event/event.json
index d846a16ef5..c58b463c78 100644
--- a/frappe/desk/doctype/event/event.json
+++ b/frappe/desk/doctype/event/event.json
@@ -7,6 +7,7 @@
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
+ "document_type": "Document",
"fields": [
{
"allow_on_submit": 0,
@@ -16,6 +17,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "",
@@ -40,6 +42,7 @@
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Subject",
@@ -63,6 +66,7 @@
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Event Type",
@@ -90,6 +94,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Send an email reminder in the morning",
@@ -113,6 +118,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -135,6 +141,7 @@
"fieldtype": "Datetime",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Starts on",
@@ -158,6 +165,7 @@
"fieldtype": "Datetime",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Ends on",
@@ -181,6 +189,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "All Day",
@@ -204,6 +213,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -226,6 +236,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Repeat this Event",
@@ -250,6 +261,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -273,6 +285,7 @@
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Repeat On",
@@ -299,6 +312,7 @@
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Repeat Till",
@@ -322,6 +336,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -345,6 +360,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Monday",
@@ -369,6 +385,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Tuesday",
@@ -393,6 +410,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Wednesday",
@@ -417,6 +435,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Thursday",
@@ -441,6 +460,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Friday",
@@ -465,6 +485,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Saturday",
@@ -489,6 +510,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Sunday",
@@ -512,6 +534,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -534,6 +557,7 @@
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Description",
@@ -561,6 +585,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Participants",
@@ -585,6 +610,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Groups",
@@ -611,6 +637,7 @@
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Roles",
@@ -637,6 +664,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Ref Type",
@@ -663,6 +691,7 @@
"fieldtype": "Dynamic Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Ref Name",
@@ -692,7 +721,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-01-08 04:50:37.240223",
+ "modified": "2016-03-25 06:09:03.205236",
"modified_by": "Administrator",
"module": "Desk",
"name": "Event",
@@ -741,5 +770,7 @@
],
"read_only": 1,
"read_only_onload": 0,
- "title_field": "subject"
+ "sort_order": "DESC",
+ "title_field": "subject",
+ "track_seen": 1
}
\ No newline at end of file
diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py
index a9c58cff9b..f306a2cc81 100644
--- a/frappe/desk/form/load.py
+++ b/frappe/desk/form/load.py
@@ -45,6 +45,8 @@ def getdoc(doctype, name, user=None):
if doc and not name.startswith('_'):
frappe.get_user().update_recent(doctype, name)
+ doc.add_seen()
+
frappe.response.docs.append(doc)
@frappe.whitelist()
diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py
index 64b40502e9..a2903c0013 100644
--- a/frappe/model/__init__.py
+++ b/frappe/model/__init__.py
@@ -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", "_liked_by")
+optional_fields = ("_user_tags", "_comments", "_assign", "_liked_by", "_seen")
def rename(doctype, old, new, debug=False):
import frappe.model.rename_doc
diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py
index 7582f0d720..0b08a05e7d 100644
--- a/frappe/model/db_query.py
+++ b/frappe/model/db_query.py
@@ -85,7 +85,7 @@ class DatabaseQuery(object):
def prepare_args(self):
self.parse_args()
self.extract_tables()
- self.remove_user_tags()
+ self.set_optional_columns()
self.build_conditions()
args = frappe._dict()
@@ -176,7 +176,7 @@ class DatabaseQuery(object):
if (not self.flags.ignore_permissions) and (not frappe.has_permission(doctype)):
raise frappe.PermissionError, doctype
- def remove_user_tags(self):
+ def set_optional_columns(self):
"""Removes optional columns like `_user_tags`, `_comments` etc. if not in table"""
columns = frappe.db.get_table_columns(self.doctype)
@@ -206,6 +206,10 @@ class DatabaseQuery(object):
else:
self.filters.remove(each)
+ # add _seen if track_seen is set
+ if frappe.get_meta(self.doctype).track_seen:
+ self.fields.append('_seen')
+
def build_conditions(self):
self.conditions = []
self.grouped_or_conditions = []
diff --git a/frappe/model/db_schema.py b/frappe/model/db_schema.py
index 6a902d992c..7d773ca3c7 100644
--- a/frappe/model/db_schema.py
+++ b/frappe/model/db_schema.py
@@ -212,6 +212,13 @@ class DbTable:
"fieldtype": "Text"
})
+ # add _seen column if track_seen
+ if self.meta.track_seen:
+ fl.append({
+ 'fieldname': '_seen',
+ 'fieldtype': 'Text'
+ })
+
if not frappe.flags.in_install_db and frappe.flags.in_install != "frappe":
custom_fl = frappe.db.sql("""\
SELECT * FROM `tabCustom Field`
diff --git a/frappe/model/document.py b/frappe/model/document.py
index 6183913a4e..6c42e5d972 100644
--- a/frappe/model/document.py
+++ b/frappe/model/document.py
@@ -609,6 +609,7 @@ class Document(BaseDocument):
Will also update title_field if set"""
self.set_title_field()
+ self.reset_seen()
if self.flags.ignore_validate:
return
@@ -675,6 +676,11 @@ class Document(BaseDocument):
for d in self.get_all_children():
_clear_cache(d)
+ def reset_seen(self):
+ '''Clear _seen property and set current user as seen'''
+ if self.meta.track_seen:
+ self._seen = json.dumps([frappe.session.user])
+
def notify_update(self):
"""Publish realtime that the current document is modified"""
frappe.publish_realtime("doc_update", {"modified": self.modified, "doctype": self.doctype, "name": self.name},
@@ -811,6 +817,22 @@ class Document(BaseDocument):
}).insert(ignore_permissions=True)
return comment
+ def add_seen(self, user=None):
+ '''add the given/current user to list of users who have seen this document (_seen)'''
+ if not user:
+ user = frappe.session.user
+
+ if self.meta.track_seen:
+ if self._seen:
+ _seen = json.loads(self._seen)
+ else:
+ _seen = []
+
+ if user not in _seen:
+ _seen.append(user)
+ self.db_set('_seen', json.dumps(_seen))
+ frappe.local.flags.commit = True
+
def get_signature(self):
"""Returns signature (hash) for private URL."""
return hashlib.sha224(get_datetime_str(self.creation)).hexdigest()
diff --git a/frappe/public/css/list.css b/frappe/public/css/list.css
index 2ed7e89401..a5f3a27158 100644
--- a/frappe/public/css/list.css
+++ b/frappe/public/css/list.css
@@ -153,6 +153,9 @@
.list-id {
font-weight: bold;
}
+.list-id.seen {
+ font-weight: normal;
+}
.list-col {
height: 20px;
}
diff --git a/frappe/public/js/frappe/list/list_item_subject.html b/frappe/public/js/frappe/list/list_item_subject.html
index 1a00e96db7..23c2b98ba3 100644
--- a/frappe/public/js/frappe/list/list_item_subject.html
+++ b/frappe/public/js/frappe/list/list_item_subject.html
@@ -12,7 +12,7 @@
{{ (_liked_by.length > 9 ? "9+" : _liked_by.length) || "" }}
-
{{ _title }}
diff --git a/frappe/public/js/frappe/list/listview.js b/frappe/public/js/frappe/list/listview.js
index 27a2dbc0c5..9a63082481 100644
--- a/frappe/public/js/frappe/list/listview.js
+++ b/frappe/public/js/frappe/list/listview.js
@@ -259,6 +259,14 @@ frappe.views.ListView = Class.extend({
get_avatar_and_id: function(data, without_workflow) {
data._without_workflow = without_workflow;
+
+ var seen = JSON.parse(data._seen);
+
+ data.css_seen = '';
+ if(seen && seen.indexOf(frappe.session.user) !== -1) {
+ data.css_seen = 'seen'
+ }
+
return frappe.render_template("list_item_subject", data);
},
diff --git a/frappe/public/less/list.less b/frappe/public/less/list.less
index 880543a549..39b2c86116 100644
--- a/frappe/public/less/list.less
+++ b/frappe/public/less/list.less
@@ -197,6 +197,10 @@
font-weight: bold;
}
+.list-id.seen {
+ font-weight: normal;
+}
+
.list-col {
height: 20px;
}
diff --git a/frappe/tests/test_seen.py b/frappe/tests/test_seen.py
new file mode 100644
index 0000000000..d50e8a73ad
--- /dev/null
+++ b/frappe/tests/test_seen.py
@@ -0,0 +1,43 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See license.txt
+from __future__ import unicode_literals
+
+import frappe, unittest, json
+
+class TestSeen(unittest.TestCase):
+ def test_if_user_is_added(self):
+ ev = frappe.get_doc({
+ 'doctype': 'Event',
+ 'subject': 'test event for seen',
+ 'starts_on': '2016-01-01 10:10:00',
+ 'event_type': 'Public'
+ }).insert()
+
+ frappe.set_user('test@example.com')
+
+ from frappe.desk.form.load import getdoc
+
+ # load the form
+ getdoc('Event', ev.name)
+
+ # reload the event
+ ev = frappe.get_doc('Event', ev.name)
+
+ self.assertTrue('test@example.com' in json.loads(ev._seen))
+
+ # test another user
+ frappe.set_user('test1@example.com')
+
+ # load the form
+ getdoc('Event', ev.name)
+
+ # reload the event
+ ev = frappe.get_doc('Event', ev.name)
+
+ self.assertTrue('test@example.com' in json.loads(ev._seen))
+ self.assertTrue('test1@example.com' in json.loads(ev._seen))
+
+ ev.save()
+
+ self.assertFalse('test@example.com' in json.loads(ev._seen))
+ self.assertTrue('test1@example.com' in json.loads(ev._seen))
diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py
index 2d64539035..f194c2b13d 100644
--- a/frappe/website/doctype/web_form/web_form.py
+++ b/frappe/website/doctype/web_form/web_form.py
@@ -102,6 +102,7 @@ class WebForm(WebsiteGenerator):
if frappe.form_dict.name:
context.doc = frappe.get_doc(self.doc_type, frappe.form_dict.name)
context.title = context.doc.get(context.doc.meta.get_title_field())
+ context.doc.add_seen()
context.reference_doctype = context.doc.doctype
context.reference_name = context.doc.name