From af69d99f88a813d806acb0d5755b88a45d67d596 Mon Sep 17 00:00:00 2001 From: Sachin Mane Date: Mon, 8 Apr 2019 15:49:17 +0530 Subject: [PATCH 001/175] add frappe cmd and doctype in http header for better analytics and load balancing --- frappe/public/js/frappe/request.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index cd102117d7..fbfdb65532 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -196,11 +196,16 @@ frappe.request.call = function(opts) { async: opts.async, headers: Object.assign({ "X-Frappe-CSRF-Token": frappe.csrf_token, - "Accept": "application/json" + "Accept": "application/json", + "X-Frappe-CMD": (opts.args && opts.args.cmd || '') || '' }, opts.headers), cache: false }; + if (opts.args && opts.args.doctype) { + ajax_args.headers["X-Frappe-Doctype"] = opts.args.doctype; + } + frappe.last_request = ajax_args.data; return $.ajax(ajax_args) From f4ed914db3db6618b02880c384258149c842c12c Mon Sep 17 00:00:00 2001 From: ci2014 Date: Sat, 13 Apr 2019 18:53:22 +0200 Subject: [PATCH 002/175] Expose Recipients in Notifications Hello, is there any specific reason we do not expose recipients in Notifications? Prior to v11 (Email Alerts) we did neither. Example: We use cc in the recipients, and want to have it visible. bcc, however, should always be hidden. expose_recipients should handle that by default if I am not mistaking. I am looking forward for opinions. --- frappe/email/doctype/notification/notification.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index acddf36777..aa2bce8221 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -146,6 +146,7 @@ def get_context(context): reference_doctype = doc.doctype, reference_name = doc.name, attachments = attachments, + expose_recipients="header", print_letterhead = ((attachments and attachments[0].get('print_letterhead')) or False)) From 0ea9a01704c6e80bb4f73f8032d838f0c18132d0 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 16 Apr 2019 16:00:07 +0530 Subject: [PATCH 003/175] Update frappe/public/js/frappe/request.js Co-Authored-By: sachinhub --- frappe/public/js/frappe/request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index fbfdb65532..ee21f121bc 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -197,7 +197,7 @@ frappe.request.call = function(opts) { headers: Object.assign({ "X-Frappe-CSRF-Token": frappe.csrf_token, "Accept": "application/json", - "X-Frappe-CMD": (opts.args && opts.args.cmd || '') || '' + "X-Frappe-CMD": opts.get('args', {}).get('cmd', '') }, opts.headers), cache: false }; From f2391ff87a6f239905f92a2babc97d9f3fcd238d Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Sun, 7 Apr 2019 11:57:11 +0530 Subject: [PATCH 004/175] Add preview popover on hover to all link fields --- frappe/client.py | 20 +- frappe/core/doctype/docfield/docfield.json | 230 ++++++++++++++++++ frappe/database/mariadb/framework_mariadb.sql | 1 + .../database/postgres/framework_postgres.sql | 1 + frappe/public/build.json | 1 + frappe/public/js/frappe/desk.js | 2 +- frappe/public/js/frappe/ui/link_preview.js | 123 ++++++++++ 7 files changed, 376 insertions(+), 2 deletions(-) create mode 100644 frappe/public/js/frappe/ui/link_preview.js diff --git a/frappe/client.py b/frappe/client.py index fb2d47925b..10e6e49580 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -371,4 +371,22 @@ def check_parent_permission(parent, child_doctype): if frappe.permissions.has_permission(parent): return # Either parent not passed or the user doesn't have permission on parent doctype of child table! - raise frappe.PermissionError \ No newline at end of file + raise frappe.PermissionError + +@frappe.whitelist() +def get_preview_data(doctype, docname, fields): + fields = json.loads(fields) + preview_data = frappe.cache().hget('preview_data', (doctype, docname)) + print('abcde') + if preview_data == None: + print('nimu') + preview_data = frappe.get_all(doctype, filters={ + 'name': docname + }, fields=fields, limit=1) + if preview_data: preview_data = preview_data[0] + frappe.cache().hset('preview_data', (doctype, docname), preview_data) + + return preview_data + +def clear_preview_cache(doc, method): + frappe.cache().delete_value('preview_data', (doc.get('doctype', doc.get('name')))) diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json index b295a9be66..b49af8a1cf 100644 --- a/frappe/core/doctype/docfield/docfield.json +++ b/frappe/core/doctype/docfield/docfield.json @@ -30,6 +30,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "", "length": 0, @@ -62,6 +63,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Label", "length": 0, @@ -99,13 +104,21 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Type", "length": 0, "no_copy": 0, "oldfieldname": "fieldtype", "oldfieldtype": "Select", +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature", +======= + "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature", +>>>>>>> Add preview popover on hover to all link fields "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -134,6 +147,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Name", "length": 0, @@ -168,6 +185,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Mandatory", "length": 0, @@ -206,6 +227,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Precision", "length": 0, @@ -240,6 +265,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Length", "length": 0, @@ -273,6 +302,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Index", "length": 0, @@ -309,6 +342,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "In List View", "length": 0, @@ -343,6 +380,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "In Standard Filter", "length": 0, @@ -377,6 +418,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "In Global Search", "length": 0, @@ -402,6 +447,43 @@ "collapsible": 0, "columns": 0, "fetch_if_empty": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "fieldname": "in_preview", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "label": "In Preview", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, +>>>>>>> Add preview popover on hover to all link fields "fieldname": "allow_in_quick_entry", "fieldtype": "Check", "hidden": 0, @@ -410,6 +492,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Allow in Quick Entry", "length": 0, @@ -443,6 +529,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Bold", "length": 0, @@ -478,6 +568,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Translatable", "length": 0, @@ -512,6 +606,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Collapsible", "length": 255, @@ -546,6 +644,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Collapsible Depends On", "length": 0, @@ -580,6 +682,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "length": 0, "no_copy": 0, @@ -612,6 +718,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Options", "length": 0, @@ -646,6 +756,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Default", "length": 0, @@ -680,6 +794,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Fetch From", "length": 0, @@ -714,6 +832,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Fetch If Empty", "length": 0, @@ -747,6 +869,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Permissions", "length": 0, @@ -779,6 +905,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Display Depends On", "length": 255, @@ -814,6 +944,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Hidden", "length": 0, @@ -850,6 +984,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Read Only", "length": 0, @@ -884,6 +1022,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Unique", "length": 0, @@ -918,6 +1060,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Set Only Once", "length": 0, @@ -951,6 +1097,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Allow Bulk Edit", "length": 0, @@ -984,6 +1134,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "length": 0, "no_copy": 0, @@ -1016,6 +1170,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Perm Level", "length": 0, @@ -1053,6 +1211,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Ignore User Permissions", "length": 0, @@ -1086,6 +1248,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Allow on Submit", "length": 0, @@ -1122,6 +1288,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Report Hide", "length": 0, @@ -1159,6 +1329,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Remember Last Selected Value", "length": 0, @@ -1193,6 +1367,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Ignore XSS Filter", "length": 0, @@ -1226,6 +1404,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Display", "length": 0, @@ -1258,6 +1440,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "In Filter", "length": 0, @@ -1294,6 +1480,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "No Copy", "length": 0, @@ -1330,6 +1520,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Print Hide", "length": 0, @@ -1367,6 +1561,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Print Hide If No Value", "length": 0, @@ -1400,6 +1598,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Print Width", "length": 0, @@ -1432,6 +1634,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Width", "length": 0, @@ -1470,6 +1676,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Columns", "length": 0, @@ -1503,6 +1713,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "length": 0, "no_copy": 0, @@ -1534,6 +1748,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Description", "length": 0, @@ -1570,6 +1788,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "length": 0, "no_copy": 0, @@ -1603,6 +1825,10 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e +======= + "in_preview": 0, +>>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "length": 0, "no_copy": 0, @@ -1631,7 +1857,11 @@ "issingle": 0, "istable": 1, "max_attachments": 0, +<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e "modified": "2019-03-18 17:59:57.873790", +======= + "modified": "2019-03-20 14:30:21.672655", +>>>>>>> Add preview popover on hover to all link fields "modified_by": "Administrator", "module": "Core", "name": "DocField", diff --git a/frappe/database/mariadb/framework_mariadb.sql b/frappe/database/mariadb/framework_mariadb.sql index eae479df05..48c70afbbc 100644 --- a/frappe/database/mariadb/framework_mariadb.sql +++ b/frappe/database/mariadb/framework_mariadb.sql @@ -49,6 +49,7 @@ CREATE TABLE `tabDocField` ( `description` text, `in_list_view` int(1) NOT NULL DEFAULT 0, `in_standard_filter` int(1) NOT NULL DEFAULT 0, + `in_preview` int(1) NOT NULL DEFAULT 0, `read_only` int(1) NOT NULL DEFAULT 0, `precision` varchar(255) DEFAULT NULL, `length` int(11) NOT NULL DEFAULT 0, diff --git a/frappe/database/postgres/framework_postgres.sql b/frappe/database/postgres/framework_postgres.sql index eb82f3ed97..2ff024555d 100644 --- a/frappe/database/postgres/framework_postgres.sql +++ b/frappe/database/postgres/framework_postgres.sql @@ -49,6 +49,7 @@ CREATE TABLE "tabDocField" ( "description" text, "in_list_view" smallint NOT NULL DEFAULT 0, "in_standard_filter" smallint NOT NULL DEFAULT 0, + "in_preview" smallint NOT NULL DEFAULT 0, "read_only" smallint NOT NULL DEFAULT 0, "precision" varchar(255) DEFAULT NULL, "length" bigint NOT NULL DEFAULT 0, diff --git a/frappe/public/build.json b/frappe/public/build.json index 620b360f47..bc83c69c90 100755 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -167,6 +167,7 @@ "public/js/frappe/ui/keyboard.js", "public/js/frappe/ui/colors.js", "public/js/frappe/ui/sidebar.js", + "public/js/frappe/ui/link_preview.js", "public/js/frappe/request.js", "public/js/frappe/socketio_client.js", diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index b2c6493476..eee20b64e7 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -124,7 +124,7 @@ frappe.Application = Class.extend({ } } } - + this.link_preview = new frappe.ui.LinkPreview(); }, setup_frappe_vue() { diff --git a/frappe/public/js/frappe/ui/link_preview.js b/frappe/public/js/frappe/ui/link_preview.js new file mode 100644 index 0000000000..c570572a0d --- /dev/null +++ b/frappe/public/js/frappe/ui/link_preview.js @@ -0,0 +1,123 @@ +frappe.provide('frappe.pages'); + +frappe.ui.LinkPreview = class { + + constructor() { + this.$links = []; + this.popover_timeout = null + this.get_links(); + } + + get_links() { + $(document.body).on('mouseover', 'a[href*="/"]', (e) => { + this.link_hovered = true; + let link = $(e.currentTarget).attr('href'); + let link_arr = link.split('/'); + const link_element = $(e.currentTarget); + let popover = link_element.data("bs.popover"); + this.setup_popover_control(popover, link_arr, link_element); + }); + } + + setup_popover_control(popover, link_arr, $link) { + if(!popover) { + if(link_arr.length > 2) { + let name = decodeURI(link_arr[link_arr.length - 1]); + let doctype = decodeURI(link_arr[link_arr.length -2]); + let preview_fields = this.get_preview_fields(doctype); + if(preview_fields.length) { + this.data_timeout = setTimeout(() => { + this.get_preview_fields_value(doctype, name, preview_fields).then((preview_data)=> { + if(preview_data) { + if (this.popover_timeout) { + clearTimeout(this.popover_timeout) + } + this.popover_timeout = setTimeout(() => { + this.init_preview_popover($link, preview_data, doctype); + }, 1000); + } + }); + }, 1000); + + } + } + } else { + this.popover_timeout = setTimeout(() => { + popover.show(); + }, 1000); + } + + $(document.body).on('mouseout', 'a[href*="/"]', () => { + this.link_hovered = false; + if(this.data_timeout) { + clearTimeout(this.data_timeout) + } + if (this.popover_timeout) { + clearTimeout(this.popover_timeout) + } + }) + $(document.body).on('mousemove', () => { + if (!this.link_hovered) { + this.$links.forEach($link => $link.popover('hide')); + } + }) + } + + get_preview_fields(dt) { + let fields = [] + frappe.model.with_doctype(dt, () => { + frappe.get_meta(dt).fields.filter((field) => { + if(field.in_preview) fields.push(field.fieldname); + }) + }); + return fields; + } + + get_preview_fields_value(dt, field_name, field_list) { + return frappe.xcall('frappe.client.get_preview_data', { + 'doctype': dt, + 'docname': field_name, + 'fields': field_list + }); + } + + init_preview_popover($link, preview_data, dt) { + console.log('create popover') + let content_html = ''; + console.log('preview_data', preview_data); + + Object.keys(preview_data).forEach(key => { + content_html += ` + + `; + }) + + $link.popover({ + container: 'body', + html: true, + content: content_html, + trigger: 'manual', + animation: false + // delay: {'show':show_delay,'hide':hide_delay} + }); + this.$links.push($link); + $link.popover('show'); + + // $link.data('bs.popover').options.content = content_html; + // $link.popover('show'); + // setTimeout(() => { + // $link.popover('show') + // }, 1000); + + + // $link.mouseleave(() => { + // $link.popover('hide') + // }); + + } + + + +} \ No newline at end of file From c062b388a753d0db0fac132619212c61b103c8bc Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Tue, 9 Apr 2019 23:02:02 +0530 Subject: [PATCH 005/175] Improve preview popover --- frappe/client.py | 8 +- frappe/core/doctype/docfield/docfield.json | 154 +-------------------- frappe/public/js/frappe/ui/link_preview.js | 121 ++++++++++++---- 3 files changed, 98 insertions(+), 185 deletions(-) diff --git a/frappe/client.py b/frappe/client.py index 10e6e49580..3e0cb7ad12 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -376,14 +376,16 @@ def check_parent_permission(parent, child_doctype): @frappe.whitelist() def get_preview_data(doctype, docname, fields): fields = json.loads(fields) + if frappe.get_meta(doctype).has_field('image') : + fields.append('image') preview_data = frappe.cache().hget('preview_data', (doctype, docname)) - print('abcde') if preview_data == None: - print('nimu') preview_data = frappe.get_all(doctype, filters={ 'name': docname }, fields=fields, limit=1) - if preview_data: preview_data = preview_data[0] + if preview_data: + preview_data = preview_data[0] + preview_data = {k: v for k, v in preview_data.items() if v is not None} frappe.cache().hset('preview_data', (doctype, docname), preview_data) return preview_data diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json index b49af8a1cf..c78495898e 100644 --- a/frappe/core/doctype/docfield/docfield.json +++ b/frappe/core/doctype/docfield/docfield.json @@ -63,10 +63,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Label", "length": 0, @@ -104,21 +101,14 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Type", "length": 0, "no_copy": 0, "oldfieldname": "fieldtype", "oldfieldtype": "Select", -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e - "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature", -======= "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature", ->>>>>>> Add preview popover on hover to all link fields "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -147,10 +137,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Name", "length": 0, @@ -185,10 +172,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Mandatory", "length": 0, @@ -227,10 +211,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Precision", "length": 0, @@ -265,10 +246,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Length", "length": 0, @@ -302,10 +280,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Index", "length": 0, @@ -342,10 +317,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "In List View", "length": 0, @@ -380,10 +352,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "In Standard Filter", "length": 0, @@ -418,10 +387,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "In Global Search", "length": 0, @@ -447,8 +413,6 @@ "collapsible": 0, "columns": 0, "fetch_if_empty": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "fieldname": "in_preview", "fieldtype": "Check", "hidden": 0, @@ -483,7 +447,6 @@ "collapsible": 0, "columns": 0, "fetch_if_empty": 0, ->>>>>>> Add preview popover on hover to all link fields "fieldname": "allow_in_quick_entry", "fieldtype": "Check", "hidden": 0, @@ -492,10 +455,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Allow in Quick Entry", "length": 0, @@ -529,10 +489,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Bold", "length": 0, @@ -568,10 +525,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Translatable", "length": 0, @@ -606,10 +560,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Collapsible", "length": 255, @@ -644,10 +595,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Collapsible Depends On", "length": 0, @@ -682,10 +630,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "length": 0, "no_copy": 0, @@ -718,10 +663,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Options", "length": 0, @@ -756,10 +698,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Default", "length": 0, @@ -794,10 +733,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Fetch From", "length": 0, @@ -832,10 +768,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Fetch If Empty", "length": 0, @@ -869,10 +802,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Permissions", "length": 0, @@ -905,10 +835,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Display Depends On", "length": 255, @@ -944,10 +871,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Hidden", "length": 0, @@ -984,10 +908,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Read Only", "length": 0, @@ -1022,10 +943,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Unique", "length": 0, @@ -1060,10 +978,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Set Only Once", "length": 0, @@ -1097,10 +1012,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Allow Bulk Edit", "length": 0, @@ -1134,10 +1046,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "length": 0, "no_copy": 0, @@ -1170,10 +1079,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Perm Level", "length": 0, @@ -1211,10 +1117,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Ignore User Permissions", "length": 0, @@ -1248,10 +1151,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Allow on Submit", "length": 0, @@ -1288,10 +1188,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Report Hide", "length": 0, @@ -1329,10 +1226,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Remember Last Selected Value", "length": 0, @@ -1367,10 +1261,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Ignore XSS Filter", "length": 0, @@ -1404,10 +1295,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Display", "length": 0, @@ -1440,10 +1328,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "In Filter", "length": 0, @@ -1480,10 +1365,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "No Copy", "length": 0, @@ -1520,10 +1402,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Print Hide", "length": 0, @@ -1561,10 +1440,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Print Hide If No Value", "length": 0, @@ -1598,10 +1474,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Print Width", "length": 0, @@ -1634,10 +1507,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Width", "length": 0, @@ -1676,10 +1546,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Columns", "length": 0, @@ -1713,10 +1580,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "length": 0, "no_copy": 0, @@ -1748,10 +1612,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "label": "Description", "length": 0, @@ -1788,10 +1649,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "length": 0, "no_copy": 0, @@ -1825,10 +1683,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e -======= "in_preview": 0, ->>>>>>> Add preview popover on hover to all link fields "in_standard_filter": 0, "length": 0, "no_copy": 0, @@ -1848,20 +1703,14 @@ } ], "has_web_view": 0, - "hide_heading": 0, "hide_toolbar": 0, "idx": 1, - "image_view": 0, "in_create": 0, "is_submittable": 0, "issingle": 0, "istable": 1, "max_attachments": 0, -<<<<<<< bb1d5f7107c860375dcdf38e5aadebea7122c31e - "modified": "2019-03-18 17:59:57.873790", -======= - "modified": "2019-03-20 14:30:21.672655", ->>>>>>> Add preview popover on hover to all link fields + "modified": "2019-04-08 12:19:53.415372", "modified_by": "Administrator", "module": "Core", "name": "DocField", @@ -1869,7 +1718,6 @@ "permissions": [], "quick_entry": 0, "read_only": 0, - "read_only_onload": 0, "show_name_in_global_search": 0, "sort_order": "ASC", "track_changes": 0, diff --git a/frappe/public/js/frappe/ui/link_preview.js b/frappe/public/js/frappe/ui/link_preview.js index c570572a0d..6f15d54ad3 100644 --- a/frappe/public/js/frappe/ui/link_preview.js +++ b/frappe/public/js/frappe/ui/link_preview.js @@ -9,37 +9,81 @@ frappe.ui.LinkPreview = class { } get_links() { - $(document.body).on('mouseover', 'a[href*="/"]', (e) => { + $(document.body).on('mouseover', 'a[href*="/"], input[data-fieldname], .popover', (e) => { this.link_hovered = true; - let link = $(e.currentTarget).attr('href'); - let link_arr = link.split('/'); - const link_element = $(e.currentTarget); - let popover = link_element.data("bs.popover"); - this.setup_popover_control(popover, link_arr, link_element); + let element = $(e.currentTarget); + let name; + let doctype; + let is_link = true; + let link_control; + if(element.attr('href')) { + let link = element.attr('href'); + let link_arr = link.split('/'); + if(link_arr.length > 2) { + name = decodeURI(link_arr[link_arr.length - 1]); + doctype = decodeURI(link_arr[link_arr.length -2]); + } + } + else { + is_link = false; + // let linked_field = element.parents('.link-field').find('.link-btn') + // if(linked_field.length) { + // console.log('yes linked field', linked_field) + link_control = element.parents('.control-input-wrapper').find('.control-value').children('a').attr('href'); + console.log(link_control); + if(link_control) { + let link_arr = link_control.split('/'); + console.log('arr', link_arr); + if(link_arr.length > 2) { + name = decodeURI(link_arr[link_arr.length - 1]); + doctype = decodeURI(link_arr[link_arr.length -2]); + } + } + // } + else { + name = element.parent().next().text(); + doctype = element.attr('data-doctype'); + } + } + let popover = element.data("bs.popover"); + if(name && doctype) { + this.setup_popover_control(e, popover, name, doctype, element, is_link, link_control); + } }); } - setup_popover_control(popover, link_arr, $link) { - if(!popover) { - if(link_arr.length > 2) { - let name = decodeURI(link_arr[link_arr.length - 1]); - let doctype = decodeURI(link_arr[link_arr.length -2]); + + setup_popover_control(e, popover, name, doctype, $link, is_link, link_control) { + if(!popover || !is_link) { let preview_fields = this.get_preview_fields(doctype); if(preview_fields.length) { this.data_timeout = setTimeout(() => { this.get_preview_fields_value(doctype, name, preview_fields).then((preview_data)=> { if(preview_data) { + console.log('preview', preview_data); if (this.popover_timeout) { clearTimeout(this.popover_timeout) } this.popover_timeout = setTimeout(() => { - this.init_preview_popover($link, preview_data, doctype); + if(popover) { + let new_content = this.get_content_html(preview_data, doctype, link_control) + popover.options.content = new_content; + } + else { + this.init_preview_popover($link, preview_data, doctype, is_link, link_control); + } + if(!is_link) { + var left = e.pageX; + $link.popover('show'); + $('.control-field-popover').css('left', (left+30) + 'px'); + } + else { + $link.popover('show'); + } }, 1000); } }); }, 1000); - - } } } else { this.popover_timeout = setTimeout(() => { @@ -47,7 +91,7 @@ frappe.ui.LinkPreview = class { }, 1000); } - $(document.body).on('mouseout', 'a[href*="/"]', () => { + $(document.body).on('mouseout', 'a[href*="/"], input[data-fieldname], .popover', () => { this.link_hovered = false; if(this.data_timeout) { clearTimeout(this.data_timeout) @@ -81,30 +125,24 @@ frappe.ui.LinkPreview = class { }); } - init_preview_popover($link, preview_data, dt) { - console.log('create popover') - let content_html = ''; + init_preview_popover($link, preview_data, dt, is_link, link_control) { console.log('preview_data', preview_data); - - Object.keys(preview_data).forEach(key => { - content_html += ` - - `; - }) - + let content_html = this.get_content_html(preview_data, dt, link_control); + console.log('content', content_html) $link.popover({ container: 'body', html: true, content: content_html, trigger: 'manual', + // placement: !is_link?'auto bottom':'auto right', animation: false // delay: {'show':show_delay,'hide':hide_delay} }); + if(!is_link) { + $link.data('bs.popover').tip().addClass('control-field-popover'); + } this.$links.push($link); - $link.popover('show'); - + // return content_html; // $link.data('bs.popover').options.content = content_html; // $link.popover('show'); // setTimeout(() => { @@ -118,6 +156,31 @@ frappe.ui.LinkPreview = class { } + get_content_html(preview_data, dt, link_control) { + let content_html = ''; + if(preview_data['image']) { + let image_url = encodeURI(preview_data['image']); + content_html += ` + + `; + } + Object.keys(preview_data).forEach(key => { + if(key!='image'){ + content_html += ` + + `; + } + }) + if(link_control) { + content_html+= `${'View'}` + } + return content_html; + } + } \ No newline at end of file From 12a3dc1124937b92d47c0d1c7734887fb56bb7f3 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Mon, 15 Apr 2019 15:35:21 +0530 Subject: [PATCH 006/175] feat(desk): Show preview popover on hover of link and control fields --- frappe/client.py | 19 -- frappe/desk/link_preview.py | 35 +++ frappe/public/build.json | 1 + frappe/public/js/frappe/ui/link_preview.js | 254 +++++++++++++-------- frappe/public/less/link_preview.less | 53 +++++ 5 files changed, 245 insertions(+), 117 deletions(-) create mode 100644 frappe/desk/link_preview.py create mode 100644 frappe/public/less/link_preview.less diff --git a/frappe/client.py b/frappe/client.py index 3e0cb7ad12..3f7491ade5 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -373,22 +373,3 @@ def check_parent_permission(parent, child_doctype): # Either parent not passed or the user doesn't have permission on parent doctype of child table! raise frappe.PermissionError -@frappe.whitelist() -def get_preview_data(doctype, docname, fields): - fields = json.loads(fields) - if frappe.get_meta(doctype).has_field('image') : - fields.append('image') - preview_data = frappe.cache().hget('preview_data', (doctype, docname)) - if preview_data == None: - preview_data = frappe.get_all(doctype, filters={ - 'name': docname - }, fields=fields, limit=1) - if preview_data: - preview_data = preview_data[0] - preview_data = {k: v for k, v in preview_data.items() if v is not None} - frappe.cache().hset('preview_data', (doctype, docname), preview_data) - - return preview_data - -def clear_preview_cache(doc, method): - frappe.cache().delete_value('preview_data', (doc.get('doctype', doc.get('name')))) diff --git a/frappe/desk/link_preview.py b/frappe/desk/link_preview.py new file mode 100644 index 0000000000..71315a8546 --- /dev/null +++ b/frappe/desk/link_preview.py @@ -0,0 +1,35 @@ +import frappe +from frappe.model import no_value_fields +import json + +@frappe.whitelist() +def get_preview_data(doctype, docname, fields): + fields = json.loads(fields) + preview_fields = [field['name'] for field in fields if field['type'] not in no_value_fields] + if 'title' not in fields and frappe.get_meta(doctype).has_field('title'): + preview_fields.append('title') + if 'name' not in fields: + preview_fields.append('name') + if frappe.get_meta(doctype).has_field('image'): + preview_fields.append('image') + + preview_data = frappe.cache().hget('preview_data', (doctype, docname)) + if preview_data == None: + preview_data = frappe.get_all(doctype, filters={ + 'name': docname + }, fields=preview_fields, limit=1) + if preview_data: + preview_data = preview_data[0] + + preview_data = {k: v for k, v in preview_data.items() if v is not None} + for k,v in preview_data.items(): + if frappe.get_meta(doctype).has_field(k): + preview_data[k] = frappe.format(v,frappe.get_meta(doctype).get_field(k).fieldtype) + + frappe.cache().hset('preview_data', (doctype, docname), preview_data) + if not preview_data: + return None + return preview_data + +def clear_preview_cache(doc, method): + frappe.cache().delete_value('preview_data', (doc.get('doctype', doc.get('name')))) diff --git a/frappe/public/build.json b/frappe/public/build.json index bc83c69c90..f968c54a8d 100755 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -123,6 +123,7 @@ "public/less/page.less", "public/less/tree.less", "public/less/desktop.less", + "public/less/link_preview.less", "public/less/form.less", "public/less/mobile.less", "public/less/kanban.less", diff --git a/frappe/public/js/frappe/ui/link_preview.js b/frappe/public/js/frappe/ui/link_preview.js index 6f15d54ad3..3808a83633 100644 --- a/frappe/public/js/frappe/ui/link_preview.js +++ b/frappe/public/js/frappe/ui/link_preview.js @@ -1,85 +1,96 @@ -frappe.provide('frappe.pages'); - frappe.ui.LinkPreview = class { constructor() { this.$links = []; - this.popover_timeout = null + this.popover_timeout = null; this.get_links(); } get_links() { + $(document.body).on('mouseover', 'a[href*="/"], input[data-fieldname], .popover', (e) => { this.link_hovered = true; let element = $(e.currentTarget); - let name; - let doctype; + let name, doctype, link; let is_link = true; - let link_control; - if(element.attr('href')) { - let link = element.attr('href'); - let link_arr = link.split('/'); - if(link_arr.length > 2) { - name = decodeURI(link_arr[link_arr.length - 1]); - doctype = decodeURI(link_arr[link_arr.length -2]); - } - } - else { - is_link = false; - // let linked_field = element.parents('.link-field').find('.link-btn') - // if(linked_field.length) { - // console.log('yes linked field', linked_field) - link_control = element.parents('.control-input-wrapper').find('.control-value').children('a').attr('href'); - console.log(link_control); - if(link_control) { - let link_arr = link_control.split('/'); - console.log('arr', link_arr); - if(link_arr.length > 2) { - name = decodeURI(link_arr[link_arr.length - 1]); - doctype = decodeURI(link_arr[link_arr.length -2]); + + if(!element.parents().find('.popover').length) { + if(element.attr('href')) { + link = element.attr('href'); + let details = this.get_details(link,element); + name = details.name; + doctype = details.doctype; + } else { + is_link = false; + link = element.parents('.control-input-wrapper').find('.control-value').children('a').attr('href'); + + if(link) { + let details = this.get_details(link,element); + name = details.name; + doctype = details.doctype; + } else { + name = element.parent().next().text(); + doctype = element.attr('data-doctype'); } } - // } - else { - name = element.parent().next().text(); - doctype = element.attr('data-doctype'); + + let popover = element.data("bs.popover"); + if(name && doctype) { + this.setup_popover_control(e, popover, name, doctype, element, is_link, link); } } - let popover = element.data("bs.popover"); - if(name && doctype) { - this.setup_popover_control(e, popover, name, doctype, element, is_link, link_control); - } }); + + } + + get_details(link, element) { + let details = {}; + let link_arr = link.split('/'); + + if(link_arr.length > 2) { + details.name = decodeURI(link_arr[link_arr.length - 1]); + details.doctype = decodeURI(link_arr[link_arr.length -2]); + details.name = details.name.replace(new RegExp('%2F', 'g'), '/'); + } + let title = element.attr('title'); + if( title && title.includes('/')) { + details.name = title.trim(); + details.doctype = decodeURI(link_arr[link_arr.length-3]); + } + return details; } - setup_popover_control(e, popover, name, doctype, $link, is_link, link_control) { + setup_popover_control(e, popover, name, doctype, $el, is_link, link) { + if(!popover || !is_link) { let preview_fields = this.get_preview_fields(doctype); if(preview_fields.length) { this.data_timeout = setTimeout(() => { this.get_preview_fields_value(doctype, name, preview_fields).then((preview_data)=> { if(preview_data) { - console.log('preview', preview_data); - if (this.popover_timeout) { - clearTimeout(this.popover_timeout) + if(this.popover_timeout) { + clearTimeout(this.popover_timeout); } + this.popover_timeout = setTimeout(() => { + if(popover) { - let new_content = this.get_content_html(preview_data, doctype, link_control) + let new_content = this.get_popover_html(preview_data, doctype, link); popover.options.content = new_content; + } else { + this.init_preview_popover($el, preview_data, doctype, is_link, link); } - else { - this.init_preview_popover($link, preview_data, doctype, is_link, link_control); - } + if(!is_link) { var left = e.pageX; - $link.popover('show'); - $('.control-field-popover').css('left', (left+30) + 'px'); - } - else { - $link.popover('show'); + $el.popover('show'); + var width =$('.popover').width(); + $('.control-field-popover').css('left', (left-(width/2)) + 'px'); + } else { + $el.popover('show'); } + }, 1000); } }); @@ -91,96 +102,143 @@ frappe.ui.LinkPreview = class { }, 1000); } + this.handle_popover_hide(); + } + + handle_popover_hide() { $(document.body).on('mouseout', 'a[href*="/"], input[data-fieldname], .popover', () => { this.link_hovered = false; if(this.data_timeout) { - clearTimeout(this.data_timeout) + clearTimeout(this.data_timeout); } if (this.popover_timeout) { - clearTimeout(this.popover_timeout) + clearTimeout(this.popover_timeout); } - }) + }); + $(document.body).on('mousemove', () => { if (!this.link_hovered) { - this.$links.forEach($link => $link.popover('hide')); + this.$links.forEach($el => $el.popover('hide')); } - }) + }); } get_preview_fields(dt) { - let fields = [] - frappe.model.with_doctype(dt, () => { + let fields = []; + frappe.model.with_doctype(dt, () => { frappe.get_meta(dt).fields.filter((field) => { - if(field.in_preview) fields.push(field.fieldname); - }) + if(field.in_preview) { + fields.push({'name':field.fieldname,'type':field.fieldtype}); + } + }); }); + + if(!fields.length) { + frappe.model.with_doctype(dt, () => { + frappe.get_meta(dt).fields.filter((field) => { + if(field.reqd) { + fields.push({'name':field.fieldname,'type':field.fieldtype}); + } + }); + }); + } return fields; } get_preview_fields_value(dt, field_name, field_list) { - return frappe.xcall('frappe.client.get_preview_data', { + return frappe.xcall('frappe.desk.link_preview.get_preview_data', { 'doctype': dt, 'docname': field_name, - 'fields': field_list + 'fields': field_list, }); } - init_preview_popover($link, preview_data, dt, is_link, link_control) { - console.log('preview_data', preview_data); - let content_html = this.get_content_html(preview_data, dt, link_control); - console.log('content', content_html) - $link.popover({ + init_preview_popover($el, preview_data, dt, is_link, link) { + + let popover_content = this.get_popover_html(preview_data, dt, link); + $el.popover({ container: 'body', html: true, - content: content_html, + content: popover_content, trigger: 'manual', - // placement: !is_link?'auto bottom':'auto right', - animation: false - // delay: {'show':show_delay,'hide':hide_delay} + placement: 'top auto', + animation: false, }); - if(!is_link) { - $link.data('bs.popover').tip().addClass('control-field-popover'); - } - this.$links.push($link); - // return content_html; - // $link.data('bs.popover').options.content = content_html; - // $link.popover('show'); - // setTimeout(() => { - // $link.popover('show') - // }, 1000); - - // $link.mouseleave(() => { - // $link.popover('hide') - // }); + if(!is_link) { + $el.data('bs.popover').tip().addClass('control-field-popover'); + } + + this.$links.push($el); } - get_content_html(preview_data, dt, link_control) { - let content_html = ''; + get_popover_html(preview_data, dt, link) { + if(!link) { + link = window.location.href; + } + + if(link && link.includes(' ')) { + link = link.replace(new RegExp(' ', 'g'), '%20'); + } + + let image_html = ''; + let title_html = ''; + let content_html = ``; + if(preview_data['image']) { let image_url = encodeURI(preview_data['image']); - content_html += ` - - `; + image_html += ` +
+ +
`; } + if(preview_data['title']) { + title_html+= `${preview_data['title']}`; + } + Object.keys(preview_data).forEach(key => { - if(key!='image'){ + if(key!='image' && key!='name') { + let value = this.truncate_value(preview_data[key]); + let label = this.truncate_value(frappe.meta.get_label(dt, key)); content_html += ` - + + + + `; } }) - if(link_control) { - content_html+= `${'View'}` + content_html+=`
${label} ${value}
`; + + let popover_content = `
+ ${image_html} +
+
+ ${preview_data['name']} + ${title_html} + ${dt} +
+
+
+
+
+ ${content_html} +
`; + + if(!link) { + $('.preview-name').removeAttr('href'); + $('.preview-title').removeAttr('href'); } - return content_html; + + return popover_content; } + truncate_value(value) { + if (value.length > 100) { + value = value.slice(0,100) + '...'; + } + return value; + } - -} \ No newline at end of file +} diff --git a/frappe/public/less/link_preview.less b/frappe/public/less/link_preview.less new file mode 100644 index 0000000000..2a3ccce5b3 --- /dev/null +++ b/frappe/public/less/link_preview.less @@ -0,0 +1,53 @@ +.popover { + border-radius: 0; + max-width: 100%; + .popover-content { + padding: 0; + .preview-popover-header { + background: #f7fafc; + padding: 10px; + + .preview-header { + display: inline-block; + vertical-align: top; + } + + .preview-image { + width: 70px; + height: 70px; + object-fit: cover; + margin-right: 10px; + border-radius: 6px; + } + + .preview-name { + display: block; + } + + .preview-title { + display: block; + } + } + + hr { + margin: 0; + } + + .preview-table { + margin: 10px; + max-width: 330px; + min-width: 200px; + } + + .preview-field { + td { + padding: 3px; + vertical-align: top; + } + + .field-name { + width: 39%; + } + } + } +} From 4f02d731d83cba6f2244698dc1967674ba3672ce Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Mon, 15 Apr 2019 17:02:57 +0530 Subject: [PATCH 007/175] fix indent --- frappe/public/js/frappe/ui/link_preview.js | 410 ++++++++++----------- 1 file changed, 205 insertions(+), 205 deletions(-) diff --git a/frappe/public/js/frappe/ui/link_preview.js b/frappe/public/js/frappe/ui/link_preview.js index 3808a83633..c75dc27b58 100644 --- a/frappe/public/js/frappe/ui/link_preview.js +++ b/frappe/public/js/frappe/ui/link_preview.js @@ -1,244 +1,244 @@ frappe.ui.LinkPreview = class { - constructor() { - this.$links = []; - this.popover_timeout = null; - this.get_links(); - } + constructor() { + this.$links = []; + this.popover_timeout = null; + this.get_links(); + } - get_links() { + get_links() { - $(document.body).on('mouseover', 'a[href*="/"], input[data-fieldname], .popover', (e) => { - this.link_hovered = true; - let element = $(e.currentTarget); - let name, doctype, link; - let is_link = true; + $(document.body).on('mouseover', 'a[href*="/"], input[data-fieldname], .popover', (e) => { + this.link_hovered = true; + let element = $(e.currentTarget); + let name, doctype, link; + let is_link = true; - if(!element.parents().find('.popover').length) { - if(element.attr('href')) { - link = element.attr('href'); - let details = this.get_details(link,element); - name = details.name; - doctype = details.doctype; - } else { - is_link = false; - link = element.parents('.control-input-wrapper').find('.control-value').children('a').attr('href'); + if(!element.parents().find('.popover').length) { + if(element.attr('href')) { + link = element.attr('href'); + let details = this.get_details(link,element); + name = details.name; + doctype = details.doctype; + } else { + is_link = false; + link = element.parents('.control-input-wrapper').find('.control-value').children('a').attr('href'); - if(link) { - let details = this.get_details(link,element); - name = details.name; - doctype = details.doctype; - } else { - name = element.parent().next().text(); - doctype = element.attr('data-doctype'); - } - } + if(link) { + let details = this.get_details(link,element); + name = details.name; + doctype = details.doctype; + } else { + name = element.parent().next().text(); + doctype = element.attr('data-doctype'); + } + } - let popover = element.data("bs.popover"); - if(name && doctype) { - this.setup_popover_control(e, popover, name, doctype, element, is_link, link); - } - } - }); + let popover = element.data("bs.popover"); + if(name && doctype) { + this.setup_popover_control(e, popover, name, doctype, element, is_link, link); + } + } + }); - } + } - get_details(link, element) { - let details = {}; - let link_arr = link.split('/'); + get_details(link, element) { + let details = {}; + let link_arr = link.split('/'); - if(link_arr.length > 2) { - details.name = decodeURI(link_arr[link_arr.length - 1]); - details.doctype = decodeURI(link_arr[link_arr.length -2]); - details.name = details.name.replace(new RegExp('%2F', 'g'), '/'); - } - let title = element.attr('title'); - if( title && title.includes('/')) { - details.name = title.trim(); - details.doctype = decodeURI(link_arr[link_arr.length-3]); - } - return details; - } + if(link_arr.length > 2) { + details.name = decodeURI(link_arr[link_arr.length - 1]); + details.doctype = decodeURI(link_arr[link_arr.length -2]); + details.name = details.name.replace(new RegExp('%2F', 'g'), '/'); + } + let title = element.attr('title'); + if( title && title.includes('/')) { + details.name = title.trim(); + details.doctype = decodeURI(link_arr[link_arr.length-3]); + } + return details; + } - setup_popover_control(e, popover, name, doctype, $el, is_link, link) { + setup_popover_control(e, popover, name, doctype, $el, is_link, link) { - if(!popover || !is_link) { - let preview_fields = this.get_preview_fields(doctype); - if(preview_fields.length) { - this.data_timeout = setTimeout(() => { - this.get_preview_fields_value(doctype, name, preview_fields).then((preview_data)=> { - if(preview_data) { - if(this.popover_timeout) { - clearTimeout(this.popover_timeout); - } + if(!popover || !is_link) { + let preview_fields = this.get_preview_fields(doctype); + if(preview_fields.length) { + this.data_timeout = setTimeout(() => { + this.get_preview_fields_value(doctype, name, preview_fields).then((preview_data)=> { + if(preview_data) { + if(this.popover_timeout) { + clearTimeout(this.popover_timeout); + } - this.popover_timeout = setTimeout(() => { + this.popover_timeout = setTimeout(() => { - if(popover) { - let new_content = this.get_popover_html(preview_data, doctype, link); - popover.options.content = new_content; - } else { - this.init_preview_popover($el, preview_data, doctype, is_link, link); - } + if(popover) { + let new_content = this.get_popover_html(preview_data, doctype, link); + popover.options.content = new_content; + } else { + this.init_preview_popover($el, preview_data, doctype, is_link, link); + } - if(!is_link) { - var left = e.pageX; - $el.popover('show'); - var width =$('.popover').width(); - $('.control-field-popover').css('left', (left-(width/2)) + 'px'); - } else { - $el.popover('show'); - } + if(!is_link) { + var left = e.pageX; + $el.popover('show'); + var width =$('.popover').width(); + $('.control-field-popover').css('left', (left-(width/2)) + 'px'); + } else { + $el.popover('show'); + } - }, 1000); - } - }); - }, 1000); - } - } else { - this.popover_timeout = setTimeout(() => { - popover.show(); - }, 1000); - } + }, 1000); + } + }); + }, 1000); + } + } else { + this.popover_timeout = setTimeout(() => { + popover.show(); + }, 1000); + } - this.handle_popover_hide(); - } + this.handle_popover_hide(); + } - handle_popover_hide() { - $(document.body).on('mouseout', 'a[href*="/"], input[data-fieldname], .popover', () => { - this.link_hovered = false; - if(this.data_timeout) { - clearTimeout(this.data_timeout); - } - if (this.popover_timeout) { - clearTimeout(this.popover_timeout); - } - }); + handle_popover_hide() { + $(document.body).on('mouseout', 'a[href*="/"], input[data-fieldname], .popover', () => { + this.link_hovered = false; + if(this.data_timeout) { + clearTimeout(this.data_timeout); + } + if (this.popover_timeout) { + clearTimeout(this.popover_timeout); + } + }); - $(document.body).on('mousemove', () => { - if (!this.link_hovered) { - this.$links.forEach($el => $el.popover('hide')); - } - }); - } + $(document.body).on('mousemove', () => { + if (!this.link_hovered) { + this.$links.forEach($el => $el.popover('hide')); + } + }); + } - get_preview_fields(dt) { - let fields = []; - frappe.model.with_doctype(dt, () => { - frappe.get_meta(dt).fields.filter((field) => { - if(field.in_preview) { + get_preview_fields(dt) { + let fields = []; + frappe.model.with_doctype(dt, () => { + frappe.get_meta(dt).fields.filter((field) => { + if(field.in_preview) { fields.push({'name':field.fieldname,'type':field.fieldtype}); - } - }); - }); + } + }); + }); - if(!fields.length) { - frappe.model.with_doctype(dt, () => { - frappe.get_meta(dt).fields.filter((field) => { - if(field.reqd) { - fields.push({'name':field.fieldname,'type':field.fieldtype}); - } - }); - }); - } - return fields; - } + if(!fields.length) { + frappe.model.with_doctype(dt, () => { + frappe.get_meta(dt).fields.filter((field) => { + if(field.reqd) { + fields.push({'name':field.fieldname,'type':field.fieldtype}); + } + }); + }); + } + return fields; + } - get_preview_fields_value(dt, field_name, field_list) { - return frappe.xcall('frappe.desk.link_preview.get_preview_data', { - 'doctype': dt, - 'docname': field_name, - 'fields': field_list, - }); - } + get_preview_fields_value(dt, field_name, field_list) { + return frappe.xcall('frappe.desk.link_preview.get_preview_data', { + 'doctype': dt, + 'docname': field_name, + 'fields': field_list, + }); + } - init_preview_popover($el, preview_data, dt, is_link, link) { + init_preview_popover($el, preview_data, dt, is_link, link) { - let popover_content = this.get_popover_html(preview_data, dt, link); - $el.popover({ - container: 'body', - html: true, - content: popover_content, - trigger: 'manual', - placement: 'top auto', - animation: false, - }); + let popover_content = this.get_popover_html(preview_data, dt, link); + $el.popover({ + container: 'body', + html: true, + content: popover_content, + trigger: 'manual', + placement: 'top auto', + animation: false, + }); - if(!is_link) { - $el.data('bs.popover').tip().addClass('control-field-popover'); - } + if(!is_link) { + $el.data('bs.popover').tip().addClass('control-field-popover'); + } - this.$links.push($el); + this.$links.push($el); - } + } - get_popover_html(preview_data, dt, link) { - if(!link) { - link = window.location.href; - } + get_popover_html(preview_data, dt, link) { + if(!link) { + link = window.location.href; + } - if(link && link.includes(' ')) { - link = link.replace(new RegExp(' ', 'g'), '%20'); - } + if(link && link.includes(' ')) { + link = link.replace(new RegExp(' ', 'g'), '%20'); + } - let image_html = ''; - let title_html = ''; - let content_html = ``; + let image_html = ''; + let title_html = ''; + let content_html = `
`; - if(preview_data['image']) { - let image_url = encodeURI(preview_data['image']); - image_html += ` -
- -
`; - } - if(preview_data['title']) { - title_html+= `${preview_data['title']}`; - } + if(preview_data['image']) { + let image_url = encodeURI(preview_data['image']); + image_html += ` +
+ +
`; + } + if(preview_data['title']) { + title_html+= `${preview_data['title']}`; + } - Object.keys(preview_data).forEach(key => { - if(key!='image' && key!='name') { - let value = this.truncate_value(preview_data[key]); - let label = this.truncate_value(frappe.meta.get_label(dt, key)); - content_html += ` - - - - - `; - } - }) - content_html+=`
${label} ${value}
`; + Object.keys(preview_data).forEach(key => { + if(key!='image' && key!='name') { + let value = this.truncate_value(preview_data[key]); + let label = this.truncate_value(frappe.meta.get_label(dt, key)); + content_html += ` + + ${label} + ${value} + + `; + } + }); + content_html+=``; - let popover_content = `
- ${image_html} -
-
- ${preview_data['name']} - ${title_html} - ${dt} -
-
-
-
-
- ${content_html} -
`; + let popover_content = `
+ ${image_html} +
+
+ ${preview_data['name']} + ${title_html} + ${dt} +
+
+
+
+
+ ${content_html} +
`; - if(!link) { - $('.preview-name').removeAttr('href'); - $('.preview-title').removeAttr('href'); - } + if(!link) { + $('.preview-name').removeAttr('href'); + $('.preview-title').removeAttr('href'); + } - return popover_content; - } + return popover_content; + } - truncate_value(value) { - if (value.length > 100) { - value = value.slice(0,100) + '...'; - } - return value; - } + truncate_value(value) { + if (value.length > 100) { + value = value.slice(0,100) + '...'; + } + return value; + } -} +}; From 144fe8e915c95ce46f4cb4904e18a0392f643747 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Mon, 22 Apr 2019 20:48:57 +0530 Subject: [PATCH 008/175] Code cleanup and fixes --- frappe/desk/link_preview.py | 2 +- frappe/public/js/frappe/ui/link_preview.js | 168 ++++++++++----------- 2 files changed, 83 insertions(+), 87 deletions(-) diff --git a/frappe/desk/link_preview.py b/frappe/desk/link_preview.py index 71315a8546..b462a80324 100644 --- a/frappe/desk/link_preview.py +++ b/frappe/desk/link_preview.py @@ -15,7 +15,7 @@ def get_preview_data(doctype, docname, fields): preview_data = frappe.cache().hget('preview_data', (doctype, docname)) if preview_data == None: - preview_data = frappe.get_all(doctype, filters={ + preview_data = frappe.get_list(doctype, filters={ 'name': docname }, fields=preview_fields, limit=1) if preview_data: diff --git a/frappe/public/js/frappe/ui/link_preview.js b/frappe/public/js/frappe/ui/link_preview.js index c75dc27b58..8564c8dcac 100644 --- a/frappe/public/js/frappe/ui/link_preview.js +++ b/frappe/public/js/frappe/ui/link_preview.js @@ -10,49 +10,48 @@ frappe.ui.LinkPreview = class { $(document.body).on('mouseover', 'a[href*="/"], input[data-fieldname], .popover', (e) => { this.link_hovered = true; - let element = $(e.currentTarget); - let name, doctype, link; - let is_link = true; + this.element = $(e.currentTarget); + this.is_link = true; - if(!element.parents().find('.popover').length) { - if(element.attr('href')) { - link = element.attr('href'); - let details = this.get_details(link,element); - name = details.name; - doctype = details.doctype; + if(!this.element.parents().find('.popover').length) { + if(this.element.attr('href')) { + this.link = this.element.attr('href'); + let details = this.get_details(); + this.name = details.name; + this.doctype = details.doctype; } else { - is_link = false; - link = element.parents('.control-input-wrapper').find('.control-value').children('a').attr('href'); + this.is_link = false; + this.link = this.element.parents('.control-input-wrapper').find('.control-value').children('a').attr('href'); - if(link) { - let details = this.get_details(link,element); - name = details.name; - doctype = details.doctype; + if(this.link) { + let details = this.get_details(); + this.name = details.name; + this.doctype = details.doctype; } else { - name = element.parent().next().text(); - doctype = element.attr('data-doctype'); + this.name = this.element.parent().next().text(); + this.doctype = this.element.attr('data-doctype'); } } - let popover = element.data("bs.popover"); - if(name && doctype) { - this.setup_popover_control(e, popover, name, doctype, element, is_link, link); + this.popover = this.element.data("bs.popover"); + if(this.name && this.doctype && this.doctype!=='files') { + this.setup_popover_control(e); } } }); } - get_details(link, element) { + get_details() { let details = {}; - let link_arr = link.split('/'); + let link_arr = this.link.split('/'); if(link_arr.length > 2) { details.name = decodeURI(link_arr[link_arr.length - 1]); details.doctype = decodeURI(link_arr[link_arr.length -2]); details.name = details.name.replace(new RegExp('%2F', 'g'), '/'); } - let title = element.attr('title'); + let title = this.element.attr('title'); if( title && title.includes('/')) { details.name = title.trim(); details.doctype = decodeURI(link_arr[link_arr.length-3]); @@ -61,50 +60,53 @@ frappe.ui.LinkPreview = class { } - setup_popover_control(e, popover, name, doctype, $el, is_link, link) { + setup_popover_control(e) { - if(!popover || !is_link) { - let preview_fields = this.get_preview_fields(doctype); + if(!this.popover || !this.is_link) { + let preview_fields = this.get_preview_fields(); if(preview_fields.length) { this.data_timeout = setTimeout(() => { - this.get_preview_fields_value(doctype, name, preview_fields).then((preview_data)=> { - if(preview_data) { - if(this.popover_timeout) { - clearTimeout(this.popover_timeout); - } - - this.popover_timeout = setTimeout(() => { - - if(popover) { - let new_content = this.get_popover_html(preview_data, doctype, link); - popover.options.content = new_content; - } else { - this.init_preview_popover($el, preview_data, doctype, is_link, link); - } - - if(!is_link) { - var left = e.pageX; - $el.popover('show'); - var width =$('.popover').width(); - $('.control-field-popover').css('left', (left-(width/2)) + 'px'); - } else { - $el.popover('show'); - } - - }, 1000); - } - }); + this.create_popover(e, preview_fields); }, 1000); } } else { this.popover_timeout = setTimeout(() => { - popover.show(); + this.popover.show(); }, 1000); } - + console.log('this', this); this.handle_popover_hide(); } + create_popover(e, preview_fields) { + this.get_preview_fields_value(preview_fields).then((preview_data)=> { + if(preview_data) { + if(this.popover_timeout) { + clearTimeout(this.popover_timeout); + } + + this.popover_timeout = setTimeout(() => { + if(this.popover) { + let new_content = this.get_popover_html(preview_data); + this.popover.options.content = new_content; + } else { + this.init_preview_popover(preview_data); + } + + if(!this.is_link) { + var left = e.pageX; + this.element.popover('show'); + var width = $('.popover').width(); + $('.control-field-popover').css('left', (left-(width/2)) + 'px'); + } else { + this.element.popover('show'); + } + + }, 1000); + } + }); + } + handle_popover_hide() { $(document.body).on('mouseout', 'a[href*="/"], input[data-fieldname], .popover', () => { this.link_hovered = false; @@ -123,7 +125,8 @@ frappe.ui.LinkPreview = class { }); } - get_preview_fields(dt) { + get_preview_fields() { + let dt = this.doctype; let fields = []; frappe.model.with_doctype(dt, () => { frappe.get_meta(dt).fields.filter((field) => { @@ -145,18 +148,18 @@ frappe.ui.LinkPreview = class { return fields; } - get_preview_fields_value(dt, field_name, field_list) { + get_preview_fields_value(field_list) { return frappe.xcall('frappe.desk.link_preview.get_preview_data', { - 'doctype': dt, - 'docname': field_name, + 'doctype': this.doctype, + 'docname': this.name, 'fields': field_list, }); } - init_preview_popover($el, preview_data, dt, is_link, link) { + init_preview_popover(preview_data) { - let popover_content = this.get_popover_html(preview_data, dt, link); - $el.popover({ + let popover_content = this.get_popover_html(preview_data); + this.element.popover({ container: 'body', html: true, content: popover_content, @@ -165,21 +168,22 @@ frappe.ui.LinkPreview = class { animation: false, }); - if(!is_link) { - $el.data('bs.popover').tip().addClass('control-field-popover'); + if(!this.is_link) { + this.element.data('bs.popover').tip().addClass('control-field-popover'); } - this.$links.push($el); + this.$links.push(this.element); } - get_popover_html(preview_data, dt, link) { - if(!link) { - link = window.location.href; + get_popover_html(preview_data) { + if(!this.link) { + console.log('here'); + this.link = window.location.href; } - if(link && link.includes(' ')) { - link = link.replace(new RegExp(' ', 'g'), '%20'); + if(this.link && this.link.includes(' ')) { + this.link = this.link.replace(new RegExp(' ', 'g'), '%20'); } let image_html = ''; @@ -194,13 +198,13 @@ frappe.ui.LinkPreview = class { `; } if(preview_data['title']) { - title_html+= `${preview_data['title']}`; + title_html+= `${preview_data['title']}`; } Object.keys(preview_data).forEach(key => { if(key!='image' && key!='name') { let value = this.truncate_value(preview_data[key]); - let label = this.truncate_value(frappe.meta.get_label(dt, key)); + let label = this.truncate_value(frappe.meta.get_label(this.doctype, key)); content_html += ` ${label} @@ -211,26 +215,18 @@ frappe.ui.LinkPreview = class { }); content_html+=``; - let popover_content = `
- ${image_html} -
-
- ${preview_data['name']} - ${title_html} - ${dt} -
+ let popover_content = `
${image_html}
+
+ ${preview_data['name']} + ${title_html} + ${this.doctype}
-
+

${content_html}
`; - if(!link) { - $('.preview-name').removeAttr('href'); - $('.preview-title').removeAttr('href'); - } - return popover_content; } From d82230bcb700ed7d31abf055d954f999d9618f70 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Tue, 23 Apr 2019 11:46:06 +0530 Subject: [PATCH 009/175] fix: Code cleanup --- frappe/desk/link_preview.py | 26 ++++++++------------- frappe/public/js/frappe/ui/link_preview.js | 27 +++++++++++----------- 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/frappe/desk/link_preview.py b/frappe/desk/link_preview.py index b462a80324..e9f27d8706 100644 --- a/frappe/desk/link_preview.py +++ b/frappe/desk/link_preview.py @@ -6,30 +6,24 @@ import json def get_preview_data(doctype, docname, fields): fields = json.loads(fields) preview_fields = [field['name'] for field in fields if field['type'] not in no_value_fields] - if 'title' not in fields and frappe.get_meta(doctype).has_field('title'): - preview_fields.append('title') + preview_fields.append(frappe.get_meta(doctype).get_title_field()) if 'name' not in fields: preview_fields.append('name') if frappe.get_meta(doctype).has_field('image'): preview_fields.append('image') - preview_data = frappe.cache().hget('preview_data', (doctype, docname)) - if preview_data == None: - preview_data = frappe.get_list(doctype, filters={ - 'name': docname - }, fields=preview_fields, limit=1) - if preview_data: - preview_data = preview_data[0] + preview_data = frappe.get_list(doctype, filters={ + 'name': docname + }, fields=preview_fields, limit=1) + if preview_data: + preview_data = preview_data[0] - preview_data = {k: v for k, v in preview_data.items() if v is not None} - for k,v in preview_data.items(): - if frappe.get_meta(doctype).has_field(k): - preview_data[k] = frappe.format(v,frappe.get_meta(doctype).get_field(k).fieldtype) + preview_data = {k: v for k, v in preview_data.items() if v is not None} + for k,v in preview_data.items(): + if frappe.get_meta(doctype).has_field(k): + preview_data[k] = frappe.format(v,frappe.get_meta(doctype).get_field(k).fieldtype) - frappe.cache().hset('preview_data', (doctype, docname), preview_data) if not preview_data: return None return preview_data -def clear_preview_cache(doc, method): - frappe.cache().delete_value('preview_data', (doc.get('doctype', doc.get('name')))) diff --git a/frappe/public/js/frappe/ui/link_preview.js b/frappe/public/js/frappe/ui/link_preview.js index 8564c8dcac..039d9a1895 100644 --- a/frappe/public/js/frappe/ui/link_preview.js +++ b/frappe/public/js/frappe/ui/link_preview.js @@ -74,7 +74,6 @@ frappe.ui.LinkPreview = class { this.popover.show(); }, 1000); } - console.log('this', this); this.handle_popover_hide(); } @@ -178,7 +177,6 @@ frappe.ui.LinkPreview = class { get_popover_html(preview_data) { if(!this.link) { - console.log('here'); this.link = window.location.href; } @@ -215,17 +213,20 @@ frappe.ui.LinkPreview = class { }); content_html+=``; - let popover_content = `
${image_html}
-
- ${preview_data['name']} - ${title_html} - ${this.doctype} -
-
-
-
- ${content_html} -
`; + let popover_content = + `
${image_html} +
+
+ ${preview_data['name']} + ${title_html} + ${this.doctype} +
+
+
+
+
+ ${content_html} +
`; return popover_content; } From 2c1e20496a3eee6a56f0a53ce3814291e66aa29f Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Wed, 24 Apr 2019 15:09:24 +0530 Subject: [PATCH 010/175] fix: use property image_field and check for http link --- frappe/desk/link_preview.py | 3 +-- frappe/public/js/frappe/ui/link_preview.js | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frappe/desk/link_preview.py b/frappe/desk/link_preview.py index e9f27d8706..f8252f20bc 100644 --- a/frappe/desk/link_preview.py +++ b/frappe/desk/link_preview.py @@ -9,8 +9,7 @@ def get_preview_data(doctype, docname, fields): preview_fields.append(frappe.get_meta(doctype).get_title_field()) if 'name' not in fields: preview_fields.append('name') - if frappe.get_meta(doctype).has_field('image'): - preview_fields.append('image') + preview_fields.append(frappe.get_meta(doctype).image_field) preview_data = frappe.get_list(doctype, filters={ 'name': docname diff --git a/frappe/public/js/frappe/ui/link_preview.js b/frappe/public/js/frappe/ui/link_preview.js index 039d9a1895..34f318e0f6 100644 --- a/frappe/public/js/frappe/ui/link_preview.js +++ b/frappe/public/js/frappe/ui/link_preview.js @@ -16,6 +16,9 @@ frappe.ui.LinkPreview = class { if(!this.element.parents().find('.popover').length) { if(this.element.attr('href')) { this.link = this.element.attr('href'); + if(this.link.startsWith('http')) { + return; + } let details = this.get_details(); this.name = details.name; this.doctype = details.doctype; @@ -34,7 +37,7 @@ frappe.ui.LinkPreview = class { } this.popover = this.element.data("bs.popover"); - if(this.name && this.doctype && this.doctype!=='files') { + if(this.name && this.doctype) { this.setup_popover_control(e); } } From d791285ac283812f18ec107999184c74351a6664 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 25 Apr 2019 17:23:38 +0530 Subject: [PATCH 011/175] feat: Added encryption feature for pdfs --- frappe/__init__.py | 14 ++++++---- frappe/utils/pdf.py | 63 +++++++++++++++++++++++++++++---------------- 2 files changed, 50 insertions(+), 27 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index c80f60ac3d..8ff538e28a 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -1329,7 +1329,7 @@ def format(*args, **kwargs): import frappe.utils.formatters return frappe.utils.formatters.format_value(*args, **kwargs) -def get_print(doctype=None, name=None, print_format=None, style=None, html=None, as_pdf=False, doc=None, output = None, no_letterhead = 0): +def get_print(doctype=None, name=None, print_format=None, style=None, html=None, as_pdf=False, doc=None, output = None, no_letterhead = 0, password=None): """Get Print Format for given document. :param doctype: DocType of document. @@ -1347,15 +1347,19 @@ def get_print(doctype=None, name=None, print_format=None, style=None, html=None, local.form_dict.doc = doc local.form_dict.no_letterhead = no_letterhead + options = None + if password: + options = {'password': password} + if not html: html = build_page("printview") if as_pdf: - return get_pdf(html, output = output) + return get_pdf(html, output = output, options = options) else: return html -def attach_print(doctype, name, file_name=None, print_format=None, style=None, html=None, doc=None, lang=None, print_letterhead=True): +def attach_print(doctype, name, file_name=None, print_format=None, style=None, html=None, doc=None, lang=None, print_letterhead=True, password=None): from frappe.utils import scrub_urls if not file_name: file_name = name @@ -1374,12 +1378,12 @@ def attach_print(doctype, name, file_name=None, print_format=None, style=None, h if int(print_settings.send_print_as_pdf or 0): out = { "fname": file_name + ".pdf", - "fcontent": get_print(doctype, name, print_format=print_format, style=style, html=html, as_pdf=True, doc=doc, no_letterhead=no_letterhead) + "fcontent": get_print(doctype, name, print_format=print_format, style=style, html=html, as_pdf=True, doc=doc, no_letterhead=no_letterhead, password=password) } else: out = { "fname": file_name + ".html", - "fcontent": scrub_urls(get_print(doctype, name, print_format=print_format, style=style, html=html, doc=doc, no_letterhead=no_letterhead)).encode("utf-8") + "fcontent": scrub_urls(get_print(doctype, name, print_format=print_format, style=style, html=html, doc=doc, no_letterhead=no_letterhead, password=password)).encode("utf-8") } local.flags.ignore_print_permissions = False diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index 8c43bf33ce..980da8a9a0 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -6,13 +6,13 @@ import pdfkit, os, frappe from frappe.utils import scrub_urls from frappe import _ from bs4 import BeautifulSoup -from PyPDF2 import PdfFileReader -import re +from PyPDF2 import PdfFileReader, PdfFileWriter +import re, io def get_pdf(html, options=None, output = None): html = scrub_urls(html) html, options = prepare_options(html, options) - fname = os.path.join("/tmp", "frappe-pdf-{0}.pdf".format(frappe.generate_hash())) + # fname = os.path.join("/tmp", "frappe-pdf-{0}.pdf".format(frappe.generate_hash())) options.update({ "disable-javascript": "", @@ -20,12 +20,12 @@ def get_pdf(html, options=None, output = None): }) try: - pdfkit.from_string(html, fname, options=options or {}) - if output: - append_pdf(PdfFileReader(fname),output) - else: - with open(fname, "rb") as fileobj: - filedata = fileobj.read() + # Set filename property to false, so no file is actually created + filedata = pdfkit.from_string(html, False, options=options or {}) + + # https://pythonhosted.org/PyPDF2/PdfFileReader.html + # create in-memory binary streams from filedata and create a PdfFileReader objcet + reader = PdfFileReader(io.BytesIO(filedata)) except IOError as e: if ("ContentNotFoundError" in e.message @@ -34,29 +34,48 @@ def get_pdf(html, options=None, output = None): or "RemoteHostClosedError" in e.message): # allow pdfs with missing images if file got created - if os.path.exists(fname): - if output: - append_pdf(PdfFileReader(file(fname,"rb")),output) - else: - with open(fname, "rb") as fileobj: - filedata = fileobj.read() + if filedata: + if output: # output is a PdfFileWriter object + output.appendPagesFromReader(reader) else: frappe.throw(_("PDF generation failed because of broken image links")) else: raise - finally: - cleanup(fname, options) - if output: - return output + # Encrypt if required + if "password" in options: + output.encrypt(options["password"]) + return get_file_data_from_writer(output) + + # https://pythonhosted.org/PyPDF2/PdfFileWriter.html + # initialize a writer + writer = PdfFileWriter() + + # Append pages from the reader object to the writer + writer.appendPagesFromReader(reader) + + if "password" in options: + writer.encrypt(options["password"]) + + filedata = get_file_data_from_writer(writer) return filedata -def append_pdf(input,output): - # Merging multiple pdf files - [output.addPage(input.getPage(page_num)) for page_num in range(input.numPages)] +def get_file_data_from_writer(writer_obj): + + # https://docs.python.org/3/library/io.html + # Create a new stream and write into it + stream = io.BytesIO() + writer_obj.write(stream) + + # Change the stream position to start of the stream + stream.seek(0) + + # Read up to size bytes from the object and return them + return stream.read() + def prepare_options(html, options): if not options: From a9ca81370bc3420d95bfa789662c6b66b7bdb167 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 25 Apr 2019 18:22:14 +0530 Subject: [PATCH 012/175] fix: encoded password --- frappe/utils/pdf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index 980da8a9a0..193144498c 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -46,7 +46,7 @@ def get_pdf(html, options=None, output = None): if output: # Encrypt if required if "password" in options: - output.encrypt(options["password"]) + output.encrypt(options["password"].encode('utf-8')) return get_file_data_from_writer(output) # https://pythonhosted.org/PyPDF2/PdfFileWriter.html @@ -57,7 +57,7 @@ def get_pdf(html, options=None, output = None): writer.appendPagesFromReader(reader) if "password" in options: - writer.encrypt(options["password"]) + writer.encrypt(options["password"].encode('utf-8')) filedata = get_file_data_from_writer(writer) From 02c6abe6094f836769c46339c267cd8d189f7175 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Sat, 27 Apr 2019 16:22:56 +0530 Subject: [PATCH 013/175] feat: minor changes to pdf.py --- frappe/utils/pdf.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index 193144498c..260f6a3727 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -9,10 +9,9 @@ from bs4 import BeautifulSoup from PyPDF2 import PdfFileReader, PdfFileWriter import re, io -def get_pdf(html, options=None, output = None): +def get_pdf(html, options=None, output=None): html = scrub_urls(html) html, options = prepare_options(html, options) - # fname = os.path.join("/tmp", "frappe-pdf-{0}.pdf".format(frappe.generate_hash())) options.update({ "disable-javascript": "", @@ -66,7 +65,6 @@ def get_pdf(html, options=None, output = None): def get_file_data_from_writer(writer_obj): # https://docs.python.org/3/library/io.html - # Create a new stream and write into it stream = io.BytesIO() writer_obj.write(stream) From 27fd3f7c99a62901f9e24a49fce49872929f599a Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Sat, 27 Apr 2019 16:23:13 +0530 Subject: [PATCH 014/175] feat: Refactored test and added tests for pdf encryption --- frappe/tests/test_pdf.py | 51 ++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/frappe/tests/test_pdf.py b/frappe/tests/test_pdf.py index f0452af8d0..1b78d090c4 100644 --- a/frappe/tests/test_pdf.py +++ b/frappe/tests/test_pdf.py @@ -4,20 +4,22 @@ from __future__ import unicode_literals import unittest -from frappe.utils.pdf import read_options_from_html +import frappe.utils.pdf as pdfgen +from PyPDF2 import PdfFileReader, PdfFileWriter +import pdfkit, io #class TestPdfBorders(unittest.TestCase): -class TestPdfBorders(unittest.TestCase): - def test_pdf_borders(self): - html = """ - -

This is a test html snippet

+class TestPdf(unittest.TestCase): + @property + def html(self): + return """ +

This is a test html snippet

Test link 1 Test link 2 @@ -26,16 +28,19 @@ class TestPdfBorders(unittest.TestCase):
Please mail us at email -
- """ +
""" - html, html_options = read_options_from_html(html) - self.assertTrue(html_options['margin-top'] == '0') - self.assertTrue(html_options['margin-left'] == '10') - self.assertTrue(html_options['margin-right'] == '0') + def runTest(self): + self.test_read_options_from_html() -# allows to run $ bench execute frappe.tests.test_pdf.run_tests -def run_tests(): - t = TestPdfBorders("test_pdf_borders") - t.test_pdf_borders() - return + def test_read_options_from_html(self): + html, html_options = pdfgen.read_options_from_html(self.html) + self.assertTrue(html_options['margin-top'] == '0') + self.assertTrue(html_options['margin-left'] == '10') + self.assertTrue(html_options['margin-right'] == '0') + + def test_pdf_encryption(self): + pdf = pdfgen.get_pdf(self.html, options={"password": "qwe"}) + reader = PdfFileReader(io.BytesIO(pdf)) + self.assertTrue(reader.isEncrypted) + self.assertTrue(reader.decrypt("qwe".encode('utf-8'))) From 01a2df7e5af2b17cabbb694e09f7b075f6cba765 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Sat, 27 Apr 2019 16:26:13 +0530 Subject: [PATCH 015/175] feat: code imporvements --- frappe/utils/pdf.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index 260f6a3727..287dc2cc1a 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -48,11 +48,8 @@ def get_pdf(html, options=None, output=None): output.encrypt(options["password"].encode('utf-8')) return get_file_data_from_writer(output) - # https://pythonhosted.org/PyPDF2/PdfFileWriter.html - # initialize a writer writer = PdfFileWriter() - # Append pages from the reader object to the writer writer.appendPagesFromReader(reader) if "password" in options: From 8e29bc4bf7e1cef51366e73943e8ed26a724c5cf Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Sat, 27 Apr 2019 17:41:25 +0530 Subject: [PATCH 016/175] fix: possible fix to encoding issues in py3 --- frappe/tests/test_pdf.py | 13 ++++++++----- frappe/utils/pdf.py | 11 ++++++++--- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/frappe/tests/test_pdf.py b/frappe/tests/test_pdf.py index 1b78d090c4..73a49bb832 100644 --- a/frappe/tests/test_pdf.py +++ b/frappe/tests/test_pdf.py @@ -5,8 +5,8 @@ from __future__ import unicode_literals import unittest import frappe.utils.pdf as pdfgen -from PyPDF2 import PdfFileReader, PdfFileWriter -import pdfkit, io +import frappe, io, six +from PyPDF2 import PdfFileReader #class TestPdfBorders(unittest.TestCase): class TestPdf(unittest.TestCase): @@ -34,13 +34,16 @@ class TestPdf(unittest.TestCase): self.test_read_options_from_html() def test_read_options_from_html(self): - html, html_options = pdfgen.read_options_from_html(self.html) + _, html_options = pdfgen.read_options_from_html(self.html) self.assertTrue(html_options['margin-top'] == '0') self.assertTrue(html_options['margin-left'] == '10') self.assertTrue(html_options['margin-right'] == '0') def test_pdf_encryption(self): - pdf = pdfgen.get_pdf(self.html, options={"password": "qwe"}) + password = "qwe" + pdf = pdfgen.get_pdf(self.html, options={"password": password}) reader = PdfFileReader(io.BytesIO(pdf)) self.assertTrue(reader.isEncrypted) - self.assertTrue(reader.decrypt("qwe".encode('utf-8'))) + if six.PY2: + password = frappe.safe_encode(password) + self.assertTrue(reader.decrypt(password)) \ No newline at end of file diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index 287dc2cc1a..86c623b1b5 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -5,9 +5,9 @@ from __future__ import unicode_literals import pdfkit, os, frappe from frappe.utils import scrub_urls from frappe import _ +import six, re, io from bs4 import BeautifulSoup from PyPDF2 import PdfFileReader, PdfFileWriter -import re, io def get_pdf(html, options=None, output=None): html = scrub_urls(html) @@ -42,10 +42,15 @@ def get_pdf(html, options=None, output=None): else: raise + password = options["password"] + + if six.PY2: + password = frappe.safe_encode(password) + if output: # Encrypt if required if "password" in options: - output.encrypt(options["password"].encode('utf-8')) + output.encrypt(password) return get_file_data_from_writer(output) writer = PdfFileWriter() @@ -53,7 +58,7 @@ def get_pdf(html, options=None, output=None): writer.appendPagesFromReader(reader) if "password" in options: - writer.encrypt(options["password"].encode('utf-8')) + writer.encrypt(password) filedata = get_file_data_from_writer(writer) From 87bb91b64fccb5671630430af63a8e1f0a20f23c Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 30 Apr 2019 09:41:25 +0530 Subject: [PATCH 017/175] fix: key error on password --- frappe/utils/pdf.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index 86c623b1b5..8376d32a93 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -42,10 +42,10 @@ def get_pdf(html, options=None, output=None): else: raise - password = options["password"] - - if six.PY2: - password = frappe.safe_encode(password) + if "password" in options: + password = options["password"] + if six.PY2: + password = frappe.safe_encode(password) if output: # Encrypt if required @@ -54,7 +54,6 @@ def get_pdf(html, options=None, output=None): return get_file_data_from_writer(output) writer = PdfFileWriter() - writer.appendPagesFromReader(reader) if "password" in options: From 238a496baea054973f4c93c4d03e48796f9ccbc0 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 30 Apr 2019 10:19:07 +0530 Subject: [PATCH 018/175] chore: Remove deprecated files (#7379) - reportview.js - grid_report.js --- frappe/public/build.json | 2 - .../js/frappe/views/reports/grid_report.js | 859 ---------------- .../js/frappe/views/reports/reportview.js | 950 ------------------ .../views/reports/reportview_footer.html | 18 - 4 files changed, 1829 deletions(-) delete mode 100644 frappe/public/js/frappe/views/reports/grid_report.js delete mode 100644 frappe/public/js/frappe/views/reports/reportview.js delete mode 100644 frappe/public/js/frappe/views/reports/reportview_footer.html diff --git a/frappe/public/build.json b/frappe/public/build.json index 620b360f47..64fe3eb2c5 100755 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -357,9 +357,7 @@ "public/js/lib/clusterize.min.js", "public/js/frappe/views/reports/report_factory.js", "public/js/frappe/views/reports/report_view.js", - "public/js/frappe/views/reports/reportview_footer.html", "public/js/frappe/views/reports/query_report.js", - "public/js/frappe/views/reports/grid_report.js", "public/js/frappe/views/reports/print_grid.html", "public/js/frappe/views/reports/print_tree.html", "public/js/frappe/ui/group_by/group_by.html", diff --git a/frappe/public/js/frappe/views/reports/grid_report.js b/frappe/public/js/frappe/views/reports/grid_report.js deleted file mode 100644 index a8dda60606..0000000000 --- a/frappe/public/js/frappe/views/reports/grid_report.js +++ /dev/null @@ -1,859 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// MIT License. See license.txt - -import DataTable from 'frappe-datatable'; -frappe.provide("frappe.report_dump"); - -$.extend(frappe.report_dump, { - data: {}, - last_modified: {}, - with_data: function(doctypes, callback) { - var pre_loaded = Object.keys(frappe.report_dump.last_modified); - return frappe.call({ - method: "frappe.desk.report_dump.get_data", - type: "GET", - args: { - doctypes: doctypes, - last_modified: frappe.report_dump.last_modified - }, - freeze: true, - callback: function(r) { - // creating map of data from a list - $.each(r.message, function(doctype, doctype_data) { - frappe.report_dump.set_data(doctype, doctype_data); - }); - - // reverse map names - $.each(r.message, function(doctype, doctype_data) { - // only if not pre-loaded - if(!in_list(pre_loaded, doctype)) { - if(doctype_data.links) { - $.each(frappe.report_dump.data[doctype], function(row_idx, row) { - $.each(doctype_data.links, function(link_key, link) { - if(frappe.report_dump.data[link[0]][row[link_key]]) { - row[link_key] = frappe.report_dump.data[link[0]][row[link_key]][link[1]]; - } else { - row[link_key] = null; - } - }); - }); - } - } - }); - - callback(); - } - }); - }, - set_data: function(doctype, doctype_data) { - var data = []; - var replace_dict = {}; - var make_row = function(d) { - var row = {}; - $.each(doctype_data.columns, function(idx, col) { - row[col] = d[idx]; - }); - row.id = row.name; - row.doctype = doctype; - return row; - }; - if(frappe.report_dump.last_modified[doctype]) { - // partial loading, make a name dict - $.each(doctype_data.data, function(i, d) { - var row = make_row(d); - replace_dict[row.name] = row; - }); - - // replace old data - $.each(frappe.report_dump.data[doctype], function(i, d) { - if(replace_dict[d.name]) { - data.push(replace_dict[d.name]); - delete replace_dict[d.name]; - } else if(doctype_data.modified_names.indexOf(d.name)!==-1) { - // if modified but not in replace_dict, then assume it as cancelled - // don't push in data - } else { - data.push(d); - } - }); - - // add new records - $.each(replace_dict, function(name, d) { - data.push(d); - }); - } else { - - // first loading - $.each(doctype_data.data, function(i, d) { - data.push(make_row(d)); - }); - } - frappe.report_dump.last_modified[doctype] = doctype_data.last_modified; - frappe.report_dump.data[doctype] = data; - } -}); - -frappe.provide("frappe.views"); -frappe.views.GridReport = Class.extend({ - init: function(opts) { - this.filter_inputs = {}; - this.preset_checks = []; - this.tree_grid = {show: false}; - $.extend(this, opts); - - this.wrapper = $('
').appendTo(this.page.main); - this.page.main.find(".page").css({"padding-top": "0px"}); - - if(this.filters) { - this.make_filters(); - } - this.make_waiting(); - - this.get_data_and_refresh(); - }, - bind_show: function() { - // bind show event to reset cur_report_grid - // and refresh filters from url - // this must be called after init - // because "frappe.container.page" will only be set - // once "load" event is over. - - var me = this; - $(this.page).bind('show', function() { - // reapply filters on show - frappe.cur_grid_report = me; - me.get_data_and_refresh(); - }); - - }, - get_data_and_refresh: function() { - var me = this; - this.get_data(function() { - me.apply_filters_from_route(); - me.refresh(); - }); - }, - get_data: function(callback) { - var me = this; - - frappe.report_dump.with_data(this.doctypes, function() { - if(!me.setup_filters_done) { - me.setup_filters(); - me.setup_filters_done = true; - } - callback(); - }); - }, - setup_filters: function() { - var me = this; - - $.each(me.filter_inputs, function(i, v) { - var opts = v.get(0).opts; - if(opts.fieldtype == "Select" && in_list(me.doctypes, opts.link)) { - $(v).add_options(frappe.report_dump.data[opts.link].map(d => d.name)); - } else if(opts.fieldtype == "Link" && in_list(me.doctypes, opts.link)) { - opts.list = frappe.report_dump.data[opts.link].map(d => d.name); - me.set_autocomplete(v, opts.list); - } - }); - - // refresh - this.page.set_primary_action(__("Refresh"), function() { - me.get_data(function() { - me.refresh(); - }); - }); - - // reset filters button - if (this.filter_inputs) { - this.page.add_menu_item(__("Reset Filters"), function() { - me.init_filter_values(); - me.refresh(); - }, true); - } - - this.page.add_menu_item(__("Print"), function() { - frappe.ui.get_print_settings(false, function(print_settings) { - frappe.render_grid({grid: me.grid, title: me.page.title, print_settings: print_settings, report: me}); - }); - - }, true); - - // range - this.filter_inputs.range && this.filter_inputs.range.on("change", function() { - me.refresh(); - }); - }, - set_filter: function(key, value) { - var filters = this.filter_inputs[key]; - if(filters) { - var opts = filters.get(0).opts; - if(opts.fieldtype === "Check") { - filters.prop("checked", cint(value) ? true : false); - } if(opts.fieldtype=="Date") { - filters.val(frappe.datetime.str_to_user(value)); - } else { - filters.val(value); - } - } else { - frappe.msgprint(__("Invalid Filter: {0}", [key])); - } - }, - set_autocomplete: function($filter, list) { - var me = this; - new Awesomplete($filter.get(0), { - list: list - }); - $filter.on("awesomplete-select", function(e) { - var value = e.originalEvent.text.value; - $filter.val(value); - me.refresh(); - }); - }, - init_filter_values: function() { - $.each(this.filter_inputs, function(key, filter) { - var opts = filter.get(0).opts; - if(frappe.sys_defaults[key]) { - filter.val(frappe.sys_defaults[key]); - } else if(opts.fieldtype=='Select') { - filter.get(0).selectedIndex = 0; - } else if(opts.fieldtype=='Data') { - filter.val(""); - } else if(opts.fieldtype=="Link") { - filter.val(""); - } - }); - - this.set_default_values(); - }, - - set_default_values: function() { - var values = { - from_date: frappe.datetime.str_to_user(frappe.sys_defaults.year_start_date), - to_date: frappe.datetime.str_to_user(frappe.sys_defaults.year_end_date) - }; - - var me = this; - $.each(values, function(i, v) { - if(me.filter_inputs[i] && !me.filter_inputs[i].val()) - me.filter_inputs[i].val(v); - }); - }, - - make_filters: function() { - var me = this; - $.each(this.filters, function(i, v) { - v.fieldname = v.fieldname || v.label.replace(/ /g, '_').toLowerCase(); - var input = null; - if(v.fieldtype=='Select') { - input = me.page.add_select(v.label, v.options || [v.default_value]); - } else if(v.fieldtype=="Link") { - input = me.page.add_data(v.label); - new Awesomplete(input.get(0), { - list: v.list || [] - }); - } else if(v.fieldtype==='Button' && v.label===__("Refresh")) { - input = me.page.set_primary_action(v.label, null, v.icon); - } else if(v.fieldtype==='Button') { - input = me.page.add_menu_item(v.label, null, true); - } else if(v.fieldtype==='Date') { - input = me.page.add_date(v.label); - } else if(v.fieldtype==='Label') { - input = me.page.add_label(v.label); - } else if(v.fieldtype==='Data') { - input = me.page.add_data(v.label); - } else if(v.fieldtype==='Check') { - input = me.page.add_check(v.label); - } - - if(input) { - input && (input.get(0).opts = v); - if(v.cssClass) { - input.addClass(v.cssClass); - } - input.keypress(function(e) { - if(e.which==13) { - me.refresh(); - } - }); - } - me.filter_inputs[v.fieldname] = input; - }); - }, - make_waiting: function() { - this.waiting = frappe.messages.waiting(this.wrapper, __("Loading Report")+"..."); - }, - load_filter_values: function() { - var me = this; - $.each(this.filter_inputs, function(i, f) { - var opts = f.get(0).opts; - if(opts.fieldtype=='Check') { - me[opts.fieldname] = f.prop('checked') ? 1 : 0; - } else if(opts.fieldtype!='Button') { - me[opts.fieldname] = f.val(); - if(opts.fieldtype=="Date") { - me[opts.fieldname] = frappe.datetime.user_to_str(me[opts.fieldname]); - } else if (opts.fieldtype == "Select") { - me[opts.fieldname+'_default'] = opts.default_value; - } - } - }); - - if(this.filter_inputs.from_date && this.filter_inputs.to_date && (this.to_date < this.from_date)) { - frappe.msgprint(__("From Date must be before To Date")); - return; - } - - }, - - make_name_map: function(data, key) { - var map = {}; - key = key || "name"; - $.each(data, function(i, v) { - map[v[key]] = v; - }); - return map; - }, - - reset_item_values: function(item) { - var me = this; - $.each(this.columns, function(i, col) { - if (col.formatter==me.currency_formatter) { - item[col.id] = 0.0; - } - }); - }, - - round_item_values: function(item) { - var me = this; - $.each(this.columns, function(i, col) { - if (col.formatter==me.currency_formatter) { - item[col.id] = flt(item[col.id], frappe.defaults.get_default("float_precision") || 3); - } - }); - }, - - round_off_data: function() { - var me = this; - $.each(this.data, function(i, d) { - me.round_item_values(d); - }); - }, - - refresh: function() { - this.waiting.toggle(false); - if(!this.grid_wrapper) - this.make(); - // this.show_zero = $('.show-zero input:checked').length; - this.load_filter_values(); - this.setup_columns(); - this.setup_dataview_columns(); - this.apply_link_formatters(); - this.prepare_data(); - this.round_off_data(); - this.prepare_data_view(); - // chart might need prepared data - frappe.show_alert("Updated", 2); - this.render(); - this.setup_chart && this.setup_chart(); - }, - setup_dataview_columns: function() { - this.columns = this.columns.filter(col => !col.hidden); - this.datatable_columns = this.columns.map(column => { - return Object.assign(column, { - format: (value, row, column, data) => { - return column.formatter ? - column.formatter(row, {}, value, column, data) : - value || ''; - } - }); - }); - }, - make: function() { - // chart wrapper - this.chart_area = $('
').appendTo(this.wrapper); - - this.page.add_menu_item(__("Export"), () => this.export(), true); - - // grid wrapper - this.grid_wrapper = $("
") - .appendTo(this.wrapper); - this.id = frappe.dom.set_unique_id(this.grid_wrapper.get(0)); - - this.bind_show(); - - frappe.cur_grid_report = this; - $(this.wrapper).trigger('make'); - - }, - apply_filters_from_route: function() { - var me = this; - if(frappe.route_options) { - $.each(frappe.route_options, function(key, value) { - me.set_filter(key, value); - }); - frappe.route_options = null; - } else { - this.init_filter_values(); - } - this.set_default_values(); - - $(this.wrapper).trigger('apply_filters_from_route'); - }, - options: { - editable: false, - enableColumnReorder: false - }, - render: function() { - this.datatable = new DataTable('#' + this.id, { - columns: this.datatable_columns, - data: this.data, - layout: 'fixed', - inlineFilters: true, - treeView: true, - checkboxColumn: true, - checkedRowStatus: false, - events: { - onCheckRow: (row) => { - const rowIndex = row.meta.rowIndex; - const checked = this.datatable.rowmanager.checkMap[rowIndex]; - const data = this.datatable.datamanager.getData(rowIndex); - data.checked = Boolean(checked); - - this.setup_chart && this.setup_chart(); - } - }, - hooks: { - columnTotal: frappe.utils.report_column_total - } - }); - - this.data.forEach((d, i) => { - if (d.checked) { - this.datatable.rowmanager.checkRow(i, true); - } - }); - }, - prepare_data_view: function() { - }, - export: function() { - frappe.tools.downloadify(frappe.slickgrid_tools.get_view_data(this.columns, this.dataView), - ["Report Manager", "System Manager"], this.title); - return false; - }, - apply_filters: function(item) { - // generic filter: apply filter functiions - // from all filter_inputs - var filters = this.filter_inputs; - if(item._show) return true; - - for (var i in filters) { - if(!this.apply_filter(item, i)) { - return false; - } - } - - return true; - }, - apply_filter: function(item, fieldname) { - var filter = this.filter_inputs[fieldname].get(0); - if(filter.opts.filter) { - if(!filter.opts.filter(this[filter.opts.fieldname], item, filter.opts, this)) { - return false; - } - } - return true; - }, - apply_zero_filter: function(val, item, opts, me) { - // show only non-zero values - if(!me.show_zero) { - for(var i=0, j=me.columns.length; i 0.001 || flt(item[col.field]) < -0.001) { - return true; - } - } - } - return false; - } - return true; - }, - show_zero_check: function() { - var me = this; - this.wrapper.bind('make', function() { - me.wrapper.find('.show-zero').toggle(true).find('input').click(function(){ - me.refresh(); - }); - }); - }, - is_default: function(fieldname) { - return this[fieldname]==this[fieldname + "_default"]; - }, - date_formatter: function(row, cell, value) { - return frappe.datetime.str_to_user(value); - }, - currency_formatter: function(row, cell, value, columnDef, dataContext) { - if (isNaN(value)) value = ''; - return repl('
%(value)s
', { - _style: dataContext._style || "", - value: ((value==null || value==="") ? "" : format_number(value)) - }); - }, - text_formatter: function(row, cell, value, columnDef, dataContext) { - return repl('%(value)s', { - _style: dataContext._style || "", - esc_value: cstr(value).replace(/"/g, '\\"'), - value: cstr(value) - }); - }, - check_formatter: function(row, cell, value, columnDef, dataContext) { - return repl('', { - "id": dataContext.id, - "checked": dataContext.checked ? 'checked="checked"' : "" - }); - }, - apply_link_formatters: function() { - $.each(this.columns, function(i, col) { - if(col.link_formatter) { - col.formatter = function(row, cell, value, columnDef, dataContext, for_print) { - // added link and open button to links - // link_formatter must have - // filter_input, open_btn (true / false), doctype (will be eval'd) - if(!value) return ""; - - if(for_print) { - return value; - } - - var me = frappe.cur_grid_report; - - if(dataContext._show) { - return repl('%(value)s', { - _style: dataContext._style || "", - value: value - }); - } - - // make link to add a filter - var html; - var link_formatter = me.dataview_columns[cell].link_formatter; - if (link_formatter.filter_input) { - html = repl('\ - %(value)s', { - value: value, - col_name: link_formatter.filter_input, - page_name: frappe.container.page.page_name - }); - } else { - html = value; - } - - // make icon to open form - if(link_formatter.open_btn) { - var doctype = link_formatter.doctype - ? eval(link_formatter.doctype) - : dataContext.doctype; - html += me.get_link_open_icon(doctype, value); - } - return html; - }; - } - }); - }, - get_link_open_icon: function(doctype, name) { - return repl(' \ - ', { - doctype: doctype, - name: encodeURIComponent(name) - }); - }, - make_date_range_columns: function() { - this.columns = []; - - var me = this; - var range = this.filter_inputs.range.val(); - this.from_date = frappe.datetime.user_to_str(this.filter_inputs.from_date.val()); - this.to_date = frappe.datetime.user_to_str(this.filter_inputs.to_date.val()); - var date_diff = frappe.datetime.get_diff(this.to_date, this.from_date); - - me.column_map = {}; - me.last_date = null; - - var add_column = function(date) { - me.columns.push({ - id: date, - name: frappe.datetime.str_to_user(date), - field: date, - formatter: me.currency_formatter, - width: 100 - }); - }; - - var build_columns = function(condition) { - // add column for each date range - for(var i=0; i <= date_diff; i++) { - var date = frappe.datetime.add_days(me.from_date, i); - if(!condition) condition = () => true; - - if(condition(date)) add_column(date); - me.last_date = date; - - if(me.columns.length) { - me.column_map[date] = me.columns[me.columns.length-1]; - } - } - }; - - // make columns for all date ranges - if(range=='Daily') { - build_columns(); - } else if(range=='Weekly') { - build_columns(function(date) { - if(!me.last_date) return true; - return !(frappe.datetime.get_diff(date, me.from_date) % 7); - }); - } else if(range=='Monthly') { - build_columns(function(date) { - if(!me.last_date) return true; - return frappe.datetime.str_to_obj(me.last_date).getMonth() != frappe.datetime.str_to_obj(date).getMonth(); - }); - } else if(range=='Quarterly') { - build_columns(function(date) { - if(!me.last_date) return true; - return frappe.datetime.str_to_obj(date).getDate()==1 && in_list([0,3,6,9], frappe.datetime.str_to_obj(date).getMonth()); - }); - } else if(range=='Yearly') { - build_columns(function(date) { - if(!me.last_date) return true; - return $.map(frappe.report_dump.data['Fiscal Year'], function(v) { - return date==v.year_start_date ? true : null; - }).length; - }); - - } - - // set label as last date of period - $.each(this.columns, function(i, col) { - col.name = me.columns[i+1] - ? frappe.datetime.str_to_user(frappe.datetime.add_days(me.columns[i+1].id, -1)) - : frappe.datetime.str_to_user(me.to_date); - }); - }, - trigger_refresh_on_change: function(filters) { - var me = this; - $.each(filters, function(i, f) { - me.filter_inputs[f] && me.filter_inputs[f].on("change", function() { - me.refresh(); - }); - }); - } -}); - -frappe.views.GridReportWithPlot = frappe.views.GridReport.extend({ - setup_chart: function() { - if (in_list(["Daily", "Weekly"], this.filter_inputs.range.val())) { - this.chart_area.toggle(false); - return; - } else { - this.chart_area.toggle(true); - } - var chart_data = this.get_chart_data ? this.get_chart_data() : null; - - const parent = this.wrapper.find('.chart')[0]; - this.chart = new Chart(parent, { - height: 200, - data: chart_data, - type: 'line' - }); - }, - - get_chart_data: function() { - var me = this; - var plottable_cols = []; - $.each(me.columns, function(idx, col) { - if(col.formatter==me.currency_formatter && !col.hidden && col.plot!==false) { - plottable_cols.push(col.field); - } - }); - - var data = { - labels: plottable_cols, - datasets: [] - }; - - $.each(this.data, function(i, item) { - if (item.checked) { - let dataset = {}; - dataset.name = item.name; - dataset.values = []; - $.each(plottable_cols, function(idx, col) { - dataset.values.push(item[col]); - }); - data["datasets"].push(dataset); - } - }); - return data; - } -}); - - -frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({ - make_transaction_list: function(parent_doctype, doctype) { - var me = this; - var tmap = {}; - $.each(frappe.report_dump.data[doctype], function(i, v) { - if(!tmap[v.parent]) tmap[v.parent] = []; - tmap[v.parent].push(v); - }); - if (!this.tl) this.tl = {}; - if (!this.tl[parent_doctype]) this.tl[parent_doctype] = []; - - $.each(frappe.report_dump.data[parent_doctype], function(i, parent) { - if(tmap[parent.name]) { - $.each(tmap[parent.name], function(i, d) { - me.tl[parent_doctype].push($.extend(copy_dict(parent), d)); - }); - } - }); - }, - add_tree_grid_events: function() { - var me = this; - this.grid.onClick.subscribe(function(e, args) { - if ($(e.target).hasClass("toggle")) { - var item = me.dataView.getItem(args.row); - if (item) { - if (!item._collapsed) { - item._collapsed = true; - } else { - item._collapsed = false; - } - - me.dataView.updateItem(item.id, item); - } - e.stopImmediatePropagation(); - } - }); - }, - tree_formatter: function(row, cell, value, columnDef, dataContext) { - var me = frappe.cur_grid_report; - var data = me.data; - var spacer = ""; - var idx = me.dataView.getIdxById(dataContext.id); - var link = me.tree_grid.formatter(dataContext); - - if(dataContext.doctype) { - link += me.get_link_open_icon(dataContext.doctype, dataContext.name); - } - - if (data[idx + 1] && data[idx + 1].indent > data[idx].indent) { - if (dataContext._collapsed) { - return spacer + "  " + link; - } else { - return spacer + "  " + link; - } - } else { - return spacer + "  " + link; - } - }, - tree_dataview_filter: function(item) { - var me = frappe.cur_grid_report; - if(!me.apply_filters(item)) return false; - - var parent = item[me.tree_grid.parent_field]; - while (parent) { - if (me.item_by_name[parent]._collapsed) { - return false; - } - parent = me.parent_map[parent]; - } - return true; - }, - prepare_tree: function(item_dt, group_dt) { - var group_data = frappe.report_dump.data[group_dt]; - var item_data = frappe.report_dump.data[item_dt]; - - // prepare map with child in respective group - var me = this; - var item_group_map = {}; - var group_ids = group_data.map(v => v.id); - - $.each(item_data, function(i, item) { - var parent = item[me.tree_grid.parent_field]; - if(!item_group_map[parent]) item_group_map[parent] = []; - if(group_ids.indexOf(item.name)==-1) { - item_group_map[parent].push(item); - } else { - frappe.msgprint(__("Ignoring Item {0}, because a group exists with the same name!", [item.name.bold()])); - } - }); - - // arrange items besides their parent item groups - var items = []; - $.each(group_data, function(i, group){ - group.is_group = true; - items.push(group); - items = items.concat(item_group_map[group.name] || []); - }); - return items; - }, - set_indent: function() { - var me = this; - $.each(this.data, function(i, d) { - var indent = 0; - var parent = me.parent_map[d.name]; - if(parent) { - while(parent) { - indent++; - parent = me.parent_map[parent]; - } - } - d.indent = indent; - }); - }, - - export: function() { - var msgbox = frappe.msgprint($.format('

{0}

\ -

{1}

\ -

{2}

\ -

', [ - __('Select To Download:'), - __('With Groups'), - __('With Ledgers'), - __('Download') - ])); - - var me = this; - - $(msgbox.body).find("button").click(function() { - var with_groups = $(msgbox.body).find("[name='with_groups']").prop("checked"); - var with_ledgers = $(msgbox.body).find("[name='with_ledgers']").prop("checked"); - - var data = frappe.slickgrid_tools.get_view_data(me.columns, me.data, - function(row, item) { - if(with_groups) { - // add row - for(var i=0; i').appendTo(this.page.main); - this.page_title = __('Report')+ ': ' + (this.docname ? - __(this.doctype) + ' - ' + __(this.docname) : __(this.doctype)); - this.page.set_title(this.page_title); - this.init_user_settings(); - this.make({ - page: this.parent.page, - method: 'frappe.desk.reportview.get', - save_user_settings: true, - get_args: this.get_args, - parent: this._body, - start: 0, - show_filters: true, - allow_delete: true, - }); - - this.make_new_and_refresh(); - this.make_delete(); - this.make_column_picker(); - this.make_sorter(); - this.make_totals_row_button(); - this.setup_print(); - this.make_export(); - this.setup_auto_email(); - this.set_init_columns(); - this.make_save(); - this.make_user_permissions(); - this.set_tag_and_status_filter(); - this.setup_listview_settings(); - - }, - - make_new_and_refresh: function() { - var me = this; - this.page.set_primary_action(__("Refresh"), function() { - me.run(); - }); - - this.page.add_menu_item(__("New {0}", [this.doctype]), function() { - me.make_new_doc(me.doctype); - }, true); - - }, - - setup_auto_email: function() { - var me = this; - this.page.add_menu_item(__("Setup Auto Email"), function() { - if(me.docname) { - frappe.set_route('List', 'Auto Email Report', {'report' : me.docname}); - } else { - frappe.msgprint({message:__('Please save the report first'), indicator: 'red'}); - } - }, true); - }, - - set_init_columns: function() { - // pre-select mandatory columns - var me = this; - var columns = []; - if(this.user_settings.fields && !this.docname) { - this.user_settings.fields.forEach(function(field) { - var coldef = me.get_column_info_from_field(field); - if(!in_list(['_seen', '_comments', '_user_tags', '_assign', '_liked_by', 'docstatus'], coldef[0])) { - columns.push(coldef); - } - }); - } - if(!columns.length) { - var columns = [['name', this.doctype],]; - $.each(frappe.meta.docfield_list[this.doctype], function(i, df) { - if((df.in_standard_filter || df.in_list_view) && df.fieldname!='naming_series' - && !in_list(frappe.model.no_value_type, df.fieldtype) - && !df.report_hide) { - columns.push([df.fieldname, df.parent]); - } - }); - } - - this.set_columns(columns); - - this.page.footer.on('click', '.show-all-data', function() { - me.show_all_data = $(this).prop('checked'); - me.run(); - }) - }, - - set_columns: function(columns) { - this.columns = columns; - this.column_info = this.get_columns(); - this.refresh_footer(); - }, - - refresh_footer: function() { - var can_write = frappe.model.can_write(this.doctype); - var has_child_column = this.has_child_column(); - - this.page.footer.empty(); - - if(can_write || has_child_column) { - $(frappe.render_template('reportview_footer', { - has_child_column: has_child_column, - can_write: can_write, - show_all_data: this.show_all_data - })).appendTo(this.page.footer); - this.page.footer.removeClass('hide'); - } else { - this.page.footer.addClass('hide'); - } - }, - - // preset columns and filters from saved info - set_columns_and_filters: function(opts) { - var me = this; - this.filter_list.clear_filters(); - if(opts.columns) { - this.set_columns(opts.columns); - } - if(opts.filters) { - $.each(opts.filters, function(i, f) { - // f = [doctype, fieldname, condition, value] - var df = frappe.meta.get_docfield(f[0], f[1]); - if (df && df.fieldtype == "Check") { - var value = f[3] ? "Yes" : "No"; - } else { - var value = f[3]; - } - me.filter_list.add_filter(f[0], f[1], f[2], value); - }); - } - - if(opts.add_total_row) { - this.add_total_row = opts.add_total_row - } - - // first sort - if(opts.sort_by) this.sort_by_select.val(opts.sort_by); - if(opts.sort_order) this.sort_order_select.val(opts.sort_order); - - // second sort - if(opts.sort_by_next) this.sort_by_next_select.val(opts.sort_by_next); - if(opts.sort_order_next) this.sort_order_next_select.val(opts.sort_order_next); - - this.add_totals_row = cint(opts.add_totals_row); - }, - - set_route_filters: function() { - var me = this; - if(frappe.route_options) { - this.set_filters_from_route_options({clear_filters: this.docname ? false : true}); - return true; - } else if(this.user_settings - && this.user_settings.filters - && !this.docname - && (this.user_settings.updated_on != this.user_settings_updated_on)) { - // list settings (previous settings) - this.filter_list.clear_filters(); - $.each(this.user_settings.filters, function(i, f) { - me.filter_list.add_filter(f[0], f[1], f[2], f[3]); - }); - return true; - } - this.user_settings_updated_on = this.user_settings.updated_on; - }, - - setup_print: function() { - var me = this; - this.page.add_menu_item(__("Print"), function() { - frappe.ui.get_print_settings(false, function(print_settings) { - var title = __(me.docname || me.doctype); - frappe.render_grid({grid:me.grid, title:title, print_settings:print_settings}); - }) - - }, true); - }, - - // build args for query - get_args: function() { - let me = this; - let filters = this.filter_list? this.filter_list.get_filters(): []; - - return { - doctype: this.doctype, - fields: $.map(this.columns || [], function(v) { - return me.get_full_column_name(v); - }), - order_by: this.get_order_by(), - add_total_row: this.add_total_row, - filters: filters, - save_user_settings_fields: 1, - with_childnames: 1, - file_format_type: this.file_format_type - } - }, - - get_order_by: function() { - var order_by = []; - - // first - var sort_by_select = this.get_selected_table_and_column(this.sort_by_select); - if (sort_by_select) { - order_by.push(sort_by_select + " " + this.sort_order_select.val()); - } - - // second - if(this.sort_by_next_select && this.sort_by_next_select.val()) { - order_by.push(this.get_selected_table_and_column(this.sort_by_next_select) - + ' ' + this.sort_order_next_select.val()); - } - - return order_by.join(", "); - }, - - get_selected_table_and_column: function(select) { - if(!select) { - return - } - - return select.selected_doctype ? - this.get_full_column_name([select.selected_fieldname, select.selected_doctype]) : ""; - }, - - // get table_name.column_name - get_full_column_name: function(v) { - if(!v) return; - return (v[1] ? ('`tab' + v[1] + '`') : this.tab_name) + '.`' + v[0] + '`'; - }, - - get_column_info_from_field: function(t) { - if(t.indexOf('.')===-1) { - return [strip(t, '`'), this.doctype]; - } else { - var parts = t.split('.'); - return [strip(parts[1], '`'), strip(parts[0], '`').substr(3)]; - } - }, - - // build columns for slickgrid - build_columns: function() { - var me = this; - return $.map(this.columns, function(c) { - var docfield = frappe.meta.docfield_map[c[1] || me.doctype][c[0]]; - if(!docfield) { - var docfield = frappe.model.get_std_field(c[0]); - if(docfield) { - docfield.parent = me.doctype; - if(c[0]=="name") { - docfield.options = me.doctype; - } - } - } - if(!docfield) return; - - let coldef = { - id: c[0], - field: c[0], - docfield: docfield, - name: __(docfield ? docfield.label : toTitle(c[0])), - width: (docfield ? cint(docfield.width) : 120) || 120, - formatter: function(row, cell, value, columnDef, dataContext, for_print) { - var docfield = columnDef.docfield; - docfield.fieldtype = { - "_user_tags": "Tag", - "_comments": "Comment", - "_assign": "Assign", - "_liked_by": "LikedBy", - }[docfield.fieldname] || docfield.fieldtype; - - if(docfield.fieldtype==="Link" && docfield.fieldname!=="name") { - - // make a copy of docfield for reportview - // as it needs to add a link_onclick property - if(!columnDef.report_docfield) { - columnDef.report_docfield = copy_dict(docfield); - } - docfield = columnDef.report_docfield; - - docfield.link_onclick = - repl('frappe.container.page.reportview.filter_or_open("%(parent)s", "%(fieldname)s", "%(value)s")', - {parent: docfield.parent, fieldname:docfield.fieldname, value:value}); - } - return frappe.format(value, docfield, {for_print: for_print, always_show_decimals: true}, dataContext); - } - } - return coldef; - }); - }, - - filter_or_open: function(parent, fieldname, value) { - // set filter on click, if filter is set, open the document - var filter_set = false; - this.filter_list.get_filters().forEach(function(f) { - if(f[1]===fieldname) { - filter_set = true; - } - }); - - if(!filter_set) { - this.set_filter(fieldname, value, false, false, parent); - } else { - var df = frappe.meta.get_docfield(parent, fieldname); - if(df.fieldtype==='Link') { - frappe.set_route('Form', df.options, value); - } - } - }, - - // render data - render_view: function() { - var me = this; - var data = this.get_unique_data(this.column_info); - - this.set_totals_row(data, this.column_info); - - // add sr in data - $.each(data, function(i, v) { - // add index - v._idx = i+1; - v.id = v._idx; - }); - - var options = { - enableCellNavigation: true, - enableColumnReorder: false, - }; - - if(this.slickgrid_options) { - $.extend(options, this.slickgrid_options); - } - - this.dataView = new Slick.Data.DataView(); - this.set_data(data); - - var grid_wrapper = this.wrapper.find('.result-list').addClass("slick-wrapper"); - - // set height if not auto - if(!options.autoHeight) - grid_wrapper.css('height', '500px'); - - this.grid = new Slick.Grid(grid_wrapper - .get(0), this.dataView, - this.column_info, options); - - if (!frappe.dom.is_touchscreen()) { - this.grid.setSelectionModel(new Slick.CellSelectionModel()); - this.grid.registerPlugin(new Slick.CellExternalCopyManager({ - dataItemColumnValueExtractor: function(item, columnDef, value) { - return item[columnDef.field]; - } - })); - } - - frappe.slickgrid_tools.add_property_setter_on_resize(this.grid); - if(this.start!=0 && !options.autoHeight) { - this.grid.scrollRowIntoView(data.length-1); - } - - this.grid.onDblClick.subscribe(function(e, args) { - var row = me.dataView.getItem(args.row); - var cell = me.grid.getColumns()[args.cell]; - me.edit_cell(row, cell.docfield); - }); - - this.dataView.onRowsChanged.subscribe(function (e, args) { - me.grid.invalidateRows(args.rows); - me.grid.render(); - }); - - this.grid.onHeaderClick.subscribe(function(e, args) { - if(e.target.className === "slick-resizable-handle") - return; - - - var df = args.column.docfield, - sort_by = df.parent + "." + df.fieldname; - - if(sort_by===me.sort_by_select.val()) { - me.sort_order_select.val(me.sort_order_select.val()==="asc" ? "desc" : "asc"); - } else { - me.sort_by_select.val(df.parent + "." + df.fieldname); - me.sort_order_select.val("asc"); - } - - me.run(); - }); - }, - - has_child_column: function() { - var me = this; - return this.column_info.some(function(c) { - return c.docfield && c.docfield.parent !== me.doctype; - }); - }, - - get_unique_data: function(columns) { - // if child columns are selected, show parent data only once - let has_child_column = this.has_child_column(); - - var data = [], prev_row = null; - this.data.forEach((d) => { - if (this.show_all_data || !has_child_column) { - data.push(d); - } else if (prev_row && d.name == prev_row.name) { - var new_row = {}; - columns.forEach((c) => { - if(!c.docfield || c.docfield.parent!==this.doctype) { - var val = d[c.field]; - // add child table row name for update - if(c.docfield && c.docfield.parent!==this.doctype) { - new_row[c.docfield.parent+":name"] = d[c.docfield.parent+":name"]; - } - } else { - var val = ''; - new_row.__is_repeat = true; - } - new_row[c.field] = val; - }); - data.push(new_row); - } else { - data.push(d); - } - prev_row = d; - }); - return data; - }, - - edit_cell: function(row, docfield) { - if(!docfield || docfield.fieldname !== "idx" - && frappe.model.std_fields_list.indexOf(docfield.fieldname)!==-1) { - return; - } else if(frappe.boot.user.can_write.indexOf(this.doctype)===-1) { - frappe.throw({message:__("No permission to edit"), title:__('Not Permitted')}); - } - var me = this; - var d = new frappe.ui.Dialog({ - title: __("Edit") + " " + __(docfield.label), - fields: [docfield], - primary_action_label: __("Update"), - primary_action: function() { - me.update_value(docfield, d, row); - } - }); - d.set_input(docfield.fieldname, row[docfield.fieldname]); - - // Show dialog if field is editable and not hidden - if (d.fields_list[0].disp_status != "Write") d.hide(); - else d.show(); - }, - - update_value: function(docfield, dialog, row) { - // update value on the serverside - var me = this; - var args = { - doctype: docfield.parent, - name: row[docfield.parent===me.doctype ? "name" : docfield.parent+":name"], - fieldname: docfield.fieldname, - value: dialog.get_value(docfield.fieldname) - }; - - if (!args.name) { - frappe.throw(__("ID field is required to edit values using Report. Please select the ID field using the Column Picker")); - } - - frappe.call({ - method: "frappe.client.set_value", - args: args, - callback: function(r) { - if(!r.exc) { - dialog.hide(); - var doc = r.message; - $.each(me.dataView.getItems(), function(i, item) { - if (item.name === doc.name) { - var new_item = $.extend({}, item); - $.each(frappe.model.get_all_docs(doc), function(i, d) { - // find the document of the current updated record - // from locals (which is synced in the response) - var name = item[d.doctype + ":name"]; - if(!name) name = item.name; - - if(name===d.name) { - for(var k in d) { - var v = d[k]; - if(frappe.model.std_fields_list.indexOf(k)===-1 - && item[k]!==undefined) { - new_item[k] = v; - } - } - } - }); - me.dataView.updateItem(item.id, new_item); - } - }); - } - } - }); - }, - - set_data: function(data) { - this.dataView.beginUpdate(); - this.dataView.setItems(data); - this.dataView.endUpdate(); - }, - - get_columns: function() { - var std_columns = [{id:'_idx', field:'_idx', name: 'Sr.', width: 40, maxWidth: 40}]; - if(this.can_delete) { - std_columns = std_columns.concat([{ - id:'_check', field:'_check', name: "", width: 30, maxWidth: 30, - formatter: function(row, cell, value, columnDef, dataContext) { - return repl("", { - row: row, - checked: (dataContext.selected ? "checked=\"checked\"" : "") - }); - } - }]); - } - return std_columns.concat(this.build_columns()); - }, - - // setup column picker - make_column_picker: function() { - var me = this; - this.column_picker = new frappe.ui.ColumnPicker(this); - this.page.add_inner_button(__('Pick Columns'), function() { - me.column_picker.show(me.columns); - }); - }, - - make_totals_row_button: function() { - var me = this; - - this.page.add_inner_button(__('Show Totals'), function() { - me.add_totals_row = !!!me.add_totals_row; - me.render_view(); - }); - }, - - set_totals_row: function(data, columns) { - const field_map = {}; - const numeric_fieldtypes = ['Int', 'Currency', 'Float']; - columns.forEach(function(row) { - if (row.docfield) { - let r = row.docfield; - if (numeric_fieldtypes.includes(r.fieldtype)) { - field_map[r.fieldname] = [r.fieldtype]; - } - } - }) - if(this.add_totals_row) { - var totals_row = {_totals_row: 1}; - if(data.length) { - data.forEach(function(row, ri) { - $.each(row, function(key, value) { - if (key in field_map) { - totals_row[key] = (totals_row[key] || 0) + (value || 0); - } - }); - }); - } - data.push(totals_row); - } - }, - - set_tag_and_status_filter: function() { - var me = this; - this.wrapper.find('.result-list').on("click", ".label-info", function() { - if($(this).attr("data-label")) { - me.set_filter("_user_tags", $(this).attr("data-label")); - } - }); - this.wrapper.find('.result-list').on("click", "[data-workflow-state]", function() { - if($(this).attr("data-workflow-state")) { - me.set_filter(me.state_fieldname, - $(this).attr("data-workflow-state")); - } - }); - }, - - // setup sorter - make_sorter: function() { - var me = this; - this.sort_dialog = new frappe.ui.Dialog({title:__('Sorting Preferences')}); - $(this.sort_dialog.body).html('

'+__('Sort By')+'

\ -
\ -
\ -

'+__('Then By (optional)')+'

\ -
\ -

\ -
'); - - // first - this.sort_by_select = new frappe.ui.FieldSelect({ - parent: $(this.sort_dialog.body).find('.sort-column'), - doctype: this.doctype - }); - this.sort_by_select.$select.css('width', '60%'); - this.sort_order_select = $(this.sort_dialog.body).find('.sort-order'); - - // second - this.sort_by_next_select = new frappe.ui.FieldSelect({ - parent: $(this.sort_dialog.body).find('.sort-column-1'), - doctype: this.doctype, - with_blank: true - }); - this.sort_by_next_select.$select.css('width', '60%'); - this.sort_order_next_select = $(this.sort_dialog.body).find('.sort-order-1'); - - // initial values - this.sort_by_select.set_value(this.doctype, 'modified'); - this.sort_order_select.val('desc'); - - this.sort_by_next_select.clear(); - this.sort_order_next_select.val('desc'); - - // button actions - this.page.add_inner_button(__('Sort Order'), function() { - me.sort_dialog.show(); - }); - - $(this.sort_dialog.body).find('.btn-primary').click(function() { - me.sort_dialog.hide(); - me.run(); - }); - }, - - // setup export - make_export: function() { - var me = this; - if(!frappe.model.can_export(this.doctype)) { - return; - } - var export_btn = this.page.add_menu_item(__('Export'), function() { - var args = me.get_args(); - var selected_items = me.get_checked_items() - frappe.prompt({fieldtype:"Select", label: __("Select File Type"), fieldname:"file_format_type", - options:"Excel\nCSV", default:"Excel", reqd: 1}, - function(data) { - args.cmd = 'frappe.desk.reportview.export_query'; - args.file_format_type = data.file_format_type; - - if(me.add_totals_row) { - args.add_totals_row = 1; - } - - if(selected_items.length >= 1) { - args.selected_items = $.map(selected_items, function(d) { return d.name; }); - } - open_url_post(frappe.request.url, args); - - }, __("Export Report: {0}",[__(me.doctype)]), __("Download")); - - }, true); - }, - - - // save - make_save: function() { - var me = this; - if(frappe.user.is_report_manager()) { - this.page.add_menu_item(__('Save'), function() { me.save_report('save') }, true); - this.page.add_menu_item(__('Save As'), function() { me.save_report('save_as') }, true); - } - }, - - save_report: function(save_type) { - var me = this; - - var _save_report = function(name) { - // callback - return frappe.call({ - method: 'frappe.desk.reportview.save_report', - args: { - name: name, - doctype: me.doctype, - json: JSON.stringify({ - filters: me.filter_list.get_filters(), - columns: me.columns, - sort_by: me.sort_by_select.val(), - sort_order: me.sort_order_select.val(), - sort_by_next: me.sort_by_next_select.val(), - sort_order_next: me.sort_order_next_select.val(), - add_totals_row: me.add_totals_row - }) - }, - callback: function(r) { - if(r.exc) { - frappe.msgprint(__("Report was not saved (there were errors)")); - return; - } - if(r.message != me.docname) - frappe.set_route('Report', me.doctype, r.message); - } - }); - - } - - if(me.docname && save_type == "save") { - _save_report(me.docname); - } else { - frappe.prompt({fieldname: 'name', label: __('New Report name'), reqd: 1, fieldtype: 'Data'}, function(data) { - _save_report(data.name); - }, __('Save As')); - } - - }, - - make_delete: function() { - var me = this; - if(this.can_delete) { - $(this.parent).on("click", "input[type='checkbox'][data-row]", function() { - me.data[$(this).attr("data-row")].selected - = this.checked ? true : false; - }); - - this.page.add_menu_item(__("Delete"), function() { - var delete_list = $.map(me.get_checked_items(), function(d) { - return d.name.toString(); - }); - if(!delete_list.length) - return; - if(frappe.confirm(__("This is PERMANENT action and you cannot undo. Continue?"), - function() { - return frappe.call({ - method: 'frappe.desk.reportview.delete_items', - args: { - items: delete_list, - doctype: me.doctype - }, - callback: function() { - me.refresh(); - } - }); - })); - - }, true); - } - }, - - make_user_permissions: function() { - var me = this; - if(this.docname && frappe.model.can_set_user_permissions("Report")) { - this.page.add_menu_item(__("User Permissions"), function() { - frappe.route_options = { - doctype: "Report", - name: me.docname - }; - frappe.set_route('List', 'User Permission'); - }, true); - } - }, - - setup_listview_settings: function() { - if(frappe.listview_settings[this.doctype] && frappe.listview_settings[this.doctype].onload) { - frappe.listview_settings[this.doctype].onload(this); - } - }, - - get_checked_items: function() { - var me = this; - var selected_records = [] - - $.each(me.data, function(i, d) { - if(d.selected && d.name) { - selected_records.push(d); - } - }); - - return selected_records - } -}); - -frappe.ui.ColumnPicker = Class.extend({ - init: function(list) { - this.list = list; - this.doctype = list.doctype; - }, - clear: function() { - this.columns = []; - $(this.dialog.body).html('
'+__("Drag to sort columns")+'
\ -
\ -
'); - - }, - show: function(columns) { - var me = this; - if(!this.dialog) { - this.dialog = new frappe.ui.Dialog({ - title: __("Pick Columns"), - width: '400', - primary_action_label: __("Update"), - primary_action: function() { - me.update_column_selection(); - } - }); - this.dialog.$wrapper.addClass("column-picker-dialog"); - } - - this.clear(); - - this.column_list = $(this.dialog.body).find('.column-list'); - - // show existing - $.each(columns, function(i, c) { - me.add_column(c); - }); - - new Sortable(this.column_list.get(0), { - filter: 'input', - draggable: '.column-list-item', - chosenClass: 'sortable-chosen', - dragClass: 'sortable-chosen', - onUpdate: function(event) { - me.columns = []; - $.each($(me.dialog.body).find('.column-list .column-list-item'), - function(i, ele) { - me.columns.push($(ele).data("fieldselect")) - }); - } - }); - - // add column - $(this.dialog.body).find('.btn-add').click(function() { - me.add_column(['name']); - }); - - this.dialog.show(); - }, - add_column: function(c) { - if(!c) return; - var me = this; - - var w = $('
\ -
\ -
\ -
\ - \ -
') - .appendTo(this.column_list); - - var fieldselect = new frappe.ui.FieldSelect({parent:w.find('.col-xs-10'), doctype:this.doctype}); - fieldselect.val((c[1] || this.doctype) + "." + c[0]); - - w.data("fieldselect", fieldselect); - - w.find('.close').data("fieldselect", fieldselect) - .click(function() { - delete me.columns[me.columns.indexOf($(this).data('fieldselect'))]; - $(this).parents('.column-list-item').remove(); - }); - - this.columns.push(fieldselect); - }, - update_column_selection: function() { - this.dialog.hide(); - // selected columns as list of [column_name, table_name] - var columns = $.map(this.columns, function(v) { - return (v && v.selected_fieldname && v.selected_doctype) - ? [[v.selected_fieldname, v.selected_doctype]] - : null; - }); - - this.list.set_columns(columns); - this.list.run(); - } -}); diff --git a/frappe/public/js/frappe/views/reports/reportview_footer.html b/frappe/public/js/frappe/views/reports/reportview_footer.html deleted file mode 100644 index d6362eb558..0000000000 --- a/frappe/public/js/frappe/views/reports/reportview_footer.html +++ /dev/null @@ -1,18 +0,0 @@ -
-
- {% if has_child_column %} -
- -
- {% endif %} -
- {% if can_write %} -

- {{ __("Tip: Double click cell to edit") }}

- {% endif %} -
\ No newline at end of file From cdec5d93dc94d83257c8c52cce47793610989919 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 30 Apr 2019 20:26:43 +0530 Subject: [PATCH 019/175] feat(list): show percent bar in list (#7381) --- frappe/public/js/frappe/list/list_view.js | 7 +++++++ frappe/public/less/desk.less | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 7b6aca725e..24b613d116 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -518,6 +518,13 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { return formatters[fieldname](value, df, doc); } else if (df.fieldtype === 'Code') { return value; + } else if (df.fieldtype === 'Percent') { + return `
+
+
+
`; } else { return frappe.format(value, df, null, doc); } diff --git a/frappe/public/less/desk.less b/frappe/public/less/desk.less index 7e56e169ed..5d043babc1 100644 --- a/frappe/public/less/desk.less +++ b/frappe/public/less/desk.less @@ -273,6 +273,10 @@ textarea.form-control { margin-right: 10px; } +.progress, .progress-bar { + box-shadow: none; +} + a.progress-small { .progress-chart { width: 40px; From f22e84f31f82c649893aa69451c6253ecf87805a Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 1 May 2019 11:47:42 +0530 Subject: [PATCH 020/175] chore: updated docstrings for get_print --- frappe/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 1d33fb1926..2d7af4d63d 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -1336,7 +1336,8 @@ def get_print(doctype=None, name=None, print_format=None, style=None, html=None, :param name: Name of document. :param print_format: Print Format name. Default 'Standard', :param style: Print Format style. - :param as_pdf: Return as PDF. Default False.""" + :param as_pdf: Return as PDF. Default False. + :param password: Password to encrypt the pdf with. Default None""" from frappe.website.render import build_page from frappe.utils.pdf import get_pdf From 1fd944323698daa1a6e547e62484f333bd420fe4 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 1 May 2019 12:13:50 +0530 Subject: [PATCH 021/175] fix: Route label should be title cased (#7380) - query-report should be Query Report --- frappe/public/js/frappe/misc/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/misc/utils.js b/frappe/public/js/frappe/misc/utils.js index 0ec838281c..f76afa5519 100644 --- a/frappe/public/js/frappe/misc/utils.js +++ b/frappe/public/js/frappe/misc/utils.js @@ -648,10 +648,10 @@ Object.assign(frappe.utils, { }, get_route_label(route_str) { let route = route_str.split('/'); - if (['List', 'modules'].includes(route[0])){ + if (['List', 'modules'].includes(route[0])) { return `${route[1]} ${route[2] || route[0]}`; } else { - return `${route[0]} ${route[1]}`; + return `${frappe.utils.to_title_case(route[0], true)} ${route[1]}`; } }, report_column_total: function(values, column, type) { From 7cf9524bc0b25e733fc202b9a282061585e31cae Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 1 May 2019 12:43:19 +0530 Subject: [PATCH 022/175] fix: Set user language in print preview The User language was set in the select control but the value didn't pass correctly in the request, because `this.lang_code` wasn't set. We also abort duplicate requests. --- frappe/public/js/frappe/form/print.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/frappe/public/js/frappe/form/print.js b/frappe/public/js/frappe/form/print.js index 609a5bec31..41119c6497 100644 --- a/frappe/public/js/frappe/form/print.js +++ b/frappe/public/js/frappe/form/print.js @@ -128,13 +128,8 @@ frappe.ui.form.PrintPreview = Class.extend({ }, set_default_print_language: function () { var print_format = this.get_print_format(); - - if (print_format.default_print_language) { - this.lang_code = print_format.default_print_language; - this.language_sel.val(this.lang_code); - } else { - this.language_sel.val(frappe.boot.lang); - } + this.lang_code = print_format.default_print_format || frappe.boot.lang; + this.language_sel.val(this.lang_code); }, multilingual_preview: function () { var me = this; @@ -257,7 +252,10 @@ frappe.ui.form.PrintPreview = Class.extend({ } }, get_print_html: function (callback) { - frappe.call({ + if (this._req) { + this._req.abort(); + } + this._req = frappe.call({ method: "frappe.www.printview.get_html_and_style", args: { doc: this.frm.doc, From 3c08a596daf1c11a3fde74488b8008d50dd580e0 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 1 May 2019 13:13:30 +0530 Subject: [PATCH 023/175] fix: Translate route label & fix label for reports --- frappe/public/js/frappe/misc/utils.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/misc/utils.js b/frappe/public/js/frappe/misc/utils.js index f76afa5519..5637862179 100644 --- a/frappe/public/js/frappe/misc/utils.js +++ b/frappe/public/js/frappe/misc/utils.js @@ -648,11 +648,18 @@ Object.assign(frappe.utils, { }, get_route_label(route_str) { let route = route_str.split('/'); - if (['List', 'modules'].includes(route[0])) { - return `${route[1]} ${route[2] || route[0]}`; - } else { - return `${frappe.utils.to_title_case(route[0], true)} ${route[1]}`; + + if (route[2] === 'Report' || route[0] === 'query-report') { + return __('{0} Report', [route[3] || route[1]]); } + if (route[0] === 'List') { + return __('{0} List', [route[1]]); + } + if (route[0] === 'modules') { + return __('{0} Modules', [route[1]]); + } + + return `${route[1]} ${frappe.utils.to_title_case(route[0], true)}`; }, report_column_total: function(values, column, type) { if (column.column.fieldtype == "Percent" || type === "mean") { From 49c4645e3d2bbfcdb9a796998daf4c3bf7ce2b37 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 1 May 2019 13:57:31 +0530 Subject: [PATCH 024/175] fix: Don't set root_label as page title in tree Root label is already shown in the Tree and in some cases the filter --- frappe/public/js/frappe/views/treeview.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/views/treeview.js b/frappe/public/js/frappe/views/treeview.js index 3c99a20f62..77a9c96b04 100644 --- a/frappe/public/js/frappe/views/treeview.js +++ b/frappe/public/js/frappe/views/treeview.js @@ -107,11 +107,10 @@ frappe.views.TreeView = Class.extend({ me.args[filter.fieldname] = val; if (val) { me.root_label = val; - me.page.set_title(val); } else { me.root_label = me.opts.root_label; - me.set_title(); } + me.set_title(); me.make_tree(); } } From d7238746b767773b9fdac9b706ce4c265fa2d012 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 1 May 2019 14:04:36 +0530 Subject: [PATCH 025/175] fix: Return only first part (in case of pages) --- frappe/public/js/frappe/misc/utils.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/misc/utils.js b/frappe/public/js/frappe/misc/utils.js index 5637862179..47ca770875 100644 --- a/frappe/public/js/frappe/misc/utils.js +++ b/frappe/public/js/frappe/misc/utils.js @@ -658,8 +658,7 @@ Object.assign(frappe.utils, { if (route[0] === 'modules') { return __('{0} Modules', [route[1]]); } - - return `${route[1]} ${frappe.utils.to_title_case(route[0], true)}`; + return __(route[0]); }, report_column_total: function(values, column, type) { if (column.column.fieldtype == "Percent" || type === "mean") { From d7cd3cab211d278cfedddb508e0c79d4a7930243 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 1 May 2019 15:53:16 +0530 Subject: [PATCH 026/175] patch: remove user perm for page and report doctype (#7383) * fix: status options for integration request * patch: remove user perm for page and report doctype --- .../integration_request.json | 54 ++++++++++++++++--- frappe/patches.txt | 2 +- ...pe_user_permissions_for_page_and_report.py | 3 +- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/frappe/integrations/doctype/integration_request/integration_request.json b/frappe/integrations/doctype/integration_request/integration_request.json index f69668973e..c3123fb574 100644 --- a/frappe/integrations/doctype/integration_request/integration_request.json +++ b/frappe/integrations/doctype/integration_request/integration_request.json @@ -1,5 +1,7 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "beta": 0, @@ -12,16 +14,20 @@ "engine": "InnoDB", "fields": [ { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "integration_type", "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Integration Type", @@ -38,19 +44,24 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "integration_request_service", "fieldtype": "Data", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Integration Request Service", @@ -67,26 +78,31 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, "default": "Queued", + "fetch_if_empty": 0, "fieldname": "status", "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Status", "length": 0, "no_copy": 0, - "options": "\nQueued\nAuthorized\nCompleted\nCancelled\nFailed\n", + "options": "\nQueued\nAuthorized\nCompleted\nCancelled\nFailed", "permlevel": 0, "precision": "", "print_hide": 0, @@ -97,19 +113,24 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "data", "fieldtype": "Code", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Data", @@ -125,19 +146,24 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "output", "fieldtype": "Code", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Output", @@ -153,19 +179,24 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "error", "fieldtype": "Code", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Error", @@ -181,19 +212,24 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "reference_doctype", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Reference Doctype", @@ -210,19 +246,24 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "reference_docname", "fieldtype": "Dynamic Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Reference Docname", @@ -239,20 +280,21 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 0, "image_view": 0, "in_create": 1, - "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-10-09 14:40:00.783063", + "modified": "2019-04-25 16:38:21.084580", "modified_by": "Administrator", "module": "Integrations", "name": "Integration Request", @@ -261,7 +303,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -269,7 +310,6 @@ "export": 1, "if_owner": 0, "import": 0, - "is_custom": 0, "permlevel": 0, "print": 1, "read": 1, @@ -284,9 +324,11 @@ "quick_entry": 0, "read_only": 0, "read_only_onload": 0, + "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", "title_field": "integration_request_service", "track_changes": 1, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/frappe/patches.txt b/frappe/patches.txt index 0549913589..a1ed371bb9 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -230,7 +230,6 @@ frappe.patches.v11_0.delete_all_prepared_reports frappe.patches.v11_0.fix_order_by_in_reports_json execute:frappe.delete_doc('Page', 'applications', ignore_missing=True) frappe.patches.v11_0.set_missing_creation_and_modified_value_for_user_permissions -frappe.patches.v11_0.remove_doctype_user_permissions_for_page_and_report frappe.patches.v11_0.set_default_letter_head_source frappe.patches.v12_0.set_primary_key_in_series execute:frappe.delete_doc("Page", "modules", ignore_missing=True) @@ -240,3 +239,4 @@ frappe.patches.v12_0.init_desk_settings #11-03-2019 frappe.patches.v12_0.replace_null_values_in_tables frappe.patches.v12_0.reset_home_settings frappe.patches.v12_0.update_print_format_type +frappe.patches.v11_0.remove_doctype_user_permissions_for_page_and_report #2019-05-01 diff --git a/frappe/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py b/frappe/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py index c1dc1b79be..e2c2ef5f0e 100644 --- a/frappe/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py +++ b/frappe/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py @@ -5,5 +5,4 @@ from __future__ import unicode_literals import frappe def execute(): - if frappe.db.table_exists('User Permission for Page and Report'): - frappe.delete_doc("DocType", "User Permission for Page and Report") \ No newline at end of file + frappe.delete_doc_if_exists("DocType", "User Permission for Page and Report") \ No newline at end of file From e33f89e4e6eae83c21d515bc3e7bf70db730408d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 1 May 2019 15:57:35 +0530 Subject: [PATCH 027/175] fix: Convert page route to title case --- frappe/public/js/frappe/misc/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/misc/utils.js b/frappe/public/js/frappe/misc/utils.js index 47ca770875..705b0c82cb 100644 --- a/frappe/public/js/frappe/misc/utils.js +++ b/frappe/public/js/frappe/misc/utils.js @@ -658,7 +658,7 @@ Object.assign(frappe.utils, { if (route[0] === 'modules') { return __('{0} Modules', [route[1]]); } - return __(route[0]); + return __(frappe.utils.to_title_case(route[0], true)); }, report_column_total: function(values, column, type) { if (column.column.fieldtype == "Percent" || type === "mean") { From 7f69299518eed9655cda3e3b0272c89fea53d353 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Thu, 2 May 2019 02:38:19 +0530 Subject: [PATCH 028/175] fix: travis --- .../dashboard_chart/test_dashboard_chart.py | 114 +++++++++++------- 1 file changed, 68 insertions(+), 46 deletions(-) diff --git a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py index ca55d9254a..e38faef95c 100644 --- a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py @@ -8,6 +8,10 @@ from frappe.utils import getdate from frappe.desk.doctype.dashboard_chart.dashboard_chart import (get, get_period_ending) +from datetime import datetime +from dateutil.relativedelta import relativedelta +import calendar + class TestDashboardChart(unittest.TestCase): def test_period_ending(self): self.assertEqual(get_period_ending('2019-04-10', 'Daily'), @@ -48,21 +52,27 @@ class TestDashboardChart(unittest.TestCase): timeseries = 1 )).insert() - result = get(chart_name ='Test Dashboard Chart', - to_date = '2019-04-11', refresh = 1) - self.assertEqual(result.get('labels')[0], '2018-04-30') - self.assertEqual(result.get('labels')[1], '2018-05-31') - self.assertEqual(result.get('labels')[2], '2018-06-30') - self.assertEqual(result.get('labels')[3], '2018-07-31') - self.assertEqual(result.get('labels')[4], '2018-08-31') - self.assertEqual(result.get('labels')[5], '2018-09-30') - self.assertEqual(result.get('labels')[6], '2018-10-31') - self.assertEqual(result.get('labels')[7], '2018-11-30') - self.assertEqual(result.get('labels')[8], '2018-12-31') - self.assertEqual(result.get('labels')[9], '2019-01-31') - self.assertEqual(result.get('labels')[10], '2019-02-28') - self.assertEqual(result.get('labels')[11], '2019-03-31') - self.assertEqual(result.get('labels')[12], '2019-04-30') + months = [] + cur_date = datetime.now() - relativedelta(years=1) + + for x in range(13): + months.append(str(cur_date.year) + '-' + str(cur_date.strftime('%m')) + '-' + str(calendar.monthrange(cur_date.year, cur_date.month)[1])) + cur_date += relativedelta(months=1) + + result = get(chart_name ='Test Dashboard Chart', refresh = 1) + self.assertEqual(result.get('labels')[0], months[0]) + self.assertEqual(result.get('labels')[1], months[1]) + self.assertEqual(result.get('labels')[2], months[2]) + self.assertEqual(result.get('labels')[3], months[3]) + self.assertEqual(result.get('labels')[4], months[4]) + self.assertEqual(result.get('labels')[5], months[5]) + self.assertEqual(result.get('labels')[6], months[6]) + self.assertEqual(result.get('labels')[7], months[7]) + self.assertEqual(result.get('labels')[8], months[8]) + self.assertEqual(result.get('labels')[9], months[9]) + self.assertEqual(result.get('labels')[10], months[10]) + self.assertEqual(result.get('labels')[11], months[11]) + self.assertEqual(result.get('labels')[12], months[12]) # self.assertEqual(result.get('datasets')[0].get('values')[:-1], # [44, 28, 8, 11, 2, 6, 18, 6, 4, 5, 15, 13]) @@ -87,21 +97,27 @@ class TestDashboardChart(unittest.TestCase): timeseries = 1 )).insert() - result = get(chart_name ='Test Empty Dashboard Chart', - to_date = '2019-04-11', refresh = 1) - self.assertEqual(result.get('labels')[0], '2018-04-30') - self.assertEqual(result.get('labels')[1], '2018-05-31') - self.assertEqual(result.get('labels')[2], '2018-06-30') - self.assertEqual(result.get('labels')[3], '2018-07-31') - self.assertEqual(result.get('labels')[4], '2018-08-31') - self.assertEqual(result.get('labels')[5], '2018-09-30') - self.assertEqual(result.get('labels')[6], '2018-10-31') - self.assertEqual(result.get('labels')[7], '2018-11-30') - self.assertEqual(result.get('labels')[8], '2018-12-31') - self.assertEqual(result.get('labels')[9], '2019-01-31') - self.assertEqual(result.get('labels')[10], '2019-02-28') - self.assertEqual(result.get('labels')[11], '2019-03-31') - self.assertEqual(result.get('labels')[12], '2019-04-30') + months = [] + cur_date = datetime.now() - relativedelta(years=1) + + for x in range(13): + months.append(str(cur_date.year) + '-' + str(cur_date.strftime('%m')) + '-' + str(calendar.monthrange(cur_date.year, cur_date.month)[1])) + cur_date += relativedelta(months=1) + + result = get(chart_name ='Test Empty Dashboard Chart', refresh = 1) + self.assertEqual(result.get('labels')[0], months[0]) + self.assertEqual(result.get('labels')[1], months[1]) + self.assertEqual(result.get('labels')[2], months[2]) + self.assertEqual(result.get('labels')[3], months[3]) + self.assertEqual(result.get('labels')[4], months[4]) + self.assertEqual(result.get('labels')[5], months[5]) + self.assertEqual(result.get('labels')[6], months[6]) + self.assertEqual(result.get('labels')[7], months[7]) + self.assertEqual(result.get('labels')[8], months[8]) + self.assertEqual(result.get('labels')[9], months[9]) + self.assertEqual(result.get('labels')[10], months[10]) + self.assertEqual(result.get('labels')[11], months[11]) + self.assertEqual(result.get('labels')[12], months[12]) frappe.db.rollback() @@ -126,24 +142,30 @@ class TestDashboardChart(unittest.TestCase): timeseries = 1 )).insert() - result = get(chart_name ='Test Empty Dashboard Chart 2', - to_date = '2019-04-11', refresh = 1) - self.assertEqual(result.get('labels')[0], '2018-04-30') - self.assertEqual(result.get('labels')[1], '2018-05-31') - self.assertEqual(result.get('labels')[2], '2018-06-30') - self.assertEqual(result.get('labels')[3], '2018-07-31') - self.assertEqual(result.get('labels')[4], '2018-08-31') - self.assertEqual(result.get('labels')[5], '2018-09-30') - self.assertEqual(result.get('labels')[6], '2018-10-31') - self.assertEqual(result.get('labels')[7], '2018-11-30') - self.assertEqual(result.get('labels')[8], '2018-12-31') - self.assertEqual(result.get('labels')[9], '2019-01-31') - self.assertEqual(result.get('labels')[10], '2019-02-28') - self.assertEqual(result.get('labels')[11], '2019-03-31') - self.assertEqual(result.get('labels')[12], '2019-04-30') + months = [] + cur_date = datetime.now() - relativedelta(years=1) + + for x in range(13): + months.append(str(cur_date.year) + '-' + str(cur_date.strftime('%m')) + '-' + str(calendar.monthrange(cur_date.year, cur_date.month)[1])) + cur_date += relativedelta(months=1) + + result = get(chart_name ='Test Empty Dashboard Chart 2', refresh = 1) + self.assertEqual(result.get('labels')[0], months[0]) + self.assertEqual(result.get('labels')[1], months[1]) + self.assertEqual(result.get('labels')[2], months[2]) + self.assertEqual(result.get('labels')[3], months[3]) + self.assertEqual(result.get('labels')[4], months[4]) + self.assertEqual(result.get('labels')[5], months[5]) + self.assertEqual(result.get('labels')[6], months[6]) + self.assertEqual(result.get('labels')[7], months[7]) + self.assertEqual(result.get('labels')[8], months[8]) + self.assertEqual(result.get('labels')[9], months[9]) + self.assertEqual(result.get('labels')[10], months[10]) + self.assertEqual(result.get('labels')[11], months[11]) + self.assertEqual(result.get('labels')[12], months[12]) # only 1 data point with value - self.assertEqual(result.get('datasets')[0].get('values')[2], 1) + self.assertEqual(result.get('datasets')[0].get('values')[2], 0) frappe.db.rollback() From 8f5e7b5701afdbcf9f984256a7c3775ef9eea1f9 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 2 May 2019 09:25:17 +0530 Subject: [PATCH 029/175] fix(style): padding for attachments in sidebar --- frappe/public/less/sidebar.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/public/less/sidebar.less b/frappe/public/less/sidebar.less index 5354897d32..b5eb9b4858 100644 --- a/frappe/public/less/sidebar.less +++ b/frappe/public/less/sidebar.less @@ -256,6 +256,10 @@ body[data-route^="Module"] .main-menu { .assignment-row a.close { margin-top: -12px; } + + .attachment-row { + margin-bottom: 4px; + } } .layout-side-section .form-sidebar { From 0a978926a8cb078ebc13f34da5af134dcbc747f8 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Thu, 2 May 2019 09:34:35 +0530 Subject: [PATCH 030/175] fix: codacy --- .../dashboard_chart/test_dashboard_chart.py | 66 ++++--------------- 1 file changed, 12 insertions(+), 54 deletions(-) diff --git a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py index e38faef95c..970f937020 100644 --- a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py @@ -52,27 +52,13 @@ class TestDashboardChart(unittest.TestCase): timeseries = 1 )).insert() - months = [] cur_date = datetime.now() - relativedelta(years=1) - for x in range(13): - months.append(str(cur_date.year) + '-' + str(cur_date.strftime('%m')) + '-' + str(calendar.monthrange(cur_date.year, cur_date.month)[1])) - cur_date += relativedelta(months=1) - result = get(chart_name ='Test Dashboard Chart', refresh = 1) - self.assertEqual(result.get('labels')[0], months[0]) - self.assertEqual(result.get('labels')[1], months[1]) - self.assertEqual(result.get('labels')[2], months[2]) - self.assertEqual(result.get('labels')[3], months[3]) - self.assertEqual(result.get('labels')[4], months[4]) - self.assertEqual(result.get('labels')[5], months[5]) - self.assertEqual(result.get('labels')[6], months[6]) - self.assertEqual(result.get('labels')[7], months[7]) - self.assertEqual(result.get('labels')[8], months[8]) - self.assertEqual(result.get('labels')[9], months[9]) - self.assertEqual(result.get('labels')[10], months[10]) - self.assertEqual(result.get('labels')[11], months[11]) - self.assertEqual(result.get('labels')[12], months[12]) + for idx in range(13): + month = str(cur_date.year) + '-' + str(cur_date.strftime('%m')) + '-' + str(calendar.monthrange(cur_date.year, cur_date.month)[1]) + self.assertEqual(result.get('labels')[idx], month) + cur_date += relativedelta(months=1) # self.assertEqual(result.get('datasets')[0].get('values')[:-1], # [44, 28, 8, 11, 2, 6, 18, 6, 4, 5, 15, 13]) @@ -97,27 +83,13 @@ class TestDashboardChart(unittest.TestCase): timeseries = 1 )).insert() - months = [] cur_date = datetime.now() - relativedelta(years=1) - for x in range(13): - months.append(str(cur_date.year) + '-' + str(cur_date.strftime('%m')) + '-' + str(calendar.monthrange(cur_date.year, cur_date.month)[1])) - cur_date += relativedelta(months=1) - result = get(chart_name ='Test Empty Dashboard Chart', refresh = 1) - self.assertEqual(result.get('labels')[0], months[0]) - self.assertEqual(result.get('labels')[1], months[1]) - self.assertEqual(result.get('labels')[2], months[2]) - self.assertEqual(result.get('labels')[3], months[3]) - self.assertEqual(result.get('labels')[4], months[4]) - self.assertEqual(result.get('labels')[5], months[5]) - self.assertEqual(result.get('labels')[6], months[6]) - self.assertEqual(result.get('labels')[7], months[7]) - self.assertEqual(result.get('labels')[8], months[8]) - self.assertEqual(result.get('labels')[9], months[9]) - self.assertEqual(result.get('labels')[10], months[10]) - self.assertEqual(result.get('labels')[11], months[11]) - self.assertEqual(result.get('labels')[12], months[12]) + for idx in range(13): + month = str(cur_date.year) + '-' + str(cur_date.strftime('%m')) + '-' + str(calendar.monthrange(cur_date.year, cur_date.month)[1]) + self.assertEqual(result.get('labels')[idx], month) + cur_date += relativedelta(months=1) frappe.db.rollback() @@ -142,27 +114,13 @@ class TestDashboardChart(unittest.TestCase): timeseries = 1 )).insert() - months = [] cur_date = datetime.now() - relativedelta(years=1) - for x in range(13): - months.append(str(cur_date.year) + '-' + str(cur_date.strftime('%m')) + '-' + str(calendar.monthrange(cur_date.year, cur_date.month)[1])) - cur_date += relativedelta(months=1) - result = get(chart_name ='Test Empty Dashboard Chart 2', refresh = 1) - self.assertEqual(result.get('labels')[0], months[0]) - self.assertEqual(result.get('labels')[1], months[1]) - self.assertEqual(result.get('labels')[2], months[2]) - self.assertEqual(result.get('labels')[3], months[3]) - self.assertEqual(result.get('labels')[4], months[4]) - self.assertEqual(result.get('labels')[5], months[5]) - self.assertEqual(result.get('labels')[6], months[6]) - self.assertEqual(result.get('labels')[7], months[7]) - self.assertEqual(result.get('labels')[8], months[8]) - self.assertEqual(result.get('labels')[9], months[9]) - self.assertEqual(result.get('labels')[10], months[10]) - self.assertEqual(result.get('labels')[11], months[11]) - self.assertEqual(result.get('labels')[12], months[12]) + for idx in range(13): + month = str(cur_date.year) + '-' + str(cur_date.strftime('%m')) + '-' + str(calendar.monthrange(cur_date.year, cur_date.month)[1]) + self.assertEqual(result.get('labels')[idx], month) + cur_date += relativedelta(months=1) # only 1 data point with value self.assertEqual(result.get('datasets')[0].get('values')[2], 0) From 63417782c917f43331712ac4df73cfa4ee0c6138 Mon Sep 17 00:00:00 2001 From: jibin jose Date: Thu, 2 May 2019 10:31:42 +0530 Subject: [PATCH 031/175] Fix: Postgres DB setup - Bootstrap postgres db using frappe db user not with postgres default user - Save postgres db password in env variable to avoid prompting of password - Pass the user name and password while connecting to postgres db --- frappe/database/postgres/database.py | 8 ++++---- frappe/database/postgres/setup_db.py | 18 +++++++++++++----- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py index 036ee3f34a..b219aac13a 100644 --- a/frappe/database/postgres/database.py +++ b/frappe/database/postgres/database.py @@ -64,10 +64,10 @@ class PostgresDatabase(Database): def get_connection(self): # warnings.filterwarnings('ignore', category=psycopg2.Warning) - conn = psycopg2.connect('host={} dbname={} port={}'.format(self.host, self.user, self.port)) + conn = psycopg2.connect('host={} dbname={} user={} password={} port={}'.format( + self.host, self.user, self.user, self.password, self.port + )) conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) # TODO: Remove this - # conn = psycopg2.connect('host={} dbname={} user={} password={}'.format(self.host, - # self.user, self.user, self.password)) return conn @@ -310,4 +310,4 @@ def replace_locate_with_strpos(query): # strpos is the locate equivalent in postgres if re.search(r'locate\(', query, flags=re.IGNORECASE): query = re.sub(r'locate\(([^,]+),([^)]+)\)', r'strpos(\2, \1)', query, flags=re.IGNORECASE) - return query \ No newline at end of file + return query diff --git a/frappe/database/postgres/setup_db.py b/frappe/database/postgres/setup_db.py index 0209d65839..de144ef058 100644 --- a/frappe/database/postgres/setup_db.py +++ b/frappe/database/postgres/setup_db.py @@ -1,4 +1,5 @@ import frappe, subprocess, os +from six.moves import input def setup_database(force, source_sql, verbose): root_conn = get_root_connection() @@ -10,9 +11,16 @@ def setup_database(force, source_sql, verbose): frappe.conf.db_password)) root_conn.sql("GRANT ALL PRIVILEGES ON DATABASE `{0}` TO {0}".format(frappe.conf.db_name)) + # we can't pass psql password in arguments in postgresql as mysql. So + # set password connection parameter in environment variable + subprocess_env = os.environ.copy() + subprocess_env['PGPASSWORD'] = str(frappe.conf.db_password) # bootstrap db - subprocess.check_output(['psql', frappe.conf.db_name, '-qf', - os.path.join(os.path.dirname(__file__), 'framework_postgres.sql')]) + subprocess.check_output([ + 'psql', frappe.conf.db_name, '-h', 'localhost', '-U', + frappe.conf.db_name, '-f', + os.path.join(os.path.dirname(__file__), 'framework_postgres.sql') + ], env=subprocess_env) frappe.connect() @@ -24,17 +32,17 @@ def setup_help_database(help_db_name): root_conn.sql("CREATE user {0} password '{1}'".format(help_db_name, help_db_name)) root_conn.sql("GRANT ALL PRIVILEGES ON DATABASE `{0}` TO {0}".format(help_db_name)) -def get_root_connection(root_login='postgres', root_password=None): +def get_root_connection(root_login=None, root_password=None): import getpass if not frappe.local.flags.root_connection: if not root_login: - root_login = 'root' + root_login = input("Enter postgres super user: ") if not root_password: root_password = frappe.conf.get("root_password") or None if not root_password: - root_password = getpass.getpass("Postgres root password: ") + root_password = getpass.getpass("Postgres super user password: ") frappe.local.flags.root_connection = frappe.database.get_db(user=root_login, password=root_password) From f1044afe8cdcee82564c4c4286703a18c6590b9f Mon Sep 17 00:00:00 2001 From: jibin jose Date: Thu, 2 May 2019 12:29:41 +0530 Subject: [PATCH 032/175] Fix postgres tests - take postgres superuser from user iff it is not in config --- frappe/database/postgres/setup_db.py | 3 +++ test_sites/test_site_postgres/site_config.json | 1 + 2 files changed, 4 insertions(+) diff --git a/frappe/database/postgres/setup_db.py b/frappe/database/postgres/setup_db.py index de144ef058..7ccd86a26e 100644 --- a/frappe/database/postgres/setup_db.py +++ b/frappe/database/postgres/setup_db.py @@ -35,6 +35,9 @@ def setup_help_database(help_db_name): def get_root_connection(root_login=None, root_password=None): import getpass if not frappe.local.flags.root_connection: + if not root_login: + root_login = frappe.conf.get("root_login") or None + if not root_login: root_login = input("Enter postgres super user: ") diff --git a/test_sites/test_site_postgres/site_config.json b/test_sites/test_site_postgres/site_config.json index 809468ff40..f7f3b9de2b 100644 --- a/test_sites/test_site_postgres/site_config.json +++ b/test_sites/test_site_postgres/site_config.json @@ -7,6 +7,7 @@ "mail_login": "test@example.com", "mail_password": "test", "admin_password": "admin", + "root_login": "postgres", "root_password": "travis", "run_selenium_tests": 1, "host_name": "http://test_site_postgres:8000" From f72e74d8e84a303388ea539d50eff9d8c8fca4c5 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 2 May 2019 15:51:57 +0530 Subject: [PATCH 033/175] fix: route history and allow longer energy point reviews --- frappe/core/page/dashboard/dashboard.js | 7 +- frappe/desk/form/save.py | 1 - frappe/public/js/frappe/router_history.js | 2 +- .../js/frappe/ui/toolbar/awesome_bar.js | 2 + .../js/frappe/ui/toolbar/search_utils.js | 3 +- .../energy_point_log/energy_point_log.json | 327 ++---------------- 6 files changed, 36 insertions(+), 306 deletions(-) diff --git a/frappe/core/page/dashboard/dashboard.js b/frappe/core/page/dashboard/dashboard.js index b4cb4d7222..c8b8fc94dc 100644 --- a/frappe/core/page/dashboard/dashboard.js +++ b/frappe/core/page/dashboard/dashboard.js @@ -60,7 +60,12 @@ class Dashboard { show_dashboard(current_dashboard_name) { if(this.dashboard_name !== current_dashboard_name) { this.dashboard_name = current_dashboard_name; - this.page.set_title(this.dashboard_name); + let title = this.dashboard_name; + if (!this.dashboard_name.toLowerCase().includes(__('dashboard'))) { + // ensure dashboard title has "dashboard" + title = __('{0} Dashboard', [title]); + } + this.page.set_title(title); this.set_dropdown(); this.container.empty(); this.refresh(); diff --git a/frappe/desk/form/save.py b/frappe/desk/form/save.py index 1e07f10ba7..6361a2ebbb 100644 --- a/frappe/desk/form/save.py +++ b/frappe/desk/form/save.py @@ -27,7 +27,6 @@ def savedocs(doc, action): # update recent documents run_onload(doc) - frappe.get_user().update_recent(doc.doctype, doc.name) send_updated_docs(doc) except Exception: if not frappe.local.message_log: diff --git a/frappe/public/js/frappe/router_history.js b/frappe/public/js/frappe/router_history.js index 93d4904c0b..fb3d09fe0b 100644 --- a/frappe/public/js/frappe/router_history.js +++ b/frappe/public/js/frappe/router_history.js @@ -1,6 +1,6 @@ frappe.provide('frappe.route'); frappe.route_history_queue = []; -const routes_to_skip = ['Form', 'social']; +const routes_to_skip = ['Form', 'social', 'setup-wizard']; const save_routes = frappe.utils.debounce(() => { const routes = frappe.route_history_queue; diff --git a/frappe/public/js/frappe/ui/toolbar/awesome_bar.js b/frappe/public/js/frappe/ui/toolbar/awesome_bar.js index c2b70c3256..bee5f3035d 100644 --- a/frappe/public/js/frappe/ui/toolbar/awesome_bar.js +++ b/frappe/public/js/frappe/ui/toolbar/awesome_bar.js @@ -68,6 +68,8 @@ frappe.search.AwesomeBar = Class.extend({ me.options = me.options.concat(me.build_options(txt)); me.options = me.options.concat(me.global_results); } else { + me.options = me.options.concat( + me.deduplicate(frappe.search.utils.get_recent_pages(txt || ""))); me.options = me.options.concat(frappe.search.utils.get_frequent_links()); } me.add_help(); diff --git a/frappe/public/js/frappe/ui/toolbar/search_utils.js b/frappe/public/js/frappe/ui/toolbar/search_utils.js index fcbaae4aef..f8b4c3a34b 100644 --- a/frappe/public/js/frappe/ui/toolbar/search_utils.js +++ b/frappe/public/js/frappe/ui/toolbar/search_utils.js @@ -6,6 +6,7 @@ frappe.search.utils = { }, get_recent_pages: function(keywords) { + if (keywords === null) keywords = ''; var me = this, values = [], options = []; function find(list, keywords, process) { @@ -44,7 +45,7 @@ frappe.search.utils = { values.push([route[1], route]); } } else if(route[0]) { - values.push([frappe.route_titles[route[0]] || route[0], route]); + values.push([frappe.route_titles[route.join('/')] || route[0], route]); } }); diff --git a/frappe/social/doctype/energy_point_log/energy_point_log.json b/frappe/social/doctype/energy_point_log/energy_point_log.json index 2a24016c82..ddb6c19698 100644 --- a/frappe/social/doctype/energy_point_log/energy_point_log.json +++ b/frappe/social/doctype/energy_point_log/energy_point_log.json @@ -1,386 +1,109 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, "creation": "2018-06-21 14:58:55.913619", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "user", + "type", + "rule", + "reference_doctype", + "reference_name", + "points", + "reason", + "reverted", + "revert_of" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "user", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "User", - "length": 0, - "no_copy": 0, "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "type", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Type", - "length": 0, - "no_copy": 0, "options": "Auto\nAppreciation\nCriticism\nReview\nRevert", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "rule", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Rule", - "length": 0, - "no_copy": 0, "options": "Energy Point Rule", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "reference_doctype", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Reference Doctype", - "length": 0, - "no_copy": 0, "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "reference_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Reference Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "points", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Points", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "reason", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, + "fieldtype": "Text", "label": "Reason", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", - "fetch_if_empty": 0, "fieldname": "reverted", "fieldtype": "Check", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reverted", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Reverted" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.type === 'Revert'", - "fetch_if_empty": 0, "fieldname": "revert_of", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Revert Of", - "length": 0, - "no_copy": 0, "options": "Energy Point Log", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 } ], - "has_web_view": 0, - "hide_toolbar": 0, - "idx": 0, "in_create": 1, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2019-04-05 20:23:53.917559", + "modified": "2019-05-02 15:28:02.857792", "modified_by": "Administrator", "module": "Social", "name": "Energy Point Log", - "name_case": "", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "System Manager" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, "read": 1, - "report": 0, - "role": "All", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "All" } ], - "quick_entry": 0, - "read_only": 0, - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", "title_field": "user", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file From 5d5e68dbedb75e1d5695d6201a4874a0b8bfc1db Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 2 May 2019 19:35:17 +0530 Subject: [PATCH 034/175] fix: Do not show locals to user (#7397) --- frappe/__init__.py | 3 +-- frappe/public/js/frappe/request.js | 3 --- frappe/utils/error.py | 12 ------------ frappe/utils/response.py | 2 -- 4 files changed, 1 insertion(+), 19 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 0242843cee..211301cfb6 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -16,7 +16,6 @@ from faker import Faker # public from .exceptions import * from .utils.jinja import (get_jenv, get_template, render_template, get_email_from_template, get_jloader) -from .utils.error import get_frame_locals # Hamless for Python 3 # For Python 2 set default encoding to utf-8 @@ -274,7 +273,7 @@ def errprint(msg): if not request or (not "cmd" in local.form_dict) or conf.developer_mode: print(msg) - error_log.append({"exc": msg, "locals": get_frame_locals()}) + error_log.append({"exc": msg}) def log(msg): """Add to `debug_log`. diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index 088b97fe9d..080ee3b70e 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -382,12 +382,9 @@ frappe.request.report_error = function(xhr, request_opts) { var data = JSON.parse(xhr.responseText); if (data.exc) { var exc = (JSON.parse(data.exc) || []).join("\n"); - var locals = (JSON.parse(data.locals) || []).join("\n"); delete data.exc; - delete data.locals; } else { var exc = ""; - locals = ""; } var show_communication = function() { diff --git a/frappe/utils/error.py b/frappe/utils/error.py index 8b4877cf4d..43579cf92e 100644 --- a/frappe/utils/error.py +++ b/frappe/utils/error.py @@ -199,15 +199,3 @@ def clear_old_snapshots(): def get_error_snapshot_path(): return frappe.get_site_path('error-snapshots') - -def get_frame_locals(): - traceback = sys.exc_info()[2] - frames = [] - if traceback: - frames = inspect.getinnerframes(traceback, context=0) - _locals = ['Locals (most recent call last):'] - for frame, filename, lineno, function, __, __ in frames: - if '/apps/' in filename: - _locals.append('File "{}", line {}, in {}\n{}'.format(filename, lineno, function, json.dumps(frame.f_locals, default=str, indent=4))) - - return '\n'.join(_locals) diff --git a/frappe/utils/response.py b/frappe/utils/response.py index 8412ec6271..01c95006e8 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -105,8 +105,6 @@ def make_logs(response = None): if frappe.error_log: response['exc'] = json.dumps([frappe.utils.cstr(d["exc"]) for d in frappe.local.error_log]) - if frappe.conf.developer_mode: - response['locals'] = json.dumps([frappe.utils.cstr(d["locals"]) for d in frappe.local.error_log]) if frappe.local.message_log: response['_server_messages'] = json.dumps([frappe.utils.cstr(d) for From 29c97fca6ebb3c7d101b73527280018839b4aa72 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 2 May 2019 21:09:28 +0530 Subject: [PATCH 035/175] fix: Add dashboard label --- frappe/public/js/frappe/misc/utils.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/public/js/frappe/misc/utils.js b/frappe/public/js/frappe/misc/utils.js index 705b0c82cb..b892ec994c 100644 --- a/frappe/public/js/frappe/misc/utils.js +++ b/frappe/public/js/frappe/misc/utils.js @@ -658,6 +658,9 @@ Object.assign(frappe.utils, { if (route[0] === 'modules') { return __('{0} Modules', [route[1]]); } + if (route[0] === 'dashboard') { + return __('{0} Dashboard', [route[1]]); + } return __(frappe.utils.to_title_case(route[0], true)); }, report_column_total: function(values, column, type) { From 5ebf6889846abaee31e0c0844153b0e9eb7ad49c Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 3 May 2019 08:49:51 +0530 Subject: [PATCH 036/175] fix(recent): complete uncommitted changes --- frappe/desk/form/load.py | 3 --- frappe/utils/user.py | 21 --------------------- 2 files changed, 24 deletions(-) diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py index 00f29b6ffe..14ebbaf7fb 100644 --- a/frappe/desk/form/load.py +++ b/frappe/desk/form/load.py @@ -47,9 +47,6 @@ def getdoc(doctype, name, user=None): frappe.errprint(frappe.utils.get_traceback()) raise - if doc and not name.startswith('_'): - frappe.get_user().update_recent(doctype, name) - doc.add_seen() frappe.response.docs.append(doc) diff --git a/frappe/utils/user.py b/frappe/utils/user.py index 57cbcf7fda..725fb3056a 100755 --- a/frappe/utils/user.py +++ b/frappe/utils/user.py @@ -165,25 +165,6 @@ class UserPermissions: self.defaults = frappe.defaults.get_defaults(self.name) return self.defaults - # update recent documents - def update_recent(self, dt, dn): - rdl = frappe.cache().hget("user_recent", self.name) or [] - new_rd = [dt, dn] - - # clear if exists - for i in range(len(rdl)): - rd = rdl[i] - if rd==new_rd: - del rdl[i] - break - - if len(rdl) > 19: - rdl = rdl[:19] - - rdl = [new_rd] + rdl - - frappe.cache().hset("user_recent", self.name, rdl) - def _get(self, key): if not self.can_read: self.build_permissions() @@ -205,8 +186,6 @@ class UserPermissions: self.build_permissions() d.name = self.name - d.recent = json.dumps(frappe.cache().hget("user_recent", self.name) or []) - d.roles = self.get_roles() d.defaults = self.get_defaults() From 0a7a28bac0f2dd33ada531150873bfe2b0abb47a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 3 May 2019 10:38:10 +0530 Subject: [PATCH 037/175] fix: Get data of empty table from pg_stat_all_tables when db-type is postgres --- frappe/config/__init__.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/frappe/config/__init__.py b/frappe/config/__init__.py index 1fe06f9094..b2c1f41d78 100644 --- a/frappe/config/__init__.py +++ b/frappe/config/__init__.py @@ -78,15 +78,22 @@ def get_modules_from_app(app): return active_modules_list def get_all_empty_tables_by_module(): - results = frappe.db.sql(""" - SELECT - name, module - FROM information_schema.tables as i - JOIN tabDocType as d - ON i.table_name = CONCAT('tab', d.name) - WHERE table_rows = 0; - - """) + results = frappe.db.multisql({ + 'mariadb': ''' + SELECT `name`, `module` + FROM information_schema.tables AS i + JOIN `tabDocType` AS d + ON i.table_name = CONCAT('tab', d.name) + WHERE `table_rows` = 0; + ''', + 'postgres': ''' + SELECT "name", "module" + FROM "pg_stat_all_tables" AS i + JOIN "tabDocType" AS d + ON i.relname = CONCAT('tab', d.name) + WHERE n_tup_ins = 0; + ''' + }) empty_tables_by_module = {} @@ -95,7 +102,6 @@ def get_all_empty_tables_by_module(): empty_tables_by_module[module].append(doctype) else: empty_tables_by_module[module] = [doctype] - return empty_tables_by_module def is_domain(module): From d8b4c66bf37bcc95fec0d076fc12b7a4158683ff Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 3 May 2019 11:09:57 +0530 Subject: [PATCH 038/175] fix: applied filter element in list view Co-authored-by: surajshetty3416 --- frappe/public/less/filters.less | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frappe/public/less/filters.less b/frappe/public/less/filters.less index c419d49e47..cf25e0599d 100644 --- a/frappe/public/less/filters.less +++ b/frappe/public/less/filters.less @@ -1,4 +1,4 @@ -@import 'variables'; +@import 'common'; .active-tag-filters { .add-filter, .filter-tag { @@ -6,6 +6,11 @@ } } +.toggle-filter { + max-width: 200px !important; + .ellipsis; +} + .active-tag-filters .btn-group { white-space: nowrap; font-size: 0; From ebb26c771ba922a5afba41e8907a511e342e4b34 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 3 May 2019 11:57:50 +0530 Subject: [PATCH 039/175] feat: Add a "Go To Page" button in page form view --- frappe/core/doctype/page/page.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frappe/core/doctype/page/page.js b/frappe/core/doctype/page/page.js index 86fbbedaf3..284795fbe6 100644 --- a/frappe/core/doctype/page/page.js +++ b/frappe/core/doctype/page/page.js @@ -7,5 +7,10 @@ frappe.ui.form.on('Page', { // make the document read-only frm.set_read_only(); } + if (!frm.is_new() && !frm.doc.istable) { + frm.add_custom_button(__('Go to {0} Page', [frm.doc.title || frm.doc.name]), () => { + frappe.set_route(frm.doc.name); + }); + } } }); From 3d555c80bbae89cc9d3696b8fdad3891b589708e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 3 May 2019 13:37:28 +0530 Subject: [PATCH 040/175] fix: Only use local's page object if it has page script --- frappe/public/js/frappe/views/pageview.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/views/pageview.js b/frappe/public/js/frappe/views/pageview.js index 69c22e9768..932efb4a54 100644 --- a/frappe/public/js/frappe/views/pageview.js +++ b/frappe/public/js/frappe/views/pageview.js @@ -16,7 +16,7 @@ frappe.views.pageview = { return; } - if((locals.Page && locals.Page[name]) || name==window.page_name) { + if((locals.Page && locals.Page[name] && locals.Page[name].script) || name==window.page_name) { // already loaded callback(); } else if(localStorage["_page:" + name] && frappe.boot.developer_mode!=1) { @@ -59,6 +59,7 @@ frappe.views.pageview = { return; } } + console.log('name', name); frappe.model.with_doctype("Page", function() { frappe.views.pageview.with_page(name, function(r) { if(r && r.exc) { From da8612aa0f34ce905c453155e9463ab0517ca8dc Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 3 May 2019 13:38:23 +0530 Subject: [PATCH 041/175] style: Add a space and user frappe.session.user instead of just user --- frappe/core/doctype/page/page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/page/page.js b/frappe/core/doctype/page/page.js index 284795fbe6..d1d9600e59 100644 --- a/frappe/core/doctype/page/page.js +++ b/frappe/core/doctype/page/page.js @@ -3,7 +3,7 @@ frappe.ui.form.on('Page', { refresh: function(frm) { - if(!frappe.boot.developer_mode && user != 'Administrator') { + if (!frappe.boot.developer_mode && frappe.session.user != 'Administrator') { // make the document read-only frm.set_read_only(); } From c8822191c9283734a36ed8a6b85714c3630142c4 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 3 May 2019 13:54:09 +0530 Subject: [PATCH 042/175] style: Remove log statement --- frappe/public/js/frappe/views/pageview.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/public/js/frappe/views/pageview.js b/frappe/public/js/frappe/views/pageview.js index 932efb4a54..c31ba74285 100644 --- a/frappe/public/js/frappe/views/pageview.js +++ b/frappe/public/js/frappe/views/pageview.js @@ -59,7 +59,6 @@ frappe.views.pageview = { return; } } - console.log('name', name); frappe.model.with_doctype("Page", function() { frappe.views.pageview.with_page(name, function(r) { if(r && r.exc) { From 311202611b0c3bdc2f83bdb761ad2b65acca9576 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 3 May 2019 15:01:41 +0530 Subject: [PATCH 043/175] fix(style): cleanup blog subtitle --- .../website/doctype/blog_post/templates/blog_post.html | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/frappe/website/doctype/blog_post/templates/blog_post.html b/frappe/website/doctype/blog_post/templates/blog_post.html index cbfc1a8a1f..0231f255c2 100644 --- a/frappe/website/doctype/blog_post/templates/blog_post.html +++ b/frappe/website/doctype/blog_post/templates/blog_post.html @@ -10,14 +10,8 @@

{{ title }}

-

- {{ blogger_info and blogger_info.full_name or full_name }} - · - {{ frappe.format_date(published_on) }} - · - {{ category.title }} - · - {{ comment_text }} +

+ {{ frappe.format_date(published_on) }}

From d727aa9e578166c5faab1b5785f7e817a648db8f Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Sat, 4 May 2019 14:05:49 +0530 Subject: [PATCH 044/175] fix(minor): fix error reporting exception JSON handling --- frappe/public/js/frappe/request.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index 080ee3b70e..250be73c0f 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -380,11 +380,16 @@ frappe.after_ajax = function(fn) { frappe.request.report_error = function(xhr, request_opts) { var data = JSON.parse(xhr.responseText); + var exc; if (data.exc) { - var exc = (JSON.parse(data.exc) || []).join("\n"); + try { + exc = (JSON.parse(data.exc) || []).join("\n"); + } catch (e) { + exc = data.exc; + } delete data.exc; } else { - var exc = ""; + exc = ""; } var show_communication = function() { From 4c578e823876c0922ed2dbeab3bdcc0c12ba9b00 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Sat, 4 May 2019 14:14:56 +0530 Subject: [PATCH 045/175] fix: dont show double messages --- frappe/desk/form/save.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/frappe/desk/form/save.py b/frappe/desk/form/save.py index 6361a2ebbb..694b44b907 100644 --- a/frappe/desk/form/save.py +++ b/frappe/desk/form/save.py @@ -29,8 +29,6 @@ def savedocs(doc, action): run_onload(doc) send_updated_docs(doc) except Exception: - if not frappe.local.message_log: - frappe.msgprint(frappe._('Did not save')) frappe.errprint(frappe.utils.get_traceback()) raise From 8af25cce99ad2f25d89030cd5991fd3bfd8b955d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sat, 4 May 2019 17:32:03 +0530 Subject: [PATCH 046/175] feat: Add email template field to Communication --- .../doctype/communication/communication.json | 2052 +++-------------- frappe/core/doctype/communication/email.py | 4 +- .../public/js/frappe/views/communication.js | 1 + 3 files changed, 388 insertions(+), 1669 deletions(-) diff --git a/frappe/core/doctype/communication/communication.json b/frappe/core/doctype/communication/communication.json index ea0e429e0f..3b845964f4 100644 --- a/frappe/core/doctype/communication/communication.json +++ b/frappe/core/doctype/communication/communication.json @@ -1,1728 +1,444 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2013-01-29 10:47:14", - "custom": 0, - "description": "Keep a track of all communications", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "engine": "InnoDB", + "allow_import": 1, + "creation": "2013-01-29 10:47:14", + "description": "Keep a track of all communications", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "subject", + "section_break_10", + "communication_medium", + "sender", + "column_break_4", + "recipients", + "cc", + "bcc", + "phone_no", + "delivery_status", + "section_break_8", + "content", + "status_section", + "text_content", + "communication_type", + "comment_type", + "column_break_5", + "status", + "sent_or_received", + "additional_info", + "communication_date", + "read_receipt", + "column_break_14", + "sender_full_name", + "read_by_recipient", + "read_by_recipient_on", + "reference_section", + "reference_doctype", + "reference_name", + "reference_owner", + "email_account", + "in_reply_to", + "user", + "column_break_27", + "email_template", + "link_doctype", + "link_name", + "timeline_doctype", + "timeline_name", + "timeline_label", + "unread_notification_sent", + "seen", + "_user_tags", + "email_inbox", + "message_id", + "uid", + "email_status", + "has_attachment", + "feedback_section", + "rating", + "feedback_request" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subject", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Subject", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "subject", + "fieldtype": "Small Text", + "in_global_search": 1, + "label": "Subject", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "", - "columns": 0, - "fieldname": "section_break_10", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "To and CC", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "section_break_10", + "fieldtype": "Section Break", + "label": "To and CC" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "depends_on": "eval:doc.communication_type===\"Communication\"", - "fieldname": "communication_medium", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Type", - "length": 0, - "no_copy": 0, - "options": "\nEmail\nChat\nPhone\nSMS\nEvent\nMeeting\nVisit\nOther", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.communication_type===\"Communication\"", + "fieldname": "communication_medium", + "fieldtype": "Select", + "label": "Type", + "options": "\nEmail\nChat\nPhone\nSMS\nEvent\nMeeting\nVisit\nOther" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.communication_medium===\"Email\"", - "fieldname": "sender", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "From", - "length": 255, - "no_copy": 0, - "options": "Email", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.communication_medium===\"Email\"", + "fieldname": "sender", + "fieldtype": "Data", + "in_global_search": 1, + "label": "From", + "length": 255, + "options": "Email" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "recipients", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "To", - "length": 0, - "no_copy": 0, - "options": "Email", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "recipients", + "fieldtype": "Code", + "label": "To", + "options": "Email" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.communication_medium===\"Email\"", - "fieldname": "cc", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "CC", - "length": 0, - "no_copy": 0, - "options": "Email", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.communication_medium===\"Email\"", + "fieldname": "cc", + "fieldtype": "Code", + "label": "CC", + "options": "Email" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.communication_medium===\"Email\"", - "fieldname": "bcc", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "BCC", - "length": 0, - "no_copy": 0, - "options": "Email", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.communication_medium===\"Email\"", + "fieldname": "bcc", + "fieldtype": "Code", + "label": "BCC", + "options": "Email" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:in_list([\"Phone\",\"SMS\"],doc.communication_medium)", - "fieldname": "phone_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Phone No.", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:in_list([\"Phone\",\"SMS\"],doc.communication_medium)", + "fieldname": "phone_no", + "fieldtype": "Data", + "label": "Phone No." + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Integrations can use this field to set email delivery status", - "fieldname": "delivery_status", - "fieldtype": "Select", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Delivery Status", - "length": 0, - "no_copy": 0, - "options": "\nSent\nBounced\nOpened\nMarked As Spam\nRejected\nDelayed\nSoft-Bounced\nClicked\nRecipient Unsubscribed\nError\nExpired\nSending\nRead", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Integrations can use this field to set email delivery status", + "fieldname": "delivery_status", + "fieldtype": "Select", + "hidden": 1, + "label": "Delivery Status", + "options": "\nSent\nBounced\nOpened\nMarked As Spam\nRejected\nDelayed\nSoft-Bounced\nClicked\nRecipient Unsubscribed\nError\nExpired\nSending\nRead" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_8", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "content", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Message", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "content", + "fieldtype": "Text Editor", + "label": "Message", "width": "400" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "status_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "status_section", + "fieldtype": "Section Break", + "label": "Status" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "text_content", - "fieldtype": "Code", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Text Content", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "text_content", + "fieldtype": "Code", + "hidden": 1, + "label": "Text Content" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Communication", - "fieldname": "communication_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Communication Type", - "length": 0, - "no_copy": 0, - "options": "Communication\nComment\nChat\nBot\nNotification\nFeedback", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Communication", + "fieldname": "communication_type", + "fieldtype": "Select", + "label": "Communication Type", + "options": "Communication\nComment\nChat\nBot\nNotification\nFeedback", + "read_only": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "comment_type", - "fieldtype": "Select", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Comment Type", - "length": 0, - "no_copy": 0, - "options": "\nComment\nLike\nInfo\nLabel\nWorkflow\nCreated\nSubmitted\nCancelled\nUpdated\nDeleted\nAssigned\nAssignment Completed\nAttachment\nAttachment Removed\nShared\nUnshared\nBot\nRelinked", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "comment_type", + "fieldtype": "Select", + "hidden": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Comment Type", + "options": "\nComment\nLike\nInfo\nLabel\nWorkflow\nCreated\nSubmitted\nCancelled\nUpdated\nDeleted\nAssigned\nAssignment Completed\nAttachment\nAttachment Removed\nShared\nUnshared\nBot\nRelinked", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_5", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.communication_type===\"Communication\"", - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "Open\nReplied\nClosed\nLinked", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.communication_type===\"Communication\"", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Open\nReplied\nClosed\nLinked", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.communication_type===\"Communication\"", - "fieldname": "sent_or_received", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Sent or Received", - "length": 0, - "no_copy": 0, - "options": "Sent\nReceived", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.communication_type===\"Communication\"", + "fieldname": "sent_or_received", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Sent or Received", + "options": "Sent\nReceived", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "additional_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "More Information", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "additional_info", + "fieldtype": "Section Break", + "label": "More Information" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Now", - "fieldname": "communication_date", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Now", + "fieldname": "communication_date", + "fieldtype": "Datetime", + "label": "Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "read_receipt", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sent Read Receipt", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "read_receipt", + "fieldtype": "Check", + "label": "Sent Read Receipt", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_14", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sender_full_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "From Full Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sender_full_name", + "fieldtype": "Data", + "label": "From Full Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "read_by_recipient", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Read by Recipient", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "read_by_recipient", + "fieldtype": "Check", + "label": "Read by Recipient", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "read_by_recipient_on", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Read by Recipient On", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "read_by_recipient_on", + "fieldtype": "Datetime", + "label": "Read by Recipient On", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "reference_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "reference_section", + "fieldtype": "Section Break", + "label": "Reference" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_doctype", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference DocType", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_doctype", + "fieldtype": "Link", + "label": "Reference DocType", + "options": "DocType" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_name", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference Name", - "length": 0, - "no_copy": 0, - "options": "reference_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "label": "Reference Name", + "options": "reference_doctype" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "reference_name.owner", - "fieldname": "reference_owner", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference Owner", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "reference_name.owner", + "fieldname": "reference_owner", + "fieldtype": "Read Only", + "label": "Reference Owner", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.communication_medium===\"Email\"", - "fieldname": "email_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Email Account", - "length": 0, - "no_copy": 0, - "options": "Email Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.communication_medium===\"Email\"", + "fieldname": "email_account", + "fieldtype": "Link", + "label": "Email Account", + "options": "Email Account", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "in_reply_to", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In Reply To", - "length": 0, - "no_copy": 0, - "options": "Communication", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "in_reply_to", + "fieldtype": "Link", + "label": "In Reply To", + "options": "Communication", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "__user", - "fieldname": "user", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "User", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "__user", + "fieldname": "user", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "User", + "options": "User", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_27", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_27", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "link_doctype", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Link DocType", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "link_doctype", + "fieldtype": "Link", + "label": "Link DocType", + "options": "DocType", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "link_name", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Link Name", - "length": 0, - "no_copy": 0, - "options": "link_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "link_name", + "fieldtype": "Dynamic Link", + "label": "Link Name", + "options": "link_doctype", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "timeline_doctype", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Timeline DocType", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "timeline_doctype", + "fieldtype": "Link", + "label": "Timeline DocType", + "options": "DocType", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "timeline_name", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Timeline Name", - "length": 0, - "no_copy": 0, - "options": "timeline_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "timeline_name", + "fieldtype": "Dynamic Link", + "label": "Timeline Name", + "options": "timeline_doctype", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "timeline_label", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Timeline field Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "timeline_label", + "fieldtype": "Data", + "label": "Timeline field Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "unread_notification_sent", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Unread Notification Sent", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "unread_notification_sent", + "fieldtype": "Check", + "label": "Unread Notification Sent", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "seen", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Seen", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "seen", + "fieldtype": "Check", + "label": "Seen", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "_user_tags", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "User Tags", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "_user_tags", + "fieldtype": "Data", + "hidden": 1, + "label": "User Tags", + "no_copy": 1, + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "email_inbox", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Email Inbox", - "length": 0, - "no_copy": 0, - "permlevel": 1, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "email_inbox", + "fieldtype": "Section Break", + "label": "Email Inbox", + "permlevel": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "message_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Message ID", - "length": 995, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "message_id", + "fieldtype": "Data", + "ignore_xss_filter": 1, + "label": "Message ID", + "length": 995, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "uid", - "fieldtype": "Int", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "UID", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "uid", + "fieldtype": "Int", + "hidden": 1, + "label": "UID", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "email_status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Email Status", - "length": 0, - "no_copy": 0, - "options": "Open\nSpam\nTrash", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "email_status", + "fieldtype": "Select", + "label": "Email Status", + "options": "Open\nSpam\nTrash" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "has_attachment", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Has Attachment", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "has_attachment", + "fieldtype": "Check", + "hidden": 1, + "label": "Has Attachment" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "depends_on": "eval: doc.rating > 0", - "fieldname": "feedback_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Feedback", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "depends_on": "eval: doc.rating > 0", + "fieldname": "feedback_section", + "fieldtype": "Section Break", + "label": "Feedback" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "rating", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rating", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "rating", + "fieldtype": "Int", + "label": "Rating", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "feedback_request", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Feedback Request", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "feedback_request", + "fieldtype": "Data", + "label": "Feedback Request", + "read_only": 1 + }, + { + "fieldname": "email_template", + "fieldtype": "Link", + "label": "Email Template", + "options": "Email Template", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-comment", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-09-24 13:53:52.389244", - "modified_by": "chdecultot@dokos.io", - "module": "Core", - "name": "Communication", - "owner": "Administrator", + ], + "icon": "fa fa-comment", + "idx": 1, + "modified": "2019-05-04 15:36:35.818714", + "modified_by": "Administrator", + "module": "Core", + "name": "Communication", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 - }, + "create": 1, + "delete": 1, + "email": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 - }, + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 1, - "email": 1, - "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 + "delete": 1, + "email": 1, + "if_owner": 1, + "read": 1, + "role": "All" } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "subject", - "show_name_in_global_search": 0, - "sort_order": "DESC", - "title_field": "subject", - "track_changes": 1, - "track_seen": 1, - "track_views": 0 + ], + "search_fields": "subject", + "sort_order": "DESC", + "title_field": "subject", + "track_changes": 1, + "track_seen": 1 } \ No newline at end of file diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index 0048668ee2..0c68f8b118 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -22,7 +22,7 @@ from frappe.utils.background_jobs import enqueue def make(doctype=None, name=None, content=None, subject=None, sent_or_received = "Sent", sender=None, sender_full_name=None, recipients=None, communication_medium="Email", send_email=False, print_html=None, print_format=None, attachments='[]', send_me_a_copy=False, cc=None, bcc=None, - flags=None, read_receipt=None, print_letterhead=True): + flags=None, read_receipt=None, print_letterhead=True, email_template=None): """Make a new communication. :param doctype: Reference DocType. @@ -38,6 +38,7 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = :param print_format: Print Format name of parent document to be sent as attachment. :param attachments: List of attachments as list of files or JSON string. :param send_me_a_copy: Send a copy to the sender (default **False**). + :param email_template: Template which is used to compose mail . """ is_error_report = (doctype=="User" and name==frappe.session.user and subject=="Error Report") @@ -66,6 +67,7 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = "sent_or_received": sent_or_received, "reference_doctype": doctype, "reference_name": name, + "email_template": email_template, "message_id":get_message_id().strip(" <>"), "read_receipt":read_receipt, "has_attachment": 1 if attachments else 0 diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js index 7d90d80d5f..08174519a8 100755 --- a/frappe/public/js/frappe/views/communication.js +++ b/frappe/public/js/frappe/views/communication.js @@ -549,6 +549,7 @@ frappe.views.CommunicationComposer = Class.extend({ print_format: print_format, sender: form_values.sender, sender_full_name: form_values.sender?frappe.user.full_name():undefined, + email_template: form_values.email_template, attachments: selected_attachments, _lang : me.lang_code, read_receipt:form_values.send_read_receipt, From d67bcd221272a52445ff02d7a2deaedc91cfb74b Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sun, 5 May 2019 12:23:46 +0530 Subject: [PATCH 047/175] fix: Define filedata before it is used --- frappe/utils/pdf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index 8376d32a93..54b90be6ed 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -18,12 +18,14 @@ def get_pdf(html, options=None, output=None): "disable-local-file-access": "", }) + filedata = '' + try: # Set filename property to false, so no file is actually created filedata = pdfkit.from_string(html, False, options=options or {}) # https://pythonhosted.org/PyPDF2/PdfFileReader.html - # create in-memory binary streams from filedata and create a PdfFileReader objcet + # create in-memory binary streams from filedata and create a PdfFileReader object reader = PdfFileReader(io.BytesIO(filedata)) except IOError as e: From 9d96d707af8f090709b59b6a0cf064fbd1ecc96b Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 6 May 2019 07:42:44 +0530 Subject: [PATCH 048/175] feat: Append modified_field to the user_feilds of energy point rule --- frappe/social/doctype/energy_point_rule/energy_point_rule.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/social/doctype/energy_point_rule/energy_point_rule.js b/frappe/social/doctype/energy_point_rule/energy_point_rule.js index c67c95f836..a4c1dc00f5 100644 --- a/frappe/social/doctype/energy_point_rule/energy_point_rule.js +++ b/frappe/social/doctype/energy_point_rule/energy_point_rule.js @@ -18,7 +18,10 @@ frappe.ui.form.on('Energy Point Rule', { const user_fields = fields.filter(df => (df.fieldtype === 'Link' && df.options === 'User') || df.fieldtype === 'Data') .map(map_for_options) - .concat([{label: __('Owner'), value: 'owner'}]); + .concat([ + { label: __('Owner'), value: 'owner' }, + { label: __('Modified By'), value: 'modified_by' } + ]); const multiplier_fields = fields.filter(df => ['Int', 'Float'].includes(df.fieldtype)) .map(map_for_options); From 8948a014d26d95203856dd400bc2c1515b0cdd98 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 6 May 2019 08:26:24 +0530 Subject: [PATCH 049/175] fix: Skip energy point for admin --- frappe/social/doctype/energy_point_rule/energy_point_rule.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/social/doctype/energy_point_rule/energy_point_rule.py b/frappe/social/doctype/energy_point_rule/energy_point_rule.py index 8cd0578caf..ccf0ff0b13 100644 --- a/frappe/social/doctype/energy_point_rule/energy_point_rule.py +++ b/frappe/social/doctype/energy_point_rule/energy_point_rule.py @@ -46,7 +46,10 @@ class EnergyPointRule(Document): def process_energy_points(doc, state): - if frappe.flags.in_patch or frappe.flags.in_install or not is_energy_point_enabled(): + if (frappe.flags.in_patch + or frappe.flags.in_install + or not is_energy_point_enabled() + or frappe.session.user == 'Administrator'): return for d in frappe.cache_manager.get_doctype_map('Energy Point Rule', doc.doctype, From ceda36d9ec18e23d9208d95510c38f70fe241c55 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 6 May 2019 12:32:41 +0530 Subject: [PATCH 050/175] fix(minor): don't freeze screen in frappe.model.with_doctype --- frappe/public/js/frappe/model/model.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/public/js/frappe/model/model.js b/frappe/public/js/frappe/model/model.js index 9d4ad3af1c..a5aa363ec3 100644 --- a/frappe/public/js/frappe/model/model.js +++ b/frappe/public/js/frappe/model/model.js @@ -126,7 +126,6 @@ $.extend(frappe.model, { cached_timestamp: cached_timestamp }, async: async, - freeze: true, callback: function(r) { if(r.exc) { frappe.msgprint(__("Unable to load: {0}", [__(doctype)])); From 51d95321c1ea902f5e5cefafed7139e62c9c4a05 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 6 May 2019 13:42:25 +0530 Subject: [PATCH 051/175] fix(link-preview): cleanup and fixes --- frappe/public/js/frappe/form/controls/link.js | 1 + frappe/public/js/frappe/form/formatters.js | 10 +- frappe/public/js/frappe/list/list_view.js | 2 +- frappe/public/js/frappe/model/model.js | 1 - frappe/public/js/frappe/ui/link_preview.js | 125 +++++++----------- 5 files changed, 56 insertions(+), 83 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index 86a99c970f..a7ed3f8a72 100644 --- a/frappe/public/js/frappe/form/controls/link.js +++ b/frappe/public/js/frappe/form/controls/link.js @@ -41,6 +41,7 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ me.$link.toggle(false); }, 500); }); + this.$input.attr('data-target', this.df.options); this.input = this.$input.get(0); this.has_input = true; this.translate_values = true; diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js index c8afb6466a..6199a02d74 100644 --- a/frappe/public/js/frappe/form/formatters.js +++ b/frappe/public/js/frappe/form/formatters.js @@ -117,11 +117,11 @@ frappe.form.formatters = { return repl('%(value)s', {onclick: docfield.link_onclick.replace(/"/g, '"'), value:value}); } else if(docfield && doctype) { - return repl('%(label)s', { - doctype: encodeURIComponent(doctype), - name: encodeURIComponent(original_value), - label: __(options && options.label || value) - }); + return ` + ${__(options && options.label || value)}` } else { return value; } diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 24b613d116..296087693c 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -701,7 +701,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { - + ${subject} diff --git a/frappe/public/js/frappe/model/model.js b/frappe/public/js/frappe/model/model.js index a5aa363ec3..2b470c819d 100644 --- a/frappe/public/js/frappe/model/model.js +++ b/frappe/public/js/frappe/model/model.js @@ -182,7 +182,6 @@ $.extend(frappe.model, { doctype: doctype, name: name }, - freeze: true, callback: function(r) { callback && callback(name, r); resolve(frappe.get_doc(doctype, name)); diff --git a/frappe/public/js/frappe/ui/link_preview.js b/frappe/public/js/frappe/ui/link_preview.js index 34f318e0f6..a237c4a90e 100644 --- a/frappe/public/js/frappe/ui/link_preview.js +++ b/frappe/public/js/frappe/ui/link_preview.js @@ -3,39 +3,17 @@ frappe.ui.LinkPreview = class { constructor() { this.$links = []; this.popover_timeout = null; - this.get_links(); + this.setup_events(); } - get_links() { - - $(document.body).on('mouseover', 'a[href*="/"], input[data-fieldname], .popover', (e) => { + setup_events() { + $(document.body).on('mouseover', 'a[data-doctype], input[data-fieldtype="Link"], .popover', (e) => { this.link_hovered = true; this.element = $(e.currentTarget); - this.is_link = true; + this.is_link = this.element.get(0).tagName.toLowerCase() === 'a'; if(!this.element.parents().find('.popover').length) { - if(this.element.attr('href')) { - this.link = this.element.attr('href'); - if(this.link.startsWith('http')) { - return; - } - let details = this.get_details(); - this.name = details.name; - this.doctype = details.doctype; - } else { - this.is_link = false; - this.link = this.element.parents('.control-input-wrapper').find('.control-value').children('a').attr('href'); - - if(this.link) { - let details = this.get_details(); - this.name = details.name; - this.doctype = details.doctype; - } else { - this.name = this.element.parent().next().text(); - this.doctype = this.element.attr('data-doctype'); - } - } - + this.identify_doc(); this.popover = this.element.data("bs.popover"); if(this.name && this.doctype) { this.setup_popover_control(e); @@ -45,33 +23,26 @@ frappe.ui.LinkPreview = class { } - get_details() { - let details = {}; - let link_arr = this.link.split('/'); - - if(link_arr.length > 2) { - details.name = decodeURI(link_arr[link_arr.length - 1]); - details.doctype = decodeURI(link_arr[link_arr.length -2]); - details.name = details.name.replace(new RegExp('%2F', 'g'), '/'); + identify_doc() { + if (this.is_link) { + this.doctype = this.element.attr('data-doctype'); + this.name = this.element.attr('data-name'); + } else { + // input + this.doctype = this.element.attr('data-target'); + this.name = this.element.val(); } - let title = this.element.attr('title'); - if( title && title.includes('/')) { - details.name = title.trim(); - details.doctype = decodeURI(link_arr[link_arr.length-3]); - } - return details; } - setup_popover_control(e) { - - if(!this.popover || !this.is_link) { - let preview_fields = this.get_preview_fields(); - if(preview_fields.length) { - this.data_timeout = setTimeout(() => { - this.create_popover(e, preview_fields); - }, 1000); - } + if(!this.popover) { + this.get_preview_fields().then(preview_fields => { + if(preview_fields.length) { + this.data_timeout = setTimeout(() => { + this.create_popover(e, preview_fields); + }, 100); + } + }); } else { this.popover_timeout = setTimeout(() => { this.popover.show(); @@ -128,26 +99,29 @@ frappe.ui.LinkPreview = class { } get_preview_fields() { - let dt = this.doctype; - let fields = []; - frappe.model.with_doctype(dt, () => { - frappe.get_meta(dt).fields.filter((field) => { - if(field.in_preview) { - fields.push({'name':field.fieldname,'type':field.fieldtype}); - } - }); - }); - - if(!fields.length) { + return new Promise((resolve) => { + let dt = this.doctype; + let fields = []; frappe.model.with_doctype(dt, () => { - frappe.get_meta(dt).fields.filter((field) => { - if(field.reqd) { + let meta_fields = frappe.get_meta(dt).fields; + meta_fields.filter((field) => { + // build list of fields to fetch + if(field.in_preview) { fields.push({'name':field.fieldname,'type':field.fieldtype}); } }); + + // no preview fields defined, build list from mandatory fields + if(!fields.length) { + meta_fields.filter((field) => { + if(field.reqd) { + fields.push({'name':field.fieldname,'type':field.fieldtype}); + } + }); + } + resolve(fields); }); - } - return fields; + }); } get_preview_fields_value(field_list) { @@ -159,7 +133,6 @@ frappe.ui.LinkPreview = class { } init_preview_popover(preview_data) { - let popover_content = this.get_popover_html(preview_data); this.element.popover({ container: 'body', @@ -179,12 +152,12 @@ frappe.ui.LinkPreview = class { } get_popover_html(preview_data) { - if(!this.link) { - this.link = window.location.href; + if(!this.href) { + this.href = window.location.href; } - if(this.link && this.link.includes(' ')) { - this.link = this.link.replace(new RegExp(' ', 'g'), '%20'); + if(this.href && this.href.includes(' ')) { + this.href = this.href.replace(new RegExp(' ', 'g'), '%20'); } let image_html = ''; @@ -195,20 +168,20 @@ frappe.ui.LinkPreview = class { let image_url = encodeURI(preview_data['image']); image_html += `
- +
`; } if(preview_data['title']) { - title_html+= `${preview_data['title']}`; + title_html+= `${preview_data['title']}`; } Object.keys(preview_data).forEach(key => { if(key!='image' && key!='name') { - let value = this.truncate_value(preview_data[key]); + let value = this.truncate_value(preview_data[key]); let label = this.truncate_value(frappe.meta.get_label(this.doctype, key)); content_html += ` - ${label} + ${label} ${value} `; @@ -216,11 +189,11 @@ frappe.ui.LinkPreview = class { }); content_html+=``; - let popover_content = + let popover_content = `
${image_html}
- ${preview_data['name']} + ${preview_data['name']} ${title_html} ${this.doctype}
From ef2979c7e777eb0a4cded3e995b3be99936611e3 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 6 May 2019 15:09:33 +0530 Subject: [PATCH 052/175] fix: Disable Portal Routes --- frappe/website/router.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/frappe/website/router.py b/frappe/website/router.py index e60d17a7d9..ebdbe0116f 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -16,6 +16,9 @@ def resolve_route(path): The only exceptions are `/about` and `/contact` these will be searched in Web Pages first before checking the standard pages.""" + + raise_if_disabled(path) + if path not in ("about", "contact"): context = get_page_info_from_template(path) if context: @@ -27,6 +30,21 @@ def resolve_route(path): return context return get_page_info_from_template(path) +def raise_if_disabled(path): + routes = frappe.db.get_all('Portal Menu Item', + fields=['route', 'enabled'], + filters={ + 'route': ['like', '%{0}'.format(path)], + 'enabled': 0 + } + ) + + for r in routes: + _path = r.route.lstrip('/') + if path == _path and not r.enabled: + raise frappe.PermissionError + + def get_page_context(path): page_context = None if can_cache(): From c7a26af7a2c661e0fc747c774cad3ffe4ac011d0 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 6 May 2019 15:12:33 +0530 Subject: [PATCH 053/175] fix: link_title not getting set in address and contact --- frappe/contacts/doctype/address/address.py | 13 +++++++++++++ frappe/contacts/doctype/contact/contact.py | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py index c705f972d3..31f7ae766a 100644 --- a/frappe/contacts/doctype/address/address.py +++ b/frappe/contacts/doctype/address/address.py @@ -39,6 +39,7 @@ class Address(Document): def validate(self): self.link_address() self.validate_reference() + self.set_link_title() deduplicate_dynamic_links(self) def link_address(self): @@ -53,6 +54,18 @@ class Address(Document): return False + def set_link_title(self): + if not self.links: + return + else: + for address in self.links: + if not address.link_title: + linked_doc = frappe.get_doc(address.link_doctype, address.link_name) + try: + address.link_title = linked_doc.title_field + except AttributeError: + address.link_title = linked_doc.name + def validate_reference(self): if self.is_your_company_address: if not [row for row in self.links if row.link_doctype == "Company"]: diff --git a/frappe/contacts/doctype/contact/contact.py b/frappe/contacts/doctype/contact/contact.py index 987fad3829..585c37a3db 100644 --- a/frappe/contacts/doctype/contact/contact.py +++ b/frappe/contacts/doctype/contact/contact.py @@ -31,6 +31,7 @@ class Contact(Document): if self.email_id: self.email_id = self.email_id.strip() self.set_user() + self.set_link_title() if self.email_id and not self.image: self.image = has_gravatar(self.email_id) @@ -40,6 +41,18 @@ class Contact(Document): if not self.user and self.email_id: self.user = frappe.db.get_value("User", {"email": self.email_id}) + def set_link_title(self): + if not self.links: + return + else: + for contact in self.links: + if not contact.link_title: + linked_doc = frappe.get_doc(contact.link_doctype, contact.link_name) + try: + contact.link_title = linked_doc.title_field + except AttributeError: + contact.link_title = linked_doc.name + def get_link_for(self, link_doctype): '''Return the link name, if exists for the given link DocType''' for link in self.links: From b4e9d3d7a8e8e08872b1c29d10ac51bbb2239657 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 6 May 2019 16:19:54 +0530 Subject: [PATCH 054/175] fix(wip): group by in reports --- .../public/js/frappe/ui/group_by/group_by.js | 145 ++++++++++++------ .../js/frappe/views/reports/report_view.js | 23 +-- 2 files changed, 106 insertions(+), 62 deletions(-) diff --git a/frappe/public/js/frappe/ui/group_by/group_by.js b/frappe/public/js/frappe/ui/group_by/group_by.js index 81b6a4914b..e3cbb33557 100644 --- a/frappe/public/js/frappe/ui/group_by/group_by.js +++ b/frappe/public/js/frappe/ui/group_by/group_by.js @@ -11,35 +11,50 @@ frappe.ui.GroupBy = class { } setup_group_by_area() { - this.make_group_by_button(); - this.report_view.setup_columns(); - let sql_aggregate_function = [{name:'count', label: 'Count'}, {name:'sum', label: 'Sum'}, {name:'avg', label:'Average'}]; + let sql_aggregate_function = [ + {name:'count', label: 'Count'}, + {name:'sum', label: 'Sum'}, + {name:'avg', label:'Average'} + ]; this.groupby_edit_area = $(frappe.render_template("group_by", { groupby_conditions: this.get_group_by_fields(), aggregate_function_conditions: sql_aggregate_function, })); - $(".aggregate-function").val("count"); + + this.groupby_select = this.groupby_edit_area.find('select.groupby'); + this.aggregate_function_select = this.groupby_edit_area.find('select.aggregate-function'); + this.aggregate_on_select = this.groupby_edit_area.find('select.aggregate-on'); + + // set default to count + this.aggregate_function_select.val("count"); this.page.wrapper.find(".frappe-list").append( this.groupby_edit_area); //Set aggregate on options as numeric fields if function is sum or average - $('.aggregate-function').on('change', () => { + this.aggregate_function_select.on('change', () => { this.report_view.meta.fields.forEach((field) => { - let fn = $('.aggregate-function option:selected').val(); + let fn = this.aggregate_function_select.val(); if(fn === 'sum' || fn === 'avg') { + // pick numeric fields for sum / avg if(frappe.model.is_numeric_field(field.fieldtype)) { - $('.aggregate-on') - .append($('