From e4151fb679ee18c24c125f60636f21129d1d5194 Mon Sep 17 00:00:00 2001 From: prssanna Date: Sun, 7 Jun 2020 14:22:37 +0530 Subject: [PATCH 01/15] fix: disable scroll to top for list view --- frappe/public/js/frappe/form/form.js | 1 - frappe/public/js/frappe/list/list_view.js | 2 ++ frappe/public/js/frappe/utils/utils.js | 5 +++++ frappe/public/js/frappe/views/container.js | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 369e4a56d4..e2aab7e519 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -340,7 +340,6 @@ frappe.ui.form.Form = class FrappeForm { switch_doc(docname) { // record switch if(this.docname != docname && (!this.meta.in_dialog || this.in_form) && !this.meta.istable) { - frappe.utils.scroll_to(0); if (this.print_preview) { this.print_preview.hide(); } diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index c282d43d9b..d6473bdb0c 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -29,6 +29,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { } show() { + this.parent.disable_scroll_to_top = true; + if (!this.has_permissions()) { frappe.set_route(''); frappe.msgprint(__(`Not permitted to view ${this.doctype}`)); diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index f4dde5804f..67a7d00a6d 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -149,6 +149,11 @@ Object.assign(frappe.utils, { } }, + get_scroll_position: function(element, additional_offset) { + let header_offset = $(".navbar").height() + $(".page-head").height(); + let scroll_top = $(element).offset().top - header_offset - cint(additional_offset); + return scroll_top; + }, filter_dict: function(dict, filters) { var ret = []; if(typeof filters=='string') { diff --git a/frappe/public/js/frappe/views/container.js b/frappe/public/js/frappe/views/container.js index 8e67792079..889063e24b 100644 --- a/frappe/public/js/frappe/views/container.js +++ b/frappe/public/js/frappe/views/container.js @@ -84,7 +84,7 @@ frappe.views.Container = Class.extend({ this.page._route = window.location.hash; $(this.page).trigger('show'); - frappe.utils.scroll_to(0); + !this.page.disable_scroll_to_top && frappe.utils.scroll_to(0); frappe.breadcrumbs.update(); return this.page; From b3c1e1fc58a58b53d1e4a2b512a54506d2e36524 Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 9 Jun 2020 16:32:14 +0530 Subject: [PATCH 02/15] fix: remove unused function --- frappe/public/js/frappe/utils/utils.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index 67a7d00a6d..f4dde5804f 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -149,11 +149,6 @@ Object.assign(frappe.utils, { } }, - get_scroll_position: function(element, additional_offset) { - let header_offset = $(".navbar").height() + $(".page-head").height(); - let scroll_top = $(element).offset().top - header_offset - cint(additional_offset); - return scroll_top; - }, filter_dict: function(dict, filters) { var ret = []; if(typeof filters=='string') { From 8c4a85d13f0630953c137afb5e740c45d93f4285 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Fri, 12 Jun 2020 16:33:34 +0530 Subject: [PATCH 03/15] fix: Bypass ORM for removing apps from installed_apps list Scenario: a site (on older version) has a custom app xyz installed. this site is to be restored on a new bench (without xyz) and migrated to recent version. Problem: `bench migrate` fails because xyz is not available. `bench remove-from-installed-apps` might fail, because code is not compatible with the database. --- frappe/installer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/installer.py b/frappe/installer.py index 4fc19b282a..03691e1883 100755 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -113,7 +113,7 @@ def remove_from_installed_apps(app_name): installed_apps = frappe.get_installed_apps() if app_name in installed_apps: installed_apps.remove(app_name) - frappe.db.set_global("installed_apps", json.dumps(installed_apps)) + frappe.db.set_value("DefaultValue", {"defkey": "installed_apps"}, "defvalue", json.dumps(installed_apps)) frappe.db.commit() if frappe.flags.in_install: post_install() From fe22595e854e3fb3fa4dbcbd6d9dacdf94e73462 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 12 Jun 2020 19:21:08 +0530 Subject: [PATCH 04/15] fix: rename web.log generated by frappe to frappe.web.log to avoid conflict with web.log file generated by gunicorn process in production --- frappe/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/app.py b/frappe/app.py index 50d09177d6..57db867882 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -99,7 +99,7 @@ def application(request): frappe.monitor.stop(response) frappe.recorder.dump() - frappe.logger("web").info({ + frappe.logger("frappe.web").info({ "site": get_site_name(request.host), "remote_addr": getattr(request, "remote_addr", "NOTFOUND"), "base_url": getattr(request, "base_url", "NOTFOUND"), From fa7faf4fe62fba38393381b7a44c0e5e3b4a2a37 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sat, 13 Jun 2020 18:23:23 +0530 Subject: [PATCH 05/15] fix: permission query for dashboard chart --- .../desk/doctype/dashboard_chart/dashboard_chart.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py index ab1863ca0b..c6343dd187 100644 --- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py @@ -26,15 +26,15 @@ def get_permission_query_conditions(user): if "System Manager" in roles: return None - allowed_doctypes = tuple(frappe.permissions.get_doctypes_with_read()) - allowed_reports = tuple([key if type(key) == str else key.encode('UTF8') for key in get_allowed_reports()]) + allowed_doctypes = ['"%s"' % doctype for doctype in frappe.permissions.get_doctypes_with_read()] + allowed_reports = ['"%s"' % key if type(key) == str else key.encode('UTF8') for key in get_allowed_reports()] return ''' - `tabDashboard Chart`.`document_type` in {allowed_doctypes} - or `tabDashboard Chart`.`report_name` in {allowed_reports} + `tabDashboard Chart`.`document_type` in ({allowed_doctypes}) + or `tabDashboard Chart`.`report_name` in ({allowed_reports}) '''.format( - allowed_doctypes=allowed_doctypes, - allowed_reports=allowed_reports + allowed_doctypes=','.join(allowed_doctypes), + allowed_reports=','.join(allowed_reports) ) From d25b1414514d5823ff79f46dbf13d17abc9b540e Mon Sep 17 00:00:00 2001 From: Emil Date: Sun, 14 Jun 2020 11:37:50 +0300 Subject: [PATCH 06/15] :sparkles: Expand list with extensions for template rendering Signed-off-by: Emil --- frappe/utils/jinja.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py index 2863ec99cd..97c7bf6127 100644 --- a/frappe/utils/jinja.py +++ b/frappe/utils/jinja.py @@ -90,7 +90,7 @@ def guess_is_path(template): # if its single line and ends with a html, then its probably a path if not '\n' in template and '.' in template: extn = template.rsplit('.')[-1] - if extn in ('html', 'css', 'scss', 'py'): + if extn in ('html', 'css', 'scss', 'py', 'md', 'json', 'js', 'xml'): return True return False From 3bc66bfea1ceb6252ced09b36dcbea16e3c17160 Mon Sep 17 00:00:00 2001 From: Emil Date: Sun, 14 Jun 2020 12:28:21 +0300 Subject: [PATCH 07/15] :sparkles: add another way to check path Signed-off-by: Emil --- frappe/utils/jinja.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py index 97c7bf6127..f7eb1a2eb8 100644 --- a/frappe/utils/jinja.py +++ b/frappe/utils/jinja.py @@ -2,6 +2,9 @@ # MIT License. See license.txt from __future__ import unicode_literals +import os + + def get_jenv(): import frappe from frappe.utils.safe_exec import get_safe_globals @@ -75,7 +78,7 @@ def render_template(template, context, is_path=None, safe_render=True): if not template: return "" - if (is_path or guess_is_path(template)): + if is_path or os.path.exists(template): return get_jenv().get_template(template).render(context) else: if safe_render and ".__" in template: @@ -85,16 +88,6 @@ def render_template(template, context, is_path=None, safe_render=True): except TemplateError: throw(title="Jinja Template Error", msg="
{template}
{tb}
".format(template=template, tb=get_traceback())) -def guess_is_path(template): - # template can be passed as a path or content - # if its single line and ends with a html, then its probably a path - if not '\n' in template and '.' in template: - extn = template.rsplit('.')[-1] - if extn in ('html', 'css', 'scss', 'py', 'md', 'json', 'js', 'xml'): - return True - - return False - def get_jloader(): import frappe From db4166279ee55e9aeef32c03a478cf43d106cd38 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 14 Jun 2020 15:20:53 +0530 Subject: [PATCH 08/15] fix: permission query for number card --- frappe/desk/doctype/number_card/number_card.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py index 6bb9c7d45c..c4a427c4e0 100644 --- a/frappe/desk/doctype/number_card/number_card.py +++ b/frappe/desk/doctype/number_card/number_card.py @@ -27,12 +27,12 @@ def get_permission_query_conditions(user=None): if "System Manager" in roles: return None - allowed_doctypes = tuple(frappe.permissions.get_doctypes_with_read()) + allowed_doctypes = ['"%s"' % doctype for doctype in frappe.permissions.get_doctypes_with_read()] return ''' - `tabNumber Card`.`document_type` in {allowed_doctypes} + `tabNumber Card`.`document_type` in ({allowed_doctypes}) '''.format( - allowed_doctypes=allowed_doctypes, + allowed_doctypes=','.join(allowed_doctypes) ) def has_permission(doc, ptype, user): From 267d3119f13541a6053b22032c09a51a9c2b8001 Mon Sep 17 00:00:00 2001 From: Emil Date: Sun, 14 Jun 2020 13:26:43 +0300 Subject: [PATCH 09/15] :bug: Return way of checking path Signed-off-by: Emil --- frappe/utils/jinja.py | 300 ++++++++++++++++++++++-------------------- 1 file changed, 158 insertions(+), 142 deletions(-) diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py index f7eb1a2eb8..507ecb0d4c 100644 --- a/frappe/utils/jinja.py +++ b/frappe/utils/jinja.py @@ -2,69 +2,71 @@ # MIT License. See license.txt from __future__ import unicode_literals -import os - def get_jenv(): - import frappe - from frappe.utils.safe_exec import get_safe_globals + import frappe + from frappe.utils.safe_exec import get_safe_globals - if not getattr(frappe.local, 'jenv', None): - from jinja2 import DebugUndefined - from jinja2.sandbox import SandboxedEnvironment + if not getattr(frappe.local, 'jenv', None): + from jinja2 import DebugUndefined + from jinja2.sandbox import SandboxedEnvironment - # frappe will be loaded last, so app templates will get precedence - jenv = SandboxedEnvironment( - loader=get_jloader(), - undefined=DebugUndefined - ) - set_filters(jenv) + # frappe will be loaded last, so app templates will get precedence + jenv = SandboxedEnvironment( + loader=get_jloader(), + undefined=DebugUndefined + ) + set_filters(jenv) - jenv.globals.update(get_safe_globals()) - jenv.globals.update(get_jenv_customization('methods')) - jenv.globals.update({ - 'resolve_class': resolve_class, - 'inspect': inspect, - 'web_blocks': web_blocks - }) + jenv.globals.update(get_safe_globals()) + jenv.globals.update(get_jenv_customization('methods')) + jenv.globals.update({ + 'resolve_class': resolve_class, + 'inspect': inspect, + 'web_blocks': web_blocks + }) - frappe.local.jenv = jenv + frappe.local.jenv = jenv + + return frappe.local.jenv - return frappe.local.jenv def get_template(path): - return get_jenv().get_template(path) + return get_jenv().get_template(path) + def get_email_from_template(name, args): - from jinja2 import TemplateNotFound + from jinja2 import TemplateNotFound - args = args or {} - try: - message = get_template('templates/emails/' + name + '.html').render(args) - except TemplateNotFound as e: - raise e + args = args or {} + try: + message = get_template('templates/emails/' + name + '.html').render(args) + except TemplateNotFound as e: + raise e - try: - text_content = get_template('templates/emails/' + name + '.txt').render(args) - except TemplateNotFound: - text_content = None + try: + text_content = get_template('templates/emails/' + name + '.txt').render(args) + except TemplateNotFound: + text_content = None + + return (message, text_content) - return (message, text_content) def validate_template(html): - """Throws exception if there is a syntax error in the Jinja Template""" - import frappe - from jinja2 import TemplateSyntaxError + """Throws exception if there is a syntax error in the Jinja Template""" + import frappe + from jinja2 import TemplateSyntaxError + + jenv = get_jenv() + try: + jenv.from_string(html) + except TemplateSyntaxError as e: + frappe.msgprint('Line {}: {}'.format(e.lineno, e.message)) + frappe.throw(frappe._("Syntax error in template")) - jenv = get_jenv() - try: - jenv.from_string(html) - except TemplateSyntaxError as e: - frappe.msgprint('Line {}: {}'.format(e.lineno, e.message)) - frappe.throw(frappe._("Syntax error in template")) def render_template(template, context, is_path=None, safe_render=True): - '''Render a template using Jinja + '''Render a template using Jinja :param template: path or HTML containing the jinja template :param context: dict of properties to pass to the template @@ -72,141 +74,155 @@ def render_template(template, context, is_path=None, safe_render=True): :param safe_render: (optional) prevent server side scripting via jinja templating ''' - from frappe import get_traceback, throw - from jinja2 import TemplateError + from frappe import get_traceback, throw + from jinja2 import TemplateError - if not template: - return "" + if not template: + return "" - if is_path or os.path.exists(template): - return get_jenv().get_template(template).render(context) - else: - if safe_render and ".__" in template: - throw("Illegal template") - try: - return get_jenv().from_string(template).render(context) - except TemplateError: - throw(title="Jinja Template Error", msg="
{template}
{tb}
".format(template=template, tb=get_traceback())) + if is_path or guess_is_path(template): + return get_jenv().get_template(template).render(context) + else: + if safe_render and ".__" in template: + throw("Illegal template") + try: + return get_jenv().from_string(template).render(context) + except TemplateError: + throw(title="Jinja Template Error", + msg="
{template}
{tb}
".format(template=template, tb=get_traceback())) + + +def guess_is_path(template): + # template can be passed as a path or content + # if its single line and ends with a html, then its probably a path + if not '\n' in template and '.' in template: + extn = template.rsplit('.')[-1] + if extn in ('html', 'css', 'scss', 'py', 'md', 'json', 'js', 'xml'): + return True + + return False def get_jloader(): - import frappe - if not getattr(frappe.local, 'jloader', None): - from jinja2 import ChoiceLoader, PackageLoader, PrefixLoader + import frappe + if not getattr(frappe.local, 'jloader', None): + from jinja2 import ChoiceLoader, PackageLoader, PrefixLoader - if frappe.local.flags.in_setup_help: - apps = ['frappe'] - else: - apps = frappe.get_hooks('template_apps') - if not apps: - apps = frappe.local.flags.web_pages_apps or frappe.get_installed_apps(sort=True) - apps.reverse() + if frappe.local.flags.in_setup_help: + apps = ['frappe'] + else: + apps = frappe.get_hooks('template_apps') + if not apps: + apps = frappe.local.flags.web_pages_apps or frappe.get_installed_apps(sort=True) + apps.reverse() - if not "frappe" in apps: - apps.append('frappe') + if not "frappe" in apps: + apps.append('frappe') - frappe.local.jloader = ChoiceLoader( - # search for something like app/templates/... - [PrefixLoader(dict( - (app, PackageLoader(app, ".")) for app in apps - ))] + frappe.local.jloader = ChoiceLoader( + # search for something like app/templates/... + [PrefixLoader(dict( + (app, PackageLoader(app, ".")) for app in apps + ))] - # search for something like templates/... - + [PackageLoader(app, ".") for app in apps] - ) + # search for something like templates/... + + [PackageLoader(app, ".") for app in apps] + ) + + return frappe.local.jloader - return frappe.local.jloader def set_filters(jenv): - import frappe - from frappe.utils import global_date_format, cint, cstr, flt, markdown - from frappe.website.utils import get_shade, abs_url + import frappe + from frappe.utils import global_date_format, cint, cstr, flt, markdown + from frappe.website.utils import get_shade, abs_url - jenv.filters["global_date_format"] = global_date_format - jenv.filters["markdown"] = markdown - jenv.filters["json"] = frappe.as_json - jenv.filters["get_shade"] = get_shade - jenv.filters["len"] = len - jenv.filters["int"] = cint - jenv.filters["str"] = cstr - jenv.filters["flt"] = flt - jenv.filters["abs_url"] = abs_url + jenv.filters["global_date_format"] = global_date_format + jenv.filters["markdown"] = markdown + jenv.filters["json"] = frappe.as_json + jenv.filters["get_shade"] = get_shade + jenv.filters["len"] = len + jenv.filters["int"] = cint + jenv.filters["str"] = cstr + jenv.filters["flt"] = flt + jenv.filters["abs_url"] = abs_url - if frappe.flags.in_setup_help: - return + if frappe.flags.in_setup_help: + return - jenv.filters.update(get_jenv_customization('filters')) + jenv.filters.update(get_jenv_customization('filters')) def get_jenv_customization(customization_type): - '''Returns a dict with filter/method name as key and definition as value''' + '''Returns a dict with filter/method name as key and definition as value''' - import frappe + import frappe - out = {} - if not getattr(frappe.local, "site", None): - return out + out = {} + if not getattr(frappe.local, "site", None): + return out - values = frappe.get_hooks("jenv", {}).get(customization_type) - if not values: - return out + values = frappe.get_hooks("jenv", {}).get(customization_type) + if not values: + return out - for value in values: - fn_name, fn_string = value.split(":") - out[fn_name] = frappe.get_attr(fn_string) + for value in values: + fn_name, fn_string = value.split(":") + out[fn_name] = frappe.get_attr(fn_string) - return out + return out def resolve_class(classes): - import frappe + import frappe - if classes is None: - return '' + if classes is None: + return '' - if isinstance(classes, frappe.string_types): - return classes + if isinstance(classes, frappe.string_types): + return classes - if isinstance(classes, (list, tuple)): - return ' '.join([resolve_class(c) for c in classes]).strip() + if isinstance(classes, (list, tuple)): + return ' '.join([resolve_class(c) for c in classes]).strip() - if isinstance(classes, dict): - return ' '.join([classname for classname in classes if classes[classname]]).strip() + if isinstance(classes, dict): + return ' '.join([classname for classname in classes if classes[classname]]).strip() - return classes + return classes def inspect(var, render=True): - context = { "var": var } - if render: - html = "
{{ var | pprint | e }}
" - else: - html = "" - return get_jenv().from_string(html).render(context) + context = {"var": var} + if render: + html = "
{{ var | pprint | e }}
" + else: + html = "" + return get_jenv().from_string(html).render(context) + def web_blocks(blocks): - from frappe import get_doc - from frappe.website.doctype.web_page.web_page import get_web_blocks_html + from frappe import get_doc + from frappe.website.doctype.web_page.web_page import get_web_blocks_html - web_blocks = [] - for block in blocks: - doc = { - 'doctype': 'Web Page Block', - 'web_template': block['template'], - 'web_template_values': block['values'], - 'add_top_padding': 1, - 'add_bottom_padding': 1, - 'add_container': 1, - 'hide_block': 0, - 'css_class': '' - } - doc.update(block) - web_blocks.append(get_doc(doc)) + web_blocks = [] + for block in blocks: + doc = { + 'doctype': 'Web Page Block', + 'web_template': block['template'], + 'web_template_values': block['values'], + 'add_top_padding': 1, + 'add_bottom_padding': 1, + 'add_container': 1, + 'hide_block': 0, + 'css_class': '' + } + doc.update(block) + web_blocks.append(get_doc(doc)) - out = get_web_blocks_html(web_blocks) + out = get_web_blocks_html(web_blocks) - html = out.html - for script in out.scripts: - html += ''.format(script) + html = out.html + for script in out.scripts: + html += ''.format(script) - return html + return html From 8a9ba931199c4e51ee3400402225dcaeb924c981 Mon Sep 17 00:00:00 2001 From: Emil Date: Sun, 14 Jun 2020 14:31:17 +0300 Subject: [PATCH 10/15] :art: change construction for not in Signed-off-by: Emil --- frappe/utils/jinja.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py index 507ecb0d4c..b4b2c26bc7 100644 --- a/frappe/utils/jinja.py +++ b/frappe/utils/jinja.py @@ -95,7 +95,7 @@ def render_template(template, context, is_path=None, safe_render=True): def guess_is_path(template): # template can be passed as a path or content # if its single line and ends with a html, then its probably a path - if not '\n' in template and '.' in template: + if '\n' not in template and '.' in template: extn = template.rsplit('.')[-1] if extn in ('html', 'css', 'scss', 'py', 'md', 'json', 'js', 'xml'): return True @@ -116,7 +116,7 @@ def get_jloader(): apps = frappe.local.flags.web_pages_apps or frappe.get_installed_apps(sort=True) apps.reverse() - if not "frappe" in apps: + if "frappe" not in apps: apps.append('frappe') frappe.local.jloader = ChoiceLoader( From 2bbe4497171a00b991902d7624ebf34abee9f44c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sun, 14 Jun 2020 19:16:17 +0530 Subject: [PATCH 11/15] fix: Refactor report settings selection to avoid random failure --- .../js/frappe/views/reports/query_report.js | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index e79e43ae02..98fc62b19e 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -261,27 +261,25 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { } get_report_settings() { - if (frappe.query_reports[this.report_name]) { - this.report_settings = this.get_local_report_settings(); - return this._load_script; - } - - this._load_script = (new Promise(resolve => frappe.call({ - method: 'frappe.desk.query_report.get_script', - args: { report_name: this.report_name }, - callback: resolve - }))).then(r => { - frappe.dom.eval(r.message.script || ''); - return r; - }).then(r => { - return frappe.after_ajax(() => { - this.report_settings = this.get_local_report_settings(); - this.report_settings.html_format = r.message.html_format; - this.report_settings.execution_time = r.message.execution_time || 0; - }); + return new Promise((resolve, reject) => { + if (frappe.query_reports[this.report_name]) { + this.report_settings = frappe.query_reports[this.report_name]; + resolve(); + } else { + frappe.xcall('frappe.desk.query_report.get_script', { + report_name: this.report_name + }).then(r => { + frappe.dom.eval(r.message.script || ''); + frappe.after_ajax(() => { + this.report_settings = this.get_local_report_settings(); + this.report_settings.html_format = r.message.html_format; + this.report_settings.execution_time = r.message.execution_time || 0; + frappe.query_reports[this.report_name] = this.report_settings; + resolve(); + }); + }).catch(reject); + } }); - - return this._load_script; } get_local_report_settings() { @@ -455,6 +453,8 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { filters.prepared_report_name = query_params.prepared_report_name; } + let possible_filters = frappe.query_report[this.report_name].filters; + return new Promise(resolve => { this.last_ajax = frappe.call({ method: 'frappe.desk.query_report.run', From 48b6c9543cd8112eea6a9bd2cb0727b70863e922 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 15 Jun 2020 12:17:25 +0530 Subject: [PATCH 12/15] fix: Set fallback base_template if source found (#10670) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/website/router.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frappe/website/router.py b/frappe/website/router.py index b291671a4a..db7e6f322c 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -300,7 +300,11 @@ def setup_source(page_info): css = f.read() html += '\n{% block style %}\n\n{% endblock %}' - page_info.source = html + if html: + page_info.source = html + page_info.base_template = page_info.base_template or 'templates/web.html' + else: + page_info.source = '' # show table of contents setup_index(page_info) From edcdb66b92516c99cfd0dbc4582c886f90b11138 Mon Sep 17 00:00:00 2001 From: Emil Date: Mon, 15 Jun 2020 12:30:44 +0300 Subject: [PATCH 13/15] :art: Change indetions to tabs Signed-off-by: Emil --- frappe/utils/jinja.py | 303 ++++++++++++++++++++---------------------- 1 file changed, 147 insertions(+), 156 deletions(-) diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py index b4b2c26bc7..8653cdc30a 100644 --- a/frappe/utils/jinja.py +++ b/frappe/utils/jinja.py @@ -2,71 +2,66 @@ # MIT License. See license.txt from __future__ import unicode_literals - def get_jenv(): - import frappe - from frappe.utils.safe_exec import get_safe_globals + import frappe + from frappe.utils.safe_exec import get_safe_globals - if not getattr(frappe.local, 'jenv', None): - from jinja2 import DebugUndefined - from jinja2.sandbox import SandboxedEnvironment + if not getattr(frappe.local, 'jenv', None): + from jinja2 import DebugUndefined + from jinja2.sandbox import SandboxedEnvironment - # frappe will be loaded last, so app templates will get precedence - jenv = SandboxedEnvironment( - loader=get_jloader(), - undefined=DebugUndefined - ) - set_filters(jenv) + # frappe will be loaded last, so app templates will get precedence + jenv = SandboxedEnvironment( + loader=get_jloader(), + undefined=DebugUndefined + ) + set_filters(jenv) - jenv.globals.update(get_safe_globals()) - jenv.globals.update(get_jenv_customization('methods')) - jenv.globals.update({ - 'resolve_class': resolve_class, - 'inspect': inspect, - 'web_blocks': web_blocks - }) + jenv.globals.update(get_safe_globals()) + jenv.globals.update(get_jenv_customization('methods')) + jenv.globals.update({ + 'resolve_class': resolve_class, + 'inspect': inspect, + 'web_blocks': web_blocks + }) - frappe.local.jenv = jenv - - return frappe.local.jenv + frappe.local.jenv = jenv + return frappe.local.jenv def get_template(path): - return get_jenv().get_template(path) - + return get_jenv().get_template(path) def get_email_from_template(name, args): - from jinja2 import TemplateNotFound + from jinja2 import TemplateNotFound - args = args or {} - try: - message = get_template('templates/emails/' + name + '.html').render(args) - except TemplateNotFound as e: - raise e + args = args or {} + try: + message = get_template('templates/emails/' + name + '.html').render(args) + except TemplateNotFound as e: + raise e - try: - text_content = get_template('templates/emails/' + name + '.txt').render(args) - except TemplateNotFound: - text_content = None - - return (message, text_content) + try: + text_content = get_template('templates/emails/' + name + '.txt').render(args) + except TemplateNotFound: + text_content = None + return (message, text_content) def validate_template(html): - """Throws exception if there is a syntax error in the Jinja Template""" - import frappe - from jinja2 import TemplateSyntaxError - - jenv = get_jenv() - try: - jenv.from_string(html) - except TemplateSyntaxError as e: - frappe.msgprint('Line {}: {}'.format(e.lineno, e.message)) - frappe.throw(frappe._("Syntax error in template")) + """Throws exception if there is a syntax error in the Jinja Template""" + import frappe + from jinja2 import TemplateSyntaxError + jenv = get_jenv() + try: + jenv.from_string(html) + except TemplateSyntaxError as e: + frappe.msgprint('Line {}: {}'.format(e.lineno, e.message)) + frappe.throw(frappe._("Syntax error in template")) def render_template(template, context, is_path=None, safe_render=True): - '''Render a template using Jinja + '''Render a template using Jinja :param template: path or HTML containing the jinja template :param context: dict of properties to pass to the template @@ -74,155 +69,151 @@ def render_template(template, context, is_path=None, safe_render=True): :param safe_render: (optional) prevent server side scripting via jinja templating ''' - from frappe import get_traceback, throw - from jinja2 import TemplateError + from frappe import get_traceback, throw + from jinja2 import TemplateError - if not template: - return "" - - if is_path or guess_is_path(template): - return get_jenv().get_template(template).render(context) - else: - if safe_render and ".__" in template: - throw("Illegal template") - try: - return get_jenv().from_string(template).render(context) - except TemplateError: - throw(title="Jinja Template Error", - msg="
{template}
{tb}
".format(template=template, tb=get_traceback())) + if not template: + return "" + if (is_path or guess_is_path(template)): + return get_jenv().get_template(template).render(context) + else: + if safe_render and ".__" in template: + throw("Illegal template") + try: + return get_jenv().from_string(template).render(context) + except TemplateError: + throw(title="Jinja Template Error", msg="
{template}
{tb}
".format(template=template, tb=get_traceback())) def guess_is_path(template): - # template can be passed as a path or content - # if its single line and ends with a html, then its probably a path - if '\n' not in template and '.' in template: - extn = template.rsplit('.')[-1] - if extn in ('html', 'css', 'scss', 'py', 'md', 'json', 'js', 'xml'): - return True + # template can be passed as a path or content + # if its single line and ends with a html, then its probably a path + if '\n' not in template and '.' in template: + extn = template.rsplit('.')[-1] + if extn in ('html', 'css', 'scss', 'py', 'md', 'json', 'js', 'xml'): + return True - return False + return False def get_jloader(): - import frappe - if not getattr(frappe.local, 'jloader', None): - from jinja2 import ChoiceLoader, PackageLoader, PrefixLoader + import frappe + if not getattr(frappe.local, 'jloader', None): + from jinja2 import ChoiceLoader, PackageLoader, PrefixLoader - if frappe.local.flags.in_setup_help: - apps = ['frappe'] - else: - apps = frappe.get_hooks('template_apps') - if not apps: - apps = frappe.local.flags.web_pages_apps or frappe.get_installed_apps(sort=True) - apps.reverse() + if frappe.local.flags.in_setup_help: + apps = ['frappe'] + else: + apps = frappe.get_hooks('template_apps') + if not apps: + apps = frappe.local.flags.web_pages_apps or frappe.get_installed_apps(sort=True) + apps.reverse() - if "frappe" not in apps: - apps.append('frappe') + if "frappe" not in apps: + apps.append('frappe') - frappe.local.jloader = ChoiceLoader( - # search for something like app/templates/... - [PrefixLoader(dict( - (app, PackageLoader(app, ".")) for app in apps - ))] + frappe.local.jloader = ChoiceLoader( + # search for something like app/templates/... + [PrefixLoader(dict( + (app, PackageLoader(app, ".")) for app in apps + ))] - # search for something like templates/... - + [PackageLoader(app, ".") for app in apps] - ) - - return frappe.local.jloader + # search for something like templates/... + + [PackageLoader(app, ".") for app in apps] + ) + return frappe.local.jloader def set_filters(jenv): - import frappe - from frappe.utils import global_date_format, cint, cstr, flt, markdown - from frappe.website.utils import get_shade, abs_url + import frappe + from frappe.utils import global_date_format, cint, cstr, flt, markdown + from frappe.website.utils import get_shade, abs_url - jenv.filters["global_date_format"] = global_date_format - jenv.filters["markdown"] = markdown - jenv.filters["json"] = frappe.as_json - jenv.filters["get_shade"] = get_shade - jenv.filters["len"] = len - jenv.filters["int"] = cint - jenv.filters["str"] = cstr - jenv.filters["flt"] = flt - jenv.filters["abs_url"] = abs_url + jenv.filters["global_date_format"] = global_date_format + jenv.filters["markdown"] = markdown + jenv.filters["json"] = frappe.as_json + jenv.filters["get_shade"] = get_shade + jenv.filters["len"] = len + jenv.filters["int"] = cint + jenv.filters["str"] = cstr + jenv.filters["flt"] = flt + jenv.filters["abs_url"] = abs_url - if frappe.flags.in_setup_help: - return + if frappe.flags.in_setup_help: + return - jenv.filters.update(get_jenv_customization('filters')) + jenv.filters.update(get_jenv_customization('filters')) def get_jenv_customization(customization_type): - '''Returns a dict with filter/method name as key and definition as value''' + '''Returns a dict with filter/method name as key and definition as value''' - import frappe + import frappe - out = {} - if not getattr(frappe.local, "site", None): - return out + out = {} + if not getattr(frappe.local, "site", None): + return out - values = frappe.get_hooks("jenv", {}).get(customization_type) - if not values: - return out + values = frappe.get_hooks("jenv", {}).get(customization_type) + if not values: + return out - for value in values: - fn_name, fn_string = value.split(":") - out[fn_name] = frappe.get_attr(fn_string) + for value in values: + fn_name, fn_string = value.split(":") + out[fn_name] = frappe.get_attr(fn_string) - return out + return out def resolve_class(classes): - import frappe + import frappe - if classes is None: - return '' + if classes is None: + return '' - if isinstance(classes, frappe.string_types): - return classes + if isinstance(classes, frappe.string_types): + return classes - if isinstance(classes, (list, tuple)): - return ' '.join([resolve_class(c) for c in classes]).strip() + if isinstance(classes, (list, tuple)): + return ' '.join([resolve_class(c) for c in classes]).strip() - if isinstance(classes, dict): - return ' '.join([classname for classname in classes if classes[classname]]).strip() + if isinstance(classes, dict): + return ' '.join([classname for classname in classes if classes[classname]]).strip() - return classes + return classes def inspect(var, render=True): - context = {"var": var} - if render: - html = "
{{ var | pprint | e }}
" - else: - html = "" - return get_jenv().from_string(html).render(context) - + context = { "var": var } + if render: + html = "
{{ var | pprint | e }}
" + else: + html = "" + return get_jenv().from_string(html).render(context) def web_blocks(blocks): - from frappe import get_doc - from frappe.website.doctype.web_page.web_page import get_web_blocks_html + from frappe import get_doc + from frappe.website.doctype.web_page.web_page import get_web_blocks_html - web_blocks = [] - for block in blocks: - doc = { - 'doctype': 'Web Page Block', - 'web_template': block['template'], - 'web_template_values': block['values'], - 'add_top_padding': 1, - 'add_bottom_padding': 1, - 'add_container': 1, - 'hide_block': 0, - 'css_class': '' - } - doc.update(block) - web_blocks.append(get_doc(doc)) + web_blocks = [] + for block in blocks: + doc = { + 'doctype': 'Web Page Block', + 'web_template': block['template'], + 'web_template_values': block['values'], + 'add_top_padding': 1, + 'add_bottom_padding': 1, + 'add_container': 1, + 'hide_block': 0, + 'css_class': '' + } + doc.update(block) + web_blocks.append(get_doc(doc)) - out = get_web_blocks_html(web_blocks) + out = get_web_blocks_html(web_blocks) - html = out.html - for script in out.scripts: - html += ''.format(script) + html = out.html + for script in out.scripts: + html += ''.format(script) - return html + return html From 6ed35fd9618af88fb0c4a239f7358dd8ed90a351 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Mon, 15 Jun 2020 17:12:05 +0530 Subject: [PATCH 14/15] fix: fix report print currency formatting (#10584) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- .../js/frappe/views/reports/print_grid.html | 73 ++++++++++--------- .../js/frappe/views/reports/query_report.js | 1 + 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/frappe/public/js/frappe/views/reports/print_grid.html b/frappe/public/js/frappe/views/reports/print_grid.html index ea510fa7bd..852c2925e8 100644 --- a/frappe/public/js/frappe/views/reports/print_grid.html +++ b/frappe/public/js/frappe/views/reports/print_grid.html @@ -8,50 +8,51 @@
{% endif %} - - - - {% for col in columns %} - {% if col.name && col._id !== "_check" %} + + + + {% for col in columns %} + {% if col.name && col._id !== "_check" %} - {% endif %} - {% endfor %} - - - - - {% for row in data %} - - {% for col in columns %} - {% if col.name && col._id !== "_check" %} + {% endif %} + {% endfor %} + + + + + {% for row in data %} + + {% for col in columns %} + {% if col.name && col._id !== "_check" %} - {% var value = col.fieldname ? row[col.fieldname] : row[col.id]; %} + {% var value = col.fieldname ? row[col.fieldname] : row[col.id]; %} - - {% endif %} - {% endfor %} - - {% endfor %} - + + {% endif %} + {% endfor %} + + {% endfor %} +
{{ __(col.name) }}
- - {{ - col.formatter - ? col.formatter(row._index, col._index, value, col, row, true) - : col.format - ? col.format(value, row, col, data) - : col.docfield - ? frappe.format(value, col.docfield) - : value - }} - -
+ + {% format_data = row.is_total_row ? data[0] : row %} + {{ + col.formatter + ? col.formatter(row._index, col._index, value, col, format_data, true) + : col.format + ? col.format(value, row, col, format_data) + : col.docfield + ? frappe.format(value, col.docfield) + : value + }} + +
diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 98fc62b19e..8b2e7f9b9d 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -1158,6 +1158,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { if (this.raw_data.add_total_row) { let totalRow = this.datatable.bodyRenderer.getTotalRow().reduce((row, cell) => { row[cell.column.id] = cell.content; + row.is_total_row = true; return row; }, {}); From 0f78ba2216460a8810a1125295ec46294d68e4e5 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Mon, 15 Jun 2020 21:54:23 +0530 Subject: [PATCH 15/15] fix: Query report failure (#10701) --- frappe/public/js/frappe/views/reports/query_report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 8b2e7f9b9d..75f1f443b2 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -269,7 +269,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { frappe.xcall('frappe.desk.query_report.get_script', { report_name: this.report_name }).then(r => { - frappe.dom.eval(r.message.script || ''); + frappe.dom.eval(r.script || ''); frappe.after_ajax(() => { this.report_settings = this.get_local_report_settings(); this.report_settings.html_format = r.message.html_format;