From 739d0cd20fc5171a830869d52ecbac8d4b1ac89c Mon Sep 17 00:00:00 2001 From: Naren Date: Thu, 12 Mar 2020 17:40:32 +0530 Subject: [PATCH 001/337] fix: sticky footer logic implemented --- frappe/public/css/website.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frappe/public/css/website.css b/frappe/public/css/website.css index eecb7005e4..dd317952d9 100644 --- a/frappe/public/css/website.css +++ b/frappe/public/css/website.css @@ -1,7 +1,19 @@ /* the element that this class is applied to, should have a max width for this to work*/ +html { + height: 100%; +} body { + min-height: 100%; + display: flex; + flex-direction: column; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; } +body > div { + flex: 1 0 auto; +} +footer { + flex-shrink: 0; +} a { cursor: pointer; } From bdc9893820c5dbc0079d191010f1df52c8744f78 Mon Sep 17 00:00:00 2001 From: Naren Date: Fri, 13 Mar 2020 15:30:02 +0530 Subject: [PATCH 002/337] fix: logic moved from css to scss file --- frappe/public/css/website.css | 12 ------------ frappe/public/scss/website.scss | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/frappe/public/css/website.css b/frappe/public/css/website.css index dd317952d9..eecb7005e4 100644 --- a/frappe/public/css/website.css +++ b/frappe/public/css/website.css @@ -1,19 +1,7 @@ /* the element that this class is applied to, should have a max width for this to work*/ -html { - height: 100%; -} body { - min-height: 100%; - display: flex; - flex-direction: column; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; } -body > div { - flex: 1 0 auto; -} -footer { - flex-shrink: 0; -} a { cursor: pointer; } diff --git a/frappe/public/scss/website.scss b/frappe/public/scss/website.scss index a297054ee2..9901eb7fbf 100644 --- a/frappe/public/scss/website.scss +++ b/frappe/public/scss/website.scss @@ -4,8 +4,23 @@ @import "multilevel-dropdown"; @import "website-image"; +html { + height: 100%; +} + body { + min-height: 100%; + display: flex; + flex-direction: column; font-size: 16px; + + > div { + flex: 1 0 auto; + } +} + +footer { + flex-shrink: 0; } .navbar.bg-dark { From 6624f3a1af1c6328279c1d74303d479fd4ae41ba Mon Sep 17 00:00:00 2001 From: Saurabh Date: Fri, 3 Apr 2020 18:12:32 +0530 Subject: [PATCH 003/337] enhancement: provision to scheduled server scripts execution --- .../scheduled_job_type.json | 9 +++++++- .../scheduled_job_type/scheduled_job_type.py | 7 +++++- .../doctype/server_script/server_script.json | 14 +++++++++--- .../doctype/server_script/server_script.py | 22 +++++++++++++++++++ .../server_script/server_script_utils.py | 3 ++- frappe/utils/safe_exec.py | 3 +++ 6 files changed, 52 insertions(+), 6 deletions(-) diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json index e2ec921679..48c98c0bc4 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json @@ -12,6 +12,7 @@ "engine": "InnoDB", "field_order": [ "stopped", + "execute_via_server_script", "method", "frequency", "cron_format", @@ -63,6 +64,12 @@ "options": "All\nHourly\nHourly Long\nDaily\nDaily Long\nWeekly\nWeekly Long\nMonthly\nMonthly Long\nCron\nYearly\nAnnual", "read_only": 1, "reqd": 1 + }, + { + "default": "0", + "fieldname": "execute_via_server_script", + "fieldtype": "Check", + "label": "Execute Via Server Script" } ], "in_create": 1, @@ -72,7 +79,7 @@ "link_fieldname": "scheduled_job_type" } ], - "modified": "2019-12-09 11:10:21.259929", + "modified": "2020-04-03 17:14:13.899031", "modified_by": "Administrator", "module": "Core", "name": "Scheduled Job Type", diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py index 3cd994ebfa..b1b867d527 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -70,7 +70,12 @@ class ScheduledJobType(Document): self.scheduler_log = None try: self.log_status('Start') - frappe.get_attr(self.method)() + if self.execute_via_server_script: + script_name = frappe.db.get_value("Server Script", {'api_method': self.method}) + if script_name: + frappe.get_doc('Server Script', script_name).execute_scheduled_method() + else: + frappe.get_attr(self.method)() frappe.db.commit() self.log_status('Complete') except Exception: diff --git a/frappe/core/doctype/server_script/server_script.json b/frappe/core/doctype/server_script/server_script.json index 36c297cc26..aeed982162 100644 --- a/frappe/core/doctype/server_script/server_script.json +++ b/frappe/core/doctype/server_script/server_script.json @@ -11,6 +11,7 @@ "column_break_3", "reference_doctype", "doctype_event", + "scheduler_event", "api_method", "allow_guest", "section_break_8", @@ -22,7 +23,7 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Script Type", - "options": "DocType Event\nAPI", + "options": "DocType Event\nScheduler Event\nAPI", "reqd": 1 }, { @@ -47,7 +48,7 @@ "options": "Before Insert\nBefore Save\nAfter Save\nBefore Submit\nAfter Submit\nBefore Cancel\nAfter Cancel\nBefore Delete\nAfter Delete\nBefore Save (Submitted Document)\nAfter Save (Submitted Document)" }, { - "depends_on": "eval:doc.script_type==='API'", + "depends_on": "eval:doc.script_type==='API' || doc.script_type==='Scheduler Event'", "fieldname": "api_method", "fieldtype": "Data", "label": "API Method" @@ -72,10 +73,17 @@ { "fieldname": "section_break_8", "fieldtype": "Section Break" + }, + { + "depends_on": "eval:doc.script_type==='Scheduler Event'", + "fieldname": "scheduler_event", + "fieldtype": "Select", + "label": "Scheduler Event", + "options": "All\nHourly\nHourly Long\nDaily\nDaily Long\nWeekly\nWeekly Long\nMonthly\nMonthly Long\nCron\nYearly\nAnnual" } ], "links": [], - "modified": "2019-12-17 12:55:07.389775", + "modified": "2020-04-03 16:33:08.765621", "modified_by": "Administrator", "module": "Core", "name": "Server Script", diff --git a/frappe/core/doctype/server_script/server_script.py b/frappe/core/doctype/server_script/server_script.py index e2c6d3b7b0..c651da2269 100644 --- a/frappe/core/doctype/server_script/server_script.py +++ b/frappe/core/doctype/server_script/server_script.py @@ -8,6 +8,7 @@ import frappe from frappe.model.document import Document from frappe.utils.safe_exec import safe_exec + class ServerScript(Document): @staticmethod def validate(): @@ -15,6 +16,7 @@ class ServerScript(Document): @staticmethod def on_update(): + setup_scheduler_events() frappe.cache().delete_value('server_script_map') def execute_method(self): @@ -31,3 +33,23 @@ class ServerScript(Document): # execute event safe_exec(self.script, None, dict(doc = doc)) + def execute_scheduled_method(self): + if self.script_type == 'Scheduler Event': + safe_exec(self.script) + else: + # wrong report type! + raise frappe.DoesNotExistError + +def setup_scheduler_events(): + enabled_server_scripts = frappe.get_all('Server Script', + fields=('name', 'doctype_event','api_method', 'scheduler_event'), + filters={'disabled': 0, 'script_type': 'Scheduler Event'}) + + for script in enabled_server_scripts: + if not frappe.db.exists('Scheduled Job Type', dict(method=script.api_method)): + frappe.get_doc(dict( + doctype = 'Scheduled Job Type', + method = script.api_method, + frequency = script.scheduler_event, + has_server_script=1 + )).insert() \ No newline at end of file diff --git a/frappe/core/doctype/server_script/server_script_utils.py b/frappe/core/doctype/server_script/server_script_utils.py index 2e1a5ae8bb..e03504f30b 100644 --- a/frappe/core/doctype/server_script/server_script_utils.py +++ b/frappe/core/doctype/server_script/server_script_utils.py @@ -66,6 +66,7 @@ def get_server_script_map(): script_map.setdefault(script.reference_doctype, {}).setdefault(script.doctype_event, []).append(script.name) else: script_map.setdefault('_api', {})[script.api_method] = script.name + frappe.cache().set_value('server_script_map', script_map) - return script_map + return script_map \ No newline at end of file diff --git a/frappe/utils/safe_exec.py b/frappe/utils/safe_exec.py index 62d0286e03..ed949d8a01 100644 --- a/frappe/utils/safe_exec.py +++ b/frappe/utils/safe_exec.py @@ -11,6 +11,7 @@ from frappe.website.utils import (get_shade, get_toc, get_next_link) from frappe.modules import scrub from frappe.www.printview import get_visible_columns import frappe.exceptions +import frappe.integrations.utils class ServerScriptNotEnabled(frappe.PermissionError): pass @@ -79,6 +80,8 @@ def get_safe_globals(): user=user, csrf_token=frappe.local.session.data.csrf_token if getattr(frappe.local, "session", None) else '' ), + make_get_request = frappe.integrations.utils.make_get_request, + make_post_request = frappe.integrations.utils.make_post_request, socketio_port=frappe.conf.socketio_port, get_hooks=frappe.get_hooks, ), From 30e7d9e1512c2d9d774844c15dd30b41beee5797 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Mon, 6 Apr 2020 12:05:48 +0530 Subject: [PATCH 004/337] feat: link server scripts in scheduler --- .../scheduled_job_type.json | 14 ++++--- .../scheduled_job_type/scheduled_job_type.py | 4 +- .../doctype/server_script/server_script.js | 42 ++++++++++++++++++- .../doctype/server_script/server_script.json | 12 +----- .../doctype/server_script/server_script.py | 42 +++++++++++++------ 5 files changed, 81 insertions(+), 33 deletions(-) diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json index 48c98c0bc4..2a9c1a4573 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json @@ -12,8 +12,8 @@ "engine": "InnoDB", "field_order": [ "stopped", - "execute_via_server_script", "method", + "server_script", "frequency", "cron_format", "last_execution", @@ -66,10 +66,12 @@ "reqd": 1 }, { - "default": "0", - "fieldname": "execute_via_server_script", - "fieldtype": "Check", - "label": "Execute Via Server Script" + "fieldname": "server_script", + "fieldtype": "Link", + "label": "Server Script", + "options": "Server Script", + "read_only": 1, + "search_index": 1 } ], "in_create": 1, @@ -79,7 +81,7 @@ "link_fieldname": "scheduled_job_type" } ], - "modified": "2020-04-03 17:14:13.899031", + "modified": "2020-04-05 17:27:33.480562", "modified_by": "Administrator", "module": "Core", "name": "Scheduled Job Type", diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py index b1b867d527..c179054550 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -70,8 +70,8 @@ class ScheduledJobType(Document): self.scheduler_log = None try: self.log_status('Start') - if self.execute_via_server_script: - script_name = frappe.db.get_value("Server Script", {'api_method': self.method}) + if self.server_script: + script_name = frappe.db.get_value("Server Script", self.server_script) if script_name: frappe.get_doc('Server Script', script_name).execute_scheduled_method() else: diff --git a/frappe/core/doctype/server_script/server_script.js b/frappe/core/doctype/server_script/server_script.js index eea8558456..d7f4c3e536 100644 --- a/frappe/core/doctype/server_script/server_script.js +++ b/frappe/core/doctype/server_script/server_script.js @@ -2,7 +2,45 @@ // For license information, please see license.txt frappe.ui.form.on('Server Script', { - // refresh: function(frm) { + refresh: function(frm) { + if(frm.doc.script_type === 'Scheduler Event' && !frm.doc.disabled){ + frm.add_custom_button('Schedule Script', function() { + var d = new frappe.ui.Dialog({ + title: "Schedule Script Execution", + fields: [ + { + fieldname: "event_type", + label: __('Select Event Type'), + fieldtype: "Select", + options: "All\nHourly\nDaily\nWeekly\nMonthly\nYearly\nHourly Long\nDaily Long\nWeekly Long\nMonthly Long" + }, + ], + primary_action_label: __('Schedule Script'), + primary_action: () => { + d.get_primary_btn().attr('disabled', true); + var data = d.get_values(); + d.hide(); + if(data) { + frm.events.schedule_script(frm, data); + } + + } + }); + + d.show(); + + }); + } + }, + + schedule_script(frm, data){ + frm.call({ + method: "frappe.core.doctype.server_script.server_script.setup_scheduler_events", + args: { + 'script_name': frm.doc.name, + 'frequency': data.event_type + } + }) + } - // } }); diff --git a/frappe/core/doctype/server_script/server_script.json b/frappe/core/doctype/server_script/server_script.json index aeed982162..bef3dfc60c 100644 --- a/frappe/core/doctype/server_script/server_script.json +++ b/frappe/core/doctype/server_script/server_script.json @@ -11,7 +11,6 @@ "column_break_3", "reference_doctype", "doctype_event", - "scheduler_event", "api_method", "allow_guest", "section_break_8", @@ -48,7 +47,7 @@ "options": "Before Insert\nBefore Save\nAfter Save\nBefore Submit\nAfter Submit\nBefore Cancel\nAfter Cancel\nBefore Delete\nAfter Delete\nBefore Save (Submitted Document)\nAfter Save (Submitted Document)" }, { - "depends_on": "eval:doc.script_type==='API' || doc.script_type==='Scheduler Event'", + "depends_on": "eval:doc.script_type==='API'", "fieldname": "api_method", "fieldtype": "Data", "label": "API Method" @@ -73,17 +72,10 @@ { "fieldname": "section_break_8", "fieldtype": "Section Break" - }, - { - "depends_on": "eval:doc.script_type==='Scheduler Event'", - "fieldname": "scheduler_event", - "fieldtype": "Select", - "label": "Scheduler Event", - "options": "All\nHourly\nHourly Long\nDaily\nDaily Long\nWeekly\nWeekly Long\nMonthly\nMonthly Long\nCron\nYearly\nAnnual" } ], "links": [], - "modified": "2020-04-03 16:33:08.765621", + "modified": "2020-04-06 11:24:38.161555", "modified_by": "Administrator", "module": "Core", "name": "Server Script", diff --git a/frappe/core/doctype/server_script/server_script.py b/frappe/core/doctype/server_script/server_script.py index c651da2269..9522b77b4b 100644 --- a/frappe/core/doctype/server_script/server_script.py +++ b/frappe/core/doctype/server_script/server_script.py @@ -7,6 +7,7 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils.safe_exec import safe_exec +from frappe import _ class ServerScript(Document): @@ -16,7 +17,6 @@ class ServerScript(Document): @staticmethod def on_update(): - setup_scheduler_events() frappe.cache().delete_value('server_script_map') def execute_method(self): @@ -40,16 +40,32 @@ class ServerScript(Document): # wrong report type! raise frappe.DoesNotExistError -def setup_scheduler_events(): - enabled_server_scripts = frappe.get_all('Server Script', - fields=('name', 'doctype_event','api_method', 'scheduler_event'), - filters={'disabled': 0, 'script_type': 'Scheduler Event'}) +@frappe.whitelist() +def setup_scheduler_events(script_name, frequency): + method = frappe.scrub(script_name) + '_' + frequency.lower() + scheduled_script = frappe.db.get_value('Scheduled Job Type', + dict(method=method)) - for script in enabled_server_scripts: - if not frappe.db.exists('Scheduled Job Type', dict(method=script.api_method)): - frappe.get_doc(dict( - doctype = 'Scheduled Job Type', - method = script.api_method, - frequency = script.scheduler_event, - has_server_script=1 - )).insert() \ No newline at end of file + if not scheduled_script: + doc = frappe.get_doc(dict( + doctype = 'Scheduled Job Type', + method = method, + frequency = frequency, + server_script = script_name + )) + + doc.insert() + + frappe.msgprint(_('Enabled scheduled execution for script {0}').format(script_name)) + + else: + doc = frappe.get_doc('Scheduled Job Type', scheduled_script) + doc.update(dict( + doctype = 'Scheduled Job Type', + method = method, + frequency = frequency, + server_script = script_name + )) + doc.save() + + frappe.msgprint(_('Scheduled execution for script {0} has updated').format(script_name)) From 2e0abf4bb2c6bdcfd260239382efdd83a3512bef Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 7 Apr 2020 14:54:53 +0530 Subject: [PATCH 005/337] fix: setup invalid for grid forms Co-authored-by: Prssanna Desai --- frappe/public/js/frappe/form/controls/base_input.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/controls/base_input.js b/frappe/public/js/frappe/form/controls/base_input.js index 0dbaaeb63c..685aac90a3 100644 --- a/frappe/public/js/frappe/form/controls/base_input.js +++ b/frappe/public/js/frappe/form/controls/base_input.js @@ -180,7 +180,12 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({ this.$wrapper.toggleClass("has-error", (this.df.reqd && is_null(value)) ? true : false); }, set_invalid: function () { - this.$wrapper.toggleClass("has-error", (this.df.invalid ? true : false)); + if(this.$input) { + this.$wrapper.toggleClass("has-error", (this.df.invalid ? true : false)); + } + if(this.disp_area) { + $(this.disp_area).toggleClass("has-error", (this.df.invalid ? true : false)); + } }, set_bold: function() { if(this.$input) { From a23ba1fc56d4ee98bb0aa3a4025926a9eaaed299 Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 7 Apr 2020 17:52:10 +0530 Subject: [PATCH 006/337] feat: data validation for child table --- .../public/js/frappe/form/controls/base_control.js | 6 ++++-- frappe/public/js/frappe/form/controls/base_input.js | 12 +++++++----- frappe/public/js/frappe/form/grid_row.js | 5 ++++- frappe/public/less/form_grid.less | 8 ++++++++ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/base_control.js b/frappe/public/js/frappe/form/controls/base_control.js index c1ba41ab16..41e06537e1 100644 --- a/frappe/public/js/frappe/form/controls/base_control.js +++ b/frappe/public/js/frappe/form/controls/base_control.js @@ -152,12 +152,14 @@ frappe.ui.form.Control = Class.extend({ () => me.set_model_value(value), () => { me.set_mandatory && me.set_mandatory(value); - me.set_invalid && me.set_invalid(); if(me.df.change || me.df.onchange) { // onchange event specified in df - return (me.df.change || me.df.onchange).apply(me, [e]); + let set = (me.df.change || me.df.onchange).apply(me, [e]); + me.set_invalid && me.set_invalid(); + return set; } + me.set_invalid && me.set_invalid(); } ]); }; diff --git a/frappe/public/js/frappe/form/controls/base_input.js b/frappe/public/js/frappe/form/controls/base_input.js index 685aac90a3..f3f04ec4d8 100644 --- a/frappe/public/js/frappe/form/controls/base_input.js +++ b/frappe/public/js/frappe/form/controls/base_input.js @@ -180,11 +180,13 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({ this.$wrapper.toggleClass("has-error", (this.df.reqd && is_null(value)) ? true : false); }, set_invalid: function () { - if(this.$input) { - this.$wrapper.toggleClass("has-error", (this.df.invalid ? true : false)); - } - if(this.disp_area) { - $(this.disp_area).toggleClass("has-error", (this.df.invalid ? true : false)); + let invalid = !!this.df.invalid; + if (this.grid) { + this.$wrapper.parents('.grid-static-col').toggleClass('invalid', invalid); + this.$input.toggleClass('invalid', invalid); + this.grid_row.columns[this.df.fieldname].is_invalid = invalid; + } else { + this.$wrapper.toggleClass('has-error', invalid); } }, set_bold: function() { diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 0e36e671cc..1fd0fcce24 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -268,7 +268,10 @@ export default class GridRow { if(df.reqd && !txt) { column.addClass('error'); } - if (df.reqd || df.bold) { + if (column.is_invalid) { + column.addClass('invalid'); + } + else if ((df.reqd || df.bold)) { column.addClass('bold'); } } diff --git a/frappe/public/less/form_grid.less b/frappe/public/less/form_grid.less index 28f08635ba..102719308c 100644 --- a/frappe/public/less/form_grid.less +++ b/frappe/public/less/form_grid.less @@ -80,6 +80,10 @@ background-color: @extra-light-yellow; } +.editable-form .grid-static-col.invalid { + background-color: @label-danger-bg; +} + .validated-form .grid-static-col.error { background-color: @label-danger-bg; } @@ -150,6 +154,10 @@ } } + input.form-control.invalid { + background-color: @label-danger-bg; + } + input[data-fieldtype="Int"], input[data-fieldtype="Float"], input[data-fieldtype="Currency"] { text-align: right; } From 24a8b036393c6aa71db16cb0fccf17064505c403 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Wed, 8 Apr 2020 20:27:38 +0530 Subject: [PATCH 007/337] chore: improve error message on delete --- frappe/model/delete_doc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index d77898020d..b6db154bd4 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -210,7 +210,7 @@ def check_permission_and_not_submitted(doc): # check if submitted if doc.docstatus == 1: - frappe.msgprint(_("{0} {1}: Submitted Record cannot be deleted.").format(_(doc.doctype), doc.name), + frappe.msgprint(_("{0} {1}: Submitted Record cannot be deleted. You must Cancel it first.").format(_(doc.doctype), doc.name), raise_exception=True) def check_if_doc_is_linked(doc, method="Delete"): From 5cc11910adced726619a81a4b3824aa298a8beb3 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 10 Apr 2020 13:34:38 +0530 Subject: [PATCH 008/337] fix: return Job object on frappe.enqueue_doc --- frappe/utils/background_jobs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/background_jobs.py b/frappe/utils/background_jobs.py index 03f063e058..4b37e850f0 100755 --- a/frappe/utils/background_jobs.py +++ b/frappe/utils/background_jobs.py @@ -73,7 +73,7 @@ def enqueue(method, queue='default', timeout=None, event=None, def enqueue_doc(doctype, name=None, method=None, queue='default', timeout=300, now=False, **kwargs): '''Enqueue a method to be run on a document''' - enqueue('frappe.utils.background_jobs.run_doc_method', doctype=doctype, name=name, + return enqueue('frappe.utils.background_jobs.run_doc_method', doctype=doctype, name=name, doc_method=method, queue=queue, timeout=timeout, now=now, **kwargs) def run_doc_method(doctype, name, doc_method, **kwargs): From 0a0045e9cc7cddcc77da1f8b7a42b4dd5d0c3e4e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 10 Apr 2020 13:38:28 +0530 Subject: [PATCH 009/337] chore: raise exception via frappe.msgprint --- frappe/core/doctype/file/file.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 6633884bb3..485885ec24 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -608,8 +608,7 @@ def get_local_image(file_url): try: image = Image.open(file_path) except IOError: - frappe.msgprint(_("Unable to read file format for {0}").format(file_url)) - raise + frappe.msgprint(_("Unable to read file format for {0}").format(file_url), raise_exception=True) content = None From 558fec0d7cb12aef812419eada3c3e229e1f0641 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 10 Apr 2020 23:40:34 +0530 Subject: [PATCH 010/337] fix: move Video DocType from Education Module to Core --- frappe/core/doctype/video/__init__.py | 0 frappe/core/doctype/video/test_video.js | 23 +++++ frappe/core/doctype/video/test_video.py | 10 +++ frappe/core/doctype/video/video.js | 8 ++ frappe/core/doctype/video/video.json | 114 ++++++++++++++++++++++++ frappe/core/doctype/video/video.py | 10 +++ 6 files changed, 165 insertions(+) create mode 100644 frappe/core/doctype/video/__init__.py create mode 100644 frappe/core/doctype/video/test_video.js create mode 100644 frappe/core/doctype/video/test_video.py create mode 100644 frappe/core/doctype/video/video.js create mode 100644 frappe/core/doctype/video/video.json create mode 100644 frappe/core/doctype/video/video.py diff --git a/frappe/core/doctype/video/__init__.py b/frappe/core/doctype/video/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/core/doctype/video/test_video.js b/frappe/core/doctype/video/test_video.js new file mode 100644 index 0000000000..a82a221319 --- /dev/null +++ b/frappe/core/doctype/video/test_video.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Video", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Video + () => frappe.tests.make('Video', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/frappe/core/doctype/video/test_video.py b/frappe/core/doctype/video/test_video.py new file mode 100644 index 0000000000..0bed1e98d6 --- /dev/null +++ b/frappe/core/doctype/video/test_video.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestVideo(unittest.TestCase): + pass diff --git a/frappe/core/doctype/video/video.js b/frappe/core/doctype/video/video.js new file mode 100644 index 0000000000..36ea240a36 --- /dev/null +++ b/frappe/core/doctype/video/video.js @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Frappe Technologies and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Video', { + // refresh: function(frm) { + + // } +}); diff --git a/frappe/core/doctype/video/video.json b/frappe/core/doctype/video/video.json new file mode 100644 index 0000000000..875926088b --- /dev/null +++ b/frappe/core/doctype/video/video.json @@ -0,0 +1,114 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:title", + "creation": "2018-10-17 05:47:13.087395", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "provider", + "url", + "column_break_4", + "publish_date", + "duration", + "section_break_7", + "description" + ], + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Title", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "provider", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Provider", + "options": "YouTube\nVimeo", + "reqd": 1 + }, + { + "fieldname": "url", + "fieldtype": "Data", + "in_list_view": 1, + "label": "URL", + "reqd": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "publish_date", + "fieldtype": "Date", + "label": "Publish Date" + }, + { + "fieldname": "duration", + "fieldtype": "Data", + "label": "Duration" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "fieldname": "description", + "fieldtype": "Text Editor", + "in_list_view": 1, + "label": "Description", + "reqd": 1 + } + ], + "links": [], + "modified": "2020-04-10 23:04:41.124053", + "modified_by": "Administrator", + "module": "Core", + "name": "Video", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Instructor", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "LMS User", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/frappe/core/doctype/video/video.py b/frappe/core/doctype/video/video.py new file mode 100644 index 0000000000..fdbd3a1abe --- /dev/null +++ b/frappe/core/doctype/video/video.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class Video(Document): + pass From 275a678bd658b75bd3a5f7e3b68722d58bce0533 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 13 Apr 2020 11:42:02 +0530 Subject: [PATCH 011/337] fix: site cleanup in case of errors on new-site Added _new_site_cleanup to handle exceptions raised in case of interuppted install of Frappe site. Deletes folders after calling _drop_site --- frappe/commands/site.py | 61 +++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/frappe/commands/site.py b/frappe/commands/site.py index fd43cc8cf3..055d4317a5 100755 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -1,13 +1,25 @@ -from __future__ import unicode_literals, absolute_import, print_function +# imports - standard imports +import atexit +import compileall +import hashlib +import os +import re +import shutil +import sys + +# imports - third party imports import click -import hashlib, os, sys, compileall, re +from pymysql.err import OperationalError +from six import text_type + +# imports - module imports import frappe from frappe import _ -from frappe.commands import pass_context, get_site +from frappe.commands import get_site, pass_context from frappe.commands.scheduler import _is_scheduler_enabled from frappe.installer import update_site_config -from frappe.utils import touch_file, get_site_path -from six import text_type +from frappe.utils import get_site_path, touch_file + @click.command('new-site') @click.argument('site') @@ -68,32 +80,33 @@ def _new_site(db_name, site, mariadb_root_username=None, mariadb_root_password=N make_site_dirs() - installing = None - try: - installing = touch_file(get_site_path('locks', 'installing.lock')) + installing = touch_file(get_site_path('locks', 'installing.lock')) + atexit.register(_new_site_cleanup, site, mariadb_root_username, mariadb_root_password) - install_db(root_login=mariadb_root_username, root_password=mariadb_root_password, - db_name=db_name, admin_password=admin_password, verbose=verbose, - source_sql=source_sql, force=force, reinstall=reinstall, db_password=db_password, db_type=db_type, db_host=db_host, db_port=db_port, no_mariadb_socket=no_mariadb_socket) + install_db(root_login=mariadb_root_username, root_password=mariadb_root_password, + db_name=db_name, admin_password=admin_password, verbose=verbose, + source_sql=source_sql, force=force, reinstall=reinstall, db_password=db_password, db_type=db_type, db_host=db_host, db_port=db_port, no_mariadb_socket=no_mariadb_socket) + apps_to_install = ['frappe'] + (frappe.conf.get("install_apps") or []) + (list(install_apps) or []) + for app in apps_to_install: + _install_app(app, verbose=verbose, set_as_patched=not source_sql) - apps_to_install = ['frappe'] + (frappe.conf.get("install_apps") or []) + (list(install_apps) or []) - for app in apps_to_install: - _install_app(app, verbose=verbose, set_as_patched=not source_sql) + os.remove(installing) - frappe.utils.scheduler.toggle_scheduler(enable_scheduler) - frappe.db.commit() + frappe.utils.scheduler.toggle_scheduler(enable_scheduler) + frappe.db.commit() - scheduler_status = "disabled" if frappe.utils.scheduler.is_scheduler_disabled() else "enabled" - print("*** Scheduler is", scheduler_status, "***") + scheduler_status = "disabled" if frappe.utils.scheduler.is_scheduler_disabled() else "enabled" + print("*** Scheduler is", scheduler_status, "***") - except frappe.exceptions.ImproperDBConfigurationError: - _drop_site(site, mariadb_root_username, mariadb_root_password, force=True) +def _new_site_cleanup(site, mariadb_root_username, mariadb_root_password): + installing = get_site_path('locks', 'installing.lock') - finally: - if installing and os.path.exists(installing): - os.remove(installing) + if installing and os.path.exists(installing): + if mariadb_root_password: + _drop_site(site, mariadb_root_username, mariadb_root_password, force=True) + shutil.rmtree(site) - frappe.destroy() + frappe.destroy() @click.command('restore') @click.argument('sql-file-path') From 41c6c09fb9e157df5b867a42d3f5926f9437e736 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 13 Apr 2020 12:05:46 +0530 Subject: [PATCH 012/337] feat: handle failed cases in publish_realtime --- frappe/public/js/frappe/form/dashboard.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/dashboard.js b/frappe/public/js/frappe/form/dashboard.js index c4c739dbcc..3b6ccd9a5c 100644 --- a/frappe/public/js/frappe/form/dashboard.js +++ b/frappe/public/js/frappe/form/dashboard.js @@ -125,8 +125,9 @@ frappe.ui.form.Dashboard = Class.extend({ }, format_percent: function(title, percent) { - var width = cint(percent) < 1 ? 1 : cint(percent); - var progress_class = "progress-bar-success"; + const percentage = cint(percent); + const width = percentage < 0 ? 100 : percentage; + const progress_class = percentage < 0 ? "progress-bar-danger" : "progress-bar-success"; return [{ title: title, From 30e0fa7561b9d21caff689fedbd943e7dd8f33c6 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 13 Apr 2020 12:21:31 +0530 Subject: [PATCH 013/337] style: remove unused imports --- frappe/commands/site.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/frappe/commands/site.py b/frappe/commands/site.py index 055d4317a5..540f250918 100755 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -9,8 +9,6 @@ import sys # imports - third party imports import click -from pymysql.err import OperationalError -from six import text_type # imports - module imports import frappe From 5b170c031dd622018133b0f88a1e751c54db69d5 Mon Sep 17 00:00:00 2001 From: Abhishek Kedar <44434910+AKedar21@users.noreply.github.com> Date: Mon, 13 Apr 2020 16:40:09 +0530 Subject: [PATCH 014/337] perf: enqueue instance of send_one for better performance Usecase: Suppose I have hundred's of emails in the Email Queue and the flush job executes. While the execution of the flush job is going another flush job starts and so on. Because of this a list of flush jobs gets lined up in the background jobs queue. To avoid this it is better to enqueue the send_one function inside the flush job this will very efficiently improve the performance of the flush job. --- frappe/email/queue.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/frappe/email/queue.py b/frappe/email/queue.py index b35f5944b2..9b1cfdd6b4 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -348,7 +348,19 @@ def flush(from_test=False): smtpserver = SMTPServer() smtpserver_dict[email.sender] = smtpserver - send_one(email.name, smtpserver, auto_commit, from_test=from_test) + # send_one(email.name, smtpserver, auto_commit, from_test=from_test) + from frappe import enqueue + send_one_args = { + 'email': email.name, + 'smtpserver':smtpserver, + 'auto_commit': auto_commit, + 'from_test':from_test + } + enqueue( + method='frappe.email.queue.send_one', + queue='short', + **send_one_args + ) # NOTE: removing commit here because we pass auto_commit # finally: From b0e236badd319bd247c1e215cdee53d38aafb5cb Mon Sep 17 00:00:00 2001 From: Afshan Date: Mon, 13 Apr 2020 18:07:08 +0530 Subject: [PATCH 015/337] workflow for github actions to check translation formats --- .../workflows/check_translation_format.yml | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/check_translation_format.yml diff --git a/.github/workflows/check_translation_format.yml b/.github/workflows/check_translation_format.yml new file mode 100644 index 0000000000..e5d2031182 --- /dev/null +++ b/.github/workflows/check_translation_format.yml @@ -0,0 +1,42 @@ +name: Check format +on: + pull_request: + branches: + - master + - develop +jobs: + check_translation: + name: Check format + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Setup python3 + uses: actions/setup-python@v1 + with: + python-version: 3.6 + - name: Run check_translation for pull_request + run: | + import os + import subprocess + import re + errors_encounter = 0 + pattern = re.compile(r"_\(([\"']{,3})(?P((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P((?!\5).)*)\5)*(\s*,\s*(.)*?\s*(,\s*([\"'])(?P((?!\11).)*)\11)*)*\)") + temp = re.compile(r"_\(([\"']{,3})") + subprocess.run('git fetch origin '+os.environ['GITHUB_BASE_REF']+ ' :' +os.environ['GITHUB_BASE_REF']+ ' -q', shell=True) + files = subprocess.check_output('git diff --name-only '+os.environ['GITHUB_BASE_REF'], shell=True) + files = files.decode('utf-8') + files = files.split() + for file in files: + with open(file, 'r') as f: + for num, line in enumerate(f, 1): + all_matches = temp.finditer(line) + if all_matches: + for match in all_matches: + verify = pattern.search(line) + if not verify: + errors_encounter += 1 + print(num) + print(line) + if errors_encounter > 0 : + assert 1+1 == 3 + shell: python \ No newline at end of file From e6ff382d14af27140e13ea5014bb60e4cca5f65d Mon Sep 17 00:00:00 2001 From: Afshan Date: Mon, 13 Apr 2020 18:39:14 +0530 Subject: [PATCH 016/337] printing proper error message --- .github/workflows/check_translation_format.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check_translation_format.yml b/.github/workflows/check_translation_format.yml index e5d2031182..6b8ba91e99 100644 --- a/.github/workflows/check_translation_format.yml +++ b/.github/workflows/check_translation_format.yml @@ -1,4 +1,4 @@ -name: Check format +name: frappe_linter on: pull_request: branches: @@ -35,8 +35,9 @@ jobs: verify = pattern.search(line) if not verify: errors_encounter += 1 - print(num) - print(line) + print('A syntax error has been discovered at line number: ' + num) + print('Syntax error occured with: ' + line) if errors_encounter > 0 : + print('You can visit "https://frappe.io/docs/user/en/translations" to resolve this error.') assert 1+1 == 3 shell: python \ No newline at end of file From fe1dd4d88c9c2e538dd95b3fa16d416ac3890390 Mon Sep 17 00:00:00 2001 From: Afshan Date: Mon, 13 Apr 2020 18:40:44 +0530 Subject: [PATCH 017/337] renamed .yml file --- .../{check_translation_format.yml => translation_linter.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{check_translation_format.yml => translation_linter.yml} (100%) diff --git a/.github/workflows/check_translation_format.yml b/.github/workflows/translation_linter.yml similarity index 100% rename from .github/workflows/check_translation_format.yml rename to .github/workflows/translation_linter.yml From 7b10a2d88a2f15a3487f8ceeb337f3cbf29dbc35 Mon Sep 17 00:00:00 2001 From: Abhishek Kedar <44434910+AKedar21@users.noreply.github.com> Date: Mon, 13 Apr 2020 20:07:46 +0530 Subject: [PATCH 018/337] style: removed comment and formatted dictionary --- frappe/email/queue.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frappe/email/queue.py b/frappe/email/queue.py index 9b1cfdd6b4..d82d93fa75 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -348,13 +348,12 @@ def flush(from_test=False): smtpserver = SMTPServer() smtpserver_dict[email.sender] = smtpserver - # send_one(email.name, smtpserver, auto_commit, from_test=from_test) from frappe import enqueue send_one_args = { 'email': email.name, - 'smtpserver':smtpserver, + 'smtpserver': smtpserver, 'auto_commit': auto_commit, - 'from_test':from_test + 'from_test': from_test } enqueue( method='frappe.email.queue.send_one', From 8e9d642f44d0f394b153f45b7d4031ea85ec92a8 Mon Sep 17 00:00:00 2001 From: Abhishek Kedar <44434910+AKedar21@users.noreply.github.com> Date: Mon, 13 Apr 2020 20:11:54 +0530 Subject: [PATCH 019/337] style: formatted parameters of enqueue function --- frappe/email/queue.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/email/queue.py b/frappe/email/queue.py index d82d93fa75..4b60ee858b 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -356,8 +356,8 @@ def flush(from_test=False): 'from_test': from_test } enqueue( - method='frappe.email.queue.send_one', - queue='short', + method = 'frappe.email.queue.send_one', + queue = 'short', **send_one_args ) From 47446469bd4f21742fb7def8a8bf2797e062f60f Mon Sep 17 00:00:00 2001 From: Abhishek Kedar <44434910+AKedar21@users.noreply.github.com> Date: Mon, 13 Apr 2020 20:20:02 +0530 Subject: [PATCH 020/337] refactor: moved import to the top of the file --- frappe/email/queue.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frappe/email/queue.py b/frappe/email/queue.py index 4b60ee858b..d4fbf798c5 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -6,7 +6,7 @@ import frappe import sys from six.moves import html_parser as HTMLParser import smtplib, quopri, json -from frappe import msgprint, _, safe_decode, safe_encode +from frappe import msgprint, _, safe_decode, safe_encode, enqueue from frappe.email.smtp import SMTPServer, get_outgoing_email_account from frappe.email.email_body import get_email, get_formatted_html, add_attachment from frappe.utils.verified_command import get_signed_params, verify_request @@ -347,8 +347,7 @@ def flush(from_test=False): if not smtpserver: smtpserver = SMTPServer() smtpserver_dict[email.sender] = smtpserver - - from frappe import enqueue + send_one_args = { 'email': email.name, 'smtpserver': smtpserver, From 821934db3d715d5f7415e97b38b8fa9247c36382 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 14 Apr 2020 12:27:26 +0530 Subject: [PATCH 021/337] feat: enable customizations for hidden sections --- frappe/public/js/frappe/views/desktop/desktop.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/views/desktop/desktop.js b/frappe/public/js/frappe/views/desktop/desktop.js index 12d33affa7..9f0df6e172 100644 --- a/frappe/public/js/frappe/views/desktop/desktop.js +++ b/frappe/public/js/frappe/views/desktop/desktop.js @@ -203,8 +203,8 @@ class DesktopPage { this.allow_customization && this.make_customization_link(); let create_shortcuts_and_cards = () => { - this.data.shortcuts.items.length && this.make_shortcuts(); - this.data.cards.items.length && this.make_cards(); + this.make_shortcuts(); + this.make_cards(); if (this.allow_customization) { // Move the widget group up to align with labels if customization is allowed @@ -212,7 +212,7 @@ class DesktopPage { } }; - if (!this.sections["onboarding"] && this.data.charts.items.length) { + if (!this.sections["onboarding"]) { this.make_charts().then(() => { create_shortcuts_and_cards(); }); From 607be5ad2a1d3b0afe5e2356b57283a8edd03528 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 14 Apr 2020 12:54:55 +0530 Subject: [PATCH 022/337] feat: add sidebar items to navbar in mobile --- .../includes/navbar/navbar_items.html | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/frappe/templates/includes/navbar/navbar_items.html b/frappe/templates/includes/navbar/navbar_items.html index 10d484df65..4e5ad0dd0a 100644 --- a/frappe/templates/includes/navbar/navbar_items.html +++ b/frappe/templates/includes/navbar/navbar_items.html @@ -64,6 +64,24 @@ {% if not only_static %} {% block navbar_right_extension %}{% endblock %} {% endif %} + + {% if show_sidebar %} +
+
+ {% for item in sidebar_items -%} + + {%- endfor %} +
+
+ {% endif %} + {% include "templates/includes/navbar/navbar_search.html" %} {% include "templates/includes/navbar/navbar_login.html" %} - + \ No newline at end of file From 9b9bbd8cef77204f45bcab1a5e60318dd9350948 Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 15 Apr 2020 14:08:52 +0530 Subject: [PATCH 023/337] format: fix whitespace Co-Authored-By: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/public/scss/website.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/scss/website.scss b/frappe/public/scss/website.scss index 9901eb7fbf..6f82e25ee0 100644 --- a/frappe/public/scss/website.scss +++ b/frappe/public/scss/website.scss @@ -10,8 +10,8 @@ html { body { min-height: 100%; - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; font-size: 16px; > div { From bc042b37b7d1e014012dfed9978cbc2a43e966c2 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 16 Apr 2020 00:01:27 +0530 Subject: [PATCH 024/337] feat: added Page View doctype --- frappe/website/doctype/page_view/__init__.py | 0 frappe/website/doctype/page_view/page_view.js | 8 ++ .../website/doctype/page_view/page_view.json | 75 +++++++++++++++++++ frappe/website/doctype/page_view/page_view.py | 34 +++++++++ .../doctype/page_view/test_page_view.py | 10 +++ .../website_settings/test_website_settings.py | 10 +++ 6 files changed, 137 insertions(+) create mode 100644 frappe/website/doctype/page_view/__init__.py create mode 100644 frappe/website/doctype/page_view/page_view.js create mode 100644 frappe/website/doctype/page_view/page_view.json create mode 100644 frappe/website/doctype/page_view/page_view.py create mode 100644 frappe/website/doctype/page_view/test_page_view.py create mode 100644 frappe/website/doctype/website_settings/test_website_settings.py diff --git a/frappe/website/doctype/page_view/__init__.py b/frappe/website/doctype/page_view/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/website/doctype/page_view/page_view.js b/frappe/website/doctype/page_view/page_view.js new file mode 100644 index 0000000000..3d6bf92429 --- /dev/null +++ b/frappe/website/doctype/page_view/page_view.js @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Frappe Technologies and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Page View', { + // refresh: function(frm) { + + // } +}); diff --git a/frappe/website/doctype/page_view/page_view.json b/frappe/website/doctype/page_view/page_view.json new file mode 100644 index 0000000000..a79472154f --- /dev/null +++ b/frappe/website/doctype/page_view/page_view.json @@ -0,0 +1,75 @@ +{ + "actions": [], + "creation": "2020-04-15 22:54:46.009703", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "path", + "referrer", + "browser", + "browser_version", + "date" + ], + "fields": [ + { + "fieldname": "path", + "fieldtype": "Data", + "label": "Path", + "set_only_once": 1 + }, + { + "fieldname": "referrer", + "fieldtype": "Data", + "label": "Referrer", + "search_index": 1, + "set_only_once": 1 + }, + { + "fieldname": "browser", + "fieldtype": "Data", + "label": "Browser", + "search_index": 1, + "set_only_once": 1 + }, + { + "fieldname": "browser_version", + "fieldtype": "Data", + "label": "Browser Version", + "set_only_once": 1 + }, + { + "fieldname": "date", + "fieldtype": "Datetime", + "label": "Date", + "set_only_once": 1 + } + ], + "in_create": 1, + "links": [], + "modified": "2020-04-15 23:31:27.517793", + "modified_by": "Administrator", + "module": "Website", + "name": "Page View", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "path", + "track_changes": 1 +} \ No newline at end of file diff --git a/frappe/website/doctype/page_view/page_view.py b/frappe/website/doctype/page_view/page_view.py new file mode 100644 index 0000000000..9c41e1c4aa --- /dev/null +++ b/frappe/website/doctype/page_view/page_view.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class PageView(Document): + pass + + +@frappe.whitelist(allow_guest=True) +def make_view_log(path, referrer=None, browser=None, version=None): + if path.startswith('/'): + path = path[1:] + + if is_tracking_enabled(): + view = frappe.new_doc("Page View") + view.path = path + view.referrer = referrer + view.browser = browser + view.browser_version = version + view.date = frappe.utils.now_datetime() + view.insert(ignore_permissions=True) + + return + +@frappe.whitelist() +def get_page_view_count(path): + return frappe.db.count("Page View", filters={'path': path}) + +def is_tracking_enabled(): + return frappe.db.get_value("Website Settings", "Website Settings", "enable_view_tracking") \ No newline at end of file diff --git a/frappe/website/doctype/page_view/test_page_view.py b/frappe/website/doctype/page_view/test_page_view.py new file mode 100644 index 0000000000..2ac98f2be2 --- /dev/null +++ b/frappe/website/doctype/page_view/test_page_view.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestPageView(unittest.TestCase): + pass diff --git a/frappe/website/doctype/website_settings/test_website_settings.py b/frappe/website/doctype/website_settings/test_website_settings.py new file mode 100644 index 0000000000..9eca957713 --- /dev/null +++ b/frappe/website/doctype/website_settings/test_website_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestWebsiteSettings(unittest.TestCase): + pass From 3d454607495fd7997a5b3b909745c0ef0d0ab4f4 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 16 Apr 2020 00:01:38 +0530 Subject: [PATCH 025/337] feat: added tracking script to website --- frappe/www/website_script.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/frappe/www/website_script.js b/frappe/www/website_script.js index fd361f8986..9f0dd329c6 100644 --- a/frappe/www/website_script.js +++ b/frappe/www/website_script.js @@ -12,3 +12,17 @@ ga('create', '{{ google_analytics_id }}', 'auto'); ga('send', 'pageview'); // End Google Analytics {%- endif %} + +{% if enable_view_tracking %} + if (navigator.doNotTrack != 1) { + frappe.ready(() => { + let browser = frappe.utils.get_browser(); + frappe.call("frappe.website.doctype.page_view.page_view.make_view_log", { + path: location.pathname, + referrer: document.referrer, + browser: browser.name, + version: browser.version + }) + }) + } +{% endif %} From 9ad1cf94f2a6db4ad88f68493b52030323c9e823 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 16 Apr 2020 00:01:55 +0530 Subject: [PATCH 026/337] feat: add tracking enabled info to boot --- frappe/boot.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/boot.py b/frappe/boot.py index e6d1199b19..e11dd87990 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -17,6 +17,7 @@ from frappe.utils.change_log import get_versions from frappe.translate import get_lang_dict from frappe.email.inbox import get_email_accounts from frappe.social.doctype.energy_point_settings.energy_point_settings import is_energy_point_enabled +from frappe.website.doctype.page_view.page_view import is_tracking_enabled from frappe.social.doctype.energy_point_log.energy_point_log import get_energy_points from frappe.social.doctype.post.post import frequently_visited_links @@ -79,6 +80,7 @@ def get_bootinfo(): bootinfo.success_action = get_success_action() bootinfo.update(get_email_accounts(user=frappe.session.user)) bootinfo.energy_points_enabled = is_energy_point_enabled() + bootinfo.website_tracking_enabled = is_tracking_enabled() bootinfo.points = get_energy_points(frappe.session.user) bootinfo.frequently_visited_links = frequently_visited_links() bootinfo.link_preview_doctypes = get_link_preview_doctypes() From 6233e94dab4de5bf73f161aa7565f758662e2100 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 16 Apr 2020 00:02:27 +0530 Subject: [PATCH 027/337] feat: added utility to get browser details --- frappe/public/js/frappe/utils/utils.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index a2b03f180e..a81d6deece 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -743,7 +743,25 @@ Object.assign(frappe.utils, { }); return $el; - } + }, + + get_browser() { + var ua=navigator.userAgent,tem,M=ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || []; + if(/trident/i.test(M[1])){ + tem=/\brv[ :]+(\d+)/g.exec(ua) || []; + return {name:'IE',version:(tem[1]||'')}; + } + if(M[1]==='Chrome'){ + tem=ua.match(/\bOPR|Edge\/(\d+)/) + if(tem!=null) {return {name:'Opera', version:tem[1]};} + } + M=M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?']; + if((tem=ua.match(/version\/(\d+)/i))!=null) {M.splice(1,1,tem[1]);} + return { + name: M[0], + version: M[1] + }; + } }); // Array de duplicate From 815f05a0eb4939b73f531da9b939991916139d15 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 16 Apr 2020 00:02:52 +0530 Subject: [PATCH 028/337] feat: added enable tracking flag to default website context --- .../doctype/website_settings/website_settings.json | 11 +++++++++-- .../doctype/website_settings/website_settings.py | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/frappe/website/doctype/website_settings/website_settings.json b/frappe/website/doctype/website_settings/website_settings.json index 39ffa2329f..f247082046 100644 --- a/frappe/website/doctype/website_settings/website_settings.json +++ b/frappe/website/doctype/website_settings/website_settings.json @@ -29,6 +29,7 @@ "footer_items", "hide_footer_signup", "integrations", + "enable_view_tracking", "enable_google_indexing", "authorize_api_indexing_access", "indexing_refresh_token", @@ -186,7 +187,7 @@ "collapsible": 1, "fieldname": "integrations", "fieldtype": "Section Break", - "label": "Google Integrations" + "label": "Integrations" }, { "description": "Add Google Analytics ID: eg. UA-89XXX57-1. Please search help on Google Analytics for more information.", @@ -319,6 +320,12 @@ "fieldname": "authorize_api_indexing_access", "fieldtype": "Button", "label": "Authorize API Indexing Access" + }, + { + "default": "0", + "fieldname": "enable_view_tracking", + "fieldtype": "Check", + "label": "Enable In App Website Tracking" } ], "icon": "fa fa-cog", @@ -326,7 +333,7 @@ "issingle": 1, "links": [], "max_attachments": 10, - "modified": "2020-02-21 16:46:59.947403", + "modified": "2020-04-15 22:59:26.256662", "modified_by": "Administrator", "module": "Website", "name": "Website Settings", diff --git a/frappe/website/doctype/website_settings/website_settings.py b/frappe/website/doctype/website_settings/website_settings.py index 4356b1aa82..ff94bd15a3 100644 --- a/frappe/website/doctype/website_settings/website_settings.py +++ b/frappe/website/doctype/website_settings/website_settings.py @@ -118,7 +118,7 @@ def get_website_settings(): for k in ["banner_html", "brand_html", "copyright", "twitter_share_via", "facebook_share", "google_plus_one", "twitter_share", "linked_in_share", "disable_signup", "hide_footer_signup", "head_html", "title_prefix", - "navbar_search"]: + "navbar_search", "enable_view_tracking"]: if hasattr(settings, k): context[k] = settings.get(k) From 7a9a16b6afec7d34435004275f4b6a168c56f856 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 16 Apr 2020 00:03:17 +0530 Subject: [PATCH 029/337] feat: add page view count to form --- frappe/public/js/frappe/form/sidebar/form_sidebar.js | 9 +++++++++ frappe/public/js/frappe/form/templates/form_sidebar.html | 1 + frappe/public/js/frappe/utils/common.js | 6 ++++++ frappe/public/less/sidebar.less | 3 ++- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/sidebar/form_sidebar.js b/frappe/public/js/frappe/form/sidebar/form_sidebar.js index 02caf25557..677d53bb64 100644 --- a/frappe/public/js/frappe/form/sidebar/form_sidebar.js +++ b/frappe/public/js/frappe/form/sidebar/form_sidebar.js @@ -81,6 +81,15 @@ frappe.ui.form.Sidebar = Class.extend({ } this.frm.viewers.refresh(); this.frm.tags && this.frm.tags.refresh(this.frm.get_docinfo().tags); + + if (this.frm.doc.route && frappe.boot.website_tracking_enabled) { + let route = this.frm.doc.route; + frappe.utils.get_page_view_count(route).then(res => { + this.sidebar.find(".pageview-count").html(__("{0} Page Views", + ["" + res.message + ""])); + }) + } + this.sidebar.find(".modified-by").html(__("{0} edited this {1}", ["" + frappe.user.full_name(this.frm.doc.modified_by) + "", "
" + comment_when(this.frm.doc.modified)])); diff --git a/frappe/public/js/frappe/form/templates/form_sidebar.html b/frappe/public/js/frappe/form/templates/form_sidebar.html index b611557c43..30b2205bae 100644 --- a/frappe/public/js/frappe/form/templates/form_sidebar.html +++ b/frappe/public/js/frappe/form/templates/form_sidebar.html @@ -105,6 +105,7 @@ diff --git a/frappe/public/js/frappe/utils/common.js b/frappe/public/js/frappe/utils/common.js index e919012664..5d20b04616 100644 --- a/frappe/public/js/frappe/utils/common.js +++ b/frappe/public/js/frappe/utils/common.js @@ -352,3 +352,9 @@ frappe.utils.new_auto_repeat_prompt = function(frm) { __('Save') ); } + +frappe.utils.get_page_view_count = function(route) { + return frappe.call("frappe.website.doctype.page_view.page_view.get_page_view_count", { + path: route + }) +} diff --git a/frappe/public/less/sidebar.less b/frappe/public/less/sidebar.less index ac5a5c33d5..28dae1a948 100644 --- a/frappe/public/less/sidebar.less +++ b/frappe/public/less/sidebar.less @@ -273,7 +273,8 @@ body[data-route^="Module"] .main-menu { } .layout-side-section .form-sidebar { - .modified-by { + .modified-by, + .pageview-count { margin-bottom: 15px; } } From 5bb489c63748d5bfba0024f2f08a02b58df1f642 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Thu, 16 Apr 2020 10:06:41 +0530 Subject: [PATCH 030/337] Update frappe/model/delete_doc.py --- frappe/model/delete_doc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index b6db154bd4..c0d2c4eef9 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -210,7 +210,7 @@ def check_permission_and_not_submitted(doc): # check if submitted if doc.docstatus == 1: - frappe.msgprint(_("{0} {1}: Submitted Record cannot be deleted. You must Cancel it first.").format(_(doc.doctype), doc.name), + frappe.msgprint(_("{0} {1}: Submitted Record cannot be deleted. You must {2} Cancel {3} it first.").format(_(doc.doctype), doc.name, "", ""), raise_exception=True) def check_if_doc_is_linked(doc, method="Delete"): From 985392d07f8880f5277c3830462a58416271a3ee Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 16 Apr 2020 11:29:15 +0530 Subject: [PATCH 031/337] fix: linting fixes --- .../js/frappe/form/sidebar/form_sidebar.js | 2 +- frappe/public/js/frappe/utils/common.js | 4 +- frappe/public/js/frappe/utils/utils.js | 37 ++++++++++++------- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/frappe/public/js/frappe/form/sidebar/form_sidebar.js b/frappe/public/js/frappe/form/sidebar/form_sidebar.js index 677d53bb64..9910227590 100644 --- a/frappe/public/js/frappe/form/sidebar/form_sidebar.js +++ b/frappe/public/js/frappe/form/sidebar/form_sidebar.js @@ -87,7 +87,7 @@ frappe.ui.form.Sidebar = Class.extend({ frappe.utils.get_page_view_count(route).then(res => { this.sidebar.find(".pageview-count").html(__("{0} Page Views", ["" + res.message + ""])); - }) + }); } this.sidebar.find(".modified-by").html(__("{0} edited this {1}", diff --git a/frappe/public/js/frappe/utils/common.js b/frappe/public/js/frappe/utils/common.js index 5d20b04616..7ffec5e541 100644 --- a/frappe/public/js/frappe/utils/common.js +++ b/frappe/public/js/frappe/utils/common.js @@ -356,5 +356,5 @@ frappe.utils.new_auto_repeat_prompt = function(frm) { frappe.utils.get_page_view_count = function(route) { return frappe.call("frappe.website.doctype.page_view.page_view.get_page_view_count", { path: route - }) -} + }); +}; diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index b07e71bc91..315637092d 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -748,22 +748,33 @@ Object.assign(frappe.utils, { }, get_browser() { - var ua=navigator.userAgent,tem,M=ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || []; - if(/trident/i.test(M[1])){ - tem=/\brv[ :]+(\d+)/g.exec(ua) || []; - return {name:'IE',version:(tem[1]||'')}; + var ua = navigator.userAgent, + tem, + M = + ua.match( + /(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i + ) || []; + if (/trident/i.test(M[1])) { + tem = /\brv[ :]+(\d+)/g.exec(ua) || []; + return { name: "IE", version: tem[1] || "" }; + } + if (M[1] === "Chrome") { + tem = ua.match(/\bOPR|Edge\/(\d+)/); + if (tem != null) { + return { name: "Opera", version: tem[1] }; } - if(M[1]==='Chrome'){ - tem=ua.match(/\bOPR|Edge\/(\d+)/) - if(tem!=null) {return {name:'Opera', version:tem[1]};} - } - M=M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?']; - if((tem=ua.match(/version\/(\d+)/i))!=null) {M.splice(1,1,tem[1]);} + } + M = M[2] + ? [M[1], M[2]] + : [navigator.appName, navigator.appVersion, "-?"]; + if ((tem = ua.match(/version\/(\d+)/i)) != null) { + M.splice(1, 1, tem[1]); + } return { - name: M[0], - version: M[1] + name: M[0], + version: M[1], }; - } + }, }); // Array de duplicate From 24b737f4c003388c5cbfbb88a91b001533339687 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 16 Apr 2020 12:05:37 +0530 Subject: [PATCH 032/337] refactor: Move Syntax checking code to a separate file --- .github/frappe_linter/translation.py | 26 +++++++++++++++ .github/workflows/translation_linter.yml | 41 ++++++------------------ 2 files changed, 36 insertions(+), 31 deletions(-) create mode 100644 .github/frappe_linter/translation.py diff --git a/.github/frappe_linter/translation.py b/.github/frappe_linter/translation.py new file mode 100644 index 0000000000..c8a90e68ce --- /dev/null +++ b/.github/frappe_linter/translation.py @@ -0,0 +1,26 @@ +import re +import sys + +errors_encounter = 0 +pattern = re.compile(r"_\(([\"']{,3})(?P((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P((?!\5).)*)\5)*(\s*,\s*(.)*?\s*(,\s*([\"'])(?P((?!\11).)*)\11)*)*\)") +start_pattern = re.compile(r"_{1,2}\([\"']{1,3}") + +files = sys.argv +for _file in files: + if not _file.endswith(('.py', '.js')): + continue + with open(_file, 'r') as f: + print(f'Checking: {_file}') + for num, line in enumerate(f, 1): + all_matches = start_pattern.finditer(line) + if all_matches: + for match in all_matches: + verify = pattern.search(line) + if not verify: + errors_encounter += 1 + num_str = str(num) + print(f'A syntax error has been discovered at line number: {num}') + print(f'Syntax error occurred with: {line}') +if errors_encounter > 0 : + print('You can visit "https://frappe.io/docs/user/en/translations" to resolve this error.') + assert 1+1 == 3 \ No newline at end of file diff --git a/.github/workflows/translation_linter.yml b/.github/workflows/translation_linter.yml index 6b8ba91e99..ca7b152921 100644 --- a/.github/workflows/translation_linter.yml +++ b/.github/workflows/translation_linter.yml @@ -1,12 +1,13 @@ -name: frappe_linter +name: Frappe Linter on: pull_request: - branches: - - master - - develop + branches: + - develop + - version-12-hotfix + - version-11-hotfix jobs: check_translation: - name: Check format + name: Translation Syntax Check runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 @@ -14,30 +15,8 @@ jobs: uses: actions/setup-python@v1 with: python-version: 3.6 - - name: Run check_translation for pull_request + - name: Validating Translation Syntax run: | - import os - import subprocess - import re - errors_encounter = 0 - pattern = re.compile(r"_\(([\"']{,3})(?P((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P((?!\5).)*)\5)*(\s*,\s*(.)*?\s*(,\s*([\"'])(?P((?!\11).)*)\11)*)*\)") - temp = re.compile(r"_\(([\"']{,3})") - subprocess.run('git fetch origin '+os.environ['GITHUB_BASE_REF']+ ' :' +os.environ['GITHUB_BASE_REF']+ ' -q', shell=True) - files = subprocess.check_output('git diff --name-only '+os.environ['GITHUB_BASE_REF'], shell=True) - files = files.decode('utf-8') - files = files.split() - for file in files: - with open(file, 'r') as f: - for num, line in enumerate(f, 1): - all_matches = temp.finditer(line) - if all_matches: - for match in all_matches: - verify = pattern.search(line) - if not verify: - errors_encounter += 1 - print('A syntax error has been discovered at line number: ' + num) - print('Syntax error occured with: ' + line) - if errors_encounter > 0 : - print('You can visit "https://frappe.io/docs/user/en/translations" to resolve this error.') - assert 1+1 == 3 - shell: python \ No newline at end of file + git fetch origin $GITHUB_BASE_REF:$GITHUB_BASE_REF -q + files=$(git diff --name-only $GITHUB_BASE_REF) + python $GITHUB_WORKSPACE/.github/frappe_linter/translation.py $files \ No newline at end of file From ab673a9bb95e5ba33dd30bb537dd5f540dd5cf1e Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 16 Apr 2020 12:37:53 +0530 Subject: [PATCH 033/337] fix(patch): custom and standard scss is separated in theme --- frappe/patches.txt | 1 + frappe/patches/v13_0/website_theme_custom_scss.py | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 frappe/patches/v13_0/website_theme_custom_scss.py diff --git a/frappe/patches.txt b/frappe/patches.txt index 0e02423639..cbda8cf677 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -272,3 +272,4 @@ execute:frappe.delete_doc_if_exists('DocType', 'GCalendar Account') execute:frappe.delete_doc_if_exists('DocType', 'GCalendar Settings') frappe.patches.v12_0.remove_parent_and_parenttype_from_print_formats execute:from frappe.desk.page.setup_wizard.install_fixtures import update_genders;update_genders() +frappe.patches.v13_0.website_theme_custom_scss diff --git a/frappe/patches/v13_0/website_theme_custom_scss.py b/frappe/patches/v13_0/website_theme_custom_scss.py new file mode 100644 index 0000000000..72c98bfc88 --- /dev/null +++ b/frappe/patches/v13_0/website_theme_custom_scss.py @@ -0,0 +1,9 @@ +import frappe + +def execute(): + for theme in frappe.get_all('Website Theme'): + doc = frappe.get_doc('Website Theme', theme.name) + if not doc.custom_scss and doc.theme_scss: + # move old theme to new theme + doc.custom_scss = doc.theme_scss + doc.save() \ No newline at end of file From fd68b7f39854d9cfc30bee92b1521915ff143438 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Thu, 16 Apr 2020 12:54:06 +0530 Subject: [PATCH 034/337] fix: Skip first 2 arguments --- .github/frappe_linter/translation.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/frappe_linter/translation.py b/.github/frappe_linter/translation.py index c8a90e68ce..63c4821775 100644 --- a/.github/frappe_linter/translation.py +++ b/.github/frappe_linter/translation.py @@ -5,7 +5,8 @@ errors_encounter = 0 pattern = re.compile(r"_\(([\"']{,3})(?P((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P((?!\5).)*)\5)*(\s*,\s*(.)*?\s*(,\s*([\"'])(?P((?!\11).)*)\11)*)*\)") start_pattern = re.compile(r"_{1,2}\([\"']{1,3}") -files = sys.argv +# skip first 2 arguments +files = sys.argv[2:] for _file in files: if not _file.endswith(('.py', '.js')): continue @@ -18,9 +19,10 @@ for _file in files: verify = pattern.search(line) if not verify: errors_encounter += 1 - num_str = str(num) print(f'A syntax error has been discovered at line number: {num}') print(f'Syntax error occurred with: {line}') -if errors_encounter > 0 : +if errors_encounter > 0: print('You can visit "https://frappe.io/docs/user/en/translations" to resolve this error.') - assert 1+1 == 3 \ No newline at end of file + assert 1+1 == 3 +else: + print('Good To Go!') From 82806dd16158121af32e849419eea73d7b618eea Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 16 Apr 2020 12:48:42 +0530 Subject: [PATCH 035/337] feat: disable file browser for both attach and attach image --- frappe/public/js/frappe/web_form/webform_script.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/web_form/webform_script.js b/frappe/public/js/frappe/web_form/webform_script.js index 688c0938c4..53d9701774 100644 --- a/frappe/public/js/frappe/web_form/webform_script.js +++ b/frappe/public/js/frappe/web_form/webform_script.js @@ -113,8 +113,9 @@ frappe.ready(function() { df.only_select = true; } if (["Attach", "Attach Image"].includes(df.fieldtype)) { - if (!df.options) + if (typeof df.options !== "object") { df.options = {}; + } df.options.disable_file_browser = true; } }); From 15493a9a23e4d0a5ef69431f13962b7549c15988 Mon Sep 17 00:00:00 2001 From: P-Froggy <60393001+P-Froggy@users.noreply.github.com> Date: Thu, 16 Apr 2020 09:49:41 +0200 Subject: [PATCH 036/337] fix: Incorrect translation of short format time difference strings (#9802) --- frappe/public/js/frappe/utils/pretty_date.js | 115 ++++++++++--------- frappe/translations/de.csv | 30 +++-- 2 files changed, 81 insertions(+), 64 deletions(-) diff --git a/frappe/public/js/frappe/utils/pretty_date.js b/frappe/public/js/frappe/utils/pretty_date.js index 1ad02e675c..060ae73a98 100644 --- a/frappe/public/js/frappe/utils/pretty_date.js +++ b/frappe/public/js/frappe/utils/pretty_date.js @@ -1,64 +1,71 @@ -// moment strings for translation +function prettyDate(date, mini) { + if (!date) return ''; -function prettyDate(time, mini) { - if (!time) { - time = new Date(); - } - if ('moment' in window) { // use frappe.ImportError ;) - let ret; - if (frappe.sys_defaults && frappe.sys_defaults.time_zone) { - ret = moment.tz(time, frappe.sys_defaults.time_zone).locale(frappe.boot.lang).fromNow(mini); - } else { - ret = moment(time).locale(frappe.boot.lang).fromNow(mini); - } - if (mini) { - if (ret === moment().locale(frappe.boot.lang).fromNow(mini)) { - ret = __("now"); - } else { - var parts = ret.split(" "); - if (parts.length > 1) { - if (parts[0] === "a" || parts[0] === "an") { - parts[0] = 1; - } - if (parts[1].substr(0, 2) === "mo") { - ret = parts[0] + " M"; - } else { - ret = parts[0] + " " + parts[1].substr(0, 1); - } - } + if (typeof (date) == "string") + date = new Date((date || "").replace(/-/g, "/").replace(/[TZ]/g, " ").replace(/\.[0-9]*/, "")); + + let diff = (((new Date()).getTime() - date.getTime()) / 1000); + let day_diff = Math.floor(diff / 86400); + + if (isNaN(day_diff) || day_diff < 0) return ''; + + if (mini) { + // Return short format of time difference + if (day_diff == 0) { + if (diff < 60) { + return __("Now"); + } else if (diff < 3600) { + return __("{0} m", [Math.floor(diff / 60)]); + } else if (diff < 86400) { + return __("{0} h", [Math.floor(diff / 3600)]); + } + } else { + if (day_diff < 7) { + return __("{0} D", [day_diff]); + } else if (day_diff < 31) { + return __("{0} W", [Math.ceil(day_diff / 7)]); + } else if (day_diff < 365) { + return __("{0} M", [Math.ceil(day_diff / 30)]); + } else { + return __("{0} Y", [Math.ceil(day_diff / 365)]); } - ret = ret.substr(0, 5); } - return ret; } else { - if (!time) return ''; - var date = time; - if (typeof (time) == "string") - date = new Date((time || "").replace(/-/g, "/").replace(/[TZ]/g, " ").replace(/\.[0-9]*/, "")); - - var diff = (((new Date()).getTime() - date.getTime()) / 1000), - day_diff = Math.floor(diff / 86400); - - if (isNaN(day_diff) || day_diff < 0) - return ''; - - var when = day_diff == 0 && ( - diff < 60 && __("just now") || - diff < 120 && __("1 minute ago") || - diff < 3600 && __("{0} minutes ago", [Math.floor(diff / 60)]) || - diff < 7200 && __("1 hour ago") || - diff < 86400 && ("{0} hours ago", [Math.floor(diff / 3600)])) || - day_diff == 1 && __("Yesterday") || - day_diff < 7 && __("{0} days ago", day_diff) || - day_diff < 31 && __("{0} weeks ago", [Math.ceil(day_diff / 7)]) || - day_diff < 365 && __("{0} months ago", [Math.ceil(day_diff / 30)]) || - __("> {0} year(s) ago", [Math.floor(day_diff / 365)]); - - return when; + // Return long format of time difference + if (day_diff == 0) { + if (diff < 60) { + return __("Just now"); + } else if (diff < 120) { + return __("1 minute ago"); + } else if (diff < 3600) { + return __("{0} minutes ago", [Math.floor(diff / 60)]); + } else if (diff < 7200) { + return __("1 hour ago"); + } else if (diff < 86400) { + return __("{0} hours ago", [Math.floor(diff / 3600)]); + } + } else { + if (day_diff == 1) { + return __("Yesterday"); + } else if (day_diff < 7) { + return __("{0} days ago", [day_diff]); + } else if (day_diff < 14) { + return __("1 week ago"); + } else if (day_diff < 31) { + return __("{0} weeks ago", [Math.ceil(day_diff / 7)]); + } else if (day_diff < 62) { + return __("1 month ago"); + } else if (day_diff < 365) { + return __("{0} months ago", [Math.ceil(day_diff / 30)]); + } else if (day_diff < 730) { + return __("1 year ago"); + } else { + return __("{0} years ago", [Math.ceil(day_diff / 365)]); + } + } } } - frappe.provide("frappe.datetime"); window.comment_when = function(datetime, mini) { var timestamp = frappe.datetime.str_to_user ? diff --git a/frappe/translations/de.csv b/frappe/translations/de.csv index efeb68df62..18d66591b3 100644 --- a/frappe/translations/de.csv +++ b/frappe/translations/de.csv @@ -63,7 +63,6 @@ apps/frappe/frappe/templates/emails/download_data.html,We have received a reques DocType: System Settings,"If enabled, the password strength will be enforced based on the Minimum Password Score value. A value of 2 being medium strong and 4 being very strong.","Falls diese Option aktiviert ist, wird die Passwortstärke auf der Grundlage des Minimum Password Score Wertes erzwungen. Ein Wert von 2 ist mittelstark und 4 sehr stark." DocType: About Us Settings,"""Team Members"" or ""Management""",„Teammitglieder“ oder „Management“ apps/frappe/frappe/core/doctype/doctype/doctype.py,Default for 'Check' type of field must be either '0' or '1',Standard für 'Prüfen'-Feldtyp muss entweder '0' oder '1' sein -apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,Yesterday,Gestern DocType: Contact,Designation,Bezeichnung apps/frappe/frappe/email/doctype/email_account/email_account.py,Automatic Linking can be activated only for one Email Account.,Die automatische Verknüpfung kann nur für ein E-Mail-Konto aktiviert werden. DocType: Test Runner,Test Runner,Tester @@ -120,7 +119,6 @@ DocType: Dashboard Chart,Timespan,Zeitspanne apps/frappe/frappe/public/js/frappe/file_uploader/FileUploader.vue,Web Link,Weblink DocType: Deleted Document,Restored,Restauriert apps/frappe/frappe/public/js/frappe/form/print.js,"Error connecting to QZ Tray Application...

You need to have QZ Tray application installed and running, to use the Raw Print feature.

Click here to Download and install QZ Tray.
Click here to learn more about Raw Printing.","Fehler beim Verbinden mit der QZ-Tray-Anwendung ...

Sie müssen die QZ Tray-Anwendung installiert haben und ausführen, um die Raw Print-Funktion verwenden zu können.

Klicken Sie hier, um QZ Tray herunterzuladen und zu installieren .
Klicken Sie hier, um mehr über den Rohdruck zu erfahren ." -apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,1 minute ago,Vor 1 Minute apps/frappe/frappe/core/page/permission_manager/permission_manager_help.html,"Apart from System Manager, roles with Set User Permissions right can set permissions for other users for that Document Type.",Zusätzlich zum System-Manager können Rollen mit der Erlaubnis Benutzer anzulegen Berechtigungen für andere Nutzer für diesen Dokumententyp setzen. apps/frappe/frappe/website/doctype/website_theme/website_theme.js,Configure Theme,Thema konfigurieren DocType: Company History,Company History,Unternehmensgeschichte @@ -971,7 +969,6 @@ apps/frappe/frappe/public/js/frappe/list/list_view.js,Share URL,URL teilen DocType: System Settings,Allow Consecutive Login Attempts ,Erlaube aufeinanderfolgende Login-Versuche apps/frappe/frappe/templates/pages/integrations/stripe_checkout.html,An error occured during the payment process. Please contact us.,Während des Bezahlvorgangs ist ein Fehler aufgetreten. Bitte kontaktieren Sie uns. DocType: Onboarding Slide,If Slide Type is Create or Settings there should be a 'create_onboarding_docs' method in the {ref_doctype}.py file bound to be executed after the slide is completed.,"Wenn der Folientyp "Erstellen" oder "Einstellungen" ist, sollte die Methode "create_onboarding_docs" in der Datei "{ref_doctype} .py" enthalten sein, die nach Abschluss der Folie ausgeführt werden soll." -apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} days ago,vor {0} Tag(en) DocType: Email Account,Awaiting Password,Warte auf Passwort DocType: Address,Address Line 1,Adresse Zeile 1 apps/frappe/frappe/public/js/frappe/ui/filters/filter.js,Not Descendants Of,Nicht Nachkommen von @@ -1360,7 +1357,6 @@ apps/frappe/frappe/social/doctype/energy_point_rule/energy_point_rule.py,Referen DocType: PayPal Settings,PayPal Settings,PayPal-Einstellungen apps/frappe/frappe/core/page/permission_manager/permission_manager.js,Select Document Type,Dokumenttyp auswählen apps/frappe/frappe/utils/nestedset.py,Cannot delete {0} as it has child nodes,"{0} kann nicht gelöscht werden, da es Unterknoten gibt" -apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} minutes ago,vor {0} Minute(n) apps/frappe/frappe/automation/doctype/assignment_rule/assignment_rule.py,Assignment Day {0} has been repeated.,Der Zuordnungstag {0} wurde wiederholt. DocType: Kanban Board Column,lightblue,hellblau apps/frappe/frappe/integrations/doctype/webhook/webhook.py,Same Field is entered more than once,Gleiches Feld wird mehrmals eingegeben @@ -1787,7 +1783,6 @@ DocType: Notification Log,Assignment,Zuordnung DocType: Notification,Slack Channel,Slack-Kanal DocType: About Us Team Member,Image Link,Bildverknüpfung DocType: Auto Email Report,Report Filters,Berichtsfilter -apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,now,jetzt DocType: Workflow State,step-backward,Schritt zurück apps/frappe/frappe/utils/boilerplate.py,{app_title},{app_title} apps/frappe/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py,Please set Dropbox access keys in your site config,Bitte Dropbox-Zugriffsdaten in den Einstellungen der Seite setzen @@ -2520,7 +2515,6 @@ apps/frappe/frappe/public/js/frappe/form/print.js,QZ Tray Connection Active!,QZ- DocType: Contact Us Settings,Settings for Contact Us Page,Einstellungen Kontakt DocType: Server Script,Script Type,Skripttyp DocType: Print Settings,Enable Print Server,Aktivieren Sie den Druckserver -apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} weeks ago,vor {0} Woche(n) DocType: Email Account,Footer,Fußzeile apps/frappe/frappe/config/integrations.py,Authentication,Authentifizierung apps/frappe/frappe/utils/verified_command.py,Invalid Link,Ungültige Verknüpfung @@ -3444,7 +3438,6 @@ apps/frappe/frappe/integrations/doctype/ldap_settings/ldap_settings.py,Please In apps/frappe/frappe/core/doctype/data_import/log_details.html,Row Status,Zeilenstatus DocType: S3 Backup Settings,sa-east-1,sa-east-1 DocType: Workflow Transition,Workflow Transition,Workflow-Übergang -apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} months ago,vor {0} Monate(n) apps/frappe/frappe/custom/doctype/custom_field/custom_field.py,Custom Fields can only be added to a standard DocType.,Benutzerdefinierte Felder können nur zu einem Standard-DocType hinzugefügt werden. apps/frappe/frappe/public/js/frappe/list/list_sidebar_group_by.js,Created By,Erstellt von apps/frappe/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py,Dropbox Setup,Dropbox-Setup @@ -3500,7 +3493,6 @@ apps/frappe/frappe/website/doctype/website_theme/website_theme.js,Theme Colors,T apps/frappe/frappe/printing/page/print_format_builder/print_format_builder.js,Select a DocType to make a new format,"DocType auswählen, um ein neues Format zu erstellen" apps/frappe/frappe/desk/page/user_profile/user_profile.js,User does not exist,Benutzer existiert nicht apps/frappe/frappe/automation/doctype/auto_repeat/auto_repeat.py,'Recipients' not specified,"Keine ""Empfänger"" angegeben" -apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,just now,gerade eben apps/frappe/frappe/public/js/frappe/ui/filters/edit_filter.html,Apply,Anwenden DocType: Footer Item,Policy,Politik apps/frappe/frappe/integrations/utils.py,{0} Settings not found,{0} Einstellungen nicht gefunden @@ -3592,7 +3584,6 @@ DocType: Auto Email Report,Period,Periode apps/frappe/frappe/core/doctype/data_import_beta/data_import_beta.js,About {0} minute remaining,Noch ungefähr {0} Minuten apps/frappe/frappe/www/login.py,Invalid Login Token,Invalid Login Token apps/frappe/frappe/public/js/frappe/chat.js,Discard,Verwerfen -apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,1 hour ago,vor 1 Stunde DocType: Website Settings,Home Page,Startseite DocType: Error Snapshot,Parent Error Snapshot,Momentaufnahme des übergeordneten Fehlers apps/frappe/frappe/public/js/frappe/data_import/import_preview.js,Map columns from {0} to fields in {1},Ordnen Sie Spalten von {0} Feldern in {1} zu. @@ -3804,7 +3795,6 @@ DocType: Webhook,on_update_after_submit,on_update_after_submit DocType: System Settings,Allow Login using User Name,Login mit Benutzernamen zulassen apps/frappe/frappe/core/doctype/report/report.js,Enable Report,Bericht aktivieren DocType: DocField,Display Depends On,Anzeige ist abhängig von -apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,> {0} year(s) ago,> {0} Jahr (e) her DocType: Social Login Key,API Endpoint,API-Endpunkt DocType: Web Page,Insert Code,Code einfügen DocType: Data Migration Run,Current Mapping Type,Aktueller Kartentyp @@ -4188,3 +4178,23 @@ DocType: DocField,Ignore User Permissions,Ignorieren von Benutzerberechtigungen apps/frappe/frappe/public/js/frappe/web_form/web_form.js,Saved Successfully,Erfolgreich gespeichert apps/frappe/frappe/core/doctype/user/user.py,Please ask your administrator to verify your sign-up,Bitte fragen Sie Ihren Administrator Ihre Anmeldung bis zum überprüfen DocType: Domain Settings,Active Domains,Aktive Domains +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,Now,Jetzt +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} m,{0} m +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} h,{0} h +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} D,{0} T +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} W,{0} W +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} M,{0} M +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} Y,{0} J +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,Just now,Gerade eben +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,1 minute ago,Vor einer Minute +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} minutes ago,Vor {0} Minuten +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,1 hour ago,Vor einer Stunde +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} hours ago,Vor {0} Stunden +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,Yesterday,Gestern +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} days ago,Vor {0} Tagen +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,1 week ago,Vor einer Woche +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} weeks ago,Vor {0} Wochen +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,1 month ago,Vor einem Monat +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} months ago,Vor {0} Monaten +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,1 year ago,Vor einem Jahr +apps/frappe/frappe/public/js/frappe/utils/pretty_date.js,{0} years ago,Vor {0} Jahren \ No newline at end of file From 326439a4d1e90b085aee8b2a6eef4536c1dbd545 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 16 Apr 2020 14:09:21 +0530 Subject: [PATCH 037/337] fix: backups dont break if one site is corrupted --- frappe/commands/site.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/frappe/commands/site.py b/frappe/commands/site.py index fd43cc8cf3..b88e9f78c0 100755 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -317,10 +317,18 @@ def backup(context, with_files=False, backup_path_db=None, backup_path_files=Non "Backup" from frappe.utils.backups import scheduled_backup verbose = context.verbose + exit_code = 0 for site in context.sites: - frappe.init(site=site) - frappe.connect() - odb = scheduled_backup(ignore_files=not with_files, backup_path_db=backup_path_db, backup_path_files=backup_path_files, backup_path_private_files=backup_path_private_files, force=True) + try: + frappe.init(site=site) + frappe.connect() + odb = scheduled_backup(ignore_files=not with_files, backup_path_db=backup_path_db, backup_path_files=backup_path_files, backup_path_private_files=backup_path_private_files, force=True) + except Exception as e: + if verbose: + print("Backup failed for {0}. Database or site_config.json may be corrupted".format(site)) + exit_code = 1 + continue + if verbose: from frappe.utils import now print("database backup taken -", odb.backup_path_db, "- on", now()) @@ -329,6 +337,7 @@ def backup(context, with_files=False, backup_path_db=None, backup_path_files=Non print("private files backup taken -", odb.backup_path_private_files, "- on", now()) frappe.destroy() + sys.exit(exit_code) @click.command('remove-from-installed-apps') @click.argument('app') From 23b3f65b3bae0c04177c9a670e8f0af5d06be13a Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Thu, 16 Apr 2020 14:09:59 +0530 Subject: [PATCH 038/337] fix: limit file upload mimetype if user has no desk access limits file upload mimetype to jpg, png, and pdf in case the user does not have desk access, to prevent abuse of the servers as a file storage system Signed-off-by: Chinmay D. Pai --- frappe/handler.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/handler.py b/frappe/handler.py index 6e0bf7a6be..d3fec3cf8f 100755 --- a/frappe/handler.py +++ b/frappe/handler.py @@ -148,12 +148,14 @@ def uploadfile(): @frappe.whitelist(allow_guest=True) def upload_file(): + user = None if frappe.session.user == 'Guest': if frappe.get_system_settings('allow_guests_to_upload_files'): ignore_permissions = True else: return else: + user = frappe.get_doc("User", frappe.session.user) ignore_permissions = False files = frappe.request.files @@ -175,7 +177,7 @@ def upload_file(): frappe.local.uploaded_file = content frappe.local.uploaded_filename = filename - if frappe.session.user == 'Guest': + if frappe.session.user == 'Guest' or (user and not user.has_desk_access()): import mimetypes filetype = mimetypes.guess_type(filename)[0] if filetype not in ['image/png', 'image/jpeg', 'application/pdf']: From e20df3d83c4f76a05f40e832aff9fab097bb838a Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 16 Apr 2020 14:19:01 +0530 Subject: [PATCH 039/337] fix(path): __init__ for v13_0 folder --- frappe/patches/v13_0/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 frappe/patches/v13_0/__init__.py diff --git a/frappe/patches/v13_0/__init__.py b/frappe/patches/v13_0/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From feba8878d9739490c2240bc29cacfe022c86f5f3 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 16 Apr 2020 14:26:55 +0530 Subject: [PATCH 040/337] fix: increase the length of the password to maximum possible under current structure --- frappe/utils/password.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/utils/password.py b/frappe/utils/password.py index da5cdecc55..b939607b19 100644 --- a/frappe/utils/password.py +++ b/frappe/utils/password.py @@ -131,9 +131,9 @@ def create_auth_table(): frappe.db.create_auth_table() def encrypt(pwd): - if len(pwd) > 100: - # encrypting > 100 chars will lead to truncation - frappe.throw(_('Password cannot be more than 100 characters long')) + if len(pwd) > 127: + # encrypting > 127 chars will lead to truncation + frappe.throw(_('Password cannot be more than 127 characters long')) cipher_suite = Fernet(encode(get_encryption_key())) cipher_text = cstr(cipher_suite.encrypt(encode(pwd))) From d7e9ef60b87ba8c947d2ce89f3c645691dd95bc4 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Thu, 16 Apr 2020 14:31:25 +0530 Subject: [PATCH 041/337] fix: add support for more document mimetypes Signed-off-by: Chinmay D. Pai --- frappe/handler.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/frappe/handler.py b/frappe/handler.py index d3fec3cf8f..9699ff6d44 100755 --- a/frappe/handler.py +++ b/frappe/handler.py @@ -14,6 +14,12 @@ from frappe.core.doctype.server_script.server_script_utils import run_server_scr from werkzeug.wrappers import Response from six import string_types +ALLOWED_MIMETYPES = ('image/png', 'image/jpeg', 'application/pdf', 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.spreadsheet') + + def handle(): """handle request""" validate_auth() @@ -180,8 +186,8 @@ def upload_file(): if frappe.session.user == 'Guest' or (user and not user.has_desk_access()): import mimetypes filetype = mimetypes.guess_type(filename)[0] - if filetype not in ['image/png', 'image/jpeg', 'application/pdf']: - frappe.throw("You can only upload JPG, PNG or PDF files.") + if filetype not in ALLOWED_MIMETYPES: + frappe.throw("You can only upload JPG, PNG, PDF, or Microsoft documents.") if method: method = frappe.get_attr(method) From 7b8b5a8490d1f2927cb55b9dbd522d4d06c06cca Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 16 Apr 2020 15:46:05 +0530 Subject: [PATCH 042/337] feat: show self xss warning in console --- frappe/public/js/frappe/desk.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index a1418f9149..902f382879 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -86,6 +86,14 @@ frappe.Application = Class.extend({ this.show_update_available(); } + if (!frappe.boot.developer_mode) { + let console_security_message = __("Using this console may allow attackers to impersonate you and steal your information. Do not enter or paste code that you do not understand.") + console.log( + `%c${console_security_message}`, + "font-size: large" + ); + } + this.show_notes(); if (frappe.boot.is_first_startup) { From 82cedb86171d83485629490b94b1aa2c7c488160 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 16 Apr 2020 16:37:35 +0530 Subject: [PATCH 043/337] fix: Attribute errors and better msgprint --- .../doctype/email_domain/email_domain.py | 32 ++++++------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/frappe/email/doctype/email_domain/email_domain.py b/frappe/email/doctype/email_domain/email_domain.py index b6585d966b..08583dc228 100644 --- a/frappe/email/doctype/email_domain/email_domain.py +++ b/frappe/email/doctype/email_domain/email_domain.py @@ -39,7 +39,7 @@ class EmailDomain(Document): except Exception: frappe.throw(_("Incoming email account not correct")) - return None + finally: try: if self.use_imap: @@ -48,9 +48,10 @@ class EmailDomain(Document): test.quit() except Exception: pass + try: - if self.use_ssl_for_outgoing: - if not self.smtp_port: + if self.get('use_ssl_for_outgoing'): + if not self.get('smtp_port'): self.smtp_port = 465 sess = smtplib.SMTP_SSL((self.smtp_server or "").encode('utf-8'), @@ -62,28 +63,15 @@ class EmailDomain(Document): sess.quit() except Exception: frappe.throw(_("Outgoing email account not correct")) - return None - return def on_update(self): """update all email accounts using this domain""" - for email_account in frappe.get_all("Email Account", - filters={"domain": self.name}): - + for email_account in frappe.get_all("Email Account", filters={"domain": self.name}): try: - email_account = frappe.get_doc("Email Account", - email_account.name) - email_account.set("email_server",self.email_server) - email_account.set("use_imap",self.use_imap) - email_account.set("use_ssl",self.use_ssl) - email_account.set("use_tls",self.use_tls) - email_account.set("attachment_limit",self.attachment_limit) - email_account.set("smtp_server",self.smtp_server) - email_account.set("smtp_port",self.smtp_port) - email_account.set("use_ssl_for_outgoing", self.use_ssl_for_outgoing) - email_account.set("append_emails_to_sent_folder", self.append_emails_to_sent_folder) + email_account = frappe.get_doc("Email Account", email_account.name) + for attr in ["email_server", "use_imap", "use_ssl", "use_tls", "attachment_limit", "smtp_server", "smtp_port", "use_ssl_for_outgoing", "append_emails_to_sent_folder"]: + email_account.set(attr, self.get(attr, default=0)) email_account.save() + except Exception as e: - frappe.msgprint(email_account.name) - frappe.throw(e) - return None + frappe.msgprint(_("Error has occurred in {0}").format(email_account.name), raise_exception=e.__class__) From 33c6089049938695d1604584eb0de5af0f86a17d Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 16 Apr 2020 15:35:46 +0530 Subject: [PATCH 044/337] feat: rename doctype to Web Page View --- frappe/boot.py | 2 +- frappe/public/js/frappe/utils/common.js | 2 +- .../doctype/{page_view => web_page_view}/__init__.py | 0 .../test_web_page_view.py} | 2 +- .../page_view.js => web_page_view/web_page_view.js} | 2 +- .../page_view.json => web_page_view/web_page_view.json} | 2 +- .../page_view.py => web_page_view/web_page_view.py} | 6 +++--- frappe/website/website_theme/standard/standard.json | 4 ++-- frappe/www/website_script.js | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) rename frappe/website/doctype/{page_view => web_page_view}/__init__.py (100%) rename frappe/website/doctype/{page_view/test_page_view.py => web_page_view/test_web_page_view.py} (81%) rename frappe/website/doctype/{page_view/page_view.js => web_page_view/web_page_view.js} (80%) rename frappe/website/doctype/{page_view/page_view.json => web_page_view/web_page_view.json} (98%) rename frappe/website/doctype/{page_view/page_view.py => web_page_view/web_page_view.py} (85%) diff --git a/frappe/boot.py b/frappe/boot.py index e11dd87990..dc7ea8b3da 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -17,7 +17,7 @@ from frappe.utils.change_log import get_versions from frappe.translate import get_lang_dict from frappe.email.inbox import get_email_accounts from frappe.social.doctype.energy_point_settings.energy_point_settings import is_energy_point_enabled -from frappe.website.doctype.page_view.page_view import is_tracking_enabled +from frappe.website.doctype.web_page_view.web_page_view import is_tracking_enabled from frappe.social.doctype.energy_point_log.energy_point_log import get_energy_points from frappe.social.doctype.post.post import frequently_visited_links diff --git a/frappe/public/js/frappe/utils/common.js b/frappe/public/js/frappe/utils/common.js index 7ffec5e541..6ad15e44bf 100644 --- a/frappe/public/js/frappe/utils/common.js +++ b/frappe/public/js/frappe/utils/common.js @@ -354,7 +354,7 @@ frappe.utils.new_auto_repeat_prompt = function(frm) { } frappe.utils.get_page_view_count = function(route) { - return frappe.call("frappe.website.doctype.page_view.page_view.get_page_view_count", { + return frappe.call("frappe.website.doctype.web_page_view.web_page_view.get_page_view_count", { path: route }); }; diff --git a/frappe/website/doctype/page_view/__init__.py b/frappe/website/doctype/web_page_view/__init__.py similarity index 100% rename from frappe/website/doctype/page_view/__init__.py rename to frappe/website/doctype/web_page_view/__init__.py diff --git a/frappe/website/doctype/page_view/test_page_view.py b/frappe/website/doctype/web_page_view/test_web_page_view.py similarity index 81% rename from frappe/website/doctype/page_view/test_page_view.py rename to frappe/website/doctype/web_page_view/test_web_page_view.py index 2ac98f2be2..d51727ec68 100644 --- a/frappe/website/doctype/page_view/test_page_view.py +++ b/frappe/website/doctype/web_page_view/test_web_page_view.py @@ -6,5 +6,5 @@ from __future__ import unicode_literals # import frappe import unittest -class TestPageView(unittest.TestCase): +class TestWebPageView(unittest.TestCase): pass diff --git a/frappe/website/doctype/page_view/page_view.js b/frappe/website/doctype/web_page_view/web_page_view.js similarity index 80% rename from frappe/website/doctype/page_view/page_view.js rename to frappe/website/doctype/web_page_view/web_page_view.js index 3d6bf92429..77a047e408 100644 --- a/frappe/website/doctype/page_view/page_view.js +++ b/frappe/website/doctype/web_page_view/web_page_view.js @@ -1,7 +1,7 @@ // Copyright (c) 2020, Frappe Technologies and contributors // For license information, please see license.txt -frappe.ui.form.on('Page View', { +frappe.ui.form.on('Web Page View', { // refresh: function(frm) { // } diff --git a/frappe/website/doctype/page_view/page_view.json b/frappe/website/doctype/web_page_view/web_page_view.json similarity index 98% rename from frappe/website/doctype/page_view/page_view.json rename to frappe/website/doctype/web_page_view/web_page_view.json index a79472154f..7a1a210d62 100644 --- a/frappe/website/doctype/page_view/page_view.json +++ b/frappe/website/doctype/web_page_view/web_page_view.json @@ -50,7 +50,7 @@ "modified": "2020-04-15 23:31:27.517793", "modified_by": "Administrator", "module": "Website", - "name": "Page View", + "name": "Web Page View", "owner": "Administrator", "permissions": [ { diff --git a/frappe/website/doctype/page_view/page_view.py b/frappe/website/doctype/web_page_view/web_page_view.py similarity index 85% rename from frappe/website/doctype/page_view/page_view.py rename to frappe/website/doctype/web_page_view/web_page_view.py index 9c41e1c4aa..d09ee8ecb3 100644 --- a/frappe/website/doctype/page_view/page_view.py +++ b/frappe/website/doctype/web_page_view/web_page_view.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document -class PageView(Document): +class WebPageView(Document): pass @@ -16,7 +16,7 @@ def make_view_log(path, referrer=None, browser=None, version=None): path = path[1:] if is_tracking_enabled(): - view = frappe.new_doc("Page View") + view = frappe.new_doc("Web Page View") view.path = path view.referrer = referrer view.browser = browser @@ -28,7 +28,7 @@ def make_view_log(path, referrer=None, browser=None, version=None): @frappe.whitelist() def get_page_view_count(path): - return frappe.db.count("Page View", filters={'path': path}) + return frappe.db.count("Web Page View", filters={'path': path}) def is_tracking_enabled(): return frappe.db.get_value("Website Settings", "Website Settings", "enable_view_tracking") \ No newline at end of file diff --git a/frappe/website/website_theme/standard/standard.json b/frappe/website/website_theme/standard/standard.json index a719031c40..138168a648 100644 --- a/frappe/website/website_theme/standard/standard.json +++ b/frappe/website/website_theme/standard/standard.json @@ -5,12 +5,12 @@ "doctype": "Website Theme", "font_properties": "300,600", "idx": 26, - "modified": "2020-04-16 11:23:03.707474", + "modified": "2020-04-16 18:23:34.573404", "modified_by": "Administrator", "module": "Website", "name": "Standard", "owner": "Administrator", "theme": "Standard", "theme_scss": "\n\n\n\n\n\n\n$enable-shadows: false;\n$enable-gradients: false;\n$enable-rounded: false;\n\n@import \"frappe/public/scss/website\";\n\nbody {\n \n}", - "theme_url": "/assets/css/standard_25d87270.css" + "theme_url": "/assets/css/standard_a9402c50.css" } \ No newline at end of file diff --git a/frappe/www/website_script.js b/frappe/www/website_script.js index 9f0dd329c6..38f11a18a7 100644 --- a/frappe/www/website_script.js +++ b/frappe/www/website_script.js @@ -17,7 +17,7 @@ ga('send', 'pageview'); if (navigator.doNotTrack != 1) { frappe.ready(() => { let browser = frappe.utils.get_browser(); - frappe.call("frappe.website.doctype.page_view.page_view.make_view_log", { + frappe.call("frappe.website.doctype.web_page_view.web_page_view.make_view_log", { path: location.pathname, referrer: document.referrer, browser: browser.name, From 0bc153704ccd6ae69f11bd67748391fe6aeb2a99 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 16 Apr 2020 18:30:20 +0530 Subject: [PATCH 045/337] style: linting & formatting fixes --- .../js/frappe/form/sidebar/form_sidebar.js | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/frappe/public/js/frappe/form/sidebar/form_sidebar.js b/frappe/public/js/frappe/form/sidebar/form_sidebar.js index 9910227590..3da6bedb8c 100644 --- a/frappe/public/js/frappe/form/sidebar/form_sidebar.js +++ b/frappe/public/js/frappe/form/sidebar/form_sidebar.js @@ -69,7 +69,7 @@ frappe.ui.form.Sidebar = Class.extend({ }, refresh: function() { - if(this.frm.doc.__islocal) { + if (this.frm.doc.__islocal) { this.sidebar.toggle(false); } else { this.sidebar.toggle(true); @@ -84,18 +84,31 @@ frappe.ui.form.Sidebar = Class.extend({ if (this.frm.doc.route && frappe.boot.website_tracking_enabled) { let route = this.frm.doc.route; - frappe.utils.get_page_view_count(route).then(res => { - this.sidebar.find(".pageview-count").html(__("{0} Page Views", - ["" + res.message + ""])); + frappe.utils.get_page_view_count(route).then((res) => { + this.sidebar + .find(".pageview-count") + .html( + __("{0} Page Views", [String(res.message).bold()]) + ); }); } - this.sidebar.find(".modified-by").html(__("{0} edited this {1}", - ["" + frappe.user.full_name(this.frm.doc.modified_by) + "", - "
" + comment_when(this.frm.doc.modified)])); - this.sidebar.find(".created-by").html(__("{0} created this {1}", - ["" + frappe.user.full_name(this.frm.doc.owner) + "", - "
" + comment_when(this.frm.doc.creation)])); + this.sidebar + .find(".modified-by") + .html( + __("{0} edited this {1}", [ + frappe.user.full_name(this.frm.doc.modified_by).bold(), + "
" + comment_when(this.frm.doc.modified), + ]) + ); + this.sidebar + .find(".created-by") + .html( + __("{0} created this {1}", [ + frappe.user.full_name(this.frm.doc.owner).bold(), + "
" + comment_when(this.frm.doc.creation), + ]) + ); this.refresh_like(); frappe.ui.form.set_user_image(this.frm); From ede263ca8c357ebba4718775ed2609b4c58dfae0 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Thu, 16 Apr 2020 19:06:45 +0530 Subject: [PATCH 046/337] chore(linter): Skip only first argument --- .github/frappe_linter/translation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/frappe_linter/translation.py b/.github/frappe_linter/translation.py index 63c4821775..bb81e848f1 100644 --- a/.github/frappe_linter/translation.py +++ b/.github/frappe_linter/translation.py @@ -5,8 +5,8 @@ errors_encounter = 0 pattern = re.compile(r"_\(([\"']{,3})(?P((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P((?!\5).)*)\5)*(\s*,\s*(.)*?\s*(,\s*([\"'])(?P((?!\11).)*)\11)*)*\)") start_pattern = re.compile(r"_{1,2}\([\"']{1,3}") -# skip first 2 arguments -files = sys.argv[2:] +# skip first argument +files = sys.argv[1:] for _file in files: if not _file.endswith(('.py', '.js')): continue From 7e0e2da3bdac0cf839030e469063a6c2a4275ffb Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 16 Apr 2020 19:17:58 +0530 Subject: [PATCH 047/337] fix: render template if items exists --- frappe/templates/includes/navbar/navbar_items.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/templates/includes/navbar/navbar_items.html b/frappe/templates/includes/navbar/navbar_items.html index 4e5ad0dd0a..352fc23bbd 100644 --- a/frappe/templates/includes/navbar/navbar_items.html +++ b/frappe/templates/includes/navbar/navbar_items.html @@ -65,7 +65,7 @@ {% block navbar_right_extension %}{% endblock %} {% endif %} - {% if show_sidebar %} + {% if show_sidebar and sidebar_items %}

{% for item in sidebar_items -%} @@ -84,4 +84,4 @@ {% include "templates/includes/navbar/navbar_search.html" %} {% include "templates/includes/navbar/navbar_login.html" %} - \ No newline at end of file + From d20de0495f5e6f55be57afd473a72028cb75467b Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 16 Apr 2020 20:21:23 +0530 Subject: [PATCH 048/337] fix: Remove focus outline from summary tag --- frappe/public/less/common.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/public/less/common.less b/frappe/public/less/common.less index b0aca5cb47..c2de48d553 100644 --- a/frappe/public/less/common.less +++ b/frappe/public/less/common.less @@ -32,6 +32,10 @@ details > summary { cursor: pointer; } +details > summary:focus { + outline: none; +} + .text-color { color: @text-color !important; } From c440f13bd1df0bd0b31dffc2f3c7239577b4a643 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 16 Apr 2020 20:26:01 +0530 Subject: [PATCH 049/337] feat: Add checkbox option to text editor's toolbar --- frappe/public/js/frappe/form/controls/text_editor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/controls/text_editor.js b/frappe/public/js/frappe/form/controls/text_editor.js index b35c92c1ae..4e18b081cc 100644 --- a/frappe/public/js/frappe/form/controls/text_editor.js +++ b/frappe/public/js/frappe/form/controls/text_editor.js @@ -147,7 +147,7 @@ frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({ [{ 'color': [] }, { 'background': [] }], ['blockquote', 'code-block'], ['link', 'image'], - [{ 'list': 'ordered' }, { 'list': 'bullet' }], + [{ 'list': 'ordered' }, { 'list': 'bullet' }, { 'list': 'check' }], [{ 'align': [] }], [{ 'indent': '-1'}, { 'indent': '+1' }], [{'table': [ From 87f6c17dc887b27abe679d8b788635dbeac9d1e9 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 16 Apr 2020 20:31:01 +0530 Subject: [PATCH 050/337] Revert "feat: Add checkbox option to text editor's toolbar" This reverts commit c440f13bd1df0bd0b31dffc2f3c7239577b4a643. --- frappe/public/js/frappe/form/controls/text_editor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/controls/text_editor.js b/frappe/public/js/frappe/form/controls/text_editor.js index 4e18b081cc..b35c92c1ae 100644 --- a/frappe/public/js/frappe/form/controls/text_editor.js +++ b/frappe/public/js/frappe/form/controls/text_editor.js @@ -147,7 +147,7 @@ frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({ [{ 'color': [] }, { 'background': [] }], ['blockquote', 'code-block'], ['link', 'image'], - [{ 'list': 'ordered' }, { 'list': 'bullet' }, { 'list': 'check' }], + [{ 'list': 'ordered' }, { 'list': 'bullet' }], [{ 'align': [] }], [{ 'indent': '-1'}, { 'indent': '+1' }], [{'table': [ From 32330f46681fca749f3eca4026276a397df31950 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 16 Apr 2020 20:26:01 +0530 Subject: [PATCH 051/337] feat: Add checkbox option to text editor's toolbar --- frappe/public/js/frappe/form/controls/text_editor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/controls/text_editor.js b/frappe/public/js/frappe/form/controls/text_editor.js index b35c92c1ae..4e18b081cc 100644 --- a/frappe/public/js/frappe/form/controls/text_editor.js +++ b/frappe/public/js/frappe/form/controls/text_editor.js @@ -147,7 +147,7 @@ frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({ [{ 'color': [] }, { 'background': [] }], ['blockquote', 'code-block'], ['link', 'image'], - [{ 'list': 'ordered' }, { 'list': 'bullet' }], + [{ 'list': 'ordered' }, { 'list': 'bullet' }, { 'list': 'check' }], [{ 'align': [] }], [{ 'indent': '-1'}, { 'indent': '+1' }], [{'table': [ From 051fa9b2cc08c1a8132504921f17ea49915cf6ee Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 16 Apr 2020 20:54:20 +0530 Subject: [PATCH 052/337] fix: Check is_dirty only if action is save --- frappe/public/js/frappe/form/save.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js index d40b3ed341..7efbbe2d3d 100644 --- a/frappe/public/js/frappe/form/save.js +++ b/frappe/public/js/frappe/form/save.js @@ -21,7 +21,7 @@ frappe.ui.form.save = function (frm, action, callback, btn) { remove_empty_rows(); $(frm.wrapper).addClass('validated-form'); - if (frm.is_dirty() && check_mandatory()) { + if ((action !== 'Save' || frm.is_dirty()) && check_mandatory()) { _call({ method: "frappe.desk.form.save.savedocs", args: { doc: frm.doc, action: action }, From 0cdcfdcd9ed7cae9c3af17c86ae1ca47c170ff93 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 16 Apr 2020 22:13:51 +0530 Subject: [PATCH 053/337] fix: User should not be able to create a language --- frappe/core/doctype/language/language.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/language/language.json b/frappe/core/doctype/language/language.json index 099b383980..eed29883c1 100644 --- a/frappe/core/doctype/language/language.json +++ b/frappe/core/doctype/language/language.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_rename": 1, "autoname": "field:language_code", "creation": "2014-08-22 16:12:17.249590", @@ -41,7 +42,9 @@ } ], "icon": "fa fa-globe", - "modified": "2019-07-19 16:32:12.652550", + "in_create": 1, + "links": [], + "modified": "2020-04-16 22:11:33.066852", "modified_by": "Administrator", "module": "Core", "name": "Language", From 389034aea8d4eb359acf6e13fa3b7236b774a1b0 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 17 Apr 2020 10:44:28 +0530 Subject: [PATCH 054/337] test: Increase defaultCommandTimeout to 10sec --- cypress.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cypress.json b/cypress.json index 7d853271b9..ae0c45c3ae 100644 --- a/cypress.json +++ b/cypress.json @@ -1,5 +1,7 @@ { "baseUrl": "http://test_site_ui:8000", "projectId": "92odwv", - "adminPassword": "admin" + "adminPassword": "admin", + "defaultCommandTimeout": 10000, + "pageLoadTimeout": 15000 } From 9a3998c43c156b1ec480c694e4e7b2e80d6a261e Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 17 Apr 2020 11:17:31 +0530 Subject: [PATCH 055/337] fix(patch): reload doctype before running the patch --- frappe/patches/v13_0/website_theme_custom_scss.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/patches/v13_0/website_theme_custom_scss.py b/frappe/patches/v13_0/website_theme_custom_scss.py index 72c98bfc88..9d5a78643e 100644 --- a/frappe/patches/v13_0/website_theme_custom_scss.py +++ b/frappe/patches/v13_0/website_theme_custom_scss.py @@ -1,6 +1,7 @@ import frappe def execute(): + frappe.reload_doctype('Website Theme') for theme in frappe.get_all('Website Theme'): doc = frappe.get_doc('Website Theme', theme.name) if not doc.custom_scss and doc.theme_scss: From d93a04aa1691718e7dbf3c2b52aa51f5b7d23377 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 17 Apr 2020 11:48:59 +0530 Subject: [PATCH 056/337] fix: linting fixes --- frappe/public/js/frappe/desk.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 902f382879..b5046d4b12 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -87,7 +87,7 @@ frappe.Application = Class.extend({ } if (!frappe.boot.developer_mode) { - let console_security_message = __("Using this console may allow attackers to impersonate you and steal your information. Do not enter or paste code that you do not understand.") + let console_security_message = __("Using this console may allow attackers to impersonate you and steal your information. Do not enter or paste code that you do not understand."); console.log( `%c${console_security_message}`, "font-size: large" From 3db595f06d61a6e4e7e206c8725e4beb8c823c25 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Fri, 17 Apr 2020 12:15:49 +0530 Subject: [PATCH 057/337] Delete test_video.js --- frappe/core/doctype/video/test_video.js | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 frappe/core/doctype/video/test_video.js diff --git a/frappe/core/doctype/video/test_video.js b/frappe/core/doctype/video/test_video.js deleted file mode 100644 index a82a221319..0000000000 --- a/frappe/core/doctype/video/test_video.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Video", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Video - () => frappe.tests.make('Video', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); From 7e0e6630bd66b1b29db886e597a82475ba581a16 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 17 Apr 2020 13:00:48 +0530 Subject: [PATCH 058/337] feat: add user timezone and unique visit check --- .../website/doctype/web_page_view/web_page_view.py | 14 ++++++++++++-- frappe/www/website_script.js | 6 ++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/frappe/website/doctype/web_page_view/web_page_view.py b/frappe/website/doctype/web_page_view/web_page_view.py index d09ee8ecb3..ba2d707e44 100644 --- a/frappe/website/doctype/web_page_view/web_page_view.py +++ b/frappe/website/doctype/web_page_view/web_page_view.py @@ -11,7 +11,15 @@ class WebPageView(Document): @frappe.whitelist(allow_guest=True) -def make_view_log(path, referrer=None, browser=None, version=None): +def make_view_log(path, referrer=None, browser=None, version=None, url=None, user_tz=None): + from pprint import pprint + request_dict = frappe.request.__dict__ + user_agent = request_dict.get('environ', {}).get('HTTP_USER_AGENT') + + is_unique = True + if referrer.startswith(url): + is_unique = False + if path.startswith('/'): path = path[1:] @@ -21,7 +29,9 @@ def make_view_log(path, referrer=None, browser=None, version=None): view.referrer = referrer view.browser = browser view.browser_version = version - view.date = frappe.utils.now_datetime() + view.time_zone = user_tz + view.user_agent = user_agent + view.is_unique = is_unique view.insert(ignore_permissions=True) return diff --git a/frappe/www/website_script.js b/frappe/www/website_script.js index 38f11a18a7..7fdc2e94d6 100644 --- a/frappe/www/website_script.js +++ b/frappe/www/website_script.js @@ -21,8 +21,10 @@ ga('send', 'pageview'); path: location.pathname, referrer: document.referrer, browser: browser.name, - version: browser.version + version: browser.version, + url: location.origin, + user_tz: Intl.DateTimeFormat().resolvedOptions().timeZone }) }) } -{% endif %} +{% endif %} \ No newline at end of file From 37bae8cfac8bfca9c501c45ac9512ebe85c91f57 Mon Sep 17 00:00:00 2001 From: gavin Date: Fri, 17 Apr 2020 13:01:14 +0530 Subject: [PATCH 059/337] style: install_db re-arrange parameters Co-Authored-By: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/commands/site.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/commands/site.py b/frappe/commands/site.py index 540f250918..9dcf97005a 100755 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -81,9 +81,9 @@ def _new_site(db_name, site, mariadb_root_username=None, mariadb_root_password=N installing = touch_file(get_site_path('locks', 'installing.lock')) atexit.register(_new_site_cleanup, site, mariadb_root_username, mariadb_root_password) - install_db(root_login=mariadb_root_username, root_password=mariadb_root_password, - db_name=db_name, admin_password=admin_password, verbose=verbose, - source_sql=source_sql, force=force, reinstall=reinstall, db_password=db_password, db_type=db_type, db_host=db_host, db_port=db_port, no_mariadb_socket=no_mariadb_socket) + install_db(root_login=mariadb_root_username, root_password=mariadb_root_password, db_name=db_name, + admin_password=admin_password, verbose=verbose, source_sql=source_sql, force=force, reinstall=reinstall, + db_password=db_password, db_type=db_type, db_host=db_host, db_port=db_port, no_mariadb_socket=no_mariadb_socket) apps_to_install = ['frappe'] + (frappe.conf.get("install_apps") or []) + (list(install_apps) or []) for app in apps_to_install: _install_app(app, verbose=verbose, set_as_patched=not source_sql) From 938684759ba4925cd7a32f4bf7a850e80dd7f4a6 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 17 Apr 2020 13:35:17 +0530 Subject: [PATCH 060/337] fix: update Roles in Video DocType --- frappe/core/doctype/video/video.json | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/frappe/core/doctype/video/video.json b/frappe/core/doctype/video/video.json index 875926088b..b0a154f5d4 100644 --- a/frappe/core/doctype/video/video.json +++ b/frappe/core/doctype/video/video.json @@ -68,7 +68,7 @@ } ], "links": [], - "modified": "2020-04-10 23:04:41.124053", + "modified": "2020-04-17 13:34:06.461574", "modified_by": "Administrator", "module": "Core", "name": "Video", @@ -82,30 +82,9 @@ "print": 1, "read": 1, "report": 1, - "role": "Academics User", + "role": "All", "share": 1, "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Instructor", - "share": 1, - "write": 1 - }, - { - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "LMS User", - "share": 1 } ], "sort_field": "modified", From 00cb903462fc53c3eb415ed83e7d0902eea24056 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 17 Apr 2020 15:47:56 +0530 Subject: [PATCH 061/337] feat: remove unused import --- frappe/website/doctype/web_page_view/web_page_view.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/website/doctype/web_page_view/web_page_view.py b/frappe/website/doctype/web_page_view/web_page_view.py index ba2d707e44..08625f9d6f 100644 --- a/frappe/website/doctype/web_page_view/web_page_view.py +++ b/frappe/website/doctype/web_page_view/web_page_view.py @@ -12,7 +12,6 @@ class WebPageView(Document): @frappe.whitelist(allow_guest=True) def make_view_log(path, referrer=None, browser=None, version=None, url=None, user_tz=None): - from pprint import pprint request_dict = frappe.request.__dict__ user_agent = request_dict.get('environ', {}).get('HTTP_USER_AGENT') From a877fba5d938876e1484c9a63ab53f003adc50e5 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 17 Apr 2020 17:44:41 +0530 Subject: [PATCH 062/337] test: set_invalid controller for child tables --- cypress/integration/form.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cypress/integration/form.js b/cypress/integration/form.js index 9d1210ca2b..a622a66e13 100644 --- a/cypress/integration/form.js +++ b/cypress/integration/form.js @@ -41,4 +41,21 @@ context('Form', () => { list_view.filter_area.filter_list.clear_filters(); }); }); + it('validates behaviour of Data options validations in child table', () => { + // test email validations for set_invalid controller + let website_input = 'website.in'; + let expectBackgroundColor = 'rgb(255, 220, 220)'; + + cy.visit('/desk#Form/Contact/New Contact 1'); + cy.get('.frappe-control[data-fieldname="email_ids"]').as('table'); + cy.get('@table').find('button.grid-add-row').click(); + cy.get('.grid-body .rows [data-fieldname="email_id"]').click(); + cy.get('@table').find('input.input-with-feedback.form-control').as('email_input'); + cy.get('@email_input').type(website_input, { waitForAnimations: false }); + cy.fill_field('company_name', 'Test Company'); + cy.get('@email_input').should($div => { + const style = window.getComputedStyle($div[0]); + expect(style.backgroundColor).to.equal(expectBackgroundColor); + }); + }); }); From 06ccba048fa25b22f4e89325852dd04f4945310c Mon Sep 17 00:00:00 2001 From: gavin Date: Fri, 17 Apr 2020 17:53:59 +0530 Subject: [PATCH 063/337] style: block style fixes (via slider) --- frappe/public/js/frappe/form/grid_row.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 65c11b43bf..0b8e7e8d23 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -270,8 +270,7 @@ export default class GridRow { } if (column.is_invalid) { column.addClass('invalid'); - } - else if ((df.reqd || df.bold)) { + } else if (df.reqd || df.bold) { column.addClass('bold'); } } From 8f21ca4997ddb7323ce2f774662fff27897b1441 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sat, 18 Apr 2020 13:47:06 +0530 Subject: [PATCH 064/337] fix: Ability to resize expand/collapse code field --- frappe/public/js/frappe/form/controls/code.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/frappe/public/js/frappe/form/controls/code.js b/frappe/public/js/frappe/form/controls/code.js index 0648ad6e22..bdf36b706a 100644 --- a/frappe/public/js/frappe/form/controls/code.js +++ b/frappe/public/js/frappe/form/controls/code.js @@ -9,6 +9,12 @@ frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({ this.ace_editor_target = $('
') .appendTo(this.input_area); + this.expanded = false; + this.$expand_button = $(``).click(() => { + this.expanded = !this.expanded; + this.refresh_height(); + }).insertAfter(this.ace_editor_target); + // styling this.ace_editor_target.addClass('border rounded'); this.ace_editor_target.css('height', 300); @@ -26,6 +32,11 @@ frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({ }, 300)); }, + refresh_height() { + this.ace_editor_target.css('height', this.expanded ? 600 : 300); + this.editor.resize(); + }, + set_language() { const language_map = { 'Javascript': 'ace/mode/javascript', From 2431767fdf77815606a00f24836c04e4cfb8ce1c Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sat, 18 Apr 2020 15:02:56 +0530 Subject: [PATCH 065/337] feat: Page Builder based on Tailwind - Website Theme based on: Bootstrap 4, Tailwind - Web Page: Page Builder - Web Template: Create templates for web pages - Added some common Web Templates - Components: Standard components directly usable in Jinja - Purge Tailwind Classes in Production - PostCSS Config to support tailwind --- frappe/__init__.py | 1 + frappe/public/build.json | 3 + frappe/public/css/email.css | 47 +++ frappe/public/tailwind.css | 8 + frappe/templates/base.html | 21 +- frappe/templates/components/button.html | 27 ++ frappe/templates/components/card.html | 7 + frappe/templates/components/dropdown.html | 25 ++ frappe/templates/components/navbar.html | 20 ++ frappe/templates/components/navbar_link.html | 3 + .../components/web_page_section.html | 5 + frappe/templates/includes/navbar/navbar.html | 9 +- frappe/templates/web.html | 4 +- frappe/utils/data.py | 3 + frappe/utils/jinja.py | 77 +++- frappe/website/context.py | 2 + .../doctype/web_page/templates/web_page.html | 6 + frappe/website/doctype/web_page/web_page.js | 34 +- frappe/website/doctype/web_page/web_page.json | 14 +- frappe/website/doctype/web_page/web_page.py | 19 + .../doctype/web_page_block/__init__.py | 0 .../web_page_block/web_page_block.json | 51 +++ .../doctype/web_page_block/web_page_block.py | 10 + .../website/doctype/web_template/__init__.py | 0 .../doctype/web_template/test_web_template.py | 10 + .../doctype/web_template/web_template.js | 8 + .../doctype/web_template/web_template.json | 61 ++++ .../doctype/web_template/web_template.py | 45 +++ .../doctype/web_template_field/__init__.py | 0 .../test_web_template_field.py | 10 + .../web_template_field/web_template_field.js | 8 + .../web_template_field.json | 47 +++ .../web_template_field/web_template_field.py | 10 + .../doctype/website_theme/website_theme.json | 70 +++- frappe/website/render.py | 27 ++ frappe/website/web_template/__init__.py | 0 .../web_template/full_width_image/__init__.py | 0 .../full_width_image/full_width_image.html | 1 + .../full_width_image/full_width_image.json | 24 ++ .../hero_with_right_image/__init__.py | 0 .../hero_with_right_image.html | 39 ++ .../hero_with_right_image.json | 49 +++ .../section_with_big_cards/__init__.py | 0 .../section_with_big_cards.html | 19 + .../section_with_big_cards.json | 58 +++ .../web_template/section_with_cta/__init__.py | 0 .../section_with_cta/section_with_cta.html | 7 + .../section_with_cta/section_with_cta.json | 33 ++ .../section_with_image/__init__.py | 0 .../section_with_image.html | 4 + .../section_with_image.json | 33 ++ .../section_with_left_image/__init__.py | 0 .../section_with_left_image.html | 9 + .../section_with_left_image.json | 29 ++ .../section_with_small_cards/__init__.py | 0 .../section_with_small_cards.html | 19 + .../section_with_small_cards.json | 103 ++++++ .../section_with_tabs/__init__.py | 0 .../section_with_tabs/section_with_tabs.html | 109 ++++++ .../section_with_tabs/section_with_tabs.json | 83 +++++ .../website_theme/standard/standard.json | 36 +- package.json | 12 +- purgecss.js | 23 ++ requirements.txt | 2 +- rollup/config.js | 28 +- tailwind.config.js | 75 ++++ yarn.lock | 334 +++++++++++++++++- 67 files changed, 1753 insertions(+), 68 deletions(-) create mode 100644 frappe/public/tailwind.css create mode 100644 frappe/templates/components/button.html create mode 100644 frappe/templates/components/card.html create mode 100644 frappe/templates/components/dropdown.html create mode 100644 frappe/templates/components/navbar.html create mode 100644 frappe/templates/components/navbar_link.html create mode 100644 frappe/templates/components/web_page_section.html create mode 100644 frappe/website/doctype/web_page_block/__init__.py create mode 100644 frappe/website/doctype/web_page_block/web_page_block.json create mode 100644 frappe/website/doctype/web_page_block/web_page_block.py create mode 100644 frappe/website/doctype/web_template/__init__.py create mode 100644 frappe/website/doctype/web_template/test_web_template.py create mode 100644 frappe/website/doctype/web_template/web_template.js create mode 100644 frappe/website/doctype/web_template/web_template.json create mode 100644 frappe/website/doctype/web_template/web_template.py create mode 100644 frappe/website/doctype/web_template_field/__init__.py create mode 100644 frappe/website/doctype/web_template_field/test_web_template_field.py create mode 100644 frappe/website/doctype/web_template_field/web_template_field.js create mode 100644 frappe/website/doctype/web_template_field/web_template_field.json create mode 100644 frappe/website/doctype/web_template_field/web_template_field.py create mode 100644 frappe/website/web_template/__init__.py create mode 100644 frappe/website/web_template/full_width_image/__init__.py create mode 100644 frappe/website/web_template/full_width_image/full_width_image.html create mode 100644 frappe/website/web_template/full_width_image/full_width_image.json create mode 100644 frappe/website/web_template/hero_with_right_image/__init__.py create mode 100644 frappe/website/web_template/hero_with_right_image/hero_with_right_image.html create mode 100644 frappe/website/web_template/hero_with_right_image/hero_with_right_image.json create mode 100644 frappe/website/web_template/section_with_big_cards/__init__.py create mode 100644 frappe/website/web_template/section_with_big_cards/section_with_big_cards.html create mode 100644 frappe/website/web_template/section_with_big_cards/section_with_big_cards.json create mode 100644 frappe/website/web_template/section_with_cta/__init__.py create mode 100644 frappe/website/web_template/section_with_cta/section_with_cta.html create mode 100644 frappe/website/web_template/section_with_cta/section_with_cta.json create mode 100644 frappe/website/web_template/section_with_image/__init__.py create mode 100644 frappe/website/web_template/section_with_image/section_with_image.html create mode 100644 frappe/website/web_template/section_with_image/section_with_image.json create mode 100644 frappe/website/web_template/section_with_left_image/__init__.py create mode 100644 frappe/website/web_template/section_with_left_image/section_with_left_image.html create mode 100644 frappe/website/web_template/section_with_left_image/section_with_left_image.json create mode 100644 frappe/website/web_template/section_with_small_cards/__init__.py create mode 100644 frappe/website/web_template/section_with_small_cards/section_with_small_cards.html create mode 100644 frappe/website/web_template/section_with_small_cards/section_with_small_cards.json create mode 100644 frappe/website/web_template/section_with_tabs/__init__.py create mode 100644 frappe/website/web_template/section_with_tabs/section_with_tabs.html create mode 100644 frappe/website/web_template/section_with_tabs/section_with_tabs.json create mode 100644 purgecss.js create mode 100644 tailwind.config.js diff --git a/frappe/__init__.py b/frappe/__init__.py index e19327fcff..97ac94afc6 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -830,6 +830,7 @@ def rename_doc(*args, **kwargs): Calls `frappe.model.rename_doc.rename_doc` """ + kwargs.pop('cmd', None) kwargs.pop('ignore_permissions', None) from frappe.model.rename_doc import rename_doc return rename_doc(*args, **kwargs) diff --git a/frappe/public/build.json b/frappe/public/build.json index 75a89e5010..88f5aac577 100755 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -350,5 +350,8 @@ ], "js/data_import_tools.min.js": [ "public/js/frappe/data_import/index.js" + ], + "css/tailwind.css": [ + "public/tailwind.css" ] } diff --git a/frappe/public/css/email.css b/frappe/public/css/email.css index 40c6149927..92ac433fd2 100644 --- a/frappe/public/css/email.css +++ b/frappe/public/css/email.css @@ -1,64 +1,82 @@ /* csslint ignore:start */ + /* palette colors*/ + body { line-height: 1.5; color: #36414c; } + p { margin: 1em 0 !important; } + hr { border-top: 1px solid #d1d8dd; } + .body-table { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; } + .body-table td { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; } + .email-header, .email-body, .email-footer { width: 100% !important; min-width: 100% !important; } + .email-body { font-size: 14px; } + .email-footer { border-top: 1px solid #d1d8dd; font-size: 12px; } + .email-header { border: 1px solid #d1d8dd; border-radius: 4px 4px 0 0; } + .email-header .brand-image { width: 24px; height: 24px; display: block; } + .email-header-title { font-weight: bold; } + .body-table.has-header .email-body { border: 1px solid #d1d8dd; border-radius: 0 0 4px 4px; border-top: none; } + .body-table.has-header .email-footer { border-top: none; } + .email-footer-container { margin-top: 30px; } + .email-footer-container > div:not(:last-child) { margin-bottom: 5px; } + .email-unsubscribe a { color: #8d99a6; text-decoration: underline; } + .btn { text-decoration: none; padding: 7px 10px; @@ -66,20 +84,24 @@ hr { border: 1px solid; border-radius: 3px; } + .btn.btn-default { color: #fff; background-color: #f0f4f7; border-color: transparent; } + .btn.btn-primary { color: #fff; background-color: #5e64ff; border-color: #444bff; } + .table { width: 100%; border-collapse: collapse; } + .table td, .table th { padding: 8px; @@ -88,53 +110,68 @@ hr { border-top: 1px solid #d1d8dd; text-align: left; } + .table th { font-weight: bold; } + .table > thead > tr > th { vertical-align: middle; border-bottom: 2px solid #d1d8dd; } + .table > thead:first-child > tr:first-child > th { border-top: none; } + .table.table-bordered { border: 1px solid #d1d8dd; } + .table.table-bordered td, .table.table-bordered th { border: 1px solid #d1d8dd; } + .more-info { font-size: 80% !important; color: #8d99a6 !important; border-top: 1px solid #ebeff2; padding-top: 10px; } + .text-right { text-align: right !important; } + .text-center { text-align: center !important; } + .text-muted { color: #8d99a6 !important; } + .text-extra-muted { color: #d1d8dd !important; } + .text-regular { font-size: 14px; } + .text-medium { font-size: 12px; } + .text-small { font-size: 10px; } + .text-bold { font-weight: bold; } + .indicator { width: 8px; height: 8px; @@ -143,33 +180,43 @@ hr { display: inline-block; margin-right: 5px; } + .indicator.indicator-blue { background-color: #5e64ff; } + .indicator.indicator-green { background-color: #98d85b; } + .indicator.indicator-orange { background-color: #ffa00a; } + .indicator.indicator-red { background-color: #ff5858; } + .indicator.indicator-yellow { background-color: #feef72; } + .screenshot { box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.1); border: 1px solid #d1d8dd; margin: 8px 0; max-width: 100%; } + .list-unstyled { list-style-type: none; padding: 0; } + /* auto email report */ + .report-title { margin-bottom: 20px; } + /* csslint ignore:end */ diff --git a/frappe/public/tailwind.css b/frappe/public/tailwind.css new file mode 100644 index 0000000000..98a3b3a032 --- /dev/null +++ b/frappe/public/tailwind.css @@ -0,0 +1,8 @@ +@tailwind base; +@tailwind components; + +details.dropdown summary::-webkit-details-marker { + display: none; +} + +@tailwind utilities; diff --git a/frappe/templates/base.html b/frappe/templates/base.html index 1c5f286442..3a1cfc0da3 100644 --- a/frappe/templates/base.html +++ b/frappe/templates/base.html @@ -25,15 +25,26 @@ {{ head_html or "" }} {%- endif %} - {% if theme.theme_url %} - + {%- if theme.based_on == 'Bootstrap 4' -%} + {%- if theme.theme_url -%} + + {%- else -%} + + {%- endif -%} {% else %} - + {%- if developer_mode -%} + + + {%- else -%} + + + + {% endif %} + {%- endif -%} {%- for link in web_include_css %} {%- endfor -%} - {% endif %} {%- endblock -%} {%- block head_include %} @@ -50,7 +61,7 @@ } window.dev_server = {{ dev_server }}; window.socketio_port = {{ frappe.socketio_port }}; - + {%- block banner -%} diff --git a/frappe/templates/components/button.html b/frappe/templates/components/button.html new file mode 100644 index 0000000000..985e80315c --- /dev/null +++ b/frappe/templates/components/button.html @@ -0,0 +1,27 @@ +--- +name: "button" +variant: "secondary" +disabled: 0 +url: null +--- + +{%- set static_classes = "px-4 py-2 border rounded-md inline-flex justify-center items-center focus:outline-none leading-5 font-medium text-sm" -%} +{%- set dynamic_classes = { + "opacity-50 cursor-not-allowed pointer-events-none": disabled, + "bg-blue-500 border-transparent hover:bg-blue-400 text-white focus:shadow-outline-blue focus:border-blue-700": + variant == "primary", + "bg-white border-gray-400 hover:text-gray-800 text-gray-900 focus:shadow-outline-blue focus:border-blue-300": + variant == "secondary", + "bg-red-500 border-transparent hover:bg-red-400 text-white focus:shadow-outline-red focus:border-red-700": + variant == "danger" + } +-%} +{%- set html_tag = "a" if url else "button" -%} + +<{{html_tag}} + class="{{ resolve_class([static_classes, dynamic_classes, class]) }}" + {{ 'disabled' if disabled else '' }} + {{ ('href="' + url + '"') if url else '' }} +> + {{ label }} + diff --git a/frappe/templates/components/card.html b/frappe/templates/components/card.html new file mode 100644 index 0000000000..0ba34dd5f1 --- /dev/null +++ b/frappe/templates/components/card.html @@ -0,0 +1,7 @@ +
+

{{ title }}

+

{{ subtitle }}

+
+ {{ actions }} +
+
diff --git a/frappe/templates/components/dropdown.html b/frappe/templates/components/dropdown.html new file mode 100644 index 0000000000..4c197eaa03 --- /dev/null +++ b/frappe/templates/components/dropdown.html @@ -0,0 +1,25 @@ + diff --git a/frappe/templates/components/navbar.html b/frappe/templates/components/navbar.html new file mode 100644 index 0000000000..db167923c4 --- /dev/null +++ b/frappe/templates/components/navbar.html @@ -0,0 +1,20 @@ +
+
+ + +
+ {%- for item in theme.navbar_items -%} + {{ c('navbar_link', label=item.label, url=item.url) }} + {%- endfor -%} + {{ c('navbar_link', label=_('Login'), url='/login') }} + {%- if theme.primary_action_label and theme.primary_action_url -%} + {{ c('button', label=theme.primary_action_label, url=theme.primary_action_url, variant="primary") }} + {%- endif -%} +
+
+
diff --git a/frappe/templates/components/navbar_link.html b/frappe/templates/components/navbar_link.html new file mode 100644 index 0000000000..01d37ad22f --- /dev/null +++ b/frappe/templates/components/navbar_link.html @@ -0,0 +1,3 @@ + + {{ label }} + diff --git a/frappe/templates/components/web_page_section.html b/frappe/templates/components/web_page_section.html new file mode 100644 index 0000000000..b44162a9b5 --- /dev/null +++ b/frappe/templates/components/web_page_section.html @@ -0,0 +1,5 @@ +
+
+ {{ section.rendered_html }} +
+
diff --git a/frappe/templates/includes/navbar/navbar.html b/frappe/templates/includes/navbar/navbar.html index 3037c08c63..220970f754 100644 --- a/frappe/templates/includes/navbar/navbar.html +++ b/frappe/templates/includes/navbar/navbar.html @@ -1,4 +1,5 @@ -
+{%- else -%} +{{ c('navbar', theme=theme) }} +{%- endif -%} diff --git a/frappe/templates/web.html b/frappe/templates/web.html index e61672124a..36a9ab86b5 100644 --- a/frappe/templates/web.html +++ b/frappe/templates/web.html @@ -13,7 +13,7 @@ {% block page_container %} -
+