From 435bbe2665a447048058660e86deb6ed2005c635 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 5 Feb 2021 14:31:46 +0530 Subject: [PATCH 001/495] wip: refactor website routing and rendering --- frappe/tests/test_website_new.py | 20 ++ frappe/utils/boilerplate.py | 1 - frappe/website/serve.py | 276 ++++++++++++++++++++++++++ frappe/www/_test/static-file-test.png | Bin 0 -> 440 bytes 4 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 frappe/tests/test_website_new.py create mode 100644 frappe/website/serve.py create mode 100644 frappe/www/_test/static-file-test.png diff --git a/frappe/tests/test_website_new.py b/frappe/tests/test_website_new.py new file mode 100644 index 0000000000..aa9e77d5ba --- /dev/null +++ b/frappe/tests/test_website_new.py @@ -0,0 +1,20 @@ +from __future__ import unicode_literals + +import unittest + +import frappe +from frappe.website import serve +from frappe.website.utils import get_home_page +from frappe.utils import set_request + +class TestWebsite(unittest.TestCase): + def test_static_page(self): + set_request(method='GET', path='/_test/static-file-test.png') + response = serve.StaticPage().get() + self.assertEquals(response.status_code, 200) + + def test_error_page(self): + set_request(method='GET', path='/error') + response = serve.TemplatePage().get() + self.assertEquals(response.status_code, 200) + print(response.get_data()) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index 69d5726c34..e613c4d810 100755 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -330,7 +330,6 @@ Configuration for docs """ # source_link = "https://github.com/[org_name]/{app_name}" -# docs_base_url = "https://[org_name].github.io/{app_name}" # headline = "App that does everything" # sub_heading = "Yes, you got that right the first time, everything" diff --git a/frappe/website/serve.py b/frappe/website/serve.py new file mode 100644 index 0000000000..3f3e1d4571 --- /dev/null +++ b/frappe/website/serve.py @@ -0,0 +1,276 @@ +import frappe +import os, mimetypes + +from werkzeug.wrappers import Response +from werkzeug.wsgi import wrap_file + +from frappe.website.render import (resolve_path, build_response) +from frappe.website.redirect import resolve_redirect +from frappe.website.router import get_start_folders +from frappe.website.utils import (get_home_page, can_cache, delete_page_cache, + get_toc, get_next_link) +from frappe.website.doctype.website_settings.website_settings import get_website_settings + +def render(path=None, http_status_code=None): + """render html page""" + if not path: + path = frappe.local.request.path + + try: + path = path.strip('/ ') + resolve_redirect(path) + path = resolve_path(path) + data = None + + response = StaticPage(path).get() + if not response: + response = TemplatePage(path).get() + if not response: + response = DocTypePage(path).get() + if not response: + response = TemplatePage('404').get() + except frappe.PermissionError as e: + response = TemplatePage('403').get() + except: + response = TemplatePage('error').get() + + return response + +class WebPage(object): + def __init__(self, path=None): + self.headers = None + self.status_code = 200 + if not path: + path = frappe.local.request.path + self.path = path.strip('/ ') + + def get(self): + if self.validate(): + return self.render() + + def validate(self): + pass + + def render(self): + pass + +class StaticPage(WebPage): + def validate(self): + if ('.' not in self.path): + return False + extn = self.path.rsplit('.', 1)[-1] + if extn in ('html', 'md', 'js', 'xml', 'css', 'txt', 'py', 'json'): + return False + + if self.find_path_in_apps(): + return True + + return False + + def find_path_in_apps(self): + for app in frappe.get_installed_apps(): + file_path = frappe.get_app_path(app, 'www') + '/' + self.path + if os.path.exists(file_path): + self.file_path = file_path + return True + return False + + def render(self): + try: + f = open(self.file_path, 'rb') + except IOError: + raise NotFound + + response = Response(wrap_file(frappe.local.request.environ, f), direct_passthrough=True) + response.mimetype = mimetypes.guess_type(self.file_path)[0] or 'application/octet-stream' + return response + +class TemplatePage(WebPage): + def validate(self): + for app in frappe.get_installed_apps(frappe_last=True): + if self.find_page_in_app(app): + return True + + def find_page_in_app(self, app): + ''' + Searches for file matching the path in the /www and /templates/pages folders + ''' + app_path = frappe.get_app_path(app) + folders = get_start_folders() + + for dirname in folders: + search_path = os.path.join(app_path, dirname, self.path) + for p in self.get_index_path_options(search_path): + file_path = frappe.as_unicode(p) + if os.path.exists(file_path) and not os.path.isdir(file_path): + self.app = app + self.app_path = app_path + self.dirname = dirname + self.file_path = file_path + self.template_path = os.path.relpath(file_path, self.app_path) + return True + + def get_index_path_options(self, search_path): + return ( + search_path, + search_path + '.html', + search_path + '.md', + search_path + '/index.html', + search_path + '/index.md') + + def render(self): + return build_response(self.path, self.get_html(), self.status_code, self.headers) + + def get_html(self): + # context object should be separate from self for security + # because it will be accessed via the user defined template + self.context = frappe._dict() + + self.set_pymodule() + self.setup_template() + + if self.pymodule_name: + self.update_context() + + if self.source: + html = frappe.render_template(self.source, self.context) + + elif self.template_path: + html = self.render_template() + + html = self.update_toc(html) + + return html + + def set_pymodule(self): + ''' + A template may have a python module with a `get_context` method along with it in the + same folder. Also the hyphens will be coverted to underscore for python module names. + This method sets the pymodule_name if it exists. + ''' + self.basepath = self.template_path.rsplit('.', 1)[0] + self.pymodule_name = None + + # replace - with _ in the internal modules names + self.pymodule_path = os.path.join(self.basepath.replace("-", "_") + ".py") + + if os.path.exists(os.path.join(self.app_path, self.pymodule_path)): + self.pymodule_name = self.app + "." + self.pymodule_path.replace(os.path.sep, ".")[:-3] + + def setup_template(self): + '''Setup template source, frontmatter and markdown conversion''' + self.source = self.get_raw_template() + + if self.template_path.endswith(('.md', '.html')): + self.extract_frontmatter() + + self.convert_from_markdown() + + if self.extends_template(): + self.context.base_template_path = self.context.base_template_path or 'templates/base.html' + else: + self.source = None # clear the source + + # TODO: setup index.txt ? + + def update_context(self): + self.set_page_properties() + self.context.update(get_website_settings(self.context)) + self.context.update(frappe.local.conf.get("website_context") or {}) + + self.pymodule = frappe.get_module(self.pymodule_name) + + if self.pymodule: + self.set_pymodule_properties() + + data = self.run_pymodule_method('get_context') + # some methods may return a "context" object + if data: self.context.update(data) + + # TODO: self.context.children = self.run_pymodule_method('get_children') + + self.context.developer_mode = frappe.conf.developer_mode + + def set_pymodule_properties(self): + for prop in ("base_template_path", "template", "no_cache", "sitemap", + "condition_field"): + if hasattr(self.pymodule, prop): + self.context[prop] = getattr(self.pymodule, prop) + + def set_page_properties(self): + self.context.template = self.template_path + + def run_pymodule_method(self, method): + if hasattr(self.pymodule, method): + try: + return getattr(self.pymodule, method)(self) + except (frappe.PermissionError, frappe.DoesNotExistError, frappe.Redirect): + raise + except: + if not frappe.flags.in_migrate: + frappe.errprint(frappe.utils.get_traceback()) + + def render_template(self): + if self.path.endswith('min.js'): + # directly serve min.js pages using the jloader to find it in various apps + # (can be used as static?) + html = self.get_raw_template() + else: + html = frappe.get_template(self.template_path).render(self.context) + + def extends_template(self): + return (self.template_path.endswith(('.html', '.md', )) + and ('{%- extends' in self.source + or '{% extends' in self.source)) + + def get_raw_template(self): + return frappe.get_jloader().get_source(frappe.get_jenv(), self.template_path)[0] + + def load_colocated_files(self): + '''load co-located css/js files with the same name''' + js_path = self.basepath + '.js' + if os.path.exists(js_path) and '{% block script %}' not in self.source: + self.colocated_js = self.get_colocated_file(js_path) + + css_path = self.basepath + '.css' + if os.path.exists(css_path) and '{% block style %}' not in self.source: + self.colocated_css = self.get_colocated_file(css_path) + + def get_colocated_file(self, path): + with io.open(path, 'r', encoding = 'utf-8') as f: + return f.read() + + def extract_frontmatter(self): + try: + # values will be used to update page_info + res = get_frontmatter(self.source) + if res['attributes']: + self.context.update(res['attributes']) + self.source = res['body'] + except Exception: + pass + + def convert_from_markdown(self): + if self.template_path.endswith('.md'): + self.source = frappe.utils.md_to_html(self.source) + self.page_toc_html = self.toc_html + + if not self.show_sidebar: + self.source = '
' + self.source + '
' + + def update_toc(self, html): + if '{index}' in html: + html = html.replace('{index}', get_toc(self.path)) + + if '{next}' in html: + html = html.replace('{next}', get_next_link(self.path)) + + return html + + +class DocTypePage(WebPage): + pass + +class WebFormPage(WebPage): + pass + diff --git a/frappe/www/_test/static-file-test.png b/frappe/www/_test/static-file-test.png new file mode 100644 index 0000000000000000000000000000000000000000..b51db82f82b906b30609f807f0f663cac40d20b4 GIT binary patch literal 440 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q4M;wBd$a>caTa()7Beu2se&-0XOPMVpde#$ zkh>GZx^prwfgF}}M_)$E)e-c@Na=xA}jv*C{Z|`n2ZE+NFc(}bULexYtxXVk! zRa1H)JCmwK`xQRZ9Zdhl)Xnb@xa7e_h#Kc{rb11QMP%$ zyna9bX2;pHxqo~9%j-KE^?Uy3M6(kqS{SBbSa7?V|4TdH>w?cef9;X4JIhPRIT!|D tb@a_URnxz=-&b0D_xf+-FaWXL-%6{zP`s|39McHmdb;|#taD0e0su)Qf|39L literal 0 HcmV?d00001 From 3f38c3300462dfcda96638c0597216613bf21f19 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 5 Feb 2021 15:54:23 +0530 Subject: [PATCH 002/495] fix: status_code, login page works --- frappe/app.py | 8 ++++++-- frappe/tests/test_website_new.py | 17 ++++++++++++++++- frappe/website/serve.py | 18 ++++++++++-------- frappe/www/error.py | 2 ++ 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/frappe/app.py b/frappe/app.py index adf2bfa8c9..9495268f00 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -18,7 +18,8 @@ import frappe.handler import frappe.auth import frappe.api import frappe.utils.response -import frappe.website.render +import frappe.website.serve +import frappe.website.page from frappe.utils import get_site_name, sanitize_html from frappe.middlewares import StaticDataMiddleware from frappe.utils.error import make_error_snapshot @@ -73,7 +74,10 @@ def application(request): response = frappe.utils.response.download_private_file(request.path) elif request.method in ('GET', 'HEAD', 'POST'): - response = frappe.website.render.render() + if frappe.conf.flag_new_website: + response = frappe.website.serve.render() + else: + response = frappe.website.render.render() else: raise NotFound diff --git a/frappe/tests/test_website_new.py b/frappe/tests/test_website_new.py index aa9e77d5ba..34786611d6 100644 --- a/frappe/tests/test_website_new.py +++ b/frappe/tests/test_website_new.py @@ -8,6 +8,12 @@ from frappe.website.utils import get_home_page from frappe.utils import set_request class TestWebsite(unittest.TestCase): + def setUp(self): + frappe.set_user('Guest') + + def tearDown(self): + frappe.set_user('Administrator') + def test_static_page(self): set_request(method='GET', path='/_test/static-file-test.png') response = serve.StaticPage().get() @@ -16,5 +22,14 @@ class TestWebsite(unittest.TestCase): def test_error_page(self): set_request(method='GET', path='/error') response = serve.TemplatePage().get() + self.assertEquals(response.status_code, 500) + + def test_login(self): + set_request(method='GET', path='/login') + response = serve.TemplatePage().get() self.assertEquals(response.status_code, 200) - print(response.get_data()) + + html = frappe.safe_decode(response.get_data()) + + self.assertTrue('// login.js' in html) + self.assertTrue('' in html) diff --git a/frappe/website/serve.py b/frappe/website/serve.py index 3f3e1d4571..a0710b9988 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -22,6 +22,8 @@ def render(path=None, http_status_code=None): path = resolve_path(path) data = None + # there is no way to determine the type of the page based on the route + # so evaluate each type of page sequentially response = StaticPage(path).get() if not response: response = TemplatePage(path).get() @@ -93,7 +95,8 @@ class TemplatePage(WebPage): def find_page_in_app(self, app): ''' - Searches for file matching the path in the /www and /templates/pages folders + Searches for file matching the path in the /www + and /templates/pages folders ''' app_path = frappe.get_app_path(app) folders = get_start_folders() @@ -128,9 +131,7 @@ class TemplatePage(WebPage): self.set_pymodule() self.setup_template() - - if self.pymodule_name: - self.update_context() + self.update_context() if self.source: html = frappe.render_template(self.source, self.context) @@ -178,18 +179,19 @@ class TemplatePage(WebPage): self.context.update(get_website_settings(self.context)) self.context.update(frappe.local.conf.get("website_context") or {}) - self.pymodule = frappe.get_module(self.pymodule_name) - - if self.pymodule: + if self.pymodule_name: + self.pymodule = frappe.get_module(self.pymodule_name) self.set_pymodule_properties() data = self.run_pymodule_method('get_context') + # some methods may return a "context" object if data: self.context.update(data) # TODO: self.context.children = self.run_pymodule_method('get_children') self.context.developer_mode = frappe.conf.developer_mode + self.status_code = self.context.http_status_code or 200 def set_pymodule_properties(self): for prop in ("base_template_path", "template", "no_cache", "sitemap", @@ -203,7 +205,7 @@ class TemplatePage(WebPage): def run_pymodule_method(self, method): if hasattr(self.pymodule, method): try: - return getattr(self.pymodule, method)(self) + return getattr(self.pymodule, method)(self.context) except (frappe.PermissionError, frappe.DoesNotExistError, frappe.Redirect): raise except: diff --git a/frappe/www/error.py b/frappe/www/error.py index 161038373d..9274fe7c26 100644 --- a/frappe/www/error.py +++ b/frappe/www/error.py @@ -8,5 +8,7 @@ no_cache = 1 def get_context(context): if frappe.flags.in_migrate: return + context.http_status_code = 500 + print(frappe.get_traceback().encode("utf-8")) return {"error": frappe.get_traceback().replace("<", "<").replace(">", ">") } From 4369b8c0dc3d88c3d37af7ead06a4b35e4812778 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 12 Feb 2021 10:02:10 +0530 Subject: [PATCH 003/495] fix(rendering): add doctype generate pages --- frappe/app.py | 7 +- frappe/sessions.py | 3 +- frappe/tests/test_website_new.py | 20 ++- frappe/website/context.py | 2 +- frappe/website/render.py | 7 + frappe/website/serve.py | 231 ++++++++++++++++++++++++++++--- frappe/www/404.py | 2 + 7 files changed, 241 insertions(+), 31 deletions(-) diff --git a/frappe/app.py b/frappe/app.py index 9495268f00..52a8d2fe1c 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -18,8 +18,6 @@ import frappe.handler import frappe.auth import frappe.api import frappe.utils.response -import frappe.website.serve -import frappe.website.page from frappe.utils import get_site_name, sanitize_html from frappe.middlewares import StaticDataMiddleware from frappe.utils.error import make_error_snapshot @@ -74,10 +72,7 @@ def application(request): response = frappe.utils.response.download_private_file(request.path) elif request.method in ('GET', 'HEAD', 'POST'): - if frappe.conf.flag_new_website: - response = frappe.website.serve.render() - else: - response = frappe.website.render.render() + response = frappe.website.render.render() else: raise NotFound diff --git a/frappe/sessions.py b/frappe/sessions.py index 1ca1d4ee6f..dddeb6f37f 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -169,7 +169,8 @@ def get_csrf_token(): def generate_csrf_token(): frappe.local.session.data.csrf_token = frappe.generate_hash() - frappe.local.session_obj.update(force=True) + if not frappe.flags.in_test: + frappe.local.session_obj.update(force=True) class Session: def __init__(self, user, resume=False, full_name=None, user_type=None): diff --git a/frappe/tests/test_website_new.py b/frappe/tests/test_website_new.py index 34786611d6..3d4c2a69ef 100644 --- a/frappe/tests/test_website_new.py +++ b/frappe/tests/test_website_new.py @@ -6,7 +6,6 @@ import frappe from frappe.website import serve from frappe.website.utils import get_home_page from frappe.utils import set_request - class TestWebsite(unittest.TestCase): def setUp(self): frappe.set_user('Guest') @@ -21,15 +20,30 @@ class TestWebsite(unittest.TestCase): def test_error_page(self): set_request(method='GET', path='/error') - response = serve.TemplatePage().get() + response = serve.get_response() self.assertEquals(response.status_code, 500) def test_login(self): set_request(method='GET', path='/login') - response = serve.TemplatePage().get() + response = serve.get_response() self.assertEquals(response.status_code, 200) html = frappe.safe_decode(response.get_data()) self.assertTrue('// login.js' in html) self.assertTrue('' in html) + + def test_app(self): + frappe.set_user('Administrator') + set_request(method='GET', path='/app') + response = serve.get_response() + self.assertEquals(response.status_code, 200) + + html = frappe.safe_decode(response.get_data()) + self.assertTrue('window.app = true;' in html) + frappe.local.session_obj = None + + def test_not_found(self): + set_request(method='GET', path='/_test/missing') + response = serve.get_response() + self.assertEquals(response.status_code, 404) diff --git a/frappe/website/context.py b/frappe/website/context.py index 4236971aec..abda2e3f4f 100644 --- a/frappe/website/context.py +++ b/frappe/website/context.py @@ -243,7 +243,7 @@ def add_metatags(context): tags["og:title"] = tags["twitter:title"] = title tags["twitter:card"] = "summary" - if "description" not in tags and context.description: + if not tags.get('description') and context.description: tags["description"] = context.description description = tags.get("description") diff --git a/frappe/website/render.py b/frappe/website/render.py index 2f8bc59d6d..ce9647c62a 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -26,6 +26,13 @@ from frappe.translate import guess_language class PageNotFoundError(Exception): pass def render(path=None, http_status_code=None): + if frappe.conf.flag_new_website: + from frappe.website.serve import get_response + return get_response() + else: + return render(path, http_status_code) + +def _render(path=None, http_status_code=None): """render html page""" if not path: path = frappe.local.request.path diff --git a/frappe/website/serve.py b/frappe/website/serve.py index a0710b9988..ec54e8c149 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -6,12 +6,13 @@ from werkzeug.wsgi import wrap_file from frappe.website.render import (resolve_path, build_response) from frappe.website.redirect import resolve_redirect -from frappe.website.router import get_start_folders +from frappe.website.router import (get_start_folders, get_doctypes_with_web_view, + get_page_info_from_web_page_with_dynamic_routes) from frappe.website.utils import (get_home_page, can_cache, delete_page_cache, get_toc, get_next_link) from frappe.website.doctype.website_settings.website_settings import get_website_settings -def render(path=None, http_status_code=None): +def get_response(path=None, http_status_code=None): """render html page""" if not path: path = frappe.local.request.path @@ -87,7 +88,120 @@ class StaticPage(WebPage): response.mimetype = mimetypes.guess_type(self.file_path)[0] or 'application/octet-stream' return response -class TemplatePage(WebPage): +class BaseTemplatePage(WebPage): + def init_context(self): + self.context = frappe._dict() + self.context.update(get_website_settings(self.context)) + self.context.update(frappe.local.conf.get("website_context") or {}) + + def add_csrf_token(self, html): + if frappe.local.session: + return html.replace("", ''.format( + frappe.local.session.data.csrf_token)) + else: + return html + + def post_process_context(self): + self.add_metatags() + # add_sidebar_and_breadcrumbs(context) + + self.set_base_template_if_missing() + self.set_title_with_prefix() + self.update_website_context() + + # set using frappe.respond_as_web_page + if hasattr(frappe.local, 'response') and frappe.local.response.get('context'): + context.update(frappe.local.response.context) + + # to be able to inspect the context dict + # Use the macro "inspect" from macros.html + self.context._context_dict = self.context + + def set_base_template_if_missing(self): + if not self.context.base_template_path: + app_base = frappe.get_hooks("base_template") + self.context.base_template_path = app_base[-1] if app_base else "templates/base.html" + + def set_title_with_prefix(self): + if (self.context.title_prefix and self.context.title + and not self.context.title.startswith(self.context.title_prefix)): + self.context.title = '{0} - {1}'.format(self.context.title_prefix, self.context.title) + + def update_website_context(self): + # apply context from hooks + update_website_context = frappe.get_hooks('update_website_context') + for method in update_website_context: + values = frappe.get_attr(method)(self.context) + if values: + self.context.update(values) + + def add_metatags(self): + self.tags = frappe._dict(self.context.get("metatags") or {}) + self.init_metatags_from_context() + self.set_opengraph_tags() + self.set_twitter_tags() + self.set_meta_published_on() + self.set_metatags_from_website_route_meta() + + self.context.metatags = self.tags + + def init_metatags_from_context(self): + for key in ('title', 'description', 'image', 'author', 'url', 'published_on'): + if not key in self.tags and self.context.get(key): + self.tags[key] = self.context[key] + + if not self.tags.get('title'): self.tags['title'] = self.context.get('name') + + if self.tags.get('image'): + self.tags['image'] = frappe.utils.get_url(self.tags['image']) + + self.tags["language"] = frappe.local.lang or "en" + + def set_opengraph_tags(self): + if "og:type" not in self.tags: + self.tags["og:type"] = "article" + + for key in ('title', 'description', 'image', 'author', 'url'): + if self.tags.get(key): + self.tags['og:' + key] = self.tags.get(key) + + def set_twitter_tags(self): + for key in ('title', 'description', 'image', 'author', 'url'): + if self.tags.get(key): + self.tags['twitter:' + key] = self.tags.get(key) + + if self.tags.get('image'): + self.tags['twitter:card'] = "summary_large_image" + else: + self.tags["twitter:card"] = "summary" + + def set_meta_published_on(self): + if "published_on" in self.tags: + self.tags["datePublished"] = self.tags["published_on"] + del self.tags["published_on"] + + def set_metatags_from_website_route_meta(self): + ''' + Get meta tags from Website Route meta + they can override the defaults set above + ''' + route = self.context.path + if route == '': + # homepage + route = frappe.db.get_single_value('Website Settings', 'home_page') + + route_exists = (route + and not route.endswith(('.js', '.css')) + and frappe.db.exists('Website Route Meta', route)) + + if route_exists: + website_route_meta = frappe.get_doc('Website Route Meta', route) + for meta_tag in website_route_meta.meta_tags: + d = meta_tag.get_meta_dict() + self.tags.update(d) + + +class TemplatePage(BaseTemplatePage): def validate(self): for app in frappe.get_installed_apps(frappe_last=True): if self.find_page_in_app(app): @@ -127,19 +241,16 @@ class TemplatePage(WebPage): def get_html(self): # context object should be separate from self for security # because it will be accessed via the user defined template - self.context = frappe._dict() + self.init_context() self.set_pymodule() self.setup_template() self.update_context() - - if self.source: - html = frappe.render_template(self.source, self.context) - - elif self.template_path: - html = self.render_template() + self.post_process_context() + html = self.render_template() html = self.update_toc(html) + html = self.add_csrf_token(html) return html @@ -176,8 +287,6 @@ class TemplatePage(WebPage): def update_context(self): self.set_page_properties() - self.context.update(get_website_settings(self.context)) - self.context.update(frappe.local.conf.get("website_context") or {}) if self.pymodule_name: self.pymodule = frappe.get_module(self.pymodule_name) @@ -213,12 +322,15 @@ class TemplatePage(WebPage): frappe.errprint(frappe.utils.get_traceback()) def render_template(self): - if self.path.endswith('min.js'): - # directly serve min.js pages using the jloader to find it in various apps - # (can be used as static?) - html = self.get_raw_template() - else: - html = frappe.get_template(self.template_path).render(self.context) + if self.source: + html = frappe.render_template(self.source, self.context) + elif self.template_path: + if self.path.endswith('min.js'): + html = self.get_raw_template() # static + else: + html = frappe.get_template(self.template_path).render(self.context) + + return html def extends_template(self): return (self.template_path.endswith(('.html', '.md', )) @@ -270,8 +382,87 @@ class TemplatePage(WebPage): return html -class DocTypePage(WebPage): - pass +class DocTypePage(BaseTemplatePage): + def validate(self): + ''' + Find a document with matching `route` from all doctypes with `has_web_view`=1 + ''' + if self.search_in_doctypes_with_web_view(): + return True + + if self.search_web_page_dynamic_routes(): + return True + + return False + + def search_in_doctypes_with_web_view(self): + for doctype in get_doctypes_with_web_view(): + filters = dict(route=self.path) + meta = frappe.get_meta(doctype) + condition_field = self.get_condition_field(meta) + + if condition_field: + filters[condition_field] = 1 + + try: + self.docname = frappe.db.get_value(doctype, filters, 'name') + if self.docname: + self.doctype = doctype + self.meta = meta + return True + except Exception as e: + if not frappe.db.is_missing_column(e): raise e + + def search_web_page_dynamic_routes(self): + d = get_page_info_from_web_page_with_dynamic_routes(self.path) + if d: + self.doctype = 'Web Page' + self.docname = d.name + self.meta = frappe.get_meta(self.doctype) + return True + else: + return False + + def render(self): + self.doc = frappe.get_doc(self.doctype, self.docname) + self.init_context() + self.update_context() + self.post_process_context() + + html = frappe.get_template(self.context.template_path).render(self.context) + html = self.add_csrf_token(html) + + return build_response(self.path, html, self.status_code, self.headers) + + def update_context(self): + self.context.doc = self.doc + self.context.update(self.context.doc.as_dict()) + self.context.update(self.context.doc.get_website_properties()) + + if not self.context.template_path: + self.context.template_path = self.context.doc.meta.get_web_template() + + if hasattr(self.doc, "get_context"): + ret = self.doc.get_context(self.context) + + if ret: + self.context.update(ret) + + for prop in ("no_cache", "sitemap"): + if not prop in self.context: + self.context[prop] = getattr(self.doc, prop, False) + + def get_condition_field(self, meta): + condition_field = None + if meta.is_published_field: + condition_field = meta.is_published_field + elif not meta.custom: + controller = get_controller(doctype) + condition_field = controller.website.condition_field + + return condition_field + + class WebFormPage(WebPage): pass diff --git a/frappe/www/404.py b/frappe/www/404.py index c9de234743..e02e1c3929 100644 --- a/frappe/www/404.py +++ b/frappe/www/404.py @@ -2,3 +2,5 @@ # MIT License. See license.txt from __future__ import unicode_literals +def get_context(context): + context.http_status_code = 404 From 46dc5af29a81bbe00b022e30e096add46ecb88dd Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 15 Feb 2021 12:55:47 +0530 Subject: [PATCH 004/495] fix(list): make list pages work --- frappe/website/doctype/blog_post/blog_post.py | 5 --- frappe/website/render.py | 5 +-- frappe/website/router.py | 2 +- frappe/website/serve.py | 34 +++++++++++++------ 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/frappe/website/doctype/blog_post/blog_post.py b/frappe/website/doctype/blog_post/blog_post.py index 28549225be..e7c2772c69 100644 --- a/frappe/website/doctype/blog_post/blog_post.py +++ b/frappe/website/doctype/blog_post/blog_post.py @@ -13,11 +13,6 @@ from frappe.website.utils import (find_first_image, get_html_content_based_on_ty get_comment_list) class BlogPost(WebsiteGenerator): - website = frappe._dict( - route = 'blog', - order_by = "published_on desc" - ) - def make_route(self): if not self.route: return frappe.db.get_value('Blog Category', self.blog_category, diff --git a/frappe/website/render.py b/frappe/website/render.py index ce9647c62a..21d5dda8d5 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -26,11 +26,12 @@ from frappe.translate import guess_language class PageNotFoundError(Exception): pass def render(path=None, http_status_code=None): - if frappe.conf.flag_new_website: + # temp feature flag + if True or frappe.conf.flag_new_website: from frappe.website.serve import get_response return get_response() else: - return render(path, http_status_code) + return _render(path, http_status_code) def _render(path=None, http_status_code=None): """render html page""" diff --git a/frappe/website/router.py b/frappe/website/router.py index 946c83811a..5244c57ba8 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -441,4 +441,4 @@ def get_doctypes_with_web_view(): return frappe.cache().get_value('doctypes_with_web_view', _get) def get_start_folders(): - return frappe.local.flags.web_pages_folders or ('www', 'templates/pages') \ No newline at end of file + return frappe.local.flags.web_pages_folders or ('www', 'templates/pages') diff --git a/frappe/website/serve.py b/frappe/website/serve.py index ec54e8c149..6784dbefd9 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -29,7 +29,9 @@ def get_response(path=None, http_status_code=None): if not response: response = TemplatePage(path).get() if not response: - response = DocTypePage(path).get() + response = ListPage(path).get() + if not response: + response = DocumentPage(path).get() if not response: response = TemplatePage('404').get() except frappe.PermissionError as e: @@ -117,6 +119,11 @@ class BaseTemplatePage(WebPage): # Use the macro "inspect" from macros.html self.context._context_dict = self.context + # context sends us a new template path + if self.context.template: + self.template_path = self.context.template + + def set_base_template_if_missing(self): if not self.context.base_template_path: app_base = frappe.get_hooks("base_template") @@ -200,7 +207,6 @@ class BaseTemplatePage(WebPage): d = meta_tag.get_meta_dict() self.tags.update(d) - class TemplatePage(BaseTemplatePage): def validate(self): for app in frappe.get_installed_apps(frappe_last=True): @@ -222,8 +228,6 @@ class TemplatePage(BaseTemplatePage): if os.path.exists(file_path) and not os.path.isdir(file_path): self.app = app self.app_path = app_path - self.dirname = dirname - self.file_path = file_path self.template_path = os.path.relpath(file_path, self.app_path) return True @@ -244,9 +248,9 @@ class TemplatePage(BaseTemplatePage): self.init_context() self.set_pymodule() - self.setup_template() self.update_context() self.post_process_context() + self.setup_template() html = self.render_template() html = self.update_toc(html) @@ -380,9 +384,18 @@ class TemplatePage(BaseTemplatePage): html = html.replace('{next}', get_next_link(self.path)) return html - - -class DocTypePage(BaseTemplatePage): +class ListPage(TemplatePage): + def validate(self): + if frappe.db.get_value('DocType', self.path): + self.app = 'frappe' + self.app_path = frappe.get_app_path('frappe') + self.doctype = self.path + self.path = 'list' + self.template_path = 'www/list.html' + frappe.local.form_dict.doctype = self.doctype + return True + return False +class DocumentPage(BaseTemplatePage): def validate(self): ''' Find a document with matching `route` from all doctypes with `has_web_view`=1 @@ -462,8 +475,7 @@ class DocTypePage(BaseTemplatePage): return condition_field - - +class PrintPage(TemplatePage): + pass class WebFormPage(WebPage): pass - From 75ba0ca6a9be2f8a1d43529ff8a28a43efc42457 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 15 Feb 2021 13:49:38 +0530 Subject: [PATCH 005/495] fix(minor): website/serve.py --- frappe/website/serve.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/website/serve.py b/frappe/website/serve.py index 6784dbefd9..8f686144f1 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -113,7 +113,7 @@ class BaseTemplatePage(WebPage): # set using frappe.respond_as_web_page if hasattr(frappe.local, 'response') and frappe.local.response.get('context'): - context.update(frappe.local.response.context) + self.context.update(frappe.local.response.context) # to be able to inspect the context dict # Use the macro "inspect" from macros.html @@ -384,6 +384,7 @@ class TemplatePage(BaseTemplatePage): html = html.replace('{next}', get_next_link(self.path)) return html + class ListPage(TemplatePage): def validate(self): if frappe.db.get_value('DocType', self.path): @@ -395,6 +396,7 @@ class ListPage(TemplatePage): frappe.local.form_dict.doctype = self.doctype return True return False + class DocumentPage(BaseTemplatePage): def validate(self): ''' @@ -477,5 +479,6 @@ class DocumentPage(BaseTemplatePage): class PrintPage(TemplatePage): pass + class WebFormPage(WebPage): pass From e79e1420612b814f1e65ce9b4021d0c31f00d00a Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 15 Feb 2021 17:43:17 +0530 Subject: [PATCH 006/495] fix(minor): fix some tests and merge old and new website tests --- .../print_settings/print_settings.json | 4 +- frappe/tests/test_website.py | 46 ++++++++++- frappe/tests/test_website_new.py | 49 ------------ frappe/website/render.py | 2 +- frappe/website/serve.py | 80 ++++++++++++++----- frappe/www/message.py | 8 +- 6 files changed, 113 insertions(+), 76 deletions(-) delete mode 100644 frappe/tests/test_website_new.py diff --git a/frappe/printing/doctype/print_settings/print_settings.json b/frappe/printing/doctype/print_settings/print_settings.json index d64cb4c6d3..31962be050 100644 --- a/frappe/printing/doctype/print_settings/print_settings.json +++ b/frappe/printing/doctype/print_settings/print_settings.json @@ -148,7 +148,7 @@ "label": "Print Style" }, { - "default": "Modern", + "default": "Redesign", "fieldname": "print_style", "fieldtype": "Link", "in_list_view": 1, @@ -183,7 +183,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2020-10-22 23:42:09.471022", + "modified": "2021-02-15 14:16:18.474254", "modified_by": "Administrator", "module": "Printing", "name": "Print Settings", diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index c5da2bdfb7..91e08db457 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -4,13 +4,19 @@ import unittest import frappe from frappe.website import render +from frappe.website import serve from frappe.website.utils import get_home_page from frappe.utils import set_request - class TestWebsite(unittest.TestCase): + def setUp(self): + frappe.set_user('Guest') + + def tearDown(self): + frappe.set_user('Administrator') def test_home_page_for_role(self): + frappe.set_user('Administrator') frappe.delete_doc_if_exists('User', 'test-user-for-home-page@example.com') frappe.delete_doc_if_exists('Role', 'home-page-test') frappe.delete_doc_if_exists('Web Page', 'home-page-test') @@ -44,7 +50,6 @@ class TestWebsite(unittest.TestCase): self.assertEqual(get_home_page(), 'test-portal-home') def test_page_load(self): - frappe.set_user('Guest') set_request(method='POST', path='login') response = render.render() @@ -54,10 +59,47 @@ class TestWebsite(unittest.TestCase): self.assertTrue('// login.js' in html) self.assertTrue('' in html) + + def test_static_page(self): + set_request(method='GET', path='/_test/static-file-test.png') + response = serve.get_response() + self.assertEquals(response.status_code, 200) + + def test_error_page(self): + set_request(method='GET', path='/error') + response = serve.get_response() + self.assertEquals(response.status_code, 500) + + def test_login(self): + set_request(method='GET', path='/login') + response = serve.get_response() + self.assertEquals(response.status_code, 200) + + html = frappe.safe_decode(response.get_data()) + + self.assertTrue('// login.js' in html) + self.assertTrue('' in html) + + def test_app(self): frappe.set_user('Administrator') + set_request(method='GET', path='/app') + response = serve.get_response() + self.assertEquals(response.status_code, 200) + + html = frappe.safe_decode(response.get_data()) + self.assertTrue('window.app = true;' in html) + frappe.local.session_obj = None + + def test_not_found(self): + set_request(method='GET', path='/_test/missing') + response = serve.get_response() + self.assertEquals(response.status_code, 404) + def test_redirect(self): import frappe.hooks + frappe.set_user('Administrator') + frappe.hooks.website_redirects = [ dict(source=r'/testfrom', target=r'://testto1'), dict(source=r'/testfromregex.*', target=r'://testto2'), diff --git a/frappe/tests/test_website_new.py b/frappe/tests/test_website_new.py deleted file mode 100644 index 3d4c2a69ef..0000000000 --- a/frappe/tests/test_website_new.py +++ /dev/null @@ -1,49 +0,0 @@ -from __future__ import unicode_literals - -import unittest - -import frappe -from frappe.website import serve -from frappe.website.utils import get_home_page -from frappe.utils import set_request -class TestWebsite(unittest.TestCase): - def setUp(self): - frappe.set_user('Guest') - - def tearDown(self): - frappe.set_user('Administrator') - - def test_static_page(self): - set_request(method='GET', path='/_test/static-file-test.png') - response = serve.StaticPage().get() - self.assertEquals(response.status_code, 200) - - def test_error_page(self): - set_request(method='GET', path='/error') - response = serve.get_response() - self.assertEquals(response.status_code, 500) - - def test_login(self): - set_request(method='GET', path='/login') - response = serve.get_response() - self.assertEquals(response.status_code, 200) - - html = frappe.safe_decode(response.get_data()) - - self.assertTrue('// login.js' in html) - self.assertTrue('' in html) - - def test_app(self): - frappe.set_user('Administrator') - set_request(method='GET', path='/app') - response = serve.get_response() - self.assertEquals(response.status_code, 200) - - html = frappe.safe_decode(response.get_data()) - self.assertTrue('window.app = true;' in html) - frappe.local.session_obj = None - - def test_not_found(self): - set_request(method='GET', path='/_test/missing') - response = serve.get_response() - self.assertEquals(response.status_code, 404) diff --git a/frappe/website/render.py b/frappe/website/render.py index 21d5dda8d5..65f871b8a0 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -29,7 +29,7 @@ def render(path=None, http_status_code=None): # temp feature flag if True or frappe.conf.flag_new_website: from frappe.website.serve import get_response - return get_response() + return get_response(path, http_status_code) else: return _render(path, http_status_code) diff --git a/frappe/website/serve.py b/frappe/website/serve.py index 8f686144f1..927909e75f 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -25,26 +25,34 @@ def get_response(path=None, http_status_code=None): # there is no way to determine the type of the page based on the route # so evaluate each type of page sequentially - response = StaticPage(path).get() + response = StaticPage(path, http_status_code).get() if not response: - response = TemplatePage(path).get() + response = TemplatePage(path, http_status_code).get() if not response: - response = ListPage(path).get() + response = ListPage(path, http_status_code).get() if not response: - response = DocumentPage(path).get() + response = DocumentPage(path, http_status_code).get() if not response: - response = TemplatePage('404').get() + response = PrintPage(path, http_status_code).get() + if not response: + response = TemplatePage('404', 404).get() + except frappe.Redirect: + return build_response(path, "", 301, { + "Location": frappe.flags.redirect_location or (frappe.local.response or {}).get('location'), + "Cache-Control": "no-store, no-cache, must-revalidate" + }) except frappe.PermissionError as e: - response = TemplatePage('403').get() - except: - response = TemplatePage('error').get() + frappe.local.message = cstr(e) + response = NotPermittedPage(path, http_status_code).get() + except Exception as e: + response = TemplatePage('error', getattr(e, 'http_status_code', 500) or http_status_code).get() return response class WebPage(object): - def __init__(self, path=None): + def __init__(self, path=None, http_status_code=200): self.headers = None - self.status_code = 200 + self.http_status_code = http_status_code if not path: path = frappe.local.request.path self.path = path.strip('/ ') @@ -240,7 +248,7 @@ class TemplatePage(BaseTemplatePage): search_path + '/index.md') def render(self): - return build_response(self.path, self.get_html(), self.status_code, self.headers) + return build_response(self.path, self.get_html(), self.http_status_code, self.headers) def get_html(self): # context object should be separate from self for security @@ -304,7 +312,8 @@ class TemplatePage(BaseTemplatePage): # TODO: self.context.children = self.run_pymodule_method('get_children') self.context.developer_mode = frappe.conf.developer_mode - self.status_code = self.context.http_status_code or 200 + if self.context.http_status_code: + self.http_status_code = self.context.http_status_code def set_pymodule_properties(self): for prop in ("base_template_path", "template", "no_cache", "sitemap", @@ -385,18 +394,50 @@ class TemplatePage(BaseTemplatePage): return html + def set_standard_path(self, path): + self.app = 'frappe' + self.app_path = frappe.get_app_path('frappe') + self.path = path + self.template_path = 'www/{path}.html'.format(path=path) + + class ListPage(TemplatePage): def validate(self): if frappe.db.get_value('DocType', self.path): - self.app = 'frappe' - self.app_path = frappe.get_app_path('frappe') - self.doctype = self.path - self.path = 'list' - self.template_path = 'www/list.html' - frappe.local.form_dict.doctype = self.doctype + frappe.local.form_dict.doctype = self.path + self.set_standard_path('list') return True return False +class PrintPage(TemplatePage): + ''' + default path returns a printable object (based on permission) + /Quotation/Q-0001 + ''' + def validate(self): + parts = self.path.split('/', 1) + if len(parts)==2: + if (frappe.db.get_value('DocType', parts[0]) + and frappe.db.get_value(parts[0], parts[1])): + frappe.form_dict.doctype = parts[0] + frappe.form_dict.name = parts[1] + self.set_standard_path('printview') + return True + + return False + +class NotPermittedPage(TemplatePage): + def validate(self): + frappe.local.message_title = _("Not Permitted") + frappe.local.response['context'] = dict( + indicator_color = 'red', + primary_action = '/login', + primary_label = _('Login'), + fullpage=True + ) + self.set_standard_path('message') + return True + class DocumentPage(BaseTemplatePage): def validate(self): ''' @@ -477,8 +518,5 @@ class DocumentPage(BaseTemplatePage): return condition_field -class PrintPage(TemplatePage): - pass - class WebFormPage(WebPage): pass diff --git a/frappe/www/message.py b/frappe/www/message.py index ea45b9c4b5..d650170073 100644 --- a/frappe/www/message.py +++ b/frappe/www/message.py @@ -9,7 +9,7 @@ from frappe.utils import strip_html_tags no_cache = 1 def get_context(context): - message_context = {} + message_context = frappe._dict() if hasattr(frappe.local, "message"): message_context["header"] = frappe.local.message_title message_context["title"] = strip_html_tags(frappe.local.message_title) @@ -26,4 +26,10 @@ def get_context(context): if message.get('http_status_code'): frappe.local.response['http_status_code'] = message['http_status_code'] + if not message_context.title: + message_context.title = frappe.form_dict.title + + if not message_context.message: + message_context.message = frappe.form_dict.message + return message_context From b699d92f5ee57d54918221fb9d74c54ca4cfe24a Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 15 Feb 2021 18:05:44 +0530 Subject: [PATCH 007/495] fix(minor): http_status_code --- frappe/website/serve.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/website/serve.py b/frappe/website/serve.py index 927909e75f..b635d9615b 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -12,7 +12,7 @@ from frappe.website.utils import (get_home_page, can_cache, delete_page_cache, get_toc, get_next_link) from frappe.website.doctype.website_settings.website_settings import get_website_settings -def get_response(path=None, http_status_code=None): +def get_response(path=None, http_status_code=200): """render html page""" if not path: path = frappe.local.request.path @@ -45,14 +45,14 @@ def get_response(path=None, http_status_code=None): frappe.local.message = cstr(e) response = NotPermittedPage(path, http_status_code).get() except Exception as e: - response = TemplatePage('error', getattr(e, 'http_status_code', 500) or http_status_code).get() + response = TemplatePage('error', getattr(e, 'http_status_code', None) or http_status_code).get() return response class WebPage(object): - def __init__(self, path=None, http_status_code=200): + def __init__(self, path=None, http_status_code=None): self.headers = None - self.http_status_code = http_status_code + self.http_status_code = http_status_code or 200 if not path: path = frappe.local.request.path self.path = path.strip('/ ') @@ -488,7 +488,7 @@ class DocumentPage(BaseTemplatePage): html = frappe.get_template(self.context.template_path).render(self.context) html = self.add_csrf_token(html) - return build_response(self.path, html, self.status_code, self.headers) + return build_response(self.path, html, self.http_status_code or 200, self.headers) def update_context(self): self.context.doc = self.doc From 30659b9ee28e034c6eef7ba682eea5b302b6fb6b Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 11 Mar 2021 10:29:12 +0530 Subject: [PATCH 008/495] feat: Option to consider query string while writing website redirect - if match_with_query_string is true in a website redirect rule, system will try to match the regex with the result of route + query_string --- frappe/tests/test_website.py | 20 +++++++++++++------- frappe/website/redirect.py | 12 ++++++++---- frappe/website/serve.py | 28 ++++++++++++++++++---------- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index 91e08db457..ce165e36d9 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -51,7 +51,7 @@ class TestWebsite(unittest.TestCase): def test_page_load(self): set_request(method='POST', path='login') - response = render.render() + response = serve.get_response() self.assertEquals(response.status_code, 200) @@ -103,7 +103,8 @@ class TestWebsite(unittest.TestCase): frappe.hooks.website_redirects = [ dict(source=r'/testfrom', target=r'://testto1'), dict(source=r'/testfromregex.*', target=r'://testto2'), - dict(source=r'/testsub/(.*)', target=r'://testto3/\1') + dict(source=r'/testsub/(.*)', target=r'://testto3/\1'), + dict(source=r'/courses/course\?course=(.*)', target=r'/courses/\1', match_with_query_string=True), ] website_settings = frappe.get_doc('Website Settings') @@ -117,28 +118,33 @@ class TestWebsite(unittest.TestCase): frappe.cache().delete_key('website_redirects') set_request(method='GET', path='/testfrom') - response = render.render() + response = serve.get_response() self.assertEquals(response.status_code, 301) self.assertEquals(response.headers.get('Location'), r'://testto1') set_request(method='GET', path='/testfromregex/test') - response = render.render() + response = serve.get_response() self.assertEquals(response.status_code, 301) self.assertEquals(response.headers.get('Location'), r'://testto2') set_request(method='GET', path='/testsub/me') - response = render.render() + response = serve.get_response() self.assertEquals(response.status_code, 301) self.assertEquals(response.headers.get('Location'), r'://testto3/me') set_request(method='GET', path='/test404') - response = render.render() + response = serve.get_response() self.assertEquals(response.status_code, 404) set_request(method='GET', path='/testsource') - response = render.render() + response = serve.get_response() self.assertEquals(response.status_code, 301) self.assertEquals(response.headers.get('Location'), '/testtarget') + set_request(method='GET', path='/courses/course?course=data') + response = serve.get_response() + self.assertEquals(response.status_code, 301) + self.assertEquals(response.headers.get('Location'), '/courses/data') + delattr(frappe.hooks, 'website_redirects') frappe.cache().delete_key('app_hooks') diff --git a/frappe/website/redirect.py b/frappe/website/redirect.py index 73e3c21727..e66c0a3b7b 100644 --- a/frappe/website/redirect.py +++ b/frappe/website/redirect.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import re, frappe -def resolve_redirect(path): +def resolve_redirect(path, query_string=None): ''' Resolve redirects from hooks @@ -33,9 +33,13 @@ def resolve_redirect(path): for rule in redirects: pattern = rule['source'].strip('/ ') + '$' - if re.match(pattern, path): - redirect_to = re.sub(pattern, rule['target'], path) + path_to_match = path + if rule.get('match_with_query_string'): + path_to_match = path + '?' + frappe.safe_decode(query_string) + + if re.match(pattern, path_to_match): + redirect_to = re.sub(pattern, rule['target'], path_to_match) frappe.flags.redirect_location = redirect_to - frappe.cache().hset('website_redirects', path, redirect_to) + frappe.cache().hset('website_redirects', path_to_match, redirect_to) raise frappe.Redirect diff --git a/frappe/website/serve.py b/frappe/website/serve.py index b635d9615b..11e950573e 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -1,25 +1,33 @@ -import frappe -import os, mimetypes +import mimetypes +import os from werkzeug.wrappers import Response from werkzeug.wsgi import wrap_file -from frappe.website.render import (resolve_path, build_response) +import frappe +from frappe import _ +from frappe.utils import cint, cstr +from frappe.model.document import get_controller +from frappe.website.doctype.website_settings.website_settings import \ + get_website_settings from frappe.website.redirect import resolve_redirect -from frappe.website.router import (get_start_folders, get_doctypes_with_web_view, - get_page_info_from_web_page_with_dynamic_routes) -from frappe.website.utils import (get_home_page, can_cache, delete_page_cache, - get_toc, get_next_link) -from frappe.website.doctype.website_settings.website_settings import get_website_settings +from frappe.website.render import build_response, resolve_path +from frappe.website.router import (get_doctypes_with_web_view, + get_page_info_from_web_page_with_dynamic_routes, get_start_folders) +from frappe.website.utils import (can_cache, delete_page_cache, get_home_page, + get_next_link, get_toc) + def get_response(path=None, http_status_code=200): """render html page""" + query_string = None if not path: path = frappe.local.request.path + query_string = frappe.local.request.query_string try: path = path.strip('/ ') - resolve_redirect(path) + resolve_redirect(path, query_string) path = resolve_path(path) data = None @@ -513,7 +521,7 @@ class DocumentPage(BaseTemplatePage): if meta.is_published_field: condition_field = meta.is_published_field elif not meta.custom: - controller = get_controller(doctype) + controller = get_controller(meta.doctype) condition_field = controller.website.condition_field return condition_field From 39da825592429ab933e65368ef30d39de7a9208e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 12 Mar 2021 11:42:24 +0530 Subject: [PATCH 009/495] refactor: Move page controllers to separate files - Make template page work --- .../page_controllers/base_template_page.py | 128 +++++ .../website/page_controllers/document_page.py | 88 ++++ frappe/website/page_controllers/list_page.py | 10 + .../page_controllers/not_permitted_page.py | 15 + frappe/website/page_controllers/print_page.py | 19 + .../website/page_controllers/static_page.py | 40 ++ .../website/page_controllers/template_page.py | 276 ++++++++++ frappe/website/page_controllers/web_form.py | 4 + frappe/website/page_controllers/web_page.py | 21 + frappe/website/serve.py | 496 +----------------- 10 files changed, 610 insertions(+), 487 deletions(-) create mode 100644 frappe/website/page_controllers/base_template_page.py create mode 100644 frappe/website/page_controllers/document_page.py create mode 100644 frappe/website/page_controllers/list_page.py create mode 100644 frappe/website/page_controllers/not_permitted_page.py create mode 100644 frappe/website/page_controllers/print_page.py create mode 100644 frappe/website/page_controllers/static_page.py create mode 100644 frappe/website/page_controllers/template_page.py create mode 100644 frappe/website/page_controllers/web_form.py create mode 100644 frappe/website/page_controllers/web_page.py diff --git a/frappe/website/page_controllers/base_template_page.py b/frappe/website/page_controllers/base_template_page.py new file mode 100644 index 0000000000..10e2375356 --- /dev/null +++ b/frappe/website/page_controllers/base_template_page.py @@ -0,0 +1,128 @@ +import frappe +from frappe import _ +from frappe.website.context import add_sidebar_and_breadcrumbs +from frappe.website.doctype.website_settings.website_settings import \ + get_website_settings +from frappe.website.page_controllers.web_page import WebPage + + +class BaseTemplatePage(WebPage): + def init_context(self): + self.context = frappe._dict() + self.context.update(get_website_settings(self.context)) + self.context.update(frappe.local.conf.get("website_context") or {}) + + def add_csrf_token(self, html): + if frappe.local.session: + return html.replace("", ''.format( + frappe.local.session.data.csrf_token)) + else: + return html + + def post_process_context(self): + self.set_missing_values() + self.add_metatags() + self.add_sidebar_and_breadcrumbs() + + self.set_base_template_if_missing() + self.set_title_with_prefix() + self.update_website_context() + + # set using frappe.respond_as_web_page + if hasattr(frappe.local, 'response') and frappe.local.response.get('context'): + self.context.update(frappe.local.response.context) + + # to be able to inspect the context dict + # Use the macro "inspect" from macros.html + self.context._context_dict = self.context + + # context sends us a new template path + if self.context.template: + self.template_path = self.context.template + + + def set_base_template_if_missing(self): + if not self.context.base_template_path: + app_base = frappe.get_hooks("base_template") + self.context.base_template_path = app_base[-1] if app_base else "templates/base.html" + + def set_title_with_prefix(self): + if (self.context.title_prefix and self.context.title + and not self.context.title.startswith(self.context.title_prefix)): + self.context.title = '{0} - {1}'.format(self.context.title_prefix, self.context.title) + + def update_website_context(self): + # apply context from hooks + update_website_context = frappe.get_hooks('update_website_context') + for method in update_website_context: + values = frappe.get_attr(method)(self.context) + if values: + self.context.update(values) + + def add_metatags(self): + self.tags = frappe._dict(self.context.get("metatags") or {}) + self.init_metatags_from_context() + self.set_opengraph_tags() + self.set_twitter_tags() + self.set_meta_published_on() + self.set_metatags_from_website_route_meta() + + self.context.metatags = self.tags + + def add_sidebar_and_breadcrumbs(self): + add_sidebar_and_breadcrumbs(self.context) + + def init_metatags_from_context(self): + for key in ('title', 'description', 'image', 'author', 'url', 'published_on'): + if not key in self.tags and self.context.get(key): + self.tags[key] = self.context[key] + + if not self.tags.get('title'): self.tags['title'] = self.context.get('name') + + if self.tags.get('image'): + self.tags['image'] = frappe.utils.get_url(self.tags['image']) + + self.tags["language"] = frappe.local.lang or "en" + + def set_opengraph_tags(self): + if "og:type" not in self.tags: + self.tags["og:type"] = "article" + + for key in ('title', 'description', 'image', 'author', 'url'): + if self.tags.get(key): + self.tags['og:' + key] = self.tags.get(key) + + def set_twitter_tags(self): + for key in ('title', 'description', 'image', 'author', 'url'): + if self.tags.get(key): + self.tags['twitter:' + key] = self.tags.get(key) + + if self.tags.get('image'): + self.tags['twitter:card'] = "summary_large_image" + else: + self.tags["twitter:card"] = "summary" + + def set_meta_published_on(self): + if "published_on" in self.tags: + self.tags["datePublished"] = self.tags["published_on"] + del self.tags["published_on"] + + def set_metatags_from_website_route_meta(self): + ''' + Get meta tags from Website Route meta + they can override the defaults set above + ''' + route = self.context.path + if route == '': + # homepage + route = frappe.db.get_single_value('Website Settings', 'home_page') + + route_exists = (route + and not route.endswith(('.js', '.css')) + and frappe.db.exists('Website Route Meta', route)) + + if route_exists: + website_route_meta = frappe.get_doc('Website Route Meta', route) + for meta_tag in website_route_meta.meta_tags: + d = meta_tag.get_meta_dict() + self.tags.update(d) diff --git a/frappe/website/page_controllers/document_page.py b/frappe/website/page_controllers/document_page.py new file mode 100644 index 0000000000..bb00d78c0c --- /dev/null +++ b/frappe/website/page_controllers/document_page.py @@ -0,0 +1,88 @@ +import frappe +from frappe import _ +from frappe.model.document import get_controller +from frappe.website.page_controllers.base_template_page import BaseTemplatePage +from frappe.website.render import build_response +from frappe.website.router import (get_doctypes_with_web_view, + get_page_info_from_web_page_with_dynamic_routes) + + +class DocumentPage(BaseTemplatePage): + def validate(self): + ''' + Find a document with matching `route` from all doctypes with `has_web_view`=1 + ''' + if self.search_in_doctypes_with_web_view(): + return True + + if self.search_web_page_dynamic_routes(): + return True + + return False + + def search_in_doctypes_with_web_view(self): + for doctype in get_doctypes_with_web_view(): + filters = dict(route=self.path) + meta = frappe.get_meta(doctype) + condition_field = self.get_condition_field(meta) + + if condition_field: + filters[condition_field] = 1 + + try: + self.docname = frappe.db.get_value(doctype, filters, 'name') + if self.docname: + self.doctype = doctype + self.meta = meta + return True + except Exception as e: + if not frappe.db.is_missing_column(e): raise e + + def search_web_page_dynamic_routes(self): + d = get_page_info_from_web_page_with_dynamic_routes(self.path) + if d: + self.doctype = 'Web Page' + self.docname = d.name + self.meta = frappe.get_meta(self.doctype) + return True + else: + return False + + def render(self): + self.doc = frappe.get_doc(self.doctype, self.docname) + self.init_context() + self.update_context() + self.post_process_context() + + html = frappe.get_template(self.context.template_path).render(self.context) + html = self.add_csrf_token(html) + + return build_response(self.path, html, self.http_status_code or 200, self.headers) + + def update_context(self): + self.context.doc = self.doc + self.context.update(self.context.doc.as_dict()) + self.context.update(self.context.doc.get_website_properties()) + + if not self.context.template_path: + self.context.template_path = self.context.doc.meta.get_web_template() + + if hasattr(self.doc, "get_context"): + ret = self.doc.get_context(self.context) + + if ret: + self.context.update(ret) + + for prop in ("no_cache", "sitemap"): + if not prop in self.context: + self.context[prop] = getattr(self.doc, prop, False) + + def get_condition_field(self, meta): + condition_field = None + if meta.is_published_field: + condition_field = meta.is_published_field + elif not meta.custom: + controller = get_controller(meta.doctype) + condition_field = controller.website.condition_field + + return condition_field diff --git a/frappe/website/page_controllers/list_page.py b/frappe/website/page_controllers/list_page.py new file mode 100644 index 0000000000..2cb6ed5f26 --- /dev/null +++ b/frappe/website/page_controllers/list_page.py @@ -0,0 +1,10 @@ +import frappe +from frappe.website.page_controllers.template_page import TemplatePage + +class ListPage(TemplatePage): + def validate(self): + if frappe.db.get_value('DocType', self.path): + frappe.local.form_dict.doctype = self.path + self.set_standard_path('list') + return True + return False diff --git a/frappe/website/page_controllers/not_permitted_page.py b/frappe/website/page_controllers/not_permitted_page.py new file mode 100644 index 0000000000..e0cd6bf970 --- /dev/null +++ b/frappe/website/page_controllers/not_permitted_page.py @@ -0,0 +1,15 @@ +import frappe +from frappe import _ +from frappe.website.page_controllers.template_page import TemplatePage + +class NotPermittedPage(TemplatePage): + def validate(self): + frappe.local.message_title = _("Not Permitted") + frappe.local.response['context'] = dict( + indicator_color = 'red', + primary_action = '/login', + primary_label = _('Login'), + fullpage=True + ) + self.set_standard_path('message') + return True diff --git a/frappe/website/page_controllers/print_page.py b/frappe/website/page_controllers/print_page.py new file mode 100644 index 0000000000..6daea7fcbc --- /dev/null +++ b/frappe/website/page_controllers/print_page.py @@ -0,0 +1,19 @@ +import frappe +from frappe.website.page_controllers.template_page import TemplatePage + +class PrintPage(TemplatePage): + ''' + default path returns a printable object (based on permission) + /Quotation/Q-0001 + ''' + def validate(self): + parts = self.path.split('/', 1) + if len(parts)==2: + if (frappe.db.get_value('DocType', parts[0]) + and frappe.db.get_value(parts[0], parts[1])): + frappe.form_dict.doctype = parts[0] + frappe.form_dict.name = parts[1] + self.set_standard_path('printview') + return True + + return False diff --git a/frappe/website/page_controllers/static_page.py b/frappe/website/page_controllers/static_page.py new file mode 100644 index 0000000000..4b57c3ec42 --- /dev/null +++ b/frappe/website/page_controllers/static_page.py @@ -0,0 +1,40 @@ +import mimetypes +import os + +from werkzeug.wrappers import Response +from werkzeug.wsgi import wrap_file + +import frappe +from frappe.website.page_controllers.web_page import WebPage + + +class StaticPage(WebPage): + def validate(self): + if ('.' not in self.path): + return False + extn = self.path.rsplit('.', 1)[-1] + if extn in ('html', 'md', 'js', 'xml', 'css', 'txt', 'py', 'json'): + return False + + if self.find_path_in_apps(): + return True + + return False + + def find_path_in_apps(self): + for app in frappe.get_installed_apps(): + file_path = frappe.get_app_path(app, 'www') + '/' + self.path + if os.path.exists(file_path): + self.file_path = file_path + return True + return False + + def render(self): + try: + f = open(self.file_path, 'rb') + except IOError: + raise frappe.exceptions.NotFound + + response = Response(wrap_file(frappe.local.request.environ, f), direct_passthrough=True) + response.mimetype = mimetypes.guess_type(self.file_path)[0] or 'application/octet-stream' + return response diff --git a/frappe/website/page_controllers/template_page.py b/frappe/website/page_controllers/template_page.py new file mode 100644 index 0000000000..46e6e6a57e --- /dev/null +++ b/frappe/website/page_controllers/template_page.py @@ -0,0 +1,276 @@ +import io +import os + +import frappe +from frappe import _ +from frappe.website.page_controllers.base_template_page import BaseTemplatePage +from frappe.website.render import build_response +from frappe.website.utils import (extract_comment_tag, extract_title, + get_next_link, get_toc) + + +class TemplatePage(BaseTemplatePage): + def validate(self): + for app in frappe.get_installed_apps(frappe_last=True): + if self.find_page_in_app(app): + return True + + def find_page_in_app(self, app): + ''' + Searches for file matching the path in the /www + and /templates/pages folders + ''' + app_path = frappe.get_app_path(app) + folders = get_start_folders() + + for dirname in folders: + search_path = os.path.join(app_path, dirname, self.path) + for p in self.get_index_path_options(search_path): + file_path = frappe.as_unicode(p) + if os.path.exists(file_path) and not os.path.isdir(file_path): + self.app = app + self.app_path = app_path + self.template_path = os.path.relpath(file_path, self.app_path) + return True + + def get_index_path_options(self, search_path): + return ( + search_path, + search_path + '.html', + search_path + '.md', + search_path + '/index.html', + search_path + '/index.md') + + def render(self): + return build_response(self.path, self.get_html(), self.http_status_code, self.headers) + + def get_html(self): + # context object should be separate from self for security + # because it will be accessed via the user defined template + self.init_context() + + self.set_pymodule() + self.setup_template() + self.update_context() + self.post_process_context() + html = self.render_template() + + html = self.update_toc(html) + html = self.add_csrf_token(html) + + return html + + def set_pymodule(self): + ''' + A template may have a python module with a `get_context` method along with it in the + same folder. Also the hyphens will be coverted to underscore for python module names. + This method sets the pymodule_name if it exists. + ''' + self.basepath = self.template_path.rsplit('.', 1)[0] + self.pymodule_name = None + + # replace - with _ in the internal modules names + self.pymodule_path = os.path.join(self.basepath.replace("-", "_") + ".py") + + if os.path.exists(os.path.join(self.app_path, self.pymodule_path)): + self.pymodule_name = self.app + "." + self.pymodule_path.replace(os.path.sep, ".")[:-3] + + def setup_template(self): + '''Setup template source, frontmatter and markdown conversion''' + self.source = self.get_raw_template() + + if self.template_path.endswith(('.md', '.html')): + self.extract_frontmatter() + + self.convert_from_markdown() + + if self.extends_template(): + self.context.base_template_path = self.context.base_template_path or 'templates/base.html' + + + # TODO: setup index.txt ? + + def update_context(self): + self.set_page_properties() + self.set_properties_from_source() + self.load_colocated_files() + self.context.build_version = frappe.utils.get_build_version() + + if self.pymodule_name: + self.pymodule = frappe.get_module(self.pymodule_name) + self.set_pymodule_properties() + + data = self.run_pymodule_method('get_context') + + # some methods may return a "context" object + if data: self.context.update(data) + + # TODO: self.context.children = self.run_pymodule_method('get_children') + + self.context.developer_mode = frappe.conf.developer_mode + if self.context.http_status_code: + self.http_status_code = self.context.http_status_code + + def set_pymodule_properties(self): + for prop in ("base_template_path", "template", "no_cache", "sitemap", + "condition_field"): + if hasattr(self.pymodule, prop): + self.context[prop] = getattr(self.pymodule, prop) + + def set_page_properties(self): + self.context.template = self.template_path + self.context.base_template = self.context.base_template or 'templates/web.html' + + def set_properties_from_source(self): + if not self.source: + return + context = self.context + if not context.title: + context.title = extract_title(self.source, self.path) + + base_template = extract_comment_tag(self.source, 'base_template') + if base_template: + context.base_template = base_template + + if (context.base_template + and "{%- extends" not in self.source + and "{% extends" not in self.source + and "" not in self.source): + self.source = '''{{% extends "{0}" %}} + {{% block page_content %}}{1}{{% endblock %}}'''.format(context.base_template, self.source) + + if "" in self.source: + context.no_breadcrumbs = 1 + + if "" in self.source: + context.show_sidebar = 1 + + if "" in self.source: + context.add_breadcrumbs = 1 + + if "" in self.source: + context.no_header = 1 + + if "" in self.source: + context.add_next_prev_links = 1 + + if "" in self.source: + context.no_cache = 1 + + if "" in self.source: + context.sitemap = 0 + + if "" in self.source: + context.sitemap = 1 + + def run_pymodule_method(self, method): + if hasattr(self.pymodule, method): + try: + return getattr(self.pymodule, method)(self.context) + except (frappe.PermissionError, frappe.DoesNotExistError, frappe.Redirect): + raise + except: + if not frappe.flags.in_migrate: + frappe.errprint(frappe.utils.get_traceback()) + + def render_template(self): + if self.source: + html = frappe.render_template(self.source, self.context) + elif self.template_path: + if self.path.endswith('min.js'): + html = self.get_raw_template() # static + else: + html = frappe.get_template(self.template_path).render(self.context) + + return html + + def extends_template(self): + return (self.template_path.endswith(('.html', '.md', )) + and ('{%- extends' in self.source + or '{% extends' in self.source)) + + def get_raw_template(self): + return frappe.get_jloader().get_source(frappe.get_jenv(), self.template_path)[0] + + def load_colocated_files(self): + '''load co-located css/js files with the same name''' + js_path = self.basepath + '.js' + if os.path.exists(js_path) and '{% block script %}' not in self.source: + self.context.colocated_js = self.get_colocated_file(js_path) + + css_path = self.basepath + '.css' + if os.path.exists(css_path) and '{% block style %}' not in self.source: + self.context.colocated_css = self.get_colocated_file(css_path) + + def get_colocated_file(self, path): + with io.open(path, 'r', encoding = 'utf-8') as f: + return f.read() + + def extract_frontmatter(self): + try: + # values will be used to update self + res = get_frontmatter(self.source) + if res['attributes']: + self.context.update(res['attributes']) + self.source = res['body'] + except Exception: + pass + + def convert_from_markdown(self): + if self.template_path.endswith('.md'): + self.source = frappe.utils.md_to_html(self.source) + self.page_toc_html = self.source.toc_html + + if not self.context.show_sidebar: + self.source = '
' + self.source + '
' + + def update_toc(self, html): + if '{index}' in html: + html = html.replace('{index}', get_toc(self.path)) + + if '{next}' in html: + html = html.replace('{next}', get_next_link(self.path)) + + return html + + def set_standard_path(self, path): + self.app = 'frappe' + self.app_path = frappe.get_app_path('frappe') + self.path = path + self.template_path = 'www/{path}.html'.format(path=path) + + def set_missing_values(self): + if not "url_prefix" in self.context: + self.context.url_prefix = "" + + if self.context.url_prefix and self.context.url_prefix[-1]!='/': + self.context.url_prefix += '/' + + # for backward compatibility + self.context.docs_base_url = '/docs' + self.context.path = self.path + + +def get_start_folders(): + return frappe.local.flags.web_pages_folders or ('www', 'templates/pages') + +def get_frontmatter(string): + """ + Reference: https://github.com/jonbeebe/frontmatter + """ + import re + + import yaml + + fmatter = "" + body = "" + result = re.compile(r'^\s*(?:---|\+\+\+)(.*?)(?:---|\+\+\+)\s*(.+)$', re.S | re.M).search(string) + + if result: + fmatter = result.group(1) + body = result.group(2) + + return { + "attributes": yaml.safe_load(fmatter), + "body": body, + } diff --git a/frappe/website/page_controllers/web_form.py b/frappe/website/page_controllers/web_form.py new file mode 100644 index 0000000000..030e8bb3bc --- /dev/null +++ b/frappe/website/page_controllers/web_form.py @@ -0,0 +1,4 @@ +from frappe.website.page_controllers.web_page import WebPage + +class WebFormPage(WebPage): + pass diff --git a/frappe/website/page_controllers/web_page.py b/frappe/website/page_controllers/web_page.py new file mode 100644 index 0000000000..d4af2860f8 --- /dev/null +++ b/frappe/website/page_controllers/web_page.py @@ -0,0 +1,21 @@ +import frappe +from frappe import _ + +class WebPage(object): + def __init__(self, path=None, http_status_code=None): + self.headers = None + self.http_status_code = http_status_code or 200 + if not path: + path = frappe.local.request.path + self.path = path.strip('/ ') + + def get(self): + if self.validate(): + return self.render() + + def validate(self): + pass + + def render(self): + pass + diff --git a/frappe/website/serve.py b/frappe/website/serve.py index 11e950573e..10eddfc641 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -1,22 +1,16 @@ -import mimetypes -import os - -from werkzeug.wrappers import Response -from werkzeug.wsgi import wrap_file - import frappe from frappe import _ -from frappe.utils import cint, cstr -from frappe.model.document import get_controller -from frappe.website.doctype.website_settings.website_settings import \ - get_website_settings +from frappe.utils import cstr + +from frappe.website.page_controllers.document_page import DocumentPage +from frappe.website.page_controllers.list_page import ListPage +from frappe.website.page_controllers.not_permitted_page import NotPermittedPage +from frappe.website.page_controllers.print_page import PrintPage +from frappe.website.page_controllers.template_page import TemplatePage +from frappe.website.page_controllers.static_page import StaticPage + from frappe.website.redirect import resolve_redirect from frappe.website.render import build_response, resolve_path -from frappe.website.router import (get_doctypes_with_web_view, - get_page_info_from_web_page_with_dynamic_routes, get_start_folders) -from frappe.website.utils import (can_cache, delete_page_cache, get_home_page, - get_next_link, get_toc) - def get_response(path=None, http_status_code=200): """render html page""" @@ -56,475 +50,3 @@ def get_response(path=None, http_status_code=200): response = TemplatePage('error', getattr(e, 'http_status_code', None) or http_status_code).get() return response - -class WebPage(object): - def __init__(self, path=None, http_status_code=None): - self.headers = None - self.http_status_code = http_status_code or 200 - if not path: - path = frappe.local.request.path - self.path = path.strip('/ ') - - def get(self): - if self.validate(): - return self.render() - - def validate(self): - pass - - def render(self): - pass - -class StaticPage(WebPage): - def validate(self): - if ('.' not in self.path): - return False - extn = self.path.rsplit('.', 1)[-1] - if extn in ('html', 'md', 'js', 'xml', 'css', 'txt', 'py', 'json'): - return False - - if self.find_path_in_apps(): - return True - - return False - - def find_path_in_apps(self): - for app in frappe.get_installed_apps(): - file_path = frappe.get_app_path(app, 'www') + '/' + self.path - if os.path.exists(file_path): - self.file_path = file_path - return True - return False - - def render(self): - try: - f = open(self.file_path, 'rb') - except IOError: - raise NotFound - - response = Response(wrap_file(frappe.local.request.environ, f), direct_passthrough=True) - response.mimetype = mimetypes.guess_type(self.file_path)[0] or 'application/octet-stream' - return response - -class BaseTemplatePage(WebPage): - def init_context(self): - self.context = frappe._dict() - self.context.update(get_website_settings(self.context)) - self.context.update(frappe.local.conf.get("website_context") or {}) - - def add_csrf_token(self, html): - if frappe.local.session: - return html.replace("", ''.format( - frappe.local.session.data.csrf_token)) - else: - return html - - def post_process_context(self): - self.add_metatags() - # add_sidebar_and_breadcrumbs(context) - - self.set_base_template_if_missing() - self.set_title_with_prefix() - self.update_website_context() - - # set using frappe.respond_as_web_page - if hasattr(frappe.local, 'response') and frappe.local.response.get('context'): - self.context.update(frappe.local.response.context) - - # to be able to inspect the context dict - # Use the macro "inspect" from macros.html - self.context._context_dict = self.context - - # context sends us a new template path - if self.context.template: - self.template_path = self.context.template - - - def set_base_template_if_missing(self): - if not self.context.base_template_path: - app_base = frappe.get_hooks("base_template") - self.context.base_template_path = app_base[-1] if app_base else "templates/base.html" - - def set_title_with_prefix(self): - if (self.context.title_prefix and self.context.title - and not self.context.title.startswith(self.context.title_prefix)): - self.context.title = '{0} - {1}'.format(self.context.title_prefix, self.context.title) - - def update_website_context(self): - # apply context from hooks - update_website_context = frappe.get_hooks('update_website_context') - for method in update_website_context: - values = frappe.get_attr(method)(self.context) - if values: - self.context.update(values) - - def add_metatags(self): - self.tags = frappe._dict(self.context.get("metatags") or {}) - self.init_metatags_from_context() - self.set_opengraph_tags() - self.set_twitter_tags() - self.set_meta_published_on() - self.set_metatags_from_website_route_meta() - - self.context.metatags = self.tags - - def init_metatags_from_context(self): - for key in ('title', 'description', 'image', 'author', 'url', 'published_on'): - if not key in self.tags and self.context.get(key): - self.tags[key] = self.context[key] - - if not self.tags.get('title'): self.tags['title'] = self.context.get('name') - - if self.tags.get('image'): - self.tags['image'] = frappe.utils.get_url(self.tags['image']) - - self.tags["language"] = frappe.local.lang or "en" - - def set_opengraph_tags(self): - if "og:type" not in self.tags: - self.tags["og:type"] = "article" - - for key in ('title', 'description', 'image', 'author', 'url'): - if self.tags.get(key): - self.tags['og:' + key] = self.tags.get(key) - - def set_twitter_tags(self): - for key in ('title', 'description', 'image', 'author', 'url'): - if self.tags.get(key): - self.tags['twitter:' + key] = self.tags.get(key) - - if self.tags.get('image'): - self.tags['twitter:card'] = "summary_large_image" - else: - self.tags["twitter:card"] = "summary" - - def set_meta_published_on(self): - if "published_on" in self.tags: - self.tags["datePublished"] = self.tags["published_on"] - del self.tags["published_on"] - - def set_metatags_from_website_route_meta(self): - ''' - Get meta tags from Website Route meta - they can override the defaults set above - ''' - route = self.context.path - if route == '': - # homepage - route = frappe.db.get_single_value('Website Settings', 'home_page') - - route_exists = (route - and not route.endswith(('.js', '.css')) - and frappe.db.exists('Website Route Meta', route)) - - if route_exists: - website_route_meta = frappe.get_doc('Website Route Meta', route) - for meta_tag in website_route_meta.meta_tags: - d = meta_tag.get_meta_dict() - self.tags.update(d) - -class TemplatePage(BaseTemplatePage): - def validate(self): - for app in frappe.get_installed_apps(frappe_last=True): - if self.find_page_in_app(app): - return True - - def find_page_in_app(self, app): - ''' - Searches for file matching the path in the /www - and /templates/pages folders - ''' - app_path = frappe.get_app_path(app) - folders = get_start_folders() - - for dirname in folders: - search_path = os.path.join(app_path, dirname, self.path) - for p in self.get_index_path_options(search_path): - file_path = frappe.as_unicode(p) - if os.path.exists(file_path) and not os.path.isdir(file_path): - self.app = app - self.app_path = app_path - self.template_path = os.path.relpath(file_path, self.app_path) - return True - - def get_index_path_options(self, search_path): - return ( - search_path, - search_path + '.html', - search_path + '.md', - search_path + '/index.html', - search_path + '/index.md') - - def render(self): - return build_response(self.path, self.get_html(), self.http_status_code, self.headers) - - def get_html(self): - # context object should be separate from self for security - # because it will be accessed via the user defined template - self.init_context() - - self.set_pymodule() - self.update_context() - self.post_process_context() - self.setup_template() - html = self.render_template() - - html = self.update_toc(html) - html = self.add_csrf_token(html) - - return html - - def set_pymodule(self): - ''' - A template may have a python module with a `get_context` method along with it in the - same folder. Also the hyphens will be coverted to underscore for python module names. - This method sets the pymodule_name if it exists. - ''' - self.basepath = self.template_path.rsplit('.', 1)[0] - self.pymodule_name = None - - # replace - with _ in the internal modules names - self.pymodule_path = os.path.join(self.basepath.replace("-", "_") + ".py") - - if os.path.exists(os.path.join(self.app_path, self.pymodule_path)): - self.pymodule_name = self.app + "." + self.pymodule_path.replace(os.path.sep, ".")[:-3] - - def setup_template(self): - '''Setup template source, frontmatter and markdown conversion''' - self.source = self.get_raw_template() - - if self.template_path.endswith(('.md', '.html')): - self.extract_frontmatter() - - self.convert_from_markdown() - - if self.extends_template(): - self.context.base_template_path = self.context.base_template_path or 'templates/base.html' - else: - self.source = None # clear the source - - # TODO: setup index.txt ? - - def update_context(self): - self.set_page_properties() - - if self.pymodule_name: - self.pymodule = frappe.get_module(self.pymodule_name) - self.set_pymodule_properties() - - data = self.run_pymodule_method('get_context') - - # some methods may return a "context" object - if data: self.context.update(data) - - # TODO: self.context.children = self.run_pymodule_method('get_children') - - self.context.developer_mode = frappe.conf.developer_mode - if self.context.http_status_code: - self.http_status_code = self.context.http_status_code - - def set_pymodule_properties(self): - for prop in ("base_template_path", "template", "no_cache", "sitemap", - "condition_field"): - if hasattr(self.pymodule, prop): - self.context[prop] = getattr(self.pymodule, prop) - - def set_page_properties(self): - self.context.template = self.template_path - - def run_pymodule_method(self, method): - if hasattr(self.pymodule, method): - try: - return getattr(self.pymodule, method)(self.context) - except (frappe.PermissionError, frappe.DoesNotExistError, frappe.Redirect): - raise - except: - if not frappe.flags.in_migrate: - frappe.errprint(frappe.utils.get_traceback()) - - def render_template(self): - if self.source: - html = frappe.render_template(self.source, self.context) - elif self.template_path: - if self.path.endswith('min.js'): - html = self.get_raw_template() # static - else: - html = frappe.get_template(self.template_path).render(self.context) - - return html - - def extends_template(self): - return (self.template_path.endswith(('.html', '.md', )) - and ('{%- extends' in self.source - or '{% extends' in self.source)) - - def get_raw_template(self): - return frappe.get_jloader().get_source(frappe.get_jenv(), self.template_path)[0] - - def load_colocated_files(self): - '''load co-located css/js files with the same name''' - js_path = self.basepath + '.js' - if os.path.exists(js_path) and '{% block script %}' not in self.source: - self.colocated_js = self.get_colocated_file(js_path) - - css_path = self.basepath + '.css' - if os.path.exists(css_path) and '{% block style %}' not in self.source: - self.colocated_css = self.get_colocated_file(css_path) - - def get_colocated_file(self, path): - with io.open(path, 'r', encoding = 'utf-8') as f: - return f.read() - - def extract_frontmatter(self): - try: - # values will be used to update page_info - res = get_frontmatter(self.source) - if res['attributes']: - self.context.update(res['attributes']) - self.source = res['body'] - except Exception: - pass - - def convert_from_markdown(self): - if self.template_path.endswith('.md'): - self.source = frappe.utils.md_to_html(self.source) - self.page_toc_html = self.toc_html - - if not self.show_sidebar: - self.source = '
' + self.source + '
' - - def update_toc(self, html): - if '{index}' in html: - html = html.replace('{index}', get_toc(self.path)) - - if '{next}' in html: - html = html.replace('{next}', get_next_link(self.path)) - - return html - - def set_standard_path(self, path): - self.app = 'frappe' - self.app_path = frappe.get_app_path('frappe') - self.path = path - self.template_path = 'www/{path}.html'.format(path=path) - - -class ListPage(TemplatePage): - def validate(self): - if frappe.db.get_value('DocType', self.path): - frappe.local.form_dict.doctype = self.path - self.set_standard_path('list') - return True - return False - -class PrintPage(TemplatePage): - ''' - default path returns a printable object (based on permission) - /Quotation/Q-0001 - ''' - def validate(self): - parts = self.path.split('/', 1) - if len(parts)==2: - if (frappe.db.get_value('DocType', parts[0]) - and frappe.db.get_value(parts[0], parts[1])): - frappe.form_dict.doctype = parts[0] - frappe.form_dict.name = parts[1] - self.set_standard_path('printview') - return True - - return False - -class NotPermittedPage(TemplatePage): - def validate(self): - frappe.local.message_title = _("Not Permitted") - frappe.local.response['context'] = dict( - indicator_color = 'red', - primary_action = '/login', - primary_label = _('Login'), - fullpage=True - ) - self.set_standard_path('message') - return True - -class DocumentPage(BaseTemplatePage): - def validate(self): - ''' - Find a document with matching `route` from all doctypes with `has_web_view`=1 - ''' - if self.search_in_doctypes_with_web_view(): - return True - - if self.search_web_page_dynamic_routes(): - return True - - return False - - def search_in_doctypes_with_web_view(self): - for doctype in get_doctypes_with_web_view(): - filters = dict(route=self.path) - meta = frappe.get_meta(doctype) - condition_field = self.get_condition_field(meta) - - if condition_field: - filters[condition_field] = 1 - - try: - self.docname = frappe.db.get_value(doctype, filters, 'name') - if self.docname: - self.doctype = doctype - self.meta = meta - return True - except Exception as e: - if not frappe.db.is_missing_column(e): raise e - - def search_web_page_dynamic_routes(self): - d = get_page_info_from_web_page_with_dynamic_routes(self.path) - if d: - self.doctype = 'Web Page' - self.docname = d.name - self.meta = frappe.get_meta(self.doctype) - return True - else: - return False - - def render(self): - self.doc = frappe.get_doc(self.doctype, self.docname) - self.init_context() - self.update_context() - self.post_process_context() - - html = frappe.get_template(self.context.template_path).render(self.context) - html = self.add_csrf_token(html) - - return build_response(self.path, html, self.http_status_code or 200, self.headers) - - def update_context(self): - self.context.doc = self.doc - self.context.update(self.context.doc.as_dict()) - self.context.update(self.context.doc.get_website_properties()) - - if not self.context.template_path: - self.context.template_path = self.context.doc.meta.get_web_template() - - if hasattr(self.doc, "get_context"): - ret = self.doc.get_context(self.context) - - if ret: - self.context.update(ret) - - for prop in ("no_cache", "sitemap"): - if not prop in self.context: - self.context[prop] = getattr(self.doc, prop, False) - - def get_condition_field(self, meta): - condition_field = None - if meta.is_published_field: - condition_field = meta.is_published_field - elif not meta.custom: - controller = get_controller(meta.doctype) - condition_field = controller.website.condition_field - - return condition_field - -class WebFormPage(WebPage): - pass From ce3771ab74ab739d18960c50e1dd3826f5ffb4b1 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 12 Mar 2021 13:26:38 +0530 Subject: [PATCH 010/495] refactor: Move code to appropriate file add_sidebar_and_breadcrumbs belongs to TemplatePage --- .../page_controllers/base_template_page.py | 12 +++--------- frappe/website/page_controllers/template_page.py | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/frappe/website/page_controllers/base_template_page.py b/frappe/website/page_controllers/base_template_page.py index 10e2375356..8a5ec992cc 100644 --- a/frappe/website/page_controllers/base_template_page.py +++ b/frappe/website/page_controllers/base_template_page.py @@ -1,6 +1,7 @@ +import os + import frappe from frappe import _ -from frappe.website.context import add_sidebar_and_breadcrumbs from frappe.website.doctype.website_settings.website_settings import \ get_website_settings from frappe.website.page_controllers.web_page import WebPage @@ -15,15 +16,12 @@ class BaseTemplatePage(WebPage): def add_csrf_token(self, html): if frappe.local.session: return html.replace("", ''.format( - frappe.local.session.data.csrf_token)) + frappe.local.session.data.csrf_token)) else: return html def post_process_context(self): - self.set_missing_values() self.add_metatags() - self.add_sidebar_and_breadcrumbs() - self.set_base_template_if_missing() self.set_title_with_prefix() self.update_website_context() @@ -40,7 +38,6 @@ class BaseTemplatePage(WebPage): if self.context.template: self.template_path = self.context.template - def set_base_template_if_missing(self): if not self.context.base_template_path: app_base = frappe.get_hooks("base_template") @@ -69,9 +66,6 @@ class BaseTemplatePage(WebPage): self.context.metatags = self.tags - def add_sidebar_and_breadcrumbs(self): - add_sidebar_and_breadcrumbs(self.context) - def init_metatags_from_context(self): for key in ('title', 'description', 'image', 'author', 'url', 'published_on'): if not key in self.tags and self.context.get(key): diff --git a/frappe/website/page_controllers/template_page.py b/frappe/website/page_controllers/template_page.py index 46e6e6a57e..cfb2d2f310 100644 --- a/frappe/website/page_controllers/template_page.py +++ b/frappe/website/page_controllers/template_page.py @@ -4,6 +4,7 @@ import os import frappe from frappe import _ from frappe.website.page_controllers.base_template_page import BaseTemplatePage +from frappe.website.context import add_sidebar_and_breadcrumbs from frappe.website.render import build_response from frappe.website.utils import (extract_comment_tag, extract_title, get_next_link, get_toc) @@ -60,6 +61,14 @@ class TemplatePage(BaseTemplatePage): return html + def post_process_context(self): + self.add_sidebar_and_breadcrumbs() + self.set_missing_values() + super(TemplatePage, self).post_process_context() + + def add_sidebar_and_breadcrumbs(self): + add_sidebar_and_breadcrumbs(self.context) + def set_pymodule(self): ''' A template may have a python module with a `get_context` method along with it in the @@ -85,7 +94,7 @@ class TemplatePage(BaseTemplatePage): self.convert_from_markdown() if self.extends_template(): - self.context.base_template_path = self.context.base_template_path or 'templates/base.html' + self.context.base_template_path = self.context.base_template_path # TODO: setup index.txt ? @@ -246,9 +255,11 @@ class TemplatePage(BaseTemplatePage): if self.context.url_prefix and self.context.url_prefix[-1]!='/': self.context.url_prefix += '/' + self.context.path = self.path + self.context.pathname = frappe.local.path + # for backward compatibility self.context.docs_base_url = '/docs' - self.context.path = self.path def get_start_folders(): From c6cc70e7dfa2dddcde55acae1ffc21c3ba169a45 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 15 Mar 2021 10:13:40 +0530 Subject: [PATCH 011/495] fix: Document Page --- frappe/website/page_controllers/document_page.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/page_controllers/document_page.py b/frappe/website/page_controllers/document_page.py index bb00d78c0c..f13ea0c724 100644 --- a/frappe/website/page_controllers/document_page.py +++ b/frappe/website/page_controllers/document_page.py @@ -82,7 +82,7 @@ class DocumentPage(BaseTemplatePage): if meta.is_published_field: condition_field = meta.is_published_field elif not meta.custom: - controller = get_controller(meta.doctype) + controller = get_controller(meta.name) condition_field = controller.website.condition_field return condition_field From a5f274b5a3dbb72e109184b7a3c56b2a1dac13b4 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 15 Mar 2021 10:15:27 +0530 Subject: [PATCH 012/495] fix: Enable WebForm page --- frappe/website/serve.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/website/serve.py b/frappe/website/serve.py index 10eddfc641..ea27b07859 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -8,6 +8,7 @@ from frappe.website.page_controllers.not_permitted_page import NotPermittedPage from frappe.website.page_controllers.print_page import PrintPage from frappe.website.page_controllers.template_page import TemplatePage from frappe.website.page_controllers.static_page import StaticPage +from frappe.website.page_controllers.web_form import WebFormPage from frappe.website.redirect import resolve_redirect from frappe.website.render import build_response, resolve_path @@ -23,7 +24,6 @@ def get_response(path=None, http_status_code=200): path = path.strip('/ ') resolve_redirect(path, query_string) path = resolve_path(path) - data = None # there is no way to determine the type of the page based on the route # so evaluate each type of page sequentially @@ -32,6 +32,8 @@ def get_response(path=None, http_status_code=200): response = TemplatePage(path, http_status_code).get() if not response: response = ListPage(path, http_status_code).get() + if not response: + response = WebFormPage(path, http_status_code).get() if not response: response = DocumentPage(path, http_status_code).get() if not response: From 25a4eb07575094766111c5cca9b46f79dd1ed59f Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 29 Mar 2021 19:25:41 +0200 Subject: [PATCH 013/495] feat: google drive picker --- .../google_settings/google_settings.json | 12 ++- .../google_settings/test_google_settings.py | 10 +++ frappe/public/icons/social/google_drive.svg | 1 + .../js/frappe/file_uploader/FileUploader.vue | 46 ++++++++++- frappe/public/js/integrations/google_drive.js | 81 +++++++++++++++++++ 5 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 frappe/integrations/doctype/google_settings/test_google_settings.py create mode 100644 frappe/public/icons/social/google_drive.svg create mode 100644 frappe/public/js/integrations/google_drive.js diff --git a/frappe/integrations/doctype/google_settings/google_settings.json b/frappe/integrations/doctype/google_settings/google_settings.json index 086c56c020..6a4f181f2d 100644 --- a/frappe/integrations/doctype/google_settings/google_settings.json +++ b/frappe/integrations/doctype/google_settings/google_settings.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2019-06-14 00:08:37.255003", "doctype": "DocType", "engine": "InnoDB", @@ -8,7 +9,8 @@ "client_id", "client_secret", "sb_01", - "api_key" + "api_key", + "app_id" ], "fields": [ { @@ -46,10 +48,16 @@ "fieldname": "sb_01", "fieldtype": "Section Break", "label": "API Key" + }, + { + "fieldname": "app_id", + "fieldtype": "Data", + "label": "App ID" } ], "issingle": 1, - "modified": "2019-08-06 22:37:41.699703", + "links": [], + "modified": "2021-03-28 22:24:05.963403", "modified_by": "Administrator", "module": "Integrations", "name": "Google Settings", diff --git a/frappe/integrations/doctype/google_settings/test_google_settings.py b/frappe/integrations/doctype/google_settings/test_google_settings.py new file mode 100644 index 0000000000..476e772b58 --- /dev/null +++ b/frappe/integrations/doctype/google_settings/test_google_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestGoogleSettings(unittest.TestCase): + pass diff --git a/frappe/public/icons/social/google_drive.svg b/frappe/public/icons/social/google_drive.svg new file mode 100644 index 0000000000..d43b4d3dbd --- /dev/null +++ b/frappe/public/icons/social/google_drive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frappe/public/js/frappe/file_uploader/FileUploader.vue b/frappe/public/js/frappe/file_uploader/FileUploader.vue index d0b09c7593..61b87606a5 100644 --- a/frappe/public/js/frappe/file_uploader/FileUploader.vue +++ b/frappe/public/js/frappe/file_uploader/FileUploader.vue @@ -63,6 +63,12 @@
{{ __('Camera') }}
+
{{ upload_notes }} @@ -116,6 +122,7 @@ import FilePreview from './FilePreview.vue'; import FileBrowser from './FileBrowser.vue'; import WebLink from './WebLink.vue'; +import GoogleDrive from '../../integrations/google_drive'; export default { name: 'FileUploader', @@ -173,8 +180,16 @@ export default { currently_uploading: -1, show_file_browser: false, show_web_link: false, + allow_take_photo: false, + allow_google_drive: false } }, + created() { + this.allow_take_photo = window.navigator.mediaDevices; + frappe.db.get_single_value("Google Settings", "enable").then(resp => { + this.allow_google_drive = Boolean(resp); + }); + }, watch: { files(newvalue, oldvalue) { if (!this.allow_multiple && newvalue.length > 1) { @@ -187,9 +202,6 @@ export default { return this.files.length > 0 && this.files.every( file => file.total !== 0 && file.progress === file.total); - }, - allow_take_photo() { - return window.navigator.mediaDevices; } }, methods: { @@ -408,6 +420,10 @@ export default { form_data.append('file_url', file.file_url); } + if (file.file_name) { + form_data.append('file_name', file.file_name); + } + if (this.doctype && this.docname) { form_data.append('doctype', this.doctype); form_data.append('docname', this.docname); @@ -437,6 +453,30 @@ export default { ); }); }, + show_google_drive_picker() { + frappe.db.get_value("Google Settings", "Google Settings", ["client_id", "api_key", "app_id"]).then(resp => { + let dialog = cur_dialog; + dialog.hide(); + let google_drive = new GoogleDrive({ + pickerCallback: data => this.google_drive_callback(data, dialog), + developerKey: resp.message.api_key, + clientId: resp.message.client_id, + appId: resp.message.app_id + }); + google_drive.loadPicker(); + }); + }, + google_drive_callback(data, dialog) { + if (data.action == google.picker.Action.PICKED) { + // debugger; + this.upload_file({ + file_url: data.docs[0].url, + file_name: data.docs[0].name + }); + } else if (data.action == google.picker.Action.CANCEL) { + dialog.show(); + } + }, url_to_file(url, filename, mime_type) { return fetch(url) .then(res => res.arrayBuffer()) diff --git a/frappe/public/js/integrations/google_drive.js b/frappe/public/js/integrations/google_drive.js new file mode 100644 index 0000000000..9c6aea5375 --- /dev/null +++ b/frappe/public/js/integrations/google_drive.js @@ -0,0 +1,81 @@ +export default class GoogleDrive { + constructor({ + pickerCallback, + developerKey, + clientId, + appId + } = {}) { + console.log('GoogleDrive constructor'); + this.pickerCallback = pickerCallback; + this.pickerApiLoaded = false; + this.scope = ['https://www.googleapis.com/auth/drive.file']; + this.developerKey = developerKey; + this.clientId = clientId; + this.appId = appId; + } + + loadPicker() { + // load the google API library + $.ajax({ + method: "GET", + url: "https://apis.google.com/js/api.js", + dataType: "script", + cache: true, + context: this + }).done(function() { + this.loadGapi(); + }.bind(this)); + } + + loadGapi() { + // load auth and picker libraries + if (!frappe.boot.user.google_drive_token) { + gapi.load('auth', function() { + console.log('gapi.load("auth") callback'); + this.onAuthApiLoad(); + }.bind(this)); + } + + gapi.load('picker', function() { + console.log('gapi.load("picker") callback'); + this.onPickerApiLoad(); + }.bind(this)); + } + + onAuthApiLoad() { + gapi.auth.authorize({ + 'client_id': this.clientId, + 'scope': this.scope, + 'immediate': false + }, function(authResult) { + this.handleAuthResult(authResult); + }.bind(this)); + } + + handleAuthResult(authResult) { + if (authResult && !authResult.error) { + frappe.boot.user.google_drive_token = authResult.access_token; + this.createPicker(); + } + } + + onPickerApiLoad() { + this.pickerApiLoaded = true; + this.createPicker(); + } + + createPicker() { + // Create and render a Picker object for searching images. + if (this.pickerApiLoaded && frappe.boot.user.google_drive_token) { + var view = new google.picker.View(google.picker.ViewId.DOCS); + var picker = new google.picker.PickerBuilder() + .setAppId(this.appId) + .setOAuthToken(frappe.boot.user.google_drive_token) + .addView(view) + .setDeveloperKey(this.developerKey) + .setCallback(this.pickerCallback) + .build(); + picker.setVisible(true); + } + } +} From 7d333dbf703001c50dc80aa0a28477b741e9b51f Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 29 Mar 2021 21:09:06 +0200 Subject: [PATCH 014/495] fix: scope for google picker --- frappe/public/js/integrations/google_drive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/integrations/google_drive.js b/frappe/public/js/integrations/google_drive.js index 9c6aea5375..1c7e4dda21 100644 --- a/frappe/public/js/integrations/google_drive.js +++ b/frappe/public/js/integrations/google_drive.js @@ -8,7 +8,7 @@ export default class GoogleDrive { console.log('GoogleDrive constructor'); this.pickerCallback = pickerCallback; this.pickerApiLoaded = false; - this.scope = ['https://www.googleapis.com/auth/drive.file']; + this.scope = ['https://www.googleapis.com/auth/drive.readonly']; this.developerKey = developerKey; this.clientId = clientId; this.appId = appId; From c44cecae5db1ef7231b742ca84ade406224e17d6 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 29 Mar 2021 21:11:26 +0200 Subject: [PATCH 015/495] fix: remove log statements --- frappe/public/js/integrations/google_drive.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/frappe/public/js/integrations/google_drive.js b/frappe/public/js/integrations/google_drive.js index 1c7e4dda21..e7093fb29a 100644 --- a/frappe/public/js/integrations/google_drive.js +++ b/frappe/public/js/integrations/google_drive.js @@ -5,7 +5,6 @@ export default class GoogleDrive { clientId, appId } = {}) { - console.log('GoogleDrive constructor'); this.pickerCallback = pickerCallback; this.pickerApiLoaded = false; this.scope = ['https://www.googleapis.com/auth/drive.readonly']; @@ -31,13 +30,11 @@ export default class GoogleDrive { // load auth and picker libraries if (!frappe.boot.user.google_drive_token) { gapi.load('auth', function() { - console.log('gapi.load("auth") callback'); this.onAuthApiLoad(); }.bind(this)); } gapi.load('picker', function() { - console.log('gapi.load("picker") callback'); this.onPickerApiLoad(); }.bind(this)); } From f9dbbf69e0e89e3f36ac821994cfccb6a576f32d Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 29 Mar 2021 21:22:13 +0200 Subject: [PATCH 016/495] feat: better home view for google drive picker --- frappe/public/js/integrations/google_drive.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/integrations/google_drive.js b/frappe/public/js/integrations/google_drive.js index e7093fb29a..6b9ee9d0ff 100644 --- a/frappe/public/js/integrations/google_drive.js +++ b/frappe/public/js/integrations/google_drive.js @@ -64,7 +64,10 @@ export default class GoogleDrive { createPicker() { // Create and render a Picker object for searching images. if (this.pickerApiLoaded && frappe.boot.user.google_drive_token) { - var view = new google.picker.View(google.picker.ViewId.DOCS); + var view = new google.picker.DocsView(google.picker.ViewId.DOCS) + .setParent('root') // show the root folder by default + .setIncludeFolders(true); // also show folders, not just files + var picker = new google.picker.PickerBuilder() .setAppId(this.appId) .setOAuthToken(frappe.boot.user.google_drive_token) From 11f4edf051bf9842e58228faae2d2a349d2e763d Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 29 Mar 2021 21:22:37 +0200 Subject: [PATCH 017/495] feat: localize google drive picker --- frappe/public/js/integrations/google_drive.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/integrations/google_drive.js b/frappe/public/js/integrations/google_drive.js index 6b9ee9d0ff..076178046e 100644 --- a/frappe/public/js/integrations/google_drive.js +++ b/frappe/public/js/integrations/google_drive.js @@ -70,11 +70,13 @@ export default class GoogleDrive { var picker = new google.picker.PickerBuilder() .setAppId(this.appId) + .setDeveloperKey(this.developerKey) .setOAuthToken(frappe.boot.user.google_drive_token) .addView(view) - .setDeveloperKey(this.developerKey) + .setLocale(frappe.boot.lang) .setCallback(this.pickerCallback) .build(); + picker.setVisible(true); } } From 741011085b6ba3cccc86ba3514d9deff4eb82b54 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 30 Mar 2021 00:49:02 +0200 Subject: [PATCH 018/495] refactor: bind this --- frappe/public/js/integrations/google_drive.js | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/frappe/public/js/integrations/google_drive.js b/frappe/public/js/integrations/google_drive.js index 076178046e..06d9cbbe54 100644 --- a/frappe/public/js/integrations/google_drive.js +++ b/frappe/public/js/integrations/google_drive.js @@ -5,9 +5,9 @@ export default class GoogleDrive { clientId, appId } = {}) { - this.pickerCallback = pickerCallback; - this.pickerApiLoaded = false; this.scope = ['https://www.googleapis.com/auth/drive.readonly']; + this.pickerApiLoaded = false; + this.pickerCallback = pickerCallback; this.developerKey = developerKey; this.clientId = clientId; this.appId = appId; @@ -19,24 +19,17 @@ export default class GoogleDrive { method: "GET", url: "https://apis.google.com/js/api.js", dataType: "script", - cache: true, - context: this - }).done(function() { - this.loadGapi(); - }.bind(this)); + cache: true + }).done(this.loadGapi.bind(this)); } loadGapi() { // load auth and picker libraries if (!frappe.boot.user.google_drive_token) { - gapi.load('auth', function() { - this.onAuthApiLoad(); - }.bind(this)); + gapi.load('auth', this.onAuthApiLoad.bind(this)); } - gapi.load('picker', function() { - this.onPickerApiLoad(); - }.bind(this)); + gapi.load('picker', this.onPickerApiLoad.bind(this)); } onAuthApiLoad() { @@ -44,9 +37,7 @@ export default class GoogleDrive { 'client_id': this.clientId, 'scope': this.scope, 'immediate': false - }, function(authResult) { - this.handleAuthResult(authResult); - }.bind(this)); + }, this.handleAuthResult.bind(this)); } handleAuthResult(authResult) { From a018e7b4458c028ebed29d8e889ae72f086d0479 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 30 Mar 2021 01:28:12 +0200 Subject: [PATCH 019/495] refactor: rename GoogleDrive -> GoogleDrivePicker --- frappe/public/js/frappe/file_uploader/FileUploader.vue | 4 ++-- .../integrations/{google_drive.js => google_drive_picker.js} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename frappe/public/js/integrations/{google_drive.js => google_drive_picker.js} (98%) diff --git a/frappe/public/js/frappe/file_uploader/FileUploader.vue b/frappe/public/js/frappe/file_uploader/FileUploader.vue index 61b87606a5..154d4e0ea1 100644 --- a/frappe/public/js/frappe/file_uploader/FileUploader.vue +++ b/frappe/public/js/frappe/file_uploader/FileUploader.vue @@ -122,7 +122,7 @@ import FilePreview from './FilePreview.vue'; import FileBrowser from './FileBrowser.vue'; import WebLink from './WebLink.vue'; -import GoogleDrive from '../../integrations/google_drive'; +import GoogleDrivePicker from '../../integrations/google_drive_picker'; export default { name: 'FileUploader', @@ -457,7 +457,7 @@ export default { frappe.db.get_value("Google Settings", "Google Settings", ["client_id", "api_key", "app_id"]).then(resp => { let dialog = cur_dialog; dialog.hide(); - let google_drive = new GoogleDrive({ + let google_drive = new GoogleDrivePicker({ pickerCallback: data => this.google_drive_callback(data, dialog), developerKey: resp.message.api_key, clientId: resp.message.client_id, diff --git a/frappe/public/js/integrations/google_drive.js b/frappe/public/js/integrations/google_drive_picker.js similarity index 98% rename from frappe/public/js/integrations/google_drive.js rename to frappe/public/js/integrations/google_drive_picker.js index 06d9cbbe54..7ce9810f72 100644 --- a/frappe/public/js/integrations/google_drive.js +++ b/frappe/public/js/integrations/google_drive_picker.js @@ -1,4 +1,4 @@ -export default class GoogleDrive { +export default class GoogleDrivePicker { constructor({ pickerCallback, developerKey, From 144608241e9825af20c88b122a70f29d22aa5a41 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 30 Mar 2021 02:20:11 +0200 Subject: [PATCH 020/495] refactor: save one API call --- .../google_settings/google_settings.py | 22 +++++++++++++-- .../js/frappe/file_uploader/FileUploader.vue | 28 +++++++++---------- .../js/integrations/google_drive_picker.js | 8 ++++-- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/frappe/integrations/doctype/google_settings/google_settings.py b/frappe/integrations/doctype/google_settings/google_settings.py index ecc975235a..bd0e845977 100644 --- a/frappe/integrations/doctype/google_settings/google_settings.py +++ b/frappe/integrations/doctype/google_settings/google_settings.py @@ -3,11 +3,29 @@ # For license information, please see license.txt from __future__ import unicode_literals -# import frappe +import frappe from frappe.model.document import Document class GoogleSettings(Document): pass def get_auth_url(): - return "https://www.googleapis.com/oauth2/v4/token" \ No newline at end of file + return "https://www.googleapis.com/oauth2/v4/token" + + +@frappe.whitelist(allow_guest=True) +def get_file_picker_settings(): + """Return all the data FileUploader needs to start the Google Drive Picker.""" + if frappe.session.user == 'Guest': + return {'enabled': False} + + google_settings = frappe.get_single("Google Settings") + if not google_settings.enable: + return {'enabled': False} + + return { + 'enabled': True, + 'appId': google_settings.app_id, + 'developerKey': google_settings.api_key, + 'clientId': google_settings.client_id + } diff --git a/frappe/public/js/frappe/file_uploader/FileUploader.vue b/frappe/public/js/frappe/file_uploader/FileUploader.vue index 154d4e0ea1..64365cfc11 100644 --- a/frappe/public/js/frappe/file_uploader/FileUploader.vue +++ b/frappe/public/js/frappe/file_uploader/FileUploader.vue @@ -63,7 +63,7 @@
{{ __('Camera') }}
- \ - ')); - // add event handler for submit button - verify_token(); + $('.login-content').empty(); + $('.login-content:visible').append($('
').attr({ 'id': 'twofactor_div' }).html('{{ _("Verification") }}{{ _("Verify") }}')); + // add event handler for submit button + verify_token(); } var continue_otp_app = function (setup, qrcode) { From 2b51641799c60ae9b16a1d29e4306d6c35e66070 Mon Sep 17 00:00:00 2001 From: conncampbell Date: Mon, 10 May 2021 16:14:51 -0600 Subject: [PATCH 041/495] fix: changed translate entries to javascript rather than python to resolve semgrep linter translate checks --- frappe/templates/includes/login/login.js | 56 ++++++++++++++---------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/frappe/templates/includes/login/login.js b/frappe/templates/includes/login/login.js index 93bf95bdb7..4bc17cb14b 100644 --- a/frappe/templates/includes/login/login.js +++ b/frappe/templates/includes/login/login.js @@ -21,7 +21,7 @@ login.bind_events = function () { args.pwd = $("#login_password").val(); args.device = "desktop"; if (!args.usr || !args.pwd) { - frappe.msgprint('{{ _("Both login and password required") }}'); + frappe.msgprint(__("Both login and password required")); return false; } login.call(args); @@ -36,7 +36,7 @@ login.bind_events = function () { args.redirect_to = frappe.utils.sanitise_redirect(frappe.utils.get_url_arg("redirect-to")); args.full_name = frappe.utils.xss_sanitise(($("#signup_fullname").val() || "").trim()); if (!args.email || !validate_email(args.email) || !args.full_name) { - login.set_status('{{ _("Valid email and name required") }}', 'red'); + login.set_status(__("Valid email and name required"), 'red'); return false; } login.call(args); @@ -49,7 +49,7 @@ login.bind_events = function () { args.cmd = "frappe.core.doctype.user.user.reset_password"; args.user = ($("#forgot_email").val() || "").trim(); if (!args.user) { - login.set_status('{{ _("Valid Login id required.") }}', 'red'); + login.set_status(__("Valid Login id required."), 'red'); return false; } login.call(args); @@ -60,10 +60,10 @@ login.bind_events = function () { var input = $($(this).attr("toggle")); if (input.attr("type") == "password") { input.attr("type", "text"); - $(this).text('{{ _("Hide") }}') + $(this).text(__("Hide")) } else { input.attr("type", "password"); - $(this).text('{{ _("Show") }}') + $(this).text(__("Show")) } }); @@ -75,7 +75,7 @@ login.bind_events = function () { args.pwd = $("#login_password").val(); args.device = "desktop"; if (!args.usr || !args.pwd) { - login.set_status('{{ _("Both login and password required") }}', 'red'); + login.set_status(__("Both login and password required"), 'red'); return false; } login.call(args); @@ -136,7 +136,7 @@ login.signup = function () { // Login login.call = function (args, callback) { - login.set_status('{{ _("Verifying...") }}', 'blue'); + login.set_status(__("Verifying..."), 'blue'); return frappe.call({ type: "POST", @@ -194,7 +194,7 @@ login.login_handlers = (function () { var login_handlers = { 200: function (data) { if (data.message == 'Logged In') { - login.set_status('{{ _("Success") }}', 'green'); + login.set_status(__("Success"), 'green'); window.location.href = frappe.utils.sanitise_redirect(frappe.utils.get_url_arg("redirect-to")) || data.home_page; } else if (data.message == 'Password Reset') { window.location.href = frappe.utils.sanitise_redirect(data.redirect_to); @@ -218,13 +218,13 @@ login.login_handlers = (function () { } } else if (window.location.hash === '#forgot') { if (data.message === 'not found') { - login.set_status('{{ _("Not a valid user") }}', 'red'); + login.set_status(__("Not a valid user"), 'red'); } else if (data.message == 'not allowed') { - login.set_status('{{ _("Not Allowed") }}', 'red'); + login.set_status(__("Not Allowed"), 'red'); } else if (data.message == 'disabled') { - login.set_status('{{ _("Not Allowed: Disabled User") }}', 'red'); + login.set_status(__("Not Allowed: Disabled User"), 'red'); } else { - login.set_status('{{ _("Instructions Emailed") }}', 'green'); + login.set_status(__("Instructions Emailed"), 'green'); } @@ -232,7 +232,7 @@ login.login_handlers = (function () { if (cint(data.message[0]) == 0) { login.set_status(data.message[1], 'red'); } else { - login.set_status('{{ _("Success") }}', 'green'); + login.set_status(__("Success"), 'green'); frappe.msgprint(data.message[1]) } //login.set_status(__(data.message), 'green'); @@ -240,7 +240,7 @@ login.login_handlers = (function () { //OTP verification if (data.verification && data.message != 'Logged In') { - login.set_status('{{ _("Success") }}', 'green'); + login.set_status(__("Success"), 'green'); document.cookie = "tmp_id=" + data.tmp_id; @@ -253,8 +253,8 @@ login.login_handlers = (function () { } } }, - 401: get_error_handler('{{ _("Invalid Login. Try again.") }}'), - 417: get_error_handler('{{ _("Oops! Something went wrong") }}') + 401: get_error_handler(__("Invalid Login. Try again.")), + 417: get_error_handler(__("Oops! Something went wrong")) }; return login_handlers; @@ -282,7 +282,7 @@ var verify_token = function (event) { args.otp = $("#login_token").val(); args.tmp_id = frappe.get_cookie('tmp_id'); if (!args.otp) { - frappe.msgprint('{{ _("Login token required") }}'); + frappe.msgprint(__("Login token required")); return false; } login.call(args); @@ -292,9 +292,17 @@ var verify_token = function (event) { var request_otp = function (r) { $('.login-content').empty(); - $('.login-content:visible').append($('
').attr({ 'id': 'twofactor_div' }).html('{{ _("Verification") }}{{ _("Verify") }}')); - // add event handler for submit button - verify_token(); + $('.login-content:visible').append($('
').attr({ 'id': 'twofactor_div' }).html( + '
\ +
\ + {{ _("Verification") }}\ +
\ +
\ + \ + \ +
')); + // add event handler for submit button + verify_token(); } var continue_otp_app = function (setup, qrcode) { @@ -302,11 +310,11 @@ var continue_otp_app = function (setup, qrcode) { var qrcode_div = $('
'); if (setup) { - direction = $('
').attr('id', 'qr_info').text('{{ _("Enter Code displayed in OTP App.") }}'); + direction = $('
').attr('id', 'qr_info').text(__("Enter Code displayed in OTP App.")); qrcode_div.append(direction); $('#otp_div').prepend(qrcode_div); } else { - direction = $('
').attr('id', 'qr_info').text('{{ _("OTP setup using OTP App was not completed. Please contact Administrator.") }}'); + direction = $('
').attr('id', 'qr_info').text(__("OTP setup using OTP App was not completed. Please contact Administrator.")); qrcode_div.append(direction); $('#otp_div').prepend(qrcode_div); } @@ -320,7 +328,7 @@ var continue_sms = function (setup, prompt) { sms_div.append(prompt) $('#otp_div').prepend(sms_div); } else { - direction = $('
').attr('id', 'qr_info').text(prompt || '{{ _("SMS was not sent. Please contact Administrator.") }}'); + direction = $('
').attr('id', 'qr_info').text(prompt || __("SMS was not sent. Please contact Administrator.")); sms_div.append(direction); $('#otp_div').prepend(sms_div) } @@ -334,7 +342,7 @@ var continue_email = function (setup, prompt) { email_div.append(prompt) $('#otp_div').prepend(email_div); } else { - var direction = $('
').attr('id', 'qr_info').text(prompt || '{{ _("Verification code email not sent. Please contact Administrator.") }}'); + var direction = $('
').attr('id', 'qr_info').text(prompt || __("Verification code email not sent. Please contact Administrator.")); email_div.append(direction); $('#otp_div').prepend(email_div); } From e101105bce30b7eae0a4f3371566ba4e85e0606a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 11 May 2021 10:41:18 +0530 Subject: [PATCH 042/495] refactor: Import get_response instead of using serve.get_response --- frappe/tests/test_website.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index 97e00b9268..5221cbe6b7 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -3,8 +3,7 @@ from __future__ import unicode_literals import unittest import frappe -from frappe.website import render -from frappe.website import serve +from frappe.website.serve import get_response from frappe.website.utils import get_home_page from frappe.utils import set_request @@ -51,7 +50,7 @@ class TestWebsite(unittest.TestCase): def test_page_load(self): set_request(method='POST', path='login') - response = serve.get_response() + response = get_response() self.assertEqual(response.status_code, 200) @@ -62,17 +61,17 @@ class TestWebsite(unittest.TestCase): def test_static_page(self): set_request(method='GET', path='/_test/static-file-test.png') - response = serve.get_response() + response = get_response() self.assertEqual(response.status_code, 200) def test_error_page(self): set_request(method='GET', path='/error') - response = serve.get_response() + response = get_response() self.assertEqual(response.status_code, 500) def test_login(self): set_request(method='GET', path='/login') - response = serve.get_response() + response = get_response() self.assertEqual(response.status_code, 200) html = frappe.safe_decode(response.get_data()) @@ -83,7 +82,7 @@ class TestWebsite(unittest.TestCase): def test_app(self): frappe.set_user('Administrator') set_request(method='GET', path='/app') - response = serve.get_response() + response = get_response() self.assertEqual(response.status_code, 200) html = frappe.safe_decode(response.get_data()) @@ -92,7 +91,7 @@ class TestWebsite(unittest.TestCase): def test_not_found(self): set_request(method='GET', path='/_test/missing') - response = serve.get_response() + response = get_response() self.assertEqual(response.status_code, 404) @@ -118,31 +117,31 @@ class TestWebsite(unittest.TestCase): frappe.cache().delete_key('website_redirects') set_request(method='GET', path='/testfrom') - response = serve.get_response() + response = get_response() self.assertEqual(response.status_code, 301) self.assertEqual(response.headers.get('Location'), r'://testto1') set_request(method='GET', path='/testfromregex/test') - response = serve.get_response() + response = get_response() self.assertEqual(response.status_code, 301) self.assertEqual(response.headers.get('Location'), r'://testto2') set_request(method='GET', path='/testsub/me') - response = serve.get_response() + response = get_response() self.assertEqual(response.status_code, 301) self.assertEqual(response.headers.get('Location'), r'://testto3/me') set_request(method='GET', path='/test404') - response = serve.get_response() + response = get_response() self.assertEqual(response.status_code, 404) set_request(method='GET', path='/testsource') - response = serve.get_response() + response = get_response() self.assertEqual(response.status_code, 301) self.assertEqual(response.headers.get('Location'), '/testtarget') set_request(method='GET', path='/courses/course?course=data') - response = serve.get_response() + response = get_response() self.assertEqual(response.status_code, 301) self.assertEqual(response.headers.get('Location'), '/courses/data') From 5c92e8a12962a4c8acd9037c81ae81433357b56f Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 11 May 2021 16:43:22 +0530 Subject: [PATCH 043/495] feat: Add class for standard pages (Error & NotFound) --- frappe/website/page_controllers/error_page.py | 9 +++++++++ frappe/website/page_controllers/not_found_page.py | 9 +++++++++ frappe/website/page_controllers/not_permitted_page.py | 4 ++++ 3 files changed, 22 insertions(+) create mode 100644 frappe/website/page_controllers/error_page.py create mode 100644 frappe/website/page_controllers/not_found_page.py diff --git a/frappe/website/page_controllers/error_page.py b/frappe/website/page_controllers/error_page.py new file mode 100644 index 0000000000..caa72b015d --- /dev/null +++ b/frappe/website/page_controllers/error_page.py @@ -0,0 +1,9 @@ +from frappe import _ +from frappe.website.page_controllers.not_found_page import NotFoundPage +from frappe.website.page_controllers.template_page import TemplatePage + +class ErrorPage(TemplatePage): + def __init__(self, path, http_status_code, exception): + path = 'error' + super().__init__(path=path, http_status_code=http_status_code) + self.http_status_code = getattr(exception, 'http_status_code', None) or http_status_code or 500 diff --git a/frappe/website/page_controllers/not_found_page.py b/frappe/website/page_controllers/not_found_page.py new file mode 100644 index 0000000000..d1a1797efc --- /dev/null +++ b/frappe/website/page_controllers/not_found_page.py @@ -0,0 +1,9 @@ +from frappe import _ +from frappe.website.page_controllers.template_page import TemplatePage + +class NotFoundPage(TemplatePage): + def __init__(self, path, http_status_code): + super().__init__(path=path, http_status_code=http_status_code) + self.path = '404' + self.http_status_code = 404 + self.template_path = '404' diff --git a/frappe/website/page_controllers/not_permitted_page.py b/frappe/website/page_controllers/not_permitted_page.py index e0cd6bf970..61c6b88e09 100644 --- a/frappe/website/page_controllers/not_permitted_page.py +++ b/frappe/website/page_controllers/not_permitted_page.py @@ -3,6 +3,10 @@ from frappe import _ from frappe.website.page_controllers.template_page import TemplatePage class NotPermittedPage(TemplatePage): + def __init__(self, path, http_status_code): + super().__init__(path=path, http_status_code=http_status_code) + self.http_status_code = 403 + def validate(self): frappe.local.message_title = _("Not Permitted") frappe.local.response['context'] = dict( From 9429d06a17f6cc51716ff40e51b36150d860568f Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 11 May 2021 16:44:50 +0530 Subject: [PATCH 044/495] refactor: Remove reduntant code - Use Error & NotFound page as required --- frappe/tests/test_website.py | 2 +- frappe/website/serve.py | 22 ++++++++-------------- frappe/www/_test/problematic_page.html | 1 + 3 files changed, 10 insertions(+), 15 deletions(-) create mode 100644 frappe/www/_test/problematic_page.html diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index 5221cbe6b7..cc500d1c02 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -65,7 +65,7 @@ class TestWebsite(unittest.TestCase): self.assertEqual(response.status_code, 200) def test_error_page(self): - set_request(method='GET', path='/error') + set_request(method='GET', path='/_test/problematic_page') response = get_response() self.assertEqual(response.status_code, 500) diff --git a/frappe/website/serve.py b/frappe/website/serve.py index 1a5852d036..b6586dc1f3 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -2,7 +2,9 @@ import frappe from frappe.utils import cstr from frappe.website.page_controllers.document_page import DocumentPage +from frappe.website.page_controllers.error_page import ErrorPage from frappe.website.page_controllers.list_page import ListPage +from frappe.website.page_controllers.not_found_page import NotFoundPage from frappe.website.page_controllers.not_permitted_page import NotPermittedPage from frappe.website.page_controllers.print_page import PrintPage from frappe.website.page_controllers.template_page import TemplatePage @@ -26,19 +28,11 @@ def get_response(path=None, http_status_code=200): # there is no way to determine the type of the page based on the route # so evaluate each type of page sequentially - response = StaticPage(path, http_status_code).get() - if not response: - response = TemplatePage(path, http_status_code).get() - if not response: - response = ListPage(path, http_status_code).get() - if not response: - response = WebFormPage(path, http_status_code).get() - if not response: - response = DocumentPage(path, http_status_code).get() - if not response: - response = PrintPage(path, http_status_code).get() - if not response: - response = TemplatePage('404', 404).get() + renderers = [StaticPage, TemplatePage, ListPage, WebFormPage, DocumentPage, PrintPage, NotFoundPage] + for resolver in renderers: + response = resolver(path, http_status_code).get() + if response: + break except frappe.Redirect: return build_response(path, "", 301, { "Location": frappe.flags.redirect_location or (frappe.local.response or {}).get('location'), @@ -48,6 +42,6 @@ def get_response(path=None, http_status_code=200): frappe.local.message = cstr(e) response = NotPermittedPage(path, http_status_code).get() except Exception as e: - response = TemplatePage('error', getattr(e, 'http_status_code', None) or http_status_code).get() + response = ErrorPage(path, http_status_code, exception=e).get() return response diff --git a/frappe/www/_test/problematic_page.html b/frappe/www/_test/problematic_page.html new file mode 100644 index 0000000000..5e194421d2 --- /dev/null +++ b/frappe/www/_test/problematic_page.html @@ -0,0 +1 @@ +{% raise %} From 006f0a2bafb9945c8fdf6fcb41c015e45b5967a4 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 11 May 2021 17:03:00 +0530 Subject: [PATCH 045/495] fix: Rename resolver to renderer --- frappe/website/page_controllers/error_page.py | 2 -- frappe/website/page_controllers/not_found_page.py | 1 - frappe/website/serve.py | 7 ++++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/frappe/website/page_controllers/error_page.py b/frappe/website/page_controllers/error_page.py index caa72b015d..1f54a5f38c 100644 --- a/frappe/website/page_controllers/error_page.py +++ b/frappe/website/page_controllers/error_page.py @@ -1,5 +1,3 @@ -from frappe import _ -from frappe.website.page_controllers.not_found_page import NotFoundPage from frappe.website.page_controllers.template_page import TemplatePage class ErrorPage(TemplatePage): diff --git a/frappe/website/page_controllers/not_found_page.py b/frappe/website/page_controllers/not_found_page.py index d1a1797efc..1d15877e9d 100644 --- a/frappe/website/page_controllers/not_found_page.py +++ b/frappe/website/page_controllers/not_found_page.py @@ -1,4 +1,3 @@ -from frappe import _ from frappe.website.page_controllers.template_page import TemplatePage class NotFoundPage(TemplatePage): diff --git a/frappe/website/serve.py b/frappe/website/serve.py index b6586dc1f3..eacaef96e1 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -17,6 +17,8 @@ from frappe.website.render import build_response, resolve_path def get_response(path=None, http_status_code=200): """render html page""" query_string = None + response = None + if not path: path = frappe.local.request.path query_string = frappe.local.request.query_string @@ -25,12 +27,11 @@ def get_response(path=None, http_status_code=200): path = path.strip('/ ') resolve_redirect(path, query_string) path = resolve_path(path) - # there is no way to determine the type of the page based on the route # so evaluate each type of page sequentially renderers = [StaticPage, TemplatePage, ListPage, WebFormPage, DocumentPage, PrintPage, NotFoundPage] - for resolver in renderers: - response = resolver(path, http_status_code).get() + for renderer in renderers: + response = renderer(path, http_status_code).get() if response: break except frappe.Redirect: From 4754ab71d1cf3099b66b63bf77a770a16c64736f Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 9 May 2021 19:44:58 +0530 Subject: [PATCH 046/495] perf(minor): remove unnecessary comprehensions - remove several unnecessary comprehensions from functions that accept a generator. - Using `[x for x in iter]` causes a list to be built first then passed to the outer function. - `any` and `all` can take generator instead. This makes memory usage O(1) and actually makes these functions short-circuiting. E.g. if the first condition fails then `all` will immediately return false instead of evaluating all the entries. - `sum`, `min`, `max` => memory usage become O(1) - `list`, `set`, `.join()` => roughly halves memory usage, as list is not required to be built. - lastly, it's two fewer characters to read/think about. --- frappe/automation/doctype/auto_repeat/auto_repeat.py | 2 +- frappe/contacts/address_and_contact.py | 2 +- frappe/contacts/doctype/address/address.py | 2 +- frappe/core/doctype/data_import/importer.py | 12 ++++++------ frappe/core/doctype/data_import_legacy/importer.py | 4 ++-- frappe/core/doctype/doctype/doctype.py | 6 +++--- frappe/core/doctype/domain/domain.py | 4 ++-- frappe/core/doctype/user/user.py | 2 +- frappe/core/doctype/user_type/user_type.py | 2 +- .../custom/doctype/customize_form/customize_form.py | 2 +- frappe/database/database.py | 4 ++-- .../global_search_settings/global_search_settings.py | 4 ++-- frappe/desk/doctype/tag/tag.py | 2 +- frappe/email/doctype/email_account/email_account.py | 6 +++--- frappe/email/doctype/newsletter/test_newsletter.py | 2 +- frappe/email/inbox.py | 2 +- frappe/installer.py | 2 +- .../doctype/ldap_settings/ldap_settings.py | 2 +- frappe/model/__init__.py | 4 ++-- frappe/model/base_document.py | 4 ++-- frappe/model/db_query.py | 10 +++++----- frappe/model/meta.py | 2 +- frappe/model/rename_doc.py | 2 +- frappe/permissions.py | 2 +- frappe/tests/test_commands.py | 8 ++++---- frappe/translate.py | 4 ++-- frappe/utils/__init__.py | 4 ++-- frappe/utils/backups.py | 6 +++--- frappe/utils/bot.py | 8 ++++---- frappe/utils/jinja_globals.py | 4 ++-- frappe/website/doctype/web_form/web_form.py | 2 +- .../doctype/website_slideshow/website_slideshow.py | 2 +- 32 files changed, 62 insertions(+), 62 deletions(-) diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py index bf05baf5b6..37285ad571 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py @@ -334,7 +334,7 @@ class AutoRepeat(Document): if self.reference_doctype and self.reference_document: res = get_contacts_linking_to(self.reference_doctype, self.reference_document, fields=['email_id']) res += get_contacts_linked_from(self.reference_doctype, self.reference_document, fields=['email_id']) - email_ids = list(set([d.email_id for d in res])) + email_ids = set(d.email_id for d in res) if not email_ids: frappe.msgprint(_('No contacts linked to document'), alert=True) else: diff --git a/frappe/contacts/address_and_contact.py b/frappe/contacts/address_and_contact.py index 3ca9547188..43aa39f678 100644 --- a/frappe/contacts/address_and_contact.py +++ b/frappe/contacts/address_and_contact.py @@ -154,7 +154,7 @@ def filter_dynamic_link_doctypes(doctype, txt, searchfield, start, page_len, fil doctypes = frappe.db.get_all("DocField", filters=filters, fields=["parent"], distinct=True, as_list=True) - doctypes = tuple([d for d in doctypes if re.search(txt+".*", _(d[0]), re.IGNORECASE)]) + doctypes = tuple(d for d in doctypes if re.search(txt+".*", _(d[0]), re.IGNORECASE)) filters.update({ "dt": ("not in", [d[0] for d in doctypes]) diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py index 84b925d50e..a6fd7b1755 100644 --- a/frappe/contacts/doctype/address/address.py +++ b/frappe/contacts/doctype/address/address.py @@ -263,7 +263,7 @@ def address_query(doctype, txt, searchfield, start, page_len, filters): def get_condensed_address(doc): fields = ["address_title", "address_line1", "address_line2", "city", "county", "state", "country"] - return ", ".join([doc.get(d) for d in fields if doc.get(d)]) + return ", ".join(doc.get(d) for d in fields if doc.get(d)) def update_preferred_address(address, field): frappe.db.set_value('Address', address, field, 0) diff --git a/frappe/core/doctype/data_import/importer.py b/frappe/core/doctype/data_import/importer.py index 720fe1dda7..841319dd60 100644 --- a/frappe/core/doctype/data_import/importer.py +++ b/frappe/core/doctype/data_import/importer.py @@ -450,7 +450,7 @@ class ImportFile: for row in data_without_first_row: row_values = row.get_values(parent_column_indexes) # if the row is blank, it's a child row doc - if all([v in INVALID_VALUES for v in row_values]): + if all(v in INVALID_VALUES for v in row_values): rows.append(row) continue # if we encounter a row which has values in parent columns, @@ -607,7 +607,7 @@ class Row: if df.fieldtype == "Select": select_options = get_select_options(df) if select_options and value not in select_options: - options_string = ", ".join([frappe.bold(d) for d in select_options]) + options_string = ", ".join(frappe.bold(d) for d in select_options) msg = _("Value must be one of {0}").format(options_string) self.warnings.append( {"row": self.row_number, "field": df_as_json(df), "message": msg,} @@ -903,7 +903,7 @@ class Column: if self.df.fieldtype == "Link": # find all values that dont exist - values = list(set([cstr(v) for v in self.column_values[1:] if v])) + values = list(set(cstr(v) for v in self.column_values[1:] if v)) exists = [ d.name for d in frappe.db.get_all(self.df.options, filters={"name": ("in", values)}) ] @@ -939,11 +939,11 @@ class Column: elif self.df.fieldtype == "Select": options = get_select_options(self.df) if options: - values = list(set([cstr(v) for v in self.column_values[1:] if v])) + values = list(set(cstr(v) for v in self.column_values[1:] if v)) invalid = list(set(values) - set(options)) if invalid: - valid_values = ", ".join([frappe.bold(o) for o in options]) - invalid_values = ", ".join([frappe.bold(i) for i in invalid]) + valid_values = ", ".join(frappe.bold(o) for o in options) + invalid_values = ", ".join(frappe.bold(i) for i in invalid) self.warnings.append( { "col": self.column_number, diff --git a/frappe/core/doctype/data_import_legacy/importer.py b/frappe/core/doctype/data_import_legacy/importer.py index 35569c7186..b4be57be4f 100644 --- a/frappe/core/doctype/data_import_legacy/importer.py +++ b/frappe/core/doctype/data_import_legacy/importer.py @@ -181,7 +181,7 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, if d.get("name") and d["name"].startswith('"'): d["name"] = d["name"][1:-1] - if sum([0 if not val else 1 for val in d.values()]): + if sum(0 if not val else 1 for val in d.values()): d['doctype'] = dt if dt == doctype: doc.update(d) @@ -537,6 +537,6 @@ def get_parent_field(doctype, parenttype): def delete_child_rows(rows, doctype): """delete child rows for all parents""" - for p in list(set([r[1] for r in rows])): + for p in list(set(r[1] for r in rows)): if p: frappe.db.sql("""delete from `tab{0}` where parent=%s""".format(doctype), p) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index aff07836c0..582c5d3d89 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -196,7 +196,7 @@ class DocType(Document): self.flags.update_fields_to_fetch_queries = [] - if set(old_fields_to_fetch) != set([df.fieldname for df in new_meta.get_fields_to_fetch()]): + if set(old_fields_to_fetch) != set(df.fieldname for df in new_meta.get_fields_to_fetch()): for df in new_meta.get_fields_to_fetch(): if df.fieldname not in old_fields_to_fetch: link_fieldname, source_fieldname = df.fetch_from.split('.', 1) @@ -765,7 +765,7 @@ def validate_fields(meta): invalid_fields = ('doctype',) if fieldname in invalid_fields: frappe.throw(_("{0}: Fieldname cannot be one of {1}") - .format(docname, ", ".join([frappe.bold(d) for d in invalid_fields]))) + .format(docname, ", ".join(frappe.bold(d) for d in invalid_fields))) def check_unique_fieldname(docname, fieldname): duplicates = list(filter(None, map(lambda df: df.fieldname==fieldname and str(df.idx) or None, fields))) @@ -999,7 +999,7 @@ def validate_fields(meta): if docfield.options and (docfield.options not in data_field_options): df_str = frappe.bold(_(docfield.label)) text_str = _("{0} is an invalid Data field.").format(df_str) + "
" * 2 + _("Only Options allowed for Data field are:") + "
" - df_options_str = "
  • " + "
  • ".join([_(x) for x in data_field_options]) + "
" + df_options_str = "
  • " + "
  • ".join(_(x) for x in data_field_options) + "
" frappe.msgprint(text_str + df_options_str, title="Invalid Data Field", raise_exception=True) diff --git a/frappe/core/doctype/domain/domain.py b/frappe/core/doctype/domain/domain.py index a4e9f503ab..4f320578fb 100644 --- a/frappe/core/doctype/domain/domain.py +++ b/frappe/core/doctype/domain/domain.py @@ -111,7 +111,7 @@ class Domain(Document): # enable frappe.db.sql('''update `tabPortal Menu Item` set enabled=1 - where route in ({0})'''.format(', '.join(['"{0}"'.format(d) for d in self.data.allow_sidebar_items]))) + where route in ({0})'''.format(', '.join('"{0}"'.format(d) for d in self.data.allow_sidebar_items))) if self.data.remove_sidebar_items: # disable all @@ -119,4 +119,4 @@ class Domain(Document): # enable frappe.db.sql('''update `tabPortal Menu Item` set enabled=0 - where route in ({0})'''.format(', '.join(['"{0}"'.format(d) for d in self.data.remove_sidebar_items]))) + where route in ({0})'''.format(', '.join('"{0}"'.format(d) for d in self.data.remove_sidebar_items))) diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index a4d13a57e0..979a300a4d 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -935,7 +935,7 @@ def user_query(doctype, txt, searchfield, start, page_len, filters): LIMIT %(page_len)s OFFSET %(start)s """.format( user_type_condition = user_type_condition, - standard_users=", ".join([frappe.db.escape(u) for u in STANDARD_USERS]), + standard_users=", ".join(frappe.db.escape(u) for u in STANDARD_USERS), key=searchfield, fcond=get_filters_cond(doctype, filters, conditions), mcond=get_match_cond(doctype) diff --git a/frappe/core/doctype/user_type/user_type.py b/frappe/core/doctype/user_type/user_type.py index 0e8b692416..b3965d9b84 100644 --- a/frappe/core/doctype/user_type/user_type.py +++ b/frappe/core/doctype/user_type/user_type.py @@ -114,7 +114,7 @@ class UserType(Document): self.select_doctypes = [] select_doctypes = [] - user_doctypes = tuple([row.document_type for row in self.user_doctypes]) + user_doctypes = tuple(row.document_type for row in self.user_doctypes) for doctype in user_doctypes: doc = frappe.get_meta(doctype) diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index be0dded99c..17fd8ada05 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -356,7 +356,7 @@ class CustomizeForm(Document): def delete_custom_fields(self): meta = frappe.get_meta(self.doc_type) - fields_to_remove = (set([df.fieldname for df in meta.get("fields")]) + fields_to_remove = (set(df.fieldname for df in meta.get("fields")) - set(df.fieldname for df in self.get("fields"))) for fieldname in fields_to_remove: diff --git a/frappe/database/database.py b/frappe/database/database.py index c9c1ec3909..9ff3d25320 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -344,7 +344,7 @@ class Database(object): values[key] = value[1] if isinstance(value[1], (tuple, list)): # value is a list in tuple ("in", ("A", "B")) - _rhs = " ({0})".format(", ".join([self.escape(v) for v in value[1]])) + _rhs = " ({0})".format(", ".join(self.escape(v) for v in value[1])) del values[key] if _operator not in ["=", "!=", ">", ">=", "<", "<=", "like", "in", "not in", "not like"]: @@ -1019,7 +1019,7 @@ class Database(object): :params values: list of list of values """ insert_list = [] - fields = ", ".join(["`"+field+"`" for field in fields]) + fields = ", ".join("`"+field+"`" for field in fields) for idx, value in enumerate(values): insert_list.append(tuple(value)) diff --git a/frappe/desk/doctype/global_search_settings/global_search_settings.py b/frappe/desk/doctype/global_search_settings/global_search_settings.py index 85c9687ab3..f483376ed0 100644 --- a/frappe/desk/doctype/global_search_settings/global_search_settings.py +++ b/frappe/desk/doctype/global_search_settings/global_search_settings.py @@ -22,7 +22,7 @@ class GlobalSearchSettings(Document): dts.append(dt.document_type) if core_dts: - core_dts = (", ".join([frappe.bold(dt) for dt in core_dts])) + core_dts = (", ".join(frappe.bold(dt) for dt in core_dts)) frappe.throw(_("Core Modules {0} cannot be searched in Global Search.").format(core_dts)) if repeated_dts: @@ -61,7 +61,7 @@ def update_global_search_doctypes(): if search_doctypes.get(domain): global_search_doctypes.extend(search_doctypes.get(domain)) - doctype_list = set([dt.name for dt in frappe.get_all("DocType")]) + doctype_list = set(dt.name for dt in frappe.get_all("DocType")) allowed_in_global_search = [] for dt in global_search_doctypes: diff --git a/frappe/desk/doctype/tag/tag.py b/frappe/desk/doctype/tag/tag.py index 7e016ee91b..3ec3f05ac0 100644 --- a/frappe/desk/doctype/tag/tag.py +++ b/frappe/desk/doctype/tag/tag.py @@ -132,7 +132,7 @@ def update_tags(doc, tags): :param doc: Document to be added to global tags """ - new_tags = list(set([tag.strip() for tag in tags.split(",") if tag])) + new_tags = list(set(tag.strip() for tag in tags.split(",") if tag)) for tag in new_tags: if not frappe.db.exists("Tag Link", {"parenttype": doc.doctype, "parent": doc.name, "tag": tag}): diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 36b662bb39..769dd57481 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -594,7 +594,7 @@ class EmailAccount(Document): # get email account user and set communication as seen users = frappe.get_all("User Email", filters={ "email_account": self.name }, fields=["parent"]) - users = list(set([ user.get("parent") for user in users ])) + users = list(set(user.get("parent") for user in users)) communication._seen = json.dumps(users) communication.flags.in_receive = True @@ -851,8 +851,8 @@ class EmailAccount(Document): email_server.update_flag(uid_list=uid_list) # mark communication as read - docnames = ",".join([ "'%s'"%flag.get("communication") for flag in flags \ - if flag.get("action") == "Read" ]) + docnames = ",".join("'%s'"%flag.get("communication") for flag in flags \ + if flag.get("action") == "Read") self.set_communication_seen_status(docnames, seen=1) # mark communication as unread diff --git a/frappe/email/doctype/newsletter/test_newsletter.py b/frappe/email/doctype/newsletter/test_newsletter.py index bd8fadc29c..073b46af44 100644 --- a/frappe/email/doctype/newsletter/test_newsletter.py +++ b/frappe/email/doctype/newsletter/test_newsletter.py @@ -44,7 +44,7 @@ class TestNewsletter(unittest.TestCase): email_queue_list = [frappe.get_doc("Email Queue", e.name) for e in frappe.get_all("Email Queue")] self.assertEqual(len(email_queue_list), 4) - recipients = set([e.recipients[0].recipient for e in email_queue_list]) + recipients = set(e.recipients[0].recipient for e in email_queue_list) self.assertTrue(set(emails).issubset(recipients)) def test_unsubscribe(self): diff --git a/frappe/email/inbox.py b/frappe/email/inbox.py index 395a2d3e2d..08eef5f7bf 100644 --- a/frappe/email/inbox.py +++ b/frappe/email/inbox.py @@ -18,7 +18,7 @@ def get_email_accounts(user=None): "all_accounts": "" } - all_accounts = ",".join([ account.get("email_account") for account in accounts ]) + all_accounts = ",".join(account.get("email_account") for account in accounts) if len(accounts) > 1: email_accounts.append({ "email_account": all_accounts, diff --git a/frappe/installer.py b/frappe/installer.py index 0cd5b136ae..cc5a6a6734 100755 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -540,7 +540,7 @@ def is_downgrade(sql_file_path, verbose=False): def is_partial(sql_file_path): with open(sql_file_path) as f: - header = " ".join([f.readline() for _ in range(5)]) + header = " ".join(f.readline() for _ in range(5)) if "Partial Backup" in header: return True return False diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.py b/frappe/integrations/doctype/ldap_settings/ldap_settings.py index 80dfef2693..ef72d03cde 100644 --- a/frappe/integrations/doctype/ldap_settings/ldap_settings.py +++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.py @@ -80,7 +80,7 @@ class LDAPSettings(Document): def sync_roles(self, user, additional_groups=None): - current_roles = set([d.role for d in user.get("roles")]) + current_roles = set(d.role for d in user.get("roles")) needed_roles = set() needed_roles.add(self.default_role) diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 205b451336..d9cbe7313e 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -166,7 +166,7 @@ def delete_fields(args_dict, delete=0): frappe.db.sql(""" DELETE FROM `tabSingles` WHERE doctype='%s' AND field IN (%s) - """ % (dt, ", ".join(["'{}'".format(f) for f in fields]))) + """ % (dt, ", ".join("'{}'".format(f) for f in fields))) else: existing_fields = frappe.db.multisql({ "mariadb": "DESC `tab%s`" % dt, @@ -189,7 +189,7 @@ def delete_fields(args_dict, delete=0): frappe.db.commit() query = "ALTER TABLE `tab%s` " % dt + \ - ", ".join(["DROP COLUMN `%s`" % f for f in fields_need_to_delete]) + ", ".join("DROP COLUMN `%s`" % f for f in fields_need_to_delete) frappe.db.sql(query) if frappe.db.db_type == 'postgres': diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 154a091b8a..34c329175a 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -358,7 +358,7 @@ class BaseDocument(object): frappe.db.sql("""INSERT INTO `tab{doctype}` ({columns}) VALUES ({values})""".format( doctype = self.doctype, - columns = ", ".join(["`"+c+"`" for c in columns]), + columns = ", ".join("`"+c+"`" for c in columns), values = ", ".join(["%s"] * len(columns)) ), list(d.values())) except Exception as e: @@ -401,7 +401,7 @@ class BaseDocument(object): frappe.db.sql("""UPDATE `tab{doctype}` SET {values} WHERE `name`=%s""".format( doctype = self.doctype, - values = ", ".join(["`"+c+"`=%s" for c in columns]) + values = ", ".join("`"+c+"`=%s" for c in columns) ), list(d.values()) + [name]) except Exception as e: if frappe.db.is_unique_key_violation(e): diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index e0c3406c46..8460880794 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -620,7 +620,7 @@ class DatabaseQuery(object): def get_share_condition(self): return """`tab{0}`.name in ({1})""".format(self.doctype, ", ".join(["%s"] * len(self.shared))) % \ - tuple([frappe.db.escape(s, percent=False) for s in self.shared]) + tuple(frappe.db.escape(s, percent=False) for s in self.shared) def add_user_permissions(self, user_permissions): meta = frappe.get_meta(self.doctype) @@ -726,8 +726,8 @@ class DatabaseQuery(object): # `idx desc, modified desc` # will covert to # `tabItem`.`idx` desc, `tabItem`.`modified` desc - args.order_by = ', '.join(['`tab{0}`.`{1}` {2}'.format(self.doctype, - f.split()[0].strip(), f.split()[1].strip()) for f in meta.sort_field.split(',')]) + args.order_by = ', '.join('`tab{0}`.`{1}` {2}'.format(self.doctype, + f.split()[0].strip(), f.split()[1].strip()) for f in meta.sort_field.split(',')) else: sort_field = meta.sort_field or 'modified' sort_order = (meta.sort_field and meta.sort_order) or 'desc' @@ -807,8 +807,8 @@ def get_order_by(doctype, meta): # `idx desc, modified desc` # will covert to # `tabItem`.`idx` desc, `tabItem`.`modified` desc - order_by = ', '.join(['`tab{0}`.`{1}` {2}'.format(doctype, - f.split()[0].strip(), f.split()[1].strip()) for f in meta.sort_field.split(',')]) + order_by = ', '.join('`tab{0}`.`{1}` {2}'.format(doctype, + f.split()[0].strip(), f.split()[1].strip()) for f in meta.sort_field.split(',')) else: sort_field = meta.sort_field or 'modified' sort_order = (meta.sort_field and meta.sort_order) or 'desc' diff --git a/frappe/model/meta.py b/frappe/model/meta.py index 66e8b08d92..1c3e7f60fb 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -667,7 +667,7 @@ def trim_tables(doctype=None): and not f.startswith("_")] if columns_to_remove: print(doctype, "columns removed:", columns_to_remove) - columns_to_remove = ", ".join(["drop `{0}`".format(c) for c in columns_to_remove]) + columns_to_remove = ", ".join("drop `{0}`".format(c) for c in columns_to_remove) query = """alter table `tab{doctype}` {columns}""".format( doctype=doctype, columns=columns_to_remove) frappe.db.sql_ddl(query) diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 2c9dc5d823..1567ac544b 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -144,7 +144,7 @@ def update_user_settings(old, new, link_fields): if not link_fields: return # find the user settings for the linked doctypes - linked_doctypes = set([d.parent for d in link_fields if not d.issingle]) + linked_doctypes = set(d.parent for d in link_fields if not d.issingle) user_settings_details = frappe.db.sql('''SELECT `user`, `doctype`, `data` FROM `__UserSettings` WHERE `data` like %s diff --git a/frappe/permissions.py b/frappe/permissions.py index 19f101aab5..c0ff7fc590 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -312,7 +312,7 @@ def has_controller_permissions(doc, ptype, user=None): return None def get_doctypes_with_read(): - return list(set([p.parent if type(p.parent) == str else p.parent.encode('UTF8') for p in get_valid_perms()])) + return list(set(p.parent if type(p.parent) == str else p.parent.encode('UTF8') for p in get_valid_perms())) def get_valid_perms(doctype=None, user=None): '''Get valid permissions for the current user from DocPerm and Custom DocPerm''' diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py index b6cd0b575c..07bdf8791e 100644 --- a/frappe/tests/test_commands.py +++ b/frappe/tests/test_commands.py @@ -80,7 +80,7 @@ def exists_in_backup(doctypes, file): ) with gzip.open(file, "rb") as f: content = f.read().decode("utf8") - return all([predicate.format(doctype).lower() in content.lower() for doctype in doctypes]) + return all(predicate.format(doctype).lower() in content.lower() for doctype in doctypes) class BaseTestCommands(unittest.TestCase): @@ -355,12 +355,12 @@ class TestCommands(BaseTestCommands): # test 2: bare functionality for single site self.execute("bench --site {site} list-apps") self.assertEqual(self.returncode, 0) - list_apps = set([ + list_apps = set( _x.split()[0] for _x in self.stdout.split("\n") - ]) + ) doctype = frappe.get_single("Installed Applications").installed_applications if doctype: - installed_apps = set([x.app_name for x in doctype]) + installed_apps = set(x.app_name for x in doctype) else: installed_apps = set(frappe.get_installed_apps()) self.assertSetEqual(list_apps, installed_apps) diff --git a/frappe/translate.py b/frappe/translate.py index 1d8b1234c7..caa7bcfea6 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -283,8 +283,8 @@ def clear_cache(): def get_messages_for_app(app, deduplicate=True): """Returns all messages (list) for a specified `app`""" messages = [] - modules = ", ".join(['"{}"'.format(m.title().replace("_", " ")) \ - for m in frappe.local.app_modules[app]]) + modules = ", ".join('"{}"'.format(m.title().replace("_", " ")) \ + for m in frappe.local.app_modules[app]) # doctypes if modules: diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 1da4cd3bae..db0f39b3f5 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -186,7 +186,7 @@ def random_string(length): """generate a random string""" import string from random import choice - return ''.join([choice(string.ascii_letters + string.digits) for i in range(length)]) + return ''.join(choice(string.ascii_letters + string.digits) for i in range(length)) def has_gravatar(email): @@ -305,7 +305,7 @@ def make_esc(esc_chars): """ Function generator for Escaping special characters """ - return lambda s: ''.join(['\\' + c if c in esc_chars else c for c in s]) + return lambda s: ''.join('\\' + c if c in esc_chars else c for c in s) # esc / unescape characters -- used for command line def esc(s, esc_chars): diff --git a/frappe/utils/backups.py b/frappe/utils/backups.py index b21efc5e89..908be52452 100644 --- a/frappe/utils/backups.py +++ b/frappe/utils/backups.py @@ -307,8 +307,8 @@ class BackupGenerator: backup_summary = self.get_summary() print("Backup Summary for {0} at {1}".format(frappe.local.site, now())) - title = max([len(x) for x in backup_summary]) - path = max([len(x["path"]) for x in backup_summary.values()]) + title = max(len(x) for x in backup_summary) + path = max(len(x["path"]) for x in backup_summary.values()) for _type, info in backup_summary.items(): template = "{{0:{0}}}: {{1:{1}}} {{2}}".format(title, path) @@ -381,7 +381,7 @@ class BackupGenerator: "", ]) - generated_header = "\n".join([f"-- {x}" for x in database_header_content]) + "\n" + generated_header = "\n".join(f"-- {x}" for x in database_header_content) + "\n" with gzip.open(args.backup_path_db, "wt") as f: f.write(generated_header) diff --git a/frappe/utils/bot.py b/frappe/utils/bot.py index 45e1bd5a4e..68f49c47ed 100644 --- a/frappe/utils/bot.py +++ b/frappe/utils/bot.py @@ -40,10 +40,10 @@ class BotParser(object): def format_list(self, data): '''Format list as markdown''' - return _('I found these: ') + ', '.join([' [{title}](/app/Form/{doctype}/{name})'.format( + return _('I found these: ') + ', '.join(' [{title}](/app/Form/{doctype}/{name})'.format( title = d.title or d.name, doctype=self.get_doctype(), - name=d.name) for d in data]) + name=d.name) for d in data) def get_doctype(self): '''returns the doctype name from self.tables''' @@ -58,8 +58,8 @@ class ShowNotificationBot(BotParser): if open_items: return ("Following items need your attention:\n\n" - + "\n\n".join(["{0} [{1}](/app/List/{1})".format(d[1], d[0]) - for d in open_items if d[1] > 0])) + + "\n\n".join("{0} [{1}](/app/List/{1})".format(d[1], d[0]) + for d in open_items if d[1] > 0)) else: return 'Take it easy, nothing urgent needs your attention' diff --git a/frappe/utils/jinja_globals.py b/frappe/utils/jinja_globals.py index d8c5cb79f1..8eedca5335 100644 --- a/frappe/utils/jinja_globals.py +++ b/frappe/utils/jinja_globals.py @@ -14,10 +14,10 @@ def resolve_class(classes): return classes if isinstance(classes, (list, tuple)): - return " ".join([resolve_class(c) for c in classes]).strip() + 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() + return " ".join(classname for classname in classes if classes[classname]).strip() return classes diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index f78aaac934..5ff2165527 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -346,7 +346,7 @@ def get_context(context): if missing: frappe.throw(_('Mandatory Information missing:') + '

' - + '
'.join(['{0} ({1})'.format(d.label, d.fieldtype) for d in missing])) + + '
'.join('{0} ({1})'.format(d.label, d.fieldtype) for d in missing)) def allow_website_search_indexing(self): return False diff --git a/frappe/website/doctype/website_slideshow/website_slideshow.py b/frappe/website/doctype/website_slideshow/website_slideshow.py index 90f62d1bb1..03e26bdcfd 100644 --- a/frappe/website/doctype/website_slideshow/website_slideshow.py +++ b/frappe/website/doctype/website_slideshow/website_slideshow.py @@ -23,7 +23,7 @@ class WebsiteSlideshow(Document): files = map(lambda row: row.image, self.slideshow_items) if files: result = frappe.get_all("File", filters={ "file_url":("in", list(files)) }, fields="is_private") - if any([file.is_private for file in result]): + if any(file.is_private for file in result): frappe.throw(_("All Images attached to Website Slideshow should be public")) def get_slideshow(doc): From cbbc85424bd1ce715f813f4349b6a63211b74b96 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 12 May 2021 16:46:58 +0530 Subject: [PATCH 047/495] fix: use set instead of list for new_ags No dependent functionalities expect a list. Co-authored-by: gavin --- frappe/desk/doctype/tag/tag.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/desk/doctype/tag/tag.py b/frappe/desk/doctype/tag/tag.py index 3ec3f05ac0..a85b264cee 100644 --- a/frappe/desk/doctype/tag/tag.py +++ b/frappe/desk/doctype/tag/tag.py @@ -132,7 +132,7 @@ def update_tags(doc, tags): :param doc: Document to be added to global tags """ - new_tags = list(set(tag.strip() for tag in tags.split(",") if tag)) + new_tags = {tag.strip() for tag in tags.split(",") if tag} for tag in new_tags: if not frappe.db.exists("Tag Link", {"parenttype": doc.doctype, "parent": doc.name, "tag": tag}): @@ -187,4 +187,4 @@ def get_documents_for_tag(tag): @frappe.whitelist() def get_tags_list_for_awesomebar(): - return [t.name for t in frappe.get_list("Tag")] \ No newline at end of file + return [t.name for t in frappe.get_list("Tag")] From d8a4cf896b3eca5132d1e23d6da40491005a9f1b Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 12 May 2021 16:56:16 +0530 Subject: [PATCH 048/495] perf: prefer set builder notation over constructor --- frappe/automation/doctype/auto_repeat/auto_repeat.py | 2 +- frappe/core/doctype/data_import/importer.py | 4 ++-- .../doctype/global_search_settings/global_search_settings.py | 2 +- frappe/email/doctype/email_account/email_account.py | 2 +- frappe/email/doctype/newsletter/test_newsletter.py | 2 +- frappe/model/rename_doc.py | 2 +- frappe/permissions.py | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py index 37285ad571..dfad85d0dd 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py @@ -334,7 +334,7 @@ class AutoRepeat(Document): if self.reference_doctype and self.reference_document: res = get_contacts_linking_to(self.reference_doctype, self.reference_document, fields=['email_id']) res += get_contacts_linked_from(self.reference_doctype, self.reference_document, fields=['email_id']) - email_ids = set(d.email_id for d in res) + email_ids = {d.email_id for d in res} if not email_ids: frappe.msgprint(_('No contacts linked to document'), alert=True) else: diff --git a/frappe/core/doctype/data_import/importer.py b/frappe/core/doctype/data_import/importer.py index 841319dd60..fed6d2cb06 100644 --- a/frappe/core/doctype/data_import/importer.py +++ b/frappe/core/doctype/data_import/importer.py @@ -903,7 +903,7 @@ class Column: if self.df.fieldtype == "Link": # find all values that dont exist - values = list(set(cstr(v) for v in self.column_values[1:] if v)) + values = list({cstr(v) for v in self.column_values[1:] if v}) exists = [ d.name for d in frappe.db.get_all(self.df.options, filters={"name": ("in", values)}) ] @@ -939,7 +939,7 @@ class Column: elif self.df.fieldtype == "Select": options = get_select_options(self.df) if options: - values = list(set(cstr(v) for v in self.column_values[1:] if v)) + values = list({cstr(v) for v in self.column_values[1:] if v}) invalid = list(set(values) - set(options)) if invalid: valid_values = ", ".join(frappe.bold(o) for o in options) diff --git a/frappe/desk/doctype/global_search_settings/global_search_settings.py b/frappe/desk/doctype/global_search_settings/global_search_settings.py index f483376ed0..fead118e75 100644 --- a/frappe/desk/doctype/global_search_settings/global_search_settings.py +++ b/frappe/desk/doctype/global_search_settings/global_search_settings.py @@ -61,7 +61,7 @@ def update_global_search_doctypes(): if search_doctypes.get(domain): global_search_doctypes.extend(search_doctypes.get(domain)) - doctype_list = set(dt.name for dt in frappe.get_all("DocType")) + doctype_list = {dt.name for dt in frappe.get_all("DocType")} allowed_in_global_search = [] for dt in global_search_doctypes: diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 769dd57481..786a185585 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -594,7 +594,7 @@ class EmailAccount(Document): # get email account user and set communication as seen users = frappe.get_all("User Email", filters={ "email_account": self.name }, fields=["parent"]) - users = list(set(user.get("parent") for user in users)) + users = list({user.get("parent") for user in users}) communication._seen = json.dumps(users) communication.flags.in_receive = True diff --git a/frappe/email/doctype/newsletter/test_newsletter.py b/frappe/email/doctype/newsletter/test_newsletter.py index 073b46af44..4b9a0749d5 100644 --- a/frappe/email/doctype/newsletter/test_newsletter.py +++ b/frappe/email/doctype/newsletter/test_newsletter.py @@ -44,7 +44,7 @@ class TestNewsletter(unittest.TestCase): email_queue_list = [frappe.get_doc("Email Queue", e.name) for e in frappe.get_all("Email Queue")] self.assertEqual(len(email_queue_list), 4) - recipients = set(e.recipients[0].recipient for e in email_queue_list) + recipients = {e.recipients[0].recipient for e in email_queue_list} self.assertTrue(set(emails).issubset(recipients)) def test_unsubscribe(self): diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 1567ac544b..2c2550f39d 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -144,7 +144,7 @@ def update_user_settings(old, new, link_fields): if not link_fields: return # find the user settings for the linked doctypes - linked_doctypes = set(d.parent for d in link_fields if not d.issingle) + linked_doctypes = {d.parent for d in link_fields if not d.issingle} user_settings_details = frappe.db.sql('''SELECT `user`, `doctype`, `data` FROM `__UserSettings` WHERE `data` like %s diff --git a/frappe/permissions.py b/frappe/permissions.py index c0ff7fc590..c17f4b6bf1 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -312,7 +312,7 @@ def has_controller_permissions(doc, ptype, user=None): return None def get_doctypes_with_read(): - return list(set(p.parent if type(p.parent) == str else p.parent.encode('UTF8') for p in get_valid_perms())) + return list({p.parent if type(p.parent) == str else p.parent.encode('UTF8') for p in get_valid_perms()}) def get_valid_perms(doctype=None, user=None): '''Get valid permissions for the current user from DocPerm and Custom DocPerm''' From 6d8c691dddc502baf160b28d05c1d5baa8da8e35 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 12 May 2021 19:22:31 +0530 Subject: [PATCH 049/495] refactor: use list instead of tuple constructor --- frappe/core/doctype/user_type/user_type.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/user_type/user_type.py b/frappe/core/doctype/user_type/user_type.py index b3965d9b84..b3e61610cc 100644 --- a/frappe/core/doctype/user_type/user_type.py +++ b/frappe/core/doctype/user_type/user_type.py @@ -114,7 +114,7 @@ class UserType(Document): self.select_doctypes = [] select_doctypes = [] - user_doctypes = tuple(row.document_type for row in self.user_doctypes) + user_doctypes = [row.document_type for row in self.user_doctypes] for doctype in user_doctypes: doc = frappe.get_meta(doctype) @@ -267,4 +267,4 @@ def apply_permissions_for_non_standard_user_type(doc, method=None): user_doc.update_children() add_user_permission(doc.doctype, doc.name, doc.get(data[1])) else: - frappe.db.set_value('User Permission', perm_data[0], 'user', doc.get(data[1])) \ No newline at end of file + frappe.db.set_value('User Permission', perm_data[0], 'user', doc.get(data[1])) From d7eea226094145a4d45e4fbc6a7ef8bdfe2fad55 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 12 May 2021 19:25:21 +0530 Subject: [PATCH 050/495] chore: misc translation/semgrep fixes --- frappe/core/doctype/data_import/importer.py | 5 +---- frappe/utils/bot.py | 2 +- frappe/utils/jinja_globals.py | 4 ++-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/frappe/core/doctype/data_import/importer.py b/frappe/core/doctype/data_import/importer.py index fed6d2cb06..6337d919eb 100644 --- a/frappe/core/doctype/data_import/importer.py +++ b/frappe/core/doctype/data_import/importer.py @@ -929,10 +929,7 @@ class Column: self.warnings.append( { "col": self.column_number, - "message": _( - "Date format could not be determined from the values in" - " this column. Defaulting to yyyy-mm-dd." - ), + "message": _("Date format could not be determined from the values in this column. Defaulting to yyyy-mm-dd."), "type": "info", } ) diff --git a/frappe/utils/bot.py b/frappe/utils/bot.py index 68f49c47ed..33ec139b64 100644 --- a/frappe/utils/bot.py +++ b/frappe/utils/bot.py @@ -40,7 +40,7 @@ class BotParser(object): def format_list(self, data): '''Format list as markdown''' - return _('I found these: ') + ', '.join(' [{title}](/app/Form/{doctype}/{name})'.format( + return _('I found these:') + ' ' + ', '.join(' [{title}](/app/Form/{doctype}/{name})'.format( title = d.title or d.name, doctype=self.get_doctype(), name=d.name) for d in data) diff --git a/frappe/utils/jinja_globals.py b/frappe/utils/jinja_globals.py index 8eedca5335..906d557326 100644 --- a/frappe/utils/jinja_globals.py +++ b/frappe/utils/jinja_globals.py @@ -38,13 +38,13 @@ def web_block(template, values=None, **kwargs): def web_blocks(blocks): - from frappe import throw, _dict + from frappe import throw, _dict, _ from frappe.website.doctype.web_page.web_page import get_web_blocks_html web_blocks = [] for block in blocks: if not block.get("template"): - throw("Web Template is not specified") + throw(_("Web Template is not specified")) doc = _dict( { From fed99bffac29ff4cb99866d6a3e3246170525916 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 13 May 2021 13:34:56 +0530 Subject: [PATCH 051/495] fix: Set basepath and toc data to show sidebar --- frappe/website/page_controllers/template_page.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frappe/website/page_controllers/template_page.py b/frappe/website/page_controllers/template_page.py index b050a62bad..49a811b587 100644 --- a/frappe/website/page_controllers/template_page.py +++ b/frappe/website/page_controllers/template_page.py @@ -5,6 +5,7 @@ import frappe from frappe.website.page_controllers.base_template_page import BaseTemplatePage from frappe.website.context import add_sidebar_and_breadcrumbs from frappe.website.render import build_response +from frappe.website.router import get_base_template from frappe.website.utils import (extract_comment_tag, extract_title, get_next_link, get_toc) @@ -31,6 +32,7 @@ class TemplatePage(BaseTemplatePage): self.app = app self.app_path = app_path self.template_path = os.path.relpath(file_path, self.app_path) + self.basepath = os.path.dirname(file_path) return True def get_index_path_options(self, search_path): @@ -74,7 +76,6 @@ class TemplatePage(BaseTemplatePage): same folder. Also the hyphens will be coverted to underscore for python module names. This method sets the pymodule_name if it exists. ''' - self.basepath = self.template_path.rsplit('.', 1)[0] self.pymodule_name = None # replace - with _ in the internal modules names @@ -90,6 +91,9 @@ class TemplatePage(BaseTemplatePage): self.convert_from_markdown() def update_context(self): + self.context.base_template = self.context.base_template or get_base_template(self.path) + self.context.basepath = self.basepath + self.context.path = self.path self.set_page_properties() self.set_properties_from_source() self.load_colocated_files() @@ -196,7 +200,7 @@ class TemplatePage(BaseTemplatePage): def convert_from_markdown(self): if self.template_path.endswith('.md'): self.source = frappe.utils.md_to_html(self.source) - self.page_toc_html = self.source.toc_html + self.context.page_toc_html = self.source.toc_html if not self.context.show_sidebar: self.source = '
' + self.source + '
' From dbd8b66d815c8d255fb493c97eadb68671dcb844 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 13 May 2021 14:22:26 +0530 Subject: [PATCH 052/495] fix: Template basepath --- frappe/website/page_controllers/template_page.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/website/page_controllers/template_page.py b/frappe/website/page_controllers/template_page.py index 49a811b587..4db6d17541 100644 --- a/frappe/website/page_controllers/template_page.py +++ b/frappe/website/page_controllers/template_page.py @@ -76,10 +76,11 @@ class TemplatePage(BaseTemplatePage): same folder. Also the hyphens will be coverted to underscore for python module names. This method sets the pymodule_name if it exists. ''' + template_basepath = os.path.splitext(self.template_path)[0] self.pymodule_name = None # replace - with _ in the internal modules names - self.pymodule_path = os.path.join(self.basepath.replace("-", "_") + ".py") + self.pymodule_path = os.path.join(template_basepath.replace("-", "_") + ".py") if os.path.exists(os.path.join(self.app_path, self.pymodule_path)): self.pymodule_name = self.app + "." + self.pymodule_path.replace(os.path.sep, ".")[:-3] From 8b1b5c707b11f150d85d791354a7ebadccd27f43 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 13 May 2021 16:41:20 +0530 Subject: [PATCH 053/495] fix: remove unnecessary casting from set to list --- frappe/core/doctype/data_import/importer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/data_import/importer.py b/frappe/core/doctype/data_import/importer.py index 6337d919eb..6856fde4d0 100644 --- a/frappe/core/doctype/data_import/importer.py +++ b/frappe/core/doctype/data_import/importer.py @@ -936,8 +936,8 @@ class Column: elif self.df.fieldtype == "Select": options = get_select_options(self.df) if options: - values = list({cstr(v) for v in self.column_values[1:] if v}) - invalid = list(set(values) - set(options)) + values = {cstr(v) for v in self.column_values[1:] if v} + invalid = values - set(options) if invalid: valid_values = ", ".join(frappe.bold(o) for o in options) invalid_values = ", ".join(frappe.bold(i) for i in invalid) From 24434ed9dee018f4910898979f90d596f3f64dc3 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 14 May 2021 13:26:45 +0530 Subject: [PATCH 054/495] refactor: Delete unused code from render.py --- frappe/website/render.py | 174 +-------------------------------------- 1 file changed, 2 insertions(+), 172 deletions(-) diff --git a/frappe/website/render.py b/frappe/website/render.py index b7221e364c..ee80fa3b9f 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -22,112 +22,11 @@ from frappe.website.utils import (get_home_page, can_cache, delete_page_cache, from frappe.website.router import clear_sitemap, evaluate_dynamic_routes from frappe.translate import guess_language +from frappe.website.serve import get_response class PageNotFoundError(Exception): pass def render(path=None, http_status_code=None): - # temp feature flag - if True or frappe.conf.flag_new_website: - from frappe.website.serve import get_response - return get_response(path, http_status_code) - else: - return _render(path, http_status_code) - -def _render(path=None, http_status_code=None): - """render html page""" - if not path: - path = frappe.local.request.path - - try: - path = path.strip('/ ') - raise_if_disabled(path) - resolve_redirect(path) - path = resolve_path(path) - data = None - - # if in list of already known 404s, send it - if can_cache() and frappe.cache().hget('website_404', frappe.request.url): - data = render_page('404') - http_status_code = 404 - elif is_static_file(path): - return get_static_file_response() - elif is_web_form(path): - data = render_web_form(path) - else: - try: - data = render_page_by_language(path) - except frappe.PageDoesNotExistError: - doctype, name = get_doctype_from_path(path) - if doctype and name: - path = "printview" - frappe.local.form_dict.doctype = doctype - frappe.local.form_dict.name = name - elif doctype: - path = "list" - frappe.local.form_dict.doctype = doctype - else: - # 404s are expensive, cache them! - frappe.cache().hset('website_404', frappe.request.url, True) - data = render_page('404') - http_status_code = 404 - - if not data: - try: - data = render_page(path) - except frappe.PermissionError as e: - data, http_status_code = render_403(e, path) - - except frappe.PermissionError as e: - data, http_status_code = render_403(e, path) - - except frappe.Redirect as e: - raise e - - except Exception: - path = "error" - data = render_page(path) - http_status_code = 500 - - data = add_csrf_token(data) - - except frappe.Redirect: - return build_response(path, "", 301, { - "Location": frappe.flags.redirect_location or (frappe.local.response or {}).get('location'), - "Cache-Control": "no-store, no-cache, must-revalidate" - }) - - return build_response(path, data, http_status_code or 200) - -def is_static_file(path): - if ('.' not in path): - return False - extn = path.rsplit('.', 1)[-1] - if extn in ('html', 'md', 'js', 'xml', 'css', 'txt', 'py', 'json'): - return False - - for app in frappe.get_installed_apps(): - file_path = frappe.get_app_path(app, 'www') + '/' + path - if os.path.exists(file_path): - frappe.flags.file_path = file_path - return True - - return False - -def is_web_form(path): - return bool(frappe.get_all("Web Form", filters={'route': path})) - -def render_web_form(path): - data = render_page(path) - return data - -def get_static_file_response(): - try: - f = open(frappe.flags.file_path, 'rb') - except IOError: - raise NotFound - - response = Response(wrap_file(frappe.local.request.environ, f), direct_passthrough=True) - response.mimetype = mimetypes.guess_type(frappe.flags.file_path)[0] or 'application/octet-stream' - return response + return get_response(path, http_status_code) def build_response(path, data, http_status_code, headers=None): # build response @@ -168,23 +67,6 @@ def add_preload_headers(response): traceback.print_exc() -def render_page_by_language(path): - translated_languages = frappe.get_hooks("translated_languages_for_website") - user_lang = guess_language(translated_languages) - if translated_languages and user_lang in translated_languages: - try: - if path and path != "index": - lang_path = '{0}/{1}'.format(user_lang, path) - else: - lang_path = user_lang # index - - return render_page(lang_path) - except frappe.DoesNotExistError: - return render_page(path) - - else: - return render_page(path) - def render_page(path): """get page html""" out = None @@ -307,7 +189,6 @@ def set_content_type(response, data, path): def clear_cache(path=None): '''Clear website caches - :param path: (optional) for the given path''' for key in ('website_generator_routes', 'website_pages', 'website_full_index', 'sitemap_routes'): @@ -327,54 +208,3 @@ def clear_cache(path=None): for method in frappe.get_hooks("website_clear_cache"): frappe.get_attr(method)(path) - -def render_403(e, pathname): - frappe.local.message = cstr(e.message if six.PY2 else e) - frappe.local.message_title = _("Not Permitted") - frappe.local.response['context'] = dict( - indicator_color = 'red', - primary_action = '/login', - primary_label = _('Login'), - fullpage=True - ) - return render_page("message"), e.http_status_code - -def get_doctype_from_path(path): - doctypes = frappe.db.sql_list("select name from tabDocType") - - parts = path.split("/") - - doctype = parts[0] - name = parts[1] if len(parts) > 1 else None - - if doctype in doctypes: - return doctype, name - - # try scrubbed - doctype = doctype.replace("_", " ").title() - if doctype in doctypes: - return doctype, name - - return None, None - -def add_csrf_token(data): - if frappe.local.session: - return data.replace("", ''.format( - frappe.local.session.data.csrf_token)) - else: - return data - -def raise_if_disabled(path): - routes = frappe.db.get_all('Portal Menu Item', - fields=['route', 'enabled'], - filters={ - 'enabled': 0, - 'route': ['like', '%{0}'.format(path)] - } - ) - - for r in routes: - _path = r.route.lstrip('/') - if path == _path and not r.enabled: - raise frappe.PermissionError - From 4219ef98d18e1b9caf00558bf1adca96c76e3154 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 14 May 2021 13:31:13 +0530 Subject: [PATCH 055/495] fix: Update renderer sequence --- frappe/website/serve.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/serve.py b/frappe/website/serve.py index eacaef96e1..f11ba1c4fc 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -29,7 +29,7 @@ def get_response(path=None, http_status_code=200): path = resolve_path(path) # there is no way to determine the type of the page based on the route # so evaluate each type of page sequentially - renderers = [StaticPage, TemplatePage, ListPage, WebFormPage, DocumentPage, PrintPage, NotFoundPage] + renderers = [StaticPage, WebFormPage, TemplatePage, ListPage, DocumentPage, PrintPage, NotFoundPage] for renderer in renderers: response = renderer(path, http_status_code).get() if response: From 39c1a94f896fff0e1781b8df6b60047cb8b7e286 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 14 May 2021 13:53:04 +0530 Subject: [PATCH 056/495] fix: Circular import --- frappe/website/page_controllers/web_page.py | 1 + frappe/website/render.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/website/page_controllers/web_page.py b/frappe/website/page_controllers/web_page.py index 2020d491e4..4d9e642b4b 100644 --- a/frappe/website/page_controllers/web_page.py +++ b/frappe/website/page_controllers/web_page.py @@ -7,6 +7,7 @@ class WebPage(object): if not path: path = frappe.local.request.path self.path = path.strip('/ ') + self.basepath = '' def get(self): if self.validate(): diff --git a/frappe/website/render.py b/frappe/website/render.py index ee80fa3b9f..3868a91232 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -22,10 +22,10 @@ from frappe.website.utils import (get_home_page, can_cache, delete_page_cache, from frappe.website.router import clear_sitemap, evaluate_dynamic_routes from frappe.translate import guess_language -from frappe.website.serve import get_response class PageNotFoundError(Exception): pass def render(path=None, http_status_code=None): + from frappe.website.serve import get_response return get_response(path, http_status_code) def build_response(path, data, http_status_code, headers=None): From 56ee6e449cedd64856bc9af9539006d5149266c8 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 14 May 2021 14:17:52 +0530 Subject: [PATCH 057/495] refactor: Remove render method - also, move clear cache to utils.py --- frappe/utils/response.py | 3 ++- frappe/website/render.py | 33 +++++---------------------------- frappe/website/router.py | 3 --- frappe/website/utils.py | 27 ++++++++++++++++++++++++++- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/frappe/utils/response.py b/frappe/utils/response.py index 44b037f3cc..97097ab280 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -217,7 +217,8 @@ def send_private_file(path): return response def handle_session_stopped(): + from frappe.website.serve import get_response frappe.respond_as_web_page(_("Updating"), _("Your system is being updated. Please refresh again after a few moments."), http_status_code=503, indicator_color='orange', fullpage = True, primary_action=None) - return frappe.website.render.render("message", http_status_code=503) + return get_response("message", http_status_code=503) diff --git a/frappe/website/render.py b/frappe/website/render.py index 3868a91232..895faa5b5f 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -6,27 +6,21 @@ import frappe from frappe import _ import frappe.sessions from frappe.utils import cstr -import os, mimetypes, json +import mimetypes, json import re -import six from six import iteritems from werkzeug.wrappers import Response from werkzeug.routing import Rule from werkzeug.wsgi import wrap_file from frappe.website.context import get_context -from frappe.website.redirect import resolve_redirect from frappe.website.utils import (get_home_page, can_cache, delete_page_cache, get_toc, get_next_link) -from frappe.website.router import clear_sitemap, evaluate_dynamic_routes -from frappe.translate import guess_language +from frappe.website.router import evaluate_dynamic_routes class PageNotFoundError(Exception): pass -def render(path=None, http_status_code=None): - from frappe.website.serve import get_response - return get_response(path, http_status_code) def build_response(path, data, http_status_code, headers=None): # build response @@ -188,23 +182,6 @@ def set_content_type(response, data, path): return data def clear_cache(path=None): - '''Clear website caches - :param path: (optional) for the given path''' - for key in ('website_generator_routes', 'website_pages', - 'website_full_index', 'sitemap_routes'): - frappe.cache().delete_value(key) - - frappe.cache().delete_value("website_404") - if path: - frappe.cache().hdel('website_redirects', path) - delete_page_cache(path) - else: - clear_sitemap() - frappe.clear_cache("Guest") - for key in ('portal_menu_items', 'home_page', 'website_route_rules', - 'doctypes_with_web_view', 'website_redirects', 'page_context', - 'website_page'): - frappe.cache().delete_value(key) - - for method in frappe.get_hooks("website_clear_cache"): - frappe.get_attr(method)(path) + # TODO: Remove this + from frappe.website.utils import clear_cache + return clear_cache(path) diff --git a/frappe/website/router.py b/frappe/website/router.py index 1e61f6dcc3..36779923b5 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -85,9 +85,6 @@ def get_page_context_from_doctype(path): return frappe.get_doc(page_info.get("doctype"), page_info.get("name")).get_page_info() -def clear_sitemap(): - delete_page_cache("*") - def get_all_page_context_from_doctypes(): ''' Get all doctype generated routes (for sitemap.xml) diff --git a/frappe/website/utils.py b/frappe/website/utils.py index efa15bdb14..8c75508d07 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -11,7 +11,6 @@ from six import iteritems from past.builtins import cmp from frappe.utils import md_to_html - def delete_page_cache(path): cache = frappe.cache() cache.delete_value('full_index') @@ -399,3 +398,29 @@ def get_html_content_based_on_type(doc, fieldname, content_type): content = '' return content + + +def clear_cache(path=None): + '''Clear website caches + :param path: (optional) for the given path''' + for key in ('website_generator_routes', 'website_pages', + 'website_full_index', 'sitemap_routes'): + frappe.cache().delete_value(key) + + frappe.cache().delete_value("website_404") + if path: + frappe.cache().hdel('website_redirects', path) + delete_page_cache(path) + else: + clear_sitemap() + frappe.clear_cache("Guest") + for key in ('portal_menu_items', 'home_page', 'website_route_rules', + 'doctypes_with_web_view', 'website_redirects', 'page_context', + 'website_page'): + frappe.cache().delete_value(key) + + for method in frappe.get_hooks("website_clear_cache"): + frappe.get_attr(method)(path) + +def clear_sitemap(): + delete_page_cache("*") From 9ba5bf1133b05d2435e74099227609970501c87c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 14 May 2021 18:42:41 +0530 Subject: [PATCH 058/495] refactor: Move metatags related code into a separate file --- .../page_controllers/base_template_page.py | 75 ++----------------- frappe/website/website_components/metatags.py | 68 +++++++++++++++++ 2 files changed, 73 insertions(+), 70 deletions(-) create mode 100644 frappe/website/website_components/metatags.py diff --git a/frappe/website/page_controllers/base_template_page.py b/frappe/website/page_controllers/base_template_page.py index 74ca400372..c40afb7b1c 100644 --- a/frappe/website/page_controllers/base_template_page.py +++ b/frappe/website/page_controllers/base_template_page.py @@ -1,13 +1,13 @@ import frappe -from frappe.website.doctype.website_settings.website_settings import \ - get_website_settings +from frappe.website.doctype.website_settings.website_settings import get_website_settings from frappe.website.page_controllers.web_page import WebPage +from frappe.website.website_components.metatags import MetaTags class BaseTemplatePage(WebPage): def init_context(self): self.context = frappe._dict() - self.context.update(get_website_settings(self.context)) + self.context.update(get_website_settings()) self.context.update(frappe.local.conf.get("website_context") or {}) def add_csrf_token(self, html): @@ -19,7 +19,8 @@ class BaseTemplatePage(WebPage): return html def post_process_context(self): - self.add_metatags() + self.tags = MetaTags(self.path, self.context).tags + self.context.metatags = self.tags self.set_base_template_if_missing() self.set_title_with_prefix() self.update_website_context() @@ -53,69 +54,3 @@ class BaseTemplatePage(WebPage): values = frappe.get_attr(method)(self.context) if values: self.context.update(values) - - def add_metatags(self): - self.tags = frappe._dict(self.context.get("metatags") or {}) - self.init_metatags_from_context() - self.set_opengraph_tags() - self.set_twitter_tags() - self.set_meta_published_on() - self.set_metatags_from_website_route_meta() - - self.context.metatags = self.tags - - def init_metatags_from_context(self): - for key in ('title', 'description', 'image', 'author', 'url', 'published_on'): - if key not in self.tags and self.context.get(key): - self.tags[key] = self.context[key] - - if not self.tags.get('title'): - self.tags['title'] = self.context.get('name') - - if self.tags.get('image'): - self.tags['image'] = frappe.utils.get_url(self.tags['image']) - - self.tags["language"] = frappe.local.lang or "en" - - def set_opengraph_tags(self): - if "og:type" not in self.tags: - self.tags["og:type"] = "article" - - for key in ('title', 'description', 'image', 'author', 'url'): - if self.tags.get(key): - self.tags['og:' + key] = self.tags.get(key) - - def set_twitter_tags(self): - for key in ('title', 'description', 'image', 'author', 'url'): - if self.tags.get(key): - self.tags['twitter:' + key] = self.tags.get(key) - - if self.tags.get('image'): - self.tags['twitter:card'] = "summary_large_image" - else: - self.tags["twitter:card"] = "summary" - - def set_meta_published_on(self): - if "published_on" in self.tags: - self.tags["datePublished"] = self.tags["published_on"] - del self.tags["published_on"] - - def set_metatags_from_website_route_meta(self): - ''' - Get meta tags from Website Route meta - they can override the defaults set above - ''' - route = self.path - if route == '': - # homepage - route = frappe.db.get_single_value('Website Settings', 'home_page') - - route_exists = (route - and not route.endswith(('.js', '.css')) - and frappe.db.exists('Website Route Meta', route)) - - if route_exists: - website_route_meta = frappe.get_doc('Website Route Meta', route) - for meta_tag in website_route_meta.meta_tags: - d = meta_tag.get_meta_dict() - self.tags.update(d) diff --git a/frappe/website/website_components/metatags.py b/frappe/website/website_components/metatags.py new file mode 100644 index 0000000000..045bef8fe1 --- /dev/null +++ b/frappe/website/website_components/metatags.py @@ -0,0 +1,68 @@ +import frappe + +class MetaTags(): + def __init__(self, path, context): + self.path = path + self.context = context + self.tags = frappe._dict(self.context.get("metatags") or {}) + self.init_metatags_from_context() + self.set_opengraph_tags() + self.set_twitter_tags() + self.set_meta_published_on() + self.set_metatags_from_website_route_meta() + + def init_metatags_from_context(self): + for key in ('title', 'description', 'image', 'author', 'url', 'published_on'): + if key not in self.tags and self.context.get(key): + self.tags[key] = self.context[key] + + if not self.tags.get('title'): + self.tags['title'] = self.context.get('name') + + if self.tags.get('image'): + self.tags['image'] = frappe.utils.get_url(self.tags['image']) + + self.tags["language"] = frappe.local.lang or "en" + + def set_opengraph_tags(self): + if "og:type" not in self.tags: + self.tags["og:type"] = "article" + + for key in ('title', 'description', 'image', 'author', 'url'): + if self.tags.get(key): + self.tags['og:' + key] = self.tags.get(key) + + def set_twitter_tags(self): + for key in ('title', 'description', 'image', 'author', 'url'): + if self.tags.get(key): + self.tags['twitter:' + key] = self.tags.get(key) + + if self.tags.get('image'): + self.tags['twitter:card'] = "summary_large_image" + else: + self.tags["twitter:card"] = "summary" + + def set_meta_published_on(self): + if "published_on" in self.tags: + self.tags["datePublished"] = self.tags["published_on"] + del self.tags["published_on"] + + def set_metatags_from_website_route_meta(self): + ''' + Get meta tags from Website Route meta + they can override the defaults set above + ''' + route = self.path + if route == '': + # homepage + route = frappe.db.get_single_value('Website Settings', 'home_page') + + route_exists = (route + and not route.endswith(('.js', '.css')) + and frappe.db.exists('Website Route Meta', route)) + + if route_exists: + website_route_meta = frappe.get_doc('Website Route Meta', route) + for meta_tag in website_route_meta.meta_tags: + d = meta_tag.get_meta_dict() + self.tags.update(d) From 20bab0f631acd5dfbf3ddda250472568f057fc6a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 14 May 2021 18:58:04 +0530 Subject: [PATCH 059/495] refactor: Move code to appropriate places --- frappe/utils/__init__.py | 4 +-- .../website/page_controllers/template_page.py | 36 +++---------------- frappe/website/router.py | 2 +- frappe/website/utils.py | 16 +++++++++ 4 files changed, 24 insertions(+), 34 deletions(-) diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 43a7b82c16..845cb9be4a 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -770,9 +770,9 @@ def set_request(**kwargs): frappe.local.request = Request(builder.get_environ()) def get_html_for_route(route): - from frappe.website import render + from frappe.website.serve import get_response set_request(method='GET', path=route) - response = render.render() + response = get_response() html = frappe.safe_decode(response.get_data()) return html diff --git a/frappe/website/page_controllers/template_page.py b/frappe/website/page_controllers/template_page.py index 4db6d17541..f2664fb928 100644 --- a/frappe/website/page_controllers/template_page.py +++ b/frappe/website/page_controllers/template_page.py @@ -6,9 +6,10 @@ from frappe.website.page_controllers.base_template_page import BaseTemplatePage from frappe.website.context import add_sidebar_and_breadcrumbs from frappe.website.render import build_response from frappe.website.router import get_base_template -from frappe.website.utils import (extract_comment_tag, extract_title, - get_next_link, get_toc) +from frappe.website.utils import (extract_comment_tag, + extract_title, get_next_link, get_toc, get_frontmatter) +WEBPAGE_PY_MODULE_PROPERTIES = ("base_template_path", "template", "no_cache", "sitemap", "condition_field") class TemplatePage(BaseTemplatePage): def validate(self): @@ -36,12 +37,7 @@ class TemplatePage(BaseTemplatePage): return True def get_index_path_options(self, search_path): - return ( - search_path, - search_path + '.html', - search_path + '.md', - search_path + '/index.html', - search_path + '/index.md') + return (f'{search_path}{d}' for d in ('', '.html', '.md', '/index.html', '/index.md')) def render(self): return build_response(self.path, self.get_html(), self.http_status_code, self.headers) @@ -115,8 +111,7 @@ class TemplatePage(BaseTemplatePage): self.http_status_code = self.context.http_status_code def set_pymodule_properties(self): - for prop in ("base_template_path", "template", "no_cache", "sitemap", - "condition_field"): + for prop in WEBPAGE_PY_MODULE_PROPERTIES: if hasattr(self.pymodule, prop): self.context[prop] = getattr(self.pymodule, prop) @@ -237,24 +232,3 @@ class TemplatePage(BaseTemplatePage): def get_start_folders(): return frappe.local.flags.web_pages_folders or ('www', 'templates/pages') - -def get_frontmatter(string): - """ - Reference: https://github.com/jonbeebe/frontmatter - """ - import re - - import yaml - - fmatter = "" - body = "" - result = re.compile(r'^\s*(?:---|\+\+\+)(.*?)(?:---|\+\+\+)\s*(.+)$', re.S | re.M).search(string) - - if result: - fmatter = result.group(1) - body = result.group(2) - - return { - "attributes": yaml.safe_load(fmatter), - "body": body, - } diff --git a/frappe/website/router.py b/frappe/website/router.py index 36779923b5..b048fa55c0 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -9,7 +9,7 @@ import re import frappe from frappe.model.document import get_controller -from frappe.website.utils import can_cache, delete_page_cache, extract_comment_tag, extract_title +from frappe.website.utils import can_cache, extract_comment_tag, extract_title from werkzeug.routing import Map, Rule, NotFound def resolve_route(path): diff --git a/frappe/website/utils.py b/frappe/website/utils.py index 8c75508d07..87e47571fb 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -6,6 +6,8 @@ import functools import re import os import frappe +import re +import yaml from six import iteritems from past.builtins import cmp @@ -424,3 +426,17 @@ def clear_cache(path=None): def clear_sitemap(): delete_page_cache("*") + +def get_frontmatter(string): + "Reference: https://github.com/jonbeebe/frontmatter" + frontmatter = "" + body = "" + result = re.compile(r'^\s*(?:---|\+\+\+)(.*?)(?:---|\+\+\+)\s*(.+)$', re.S | re.M).search(string) + if result: + frontmatter = result.group(1) + body = result.group(2) + + return { + "attributes": yaml.safe_load(frontmatter), + "body": body, + } From 18497989dc71d24c3e7662382fb58cc2d84c1e0a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 14 May 2021 21:30:28 +0530 Subject: [PATCH 060/495] refactor: Remove render_page from render.py & replace all usages of render_page with get_response --- frappe/__init__.py | 4 +- frappe/search/website_search.py | 4 +- frappe/tests/test_recorder.py | 4 +- frappe/utils/global_search.py | 4 +- .../website/doctype/web_form/test_web_form.py | 1 - frappe/website/render.py | 50 +++---------------- frappe/website/router.py | 6 +-- 7 files changed, 19 insertions(+), 54 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 02b8d71e40..bd46f0f874 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -1485,7 +1485,7 @@ def get_print(doctype=None, name=None, print_format=None, style=None, :param style: Print Format style. :param as_pdf: Return as PDF. Default False. :param password: Password to encrypt the pdf with. Default None""" - from frappe.website.render import build_page + from frappe.website.serve import get_response from frappe.utils.pdf import get_pdf local.form_dict.doctype = doctype @@ -1500,7 +1500,7 @@ def get_print(doctype=None, name=None, print_format=None, style=None, options = {'password': password} if not html: - html = build_page("printview") + html = get_response("printview") if as_pdf: return get_pdf(html, output = output, options = options) diff --git a/frappe/search/website_search.py b/frappe/search/website_search.py index 452ea2a427..ad2ebb504d 100644 --- a/frappe/search/website_search.py +++ b/frappe/search/website_search.py @@ -9,7 +9,7 @@ from whoosh.fields import ID, TEXT, Schema import frappe from frappe.search.full_text_search import FullTextSearch from frappe.utils import set_request, update_progress_bar -from frappe.website.render import render_page +from frappe.website.serve import get_response INDEX_NAME = "web_routes" @@ -61,7 +61,7 @@ class WebsiteSearch(FullTextSearch): try: set_request(method="GET", path=route) - content = render_page(route) + content = get_response(route) soup = BeautifulSoup(content, "html.parser") page_content = soup.find(class_="page_content") text_content = page_content.text if page_content else "" diff --git a/frappe/tests/test_recorder.py b/frappe/tests/test_recorder.py index 64d3c52f63..7dcccd1d71 100644 --- a/frappe/tests/test_recorder.py +++ b/frappe/tests/test_recorder.py @@ -8,7 +8,7 @@ import unittest import frappe import frappe.recorder from frappe.utils import set_request -from frappe.website.render import render_page +from frappe.website.serve import get_response import sqlparse @@ -122,5 +122,5 @@ class TestRecorder(unittest.TestCase): self.assertEqual(call['exact_copies'], query[1]) def test_error_page_rendering(self): - content = render_page("error") + content = get_response("error") self.assertIn("Error", content) diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index c20f3b29d4..7541e8b31d 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -310,14 +310,14 @@ def get_routes_to_index(): def add_route_to_global_search(route): from bs4 import BeautifulSoup - from frappe.website.render import render_page + from frappe.website.serve import get_response from frappe.utils import set_request frappe.set_user('Guest') frappe.local.no_cache = True try: set_request(method='GET', path=route) - content = render_page(route) + content = get_response(route) soup = BeautifulSoup(content, 'html.parser') page_content = soup.find(class_='page_content') text_content = page_content.text if page_content else '' diff --git a/frappe/website/doctype/web_form/test_web_form.py b/frappe/website/doctype/web_form/test_web_form.py index 78f7fd6337..0e9ce7c660 100644 --- a/frappe/website/doctype/web_form/test_web_form.py +++ b/frappe/website/doctype/web_form/test_web_form.py @@ -5,7 +5,6 @@ from __future__ import unicode_literals import frappe import unittest, json -from frappe.website.render import build_page from frappe.website.doctype.web_form.web_form import accept test_dependencies = ['Web Form'] diff --git a/frappe/website/render.py b/frappe/website/render.py index 895faa5b5f..77c1af1e87 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -1,23 +1,21 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals -import frappe -from frappe import _ -import frappe.sessions -from frappe.utils import cstr -import mimetypes, json +import json +import mimetypes import re from six import iteritems -from werkzeug.wrappers import Response from werkzeug.routing import Rule -from werkzeug.wsgi import wrap_file +from werkzeug.wrappers import Response +import frappe +import frappe.sessions +from frappe import _ from frappe.website.context import get_context -from frappe.website.utils import (get_home_page, can_cache, delete_page_cache, - get_toc, get_next_link) from frappe.website.router import evaluate_dynamic_routes +from frappe.website.utils import (can_cache, get_home_page, get_next_link, get_toc) + class PageNotFoundError(Exception): pass @@ -61,38 +59,6 @@ def add_preload_headers(response): traceback.print_exc() -def render_page(path): - """get page html""" - out = None - - if can_cache(): - # return rendered page - page_cache = frappe.cache().hget("website_page", path) - if page_cache and frappe.local.lang in page_cache: - out = page_cache[frappe.local.lang] - - if out: - frappe.local.response.from_cache = True - return out - - return build(path) - -def build(path): - if not frappe.db: - frappe.connect() - - try: - return build_page(path) - except frappe.DoesNotExistError: - hooks = frappe.get_hooks() - if hooks.website_catch_all: - path = hooks.website_catch_all[0] - return build_page(path) - else: - raise - except Exception: - raise - def build_page(path): if not getattr(frappe.local, "path", None): frappe.local.path = path diff --git a/frappe/website/router.py b/frappe/website/router.py index b048fa55c0..2e27b4c690 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -1,16 +1,16 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import io import os import re +from werkzeug.routing import Map, NotFound, Rule + import frappe from frappe.model.document import get_controller from frappe.website.utils import can_cache, extract_comment_tag, extract_title -from werkzeug.routing import Map, Rule, NotFound + def resolve_route(path): """Returns the page route object based on searching in pages and generators. From 080a5b3c6a1395fe085d6d7d7218449139353da9 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 14 May 2021 21:33:39 +0530 Subject: [PATCH 061/495] chore: Delete an unnecessary file - purifycss is no longer used. --- frappe/website/purifycss.py | 43 ------------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 frappe/website/purifycss.py diff --git a/frappe/website/purifycss.py b/frappe/website/purifycss.py deleted file mode 100644 index 39e989db6e..0000000000 --- a/frappe/website/purifycss.py +++ /dev/null @@ -1,43 +0,0 @@ -from __future__ import print_function, unicode_literals -''' -Check for unused CSS Classes - -sUpdate source and target apps below and run from CLI - - bench --site [sitename] execute frappe.website.purifycss.purify.css - -''' - -import frappe, re, os - -source = frappe.get_app_path('frappe_theme', 'public', 'less', 'frappe_theme.less') -target_apps = ['erpnext_com', 'frappe_io', 'translator', 'chart_of_accounts_builder', 'frappe_theme'] - -def purifycss(): - with open(source, 'r') as f: - src = f.read() - - classes = [] - for line in src.splitlines(): - line = line.strip() - if not line: - continue - if line[0]=='@': - continue - classes.extend(re.findall('\.([^0-9][^ :&.{,(]*)', line)) - - classes = list(set(classes)) - - for app in target_apps: - for basepath, folders, files in os.walk(frappe.get_app_path(app)): - for fname in files: - if fname.endswith('.html') or fname.endswith('.md'): - #print 'checking {0}...'.format(fname) - with open(os.path.join(basepath, fname), 'r') as f: - src = f.read() - for c in classes: - if c in src: - classes.remove(c) - - for c in sorted(classes): - print(c) From c964217106538a8950961cb2a1df4445c3fb0291 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 14 May 2021 21:40:44 +0530 Subject: [PATCH 062/495] style: Fix import issues --- frappe/website/render.py | 4 ---- frappe/website/utils.py | 1 - 2 files changed, 5 deletions(-) diff --git a/frappe/website/render.py b/frappe/website/render.py index 77c1af1e87..87eddf3463 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -11,15 +11,11 @@ from werkzeug.wrappers import Response import frappe import frappe.sessions -from frappe import _ from frappe.website.context import get_context from frappe.website.router import evaluate_dynamic_routes from frappe.website.utils import (can_cache, get_home_page, get_next_link, get_toc) -class PageNotFoundError(Exception): pass - - def build_response(path, data, http_status_code, headers=None): # build response response = Response() diff --git a/frappe/website/utils.py b/frappe/website/utils.py index 87e47571fb..a5fe9c3986 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -6,7 +6,6 @@ import functools import re import os import frappe -import re import yaml from six import iteritems From c5b981524868141f2dee58fa7fc5c24e6584bc70 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 14 May 2021 23:43:36 +0530 Subject: [PATCH 063/495] fix: Add get_response_content to get page content --- frappe/__init__.py | 4 ++-- frappe/search/website_search.py | 4 ++-- frappe/utils/global_search.py | 8 ++++---- frappe/website/serve.py | 4 ++++ 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index bd46f0f874..a90fd24d7b 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -1485,7 +1485,7 @@ def get_print(doctype=None, name=None, print_format=None, style=None, :param style: Print Format style. :param as_pdf: Return as PDF. Default False. :param password: Password to encrypt the pdf with. Default None""" - from frappe.website.serve import get_response + from frappe.website.serve import get_response_content from frappe.utils.pdf import get_pdf local.form_dict.doctype = doctype @@ -1500,7 +1500,7 @@ def get_print(doctype=None, name=None, print_format=None, style=None, options = {'password': password} if not html: - html = get_response("printview") + html = get_response_content("printview") if as_pdf: return get_pdf(html, output = output, options = options) diff --git a/frappe/search/website_search.py b/frappe/search/website_search.py index ad2ebb504d..49bdade936 100644 --- a/frappe/search/website_search.py +++ b/frappe/search/website_search.py @@ -9,7 +9,7 @@ from whoosh.fields import ID, TEXT, Schema import frappe from frappe.search.full_text_search import FullTextSearch from frappe.utils import set_request, update_progress_bar -from frappe.website.serve import get_response +from frappe.website.serve import get_response_content INDEX_NAME = "web_routes" @@ -61,7 +61,7 @@ class WebsiteSearch(FullTextSearch): try: set_request(method="GET", path=route) - content = get_response(route) + content = get_response_content(route) soup = BeautifulSoup(content, "html.parser") page_content = soup.find(class_="page_content") text_content = page_content.text if page_content else "" diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index 7541e8b31d..4c78d63918 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -310,14 +310,14 @@ def get_routes_to_index(): def add_route_to_global_search(route): from bs4 import BeautifulSoup - from frappe.website.serve import get_response + from frappe.website.serve import get_response_content from frappe.utils import set_request frappe.set_user('Guest') frappe.local.no_cache = True try: set_request(method='GET', path=route) - content = get_response(route) + content = get_response_content(route) soup = BeautifulSoup(content, 'html.parser') page_content = soup.find(class_='page_content') text_content = page_content.text if page_content else '' @@ -332,8 +332,8 @@ def add_route_to_global_search(route): route=route ) sync_value_in_queue(value) - except (frappe.PermissionError, frappe.DoesNotExistError, frappe.ValidationError, Exception): - pass + except (frappe.PermissionError, frappe.DoesNotExistError, frappe.ValidationError, Exception) as e: + raise e frappe.set_user('Administrator') diff --git a/frappe/website/serve.py b/frappe/website/serve.py index f11ba1c4fc..47ca0aa1d2 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -46,3 +46,7 @@ def get_response(path=None, http_status_code=200): response = ErrorPage(path, http_status_code, exception=e).get() return response + +def get_response_content(path=None, http_status_code=200): + response = get_response(path, http_status_code) + return response.data From 9b26307fe5e2210a6c36645e220d077c1e997090 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 17 May 2021 12:23:50 +0530 Subject: [PATCH 064/495] test: Use get_response_content to get content --- frappe/tests/test_recorder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/tests/test_recorder.py b/frappe/tests/test_recorder.py index 7dcccd1d71..8a1ae36ad9 100644 --- a/frappe/tests/test_recorder.py +++ b/frappe/tests/test_recorder.py @@ -8,7 +8,7 @@ import unittest import frappe import frappe.recorder from frappe.utils import set_request -from frappe.website.serve import get_response +from frappe.website.serve import get_response_content import sqlparse @@ -122,5 +122,5 @@ class TestRecorder(unittest.TestCase): self.assertEqual(call['exact_copies'], query[1]) def test_error_page_rendering(self): - content = get_response("error") + content = get_response_content("error") self.assertIn("Error", content) From 1c4e1bc1df7e0788e235b4f735194d8567de6e58 Mon Sep 17 00:00:00 2001 From: prssanna Date: Wed, 14 Apr 2021 14:48:15 +0530 Subject: [PATCH 065/495] refactor: set amended docname to original docname --- frappe/core/doctype/doctype/doctype.py | 16 ++++++ frappe/model/document.py | 10 +++- frappe/model/naming.py | 53 +++++++++++++++++-- frappe/patches.txt | 1 + ...l_name_docfield_to_submittable_doctypes.py | 11 ++++ frappe/public/js/frappe/form/form.js | 24 +++++---- frappe/public/js/frappe/router.js | 6 +++ 7 files changed, 105 insertions(+), 16 deletions(-) create mode 100644 frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 84673f990a..3890ab3a32 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -74,6 +74,7 @@ class DocType(Document): if not self.istable: validate_permissions(self) + self.make_cancellable() self.make_amendable() self.make_repeatable() self.validate_nestedset() @@ -589,6 +590,21 @@ class DocType(Document): "no_copy": 1 }) + def make_cancellable(self): + """If is_submittable is set, add original_name docfield.""" + if self.is_submittable: + if not frappe.db.sql("""select name from tabDocField + where fieldname = 'original_name' and parent = %s""", self.name): + self.append("fields", { + "label": "Original Name", + "fieldtype": "Text", + "fieldname": "original_name", + "read_only": 1, + "hidden": 1, + "print_hide": 1, + "no_copy": 1 + }) + def make_repeatable(self): """If allow_auto_repeat is set, add auto_repeat custom field.""" if self.allow_auto_repeat: diff --git a/frappe/model/document.py b/frappe/model/document.py index 623916597e..2bb4f1f3c0 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -7,7 +7,7 @@ import time from frappe import _, msgprint, is_whitelisted from frappe.utils import flt, cstr, now, get_datetime_str, file_lock, date_diff from frappe.model.base_document import BaseDocument, get_controller -from frappe.model.naming import set_new_name +from frappe.model.naming import set_new_name, rename_cancelled_doc from six import iteritems, string_types from werkzeug.exceptions import NotFound, Forbidden import hashlib, json @@ -919,6 +919,14 @@ class Document(BaseDocument): @whitelist.__func__ def _cancel(self): """Cancel the document. Sets `docstatus` = 2, then saves.""" + + # for backward compatibility + if self.amended_from and not self.original_name: + self.original_name = None + else: + self.original_name = self.name + + self.name = rename_cancelled_doc(self) self.docstatus = 2 self.save() diff --git a/frappe/model/naming.py b/frappe/model/naming.py index b8d6a6f8d7..33a09c659a 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -223,7 +223,16 @@ def revert_series_if_last(key, name, doc=None): * prefix = #### and hashes = 2021 (hash doesn't exist) * will search hash in key then accordingly get prefix = "" """ - if ".#" in key: + + # do not revert if doc is amended, since cancelled docs still exist + if doc.docstatus != 2 and doc.amended_from: + return + + # for first cancelled doc + if doc.docstatus == 2 and not doc.amended_from and doc.original_name: + name = doc.original_name + + if ".#" in key: prefix, hashes = key.rsplit(".", 1) if "#" not in hashes: # get the hash part from the key @@ -306,14 +315,48 @@ def append_number_if_name_exists(doctype, value, fieldname="name", separator="-" def _set_amended_name(doc): + if doc.original_name: + doc.name = doc.original_name + else: + original_name = get_original_name(doc) + doc.name = doc.amended_from + + # rename original doc to next name in series, and set amended doc name as original name + next_name_in_series = get_new_name_from_amended_from(doc) + frappe.rename_doc(doc.doctype, original_name, next_name_in_series, force=True, show_alert=False) + doc.name = original_name + doc.amended_from = next_name_in_series + doc.original_name = original_name + + return doc.name + +def get_original_name(doc): + # get original doc name from chain of amended docs + amended_from = original_name = doc.amended_from + while amended_from: + original_name = amended_from + amended_from = frappe.db.get_value(doc.doctype, amended_from, "amended_from") + + return original_name + +def get_new_name_from_amended_from(doc): am_id = 1 - am_prefix = doc.amended_from - if frappe.db.get_value(doc.doctype, doc.amended_from, "amended_from"): + am_prefix = doc.name + if frappe.db.get_value(doc.doctype, doc.name, "amended_from"): am_id = cint(doc.amended_from.split("-")[-1]) + 1 am_prefix = "-".join(doc.amended_from.split("-")[:-1]) # except the last hyphen - doc.name = am_prefix + "-" + str(am_id) - return doc.name + new_name = am_prefix + "-" + str(am_id) + if new_name == doc.name: + am_id += 1 + new_name = am_prefix + "-" + str(am_id) + return new_name + +def rename_cancelled_doc(doc): + doc = frappe.parse_json(doc) + new_name = get_new_name_from_amended_from(doc) + frappe.rename_doc(doc.doctype, doc.name, new_name, force=True, show_alert=False) + return new_name def _field_autoname(autoname, doc, skip_slicing=None): diff --git a/frappe/patches.txt b/frappe/patches.txt index e70be0a37b..a722bb39fa 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -337,3 +337,4 @@ frappe.patches.v12_0.rename_uploaded_files_with_proper_name frappe.patches.v13_0.queryreport_columns frappe.patches.v13_0.jinja_hook frappe.patches.v13_0.update_notification_channel_if_empty +frappe.patches.v13_0.add_original_name_docfield_to_submittable_doctypes ####### diff --git a/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py b/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py new file mode 100644 index 0000000000..725d37fd4a --- /dev/null +++ b/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py @@ -0,0 +1,11 @@ +import frappe +from frappe.database.schema import add_column + +def execute(): + for doctype in frappe.db.get_all('DocType'): + doctype = frappe.get_doc("DocType", doctype.name) + if doctype.is_submittable and frappe.db.table_exists(doctype.name): + doctype.make_cancellable() + if not frappe.db.has_column(doctype.name, 'original_name'): + add_column(doctype.name, 'original_name', 'Text') + doctype.db_update_all() diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 6d8a6b1cb4..f9d9de967e 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -764,32 +764,36 @@ frappe.ui.form.Form = class FrappeForm { } _cancel(btn, callback, on_error, skip_confirm) { - const me = this; const cancel_doc = () => { frappe.validated = true; - me.script_manager.trigger("before_cancel").then(() => { + this.script_manager.trigger("before_cancel").then(() => { if (!frappe.validated) { - return me.handle_save_fail(btn, on_error); + return this.handle_save_fail(btn, on_error); } - var after_cancel = function(r) { + const original_name = this.docname; + const after_cancel = (r) => { if (r.exc) { - me.handle_save_fail(btn, on_error); + this.handle_save_fail(btn, on_error); } else { frappe.utils.play_sound("cancel"); - me.refresh(); callback && callback(); - me.script_manager.trigger("after_cancel"); + this.script_manager.trigger("after_cancel"); + frappe.run_serially([ + () => this.rename_notify(this.doctype, original_name, r.docs[0].name), + () => frappe.router.clear_re_route(this.doctype, original_name), + () => this.refresh(), + ]); } }; - frappe.ui.form.save(me, "cancel", after_cancel, btn); + frappe.ui.form.save(this, "cancel", after_cancel, btn); }); } if (skip_confirm) { cancel_doc(); } else { - frappe.confirm(__("Permanently Cancel {0}?", [this.docname]), cancel_doc, me.handle_save_fail(btn, on_error)); + frappe.confirm(__("Permanently Cancel {0}?", [this.docname]), cancel_doc, this.handle_save_fail(btn, on_error)); } }; @@ -811,7 +815,7 @@ frappe.ui.form.Form = class FrappeForm { 'docname': this.doc.name }).then(is_amended => { if (is_amended) { - frappe.throw(__('This document is already amended, you cannot ammend it again')); + frappe.throw(__('This document is already amended, you cannot amend it again')); } this.validate_form_action("Amend"); var me = this; diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 5378294855..0d5231260c 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -235,6 +235,12 @@ frappe.router = { } }, + clear_re_route(doctype, docname) { + delete frappe.re_route[ + `${encodeURIComponent(frappe.router.slug(doctype))}/${encodeURIComponent(docname)}` + ]; + }, + set_title(sub_path) { if (frappe.route_titles[sub_path]) { frappe.utils.set_title(frappe.route_titles[sub_path]); From 251f878009f8f0e5574307ffd4275cd6ff36e3bc Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 15 Apr 2021 11:20:16 +0530 Subject: [PATCH 066/495] test: test naming for cancelled and amended docs --- frappe/tests/test_naming.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py index 66d48e3612..765ead275f 100644 --- a/frappe/tests/test_naming.py +++ b/frappe/tests/test_naming.py @@ -117,3 +117,36 @@ class TestNaming(unittest.TestCase): self.assertEqual(current_index.get('current'), 2) frappe.db.sql("""delete from `tabSeries` where name = %s""", series) + + def test_naming_for_cancelled_and_amended_doc(self): + if not frappe.db.exists('DocType', 'Submittable Doctype'): + frappe.get_doc({ + "doctype": "DocType", + "module": "Core", + "custom": 1, + "is_submittable": 1, + "permissions": [{ + "role": "System Manager", + "read": 1 + }], + "name": 'Submittable Doctype' + }).insert() + + doc = frappe.new_doc('Submittable DocType') + doc.save() + original_name = doc.name + + doc.submit() + doc.cancel() + cancelled_name = doc.name + self.assertEqual(cancelled_name, "{}-1".format(original_name)) + + amended_doc = frappe.copy_doc(doc) + amended_doc.docstatus = 0 + amended_doc.amended_from = doc.name + amended_doc.save() + self.assertEqual(amended_doc.name, original_name) + + amended_doc.submit() + amended_doc.cancel() + self.assertEqual(amended_doc.name, "{}-2".format(original_name)) From 304a771ba2221aa15cb9f186799a0603cb5ee425 Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 15 Apr 2021 11:21:48 +0530 Subject: [PATCH 067/495] fix: check if doc has attribute amended_from --- frappe/model/naming.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/frappe/model/naming.py b/frappe/model/naming.py index 33a09c659a..b685886c58 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -224,13 +224,14 @@ def revert_series_if_last(key, name, doc=None): * will search hash in key then accordingly get prefix = "" """ - # do not revert if doc is amended, since cancelled docs still exist - if doc.docstatus != 2 and doc.amended_from: - return + if hasattr(doc, 'amended_from'): + # do not revert if doc is amended, since cancelled docs still exist + if doc.docstatus != 2 and doc.amended_from: + return - # for first cancelled doc - if doc.docstatus == 2 and not doc.amended_from and doc.original_name: - name = doc.original_name + # for first cancelled doc + if doc.docstatus == 2 and not doc.amended_from and doc.original_name: + name = doc.original_name if ".#" in key: prefix, hashes = key.rsplit(".", 1) From 3614ca2a4d9ed0831cd3bd7080e977a5c2622149 Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 15 Apr 2021 12:17:53 +0530 Subject: [PATCH 068/495] style: fix indent --- frappe/core/doctype/doctype/doctype.py | 18 +++++++++--------- frappe/tests/test_naming.py | 23 +++++++++++------------ 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 3890ab3a32..0d478015bb 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -580,15 +580,15 @@ class DocType(Document): if self.is_submittable: if not frappe.db.sql("""select name from tabDocField where fieldname = 'amended_from' and parent = %s""", self.name): - self.append("fields", { - "label": "Amended From", - "fieldtype": "Link", - "fieldname": "amended_from", - "options": self.name, - "read_only": 1, - "print_hide": 1, - "no_copy": 1 - }) + self.append("fields", { + "label": "Amended From", + "fieldtype": "Link", + "fieldname": "amended_from", + "options": self.name, + "read_only": 1, + "print_hide": 1, + "no_copy": 1 + }) def make_cancellable(self): """If is_submittable is set, add original_name docfield.""" diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py index 765ead275f..afaabcc805 100644 --- a/frappe/tests/test_naming.py +++ b/frappe/tests/test_naming.py @@ -119,18 +119,17 @@ class TestNaming(unittest.TestCase): frappe.db.sql("""delete from `tabSeries` where name = %s""", series) def test_naming_for_cancelled_and_amended_doc(self): - if not frappe.db.exists('DocType', 'Submittable Doctype'): - frappe.get_doc({ - "doctype": "DocType", - "module": "Core", - "custom": 1, - "is_submittable": 1, - "permissions": [{ - "role": "System Manager", - "read": 1 - }], - "name": 'Submittable Doctype' - }).insert() + frappe.get_doc({ + "doctype": "DocType", + "module": "Core", + "custom": 1, + "is_submittable": 1, + "permissions": [{ + "role": "System Manager", + "read": 1 + }], + "name": 'Submittable Doctype' + }).insert() doc = frappe.new_doc('Submittable DocType') doc.save() From ba267b6e628ea57009aa310645ba2f539708fe76 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 16 Apr 2021 13:03:21 +0530 Subject: [PATCH 069/495] fix: update patch --- frappe/patches.txt | 2 +- .../add_original_name_docfield_to_submittable_doctypes.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/frappe/patches.txt b/frappe/patches.txt index a722bb39fa..1db775f17c 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -337,4 +337,4 @@ frappe.patches.v12_0.rename_uploaded_files_with_proper_name frappe.patches.v13_0.queryreport_columns frappe.patches.v13_0.jinja_hook frappe.patches.v13_0.update_notification_channel_if_empty -frappe.patches.v13_0.add_original_name_docfield_to_submittable_doctypes ####### +frappe.patches.v13_0.add_original_name_docfield_to_submittable_doctypes diff --git a/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py b/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py index 725d37fd4a..d870e63305 100644 --- a/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py +++ b/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py @@ -1,11 +1,9 @@ import frappe -from frappe.database.schema import add_column def execute(): for doctype in frappe.db.get_all('DocType'): doctype = frappe.get_doc("DocType", doctype.name) if doctype.is_submittable and frappe.db.table_exists(doctype.name): doctype.make_cancellable() - if not frappe.db.has_column(doctype.name, 'original_name'): - add_column(doctype.name, 'original_name', 'Text') + frappe.reload_doctype(doctype.name) doctype.db_update_all() From 26801f215cf03f9cb921752456ca6117bbb987f3 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 16 Apr 2021 13:10:14 +0530 Subject: [PATCH 070/495] fix: use orm --- frappe/core/doctype/doctype/doctype.py | 48 +++++++++++++------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 0d478015bb..aad0321fa0 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -577,33 +577,33 @@ class DocType(Document): def make_amendable(self): """If is_submittable is set, add amended_from docfields.""" - if self.is_submittable: - if not frappe.db.sql("""select name from tabDocField - where fieldname = 'amended_from' and parent = %s""", self.name): - self.append("fields", { - "label": "Amended From", - "fieldtype": "Link", - "fieldname": "amended_from", - "options": self.name, - "read_only": 1, - "print_hide": 1, - "no_copy": 1 - }) + if self.is_submittable and\ + not frappe.db.get_value('DocField', {'fieldname': 'amended_from', 'parent': self.name}): + + self.append("fields", { + "label": "Amended From", + "fieldtype": "Link", + "fieldname": "amended_from", + "options": self.name, + "read_only": 1, + "print_hide": 1, + "no_copy": 1 + }) def make_cancellable(self): """If is_submittable is set, add original_name docfield.""" - if self.is_submittable: - if not frappe.db.sql("""select name from tabDocField - where fieldname = 'original_name' and parent = %s""", self.name): - self.append("fields", { - "label": "Original Name", - "fieldtype": "Text", - "fieldname": "original_name", - "read_only": 1, - "hidden": 1, - "print_hide": 1, - "no_copy": 1 - }) + if self.is_submittable and\ + not frappe.db.get_value('DocField', {'fieldname': 'amended_from', 'parent': self.name}): + + self.append("fields", { + "label": "Original Name", + "fieldtype": "Text", + "fieldname": "original_name", + "read_only": 1, + "hidden": 1, + "print_hide": 1, + "no_copy": 1 + }) def make_repeatable(self): """If allow_auto_repeat is set, add auto_repeat custom field.""" From 413383bf5cbbe27fb2b93c23e761e0a5b8b7f1f4 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 16 Apr 2021 13:38:06 +0530 Subject: [PATCH 071/495] fix: condition in make_cancellable --- frappe/core/doctype/doctype/doctype.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index aad0321fa0..c6f6b628cc 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -593,7 +593,7 @@ class DocType(Document): def make_cancellable(self): """If is_submittable is set, add original_name docfield.""" if self.is_submittable and\ - not frappe.db.get_value('DocField', {'fieldname': 'amended_from', 'parent': self.name}): + not frappe.db.get_value('DocField', {'fieldname': 'original_name', 'parent': self.name}): self.append("fields", { "label": "Original Name", From 2cf5915eee428e026de8b4ad9b8c75c60d756eee Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Fri, 16 Apr 2021 13:38:33 +0530 Subject: [PATCH 072/495] fix: use get_meta Co-authored-by: Faris Ansari --- .../v13_0/add_original_name_docfield_to_submittable_doctypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py b/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py index d870e63305..c5080cc692 100644 --- a/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py +++ b/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py @@ -2,7 +2,7 @@ import frappe def execute(): for doctype in frappe.db.get_all('DocType'): - doctype = frappe.get_doc("DocType", doctype.name) + doctype = frappe.get_meta(doctype.name) if doctype.is_submittable and frappe.db.table_exists(doctype.name): doctype.make_cancellable() frappe.reload_doctype(doctype.name) From 209ece8b6cc23fce390d8bb04d5463a85320dd34 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 16 Apr 2021 14:03:36 +0530 Subject: [PATCH 073/495] fix: delete test submittable doctype --- frappe/tests/test_naming.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py index afaabcc805..7be2cc3ef7 100644 --- a/frappe/tests/test_naming.py +++ b/frappe/tests/test_naming.py @@ -119,7 +119,7 @@ class TestNaming(unittest.TestCase): frappe.db.sql("""delete from `tabSeries` where name = %s""", series) def test_naming_for_cancelled_and_amended_doc(self): - frappe.get_doc({ + submittable_doctype = frappe.get_doc({ "doctype": "DocType", "module": "Core", "custom": 1, @@ -149,3 +149,5 @@ class TestNaming(unittest.TestCase): amended_doc.submit() amended_doc.cancel() self.assertEqual(amended_doc.name, "{}-2".format(original_name)) + + submittable_doctype.delete() \ No newline at end of file From 0c603f0d769536d3351386cb2cd7aaccd99797a5 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 16 Apr 2021 17:27:12 +0530 Subject: [PATCH 074/495] test: fix cancel docs tests --- frappe/core/doctype/doctype/test_doctype.py | 4 ++++ .../add_original_name_docfield_to_submittable_doctypes.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index 9c492d2c36..5a53600fff 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -350,6 +350,7 @@ class TestDocType(unittest.TestCase): dump_docs = json.dumps(docs.get('docs')) cancel_all_linked_docs(dump_docs) data_link_doc.cancel() + data_doc.name = '{}-1'.format(data_doc.name) data_doc.load_from_db() self.assertEqual(data_link_doc.docstatus, 2) self.assertEqual(data_doc.docstatus, 2) @@ -435,7 +436,10 @@ class TestDocType(unittest.TestCase): self.assertRaises(frappe.LinkExistsError, data_link_doc_1.cancel) data_doc.load_from_db() + + data_doc_2.name = '{}-1'.format(data_doc_2.name) data_doc_2.load_from_db() + self.assertEqual(data_link_doc_1.docstatus, 2) #linked doc is canceled diff --git a/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py b/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py index c5080cc692..eccff2334f 100644 --- a/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py +++ b/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py @@ -2,7 +2,7 @@ import frappe def execute(): for doctype in frappe.db.get_all('DocType'): - doctype = frappe.get_meta(doctype.name) + doctype = frappe.get_doc('DocType', doctype.name) if doctype.is_submittable and frappe.db.table_exists(doctype.name): doctype.make_cancellable() frappe.reload_doctype(doctype.name) From b550e54f15d02bbb14d79c70344a89f0f73d9c33 Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 29 Apr 2021 17:31:56 +0530 Subject: [PATCH 075/495] fix: add column in patch --- .../add_original_name_docfield_to_submittable_doctypes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py b/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py index eccff2334f..d17242e90a 100644 --- a/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py +++ b/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py @@ -1,9 +1,11 @@ import frappe +from frappe.database.schema import add_column def execute(): for doctype in frappe.db.get_all('DocType'): doctype = frappe.get_doc('DocType', doctype.name) if doctype.is_submittable and frappe.db.table_exists(doctype.name): doctype.make_cancellable() - frappe.reload_doctype(doctype.name) + if not frappe.db.has_column(doctype.name, 'original_name'): + add_column(doctype.name, 'original_name', 'Text') doctype.db_update_all() From 081677c6c4bf6190c804fb432ee2e83d14c74dda Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Fri, 7 May 2021 11:30:35 +0530 Subject: [PATCH 076/495] fix: typo in test --- frappe/tests/test_naming.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py index 7be2cc3ef7..e7933ec7b3 100644 --- a/frappe/tests/test_naming.py +++ b/frappe/tests/test_naming.py @@ -131,7 +131,7 @@ class TestNaming(unittest.TestCase): "name": 'Submittable Doctype' }).insert() - doc = frappe.new_doc('Submittable DocType') + doc = frappe.new_doc('Submittable Doctype') doc.save() original_name = doc.name From d3d4b49796c551915b965a1d195787286fc16d90 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 17 May 2021 23:00:37 +0530 Subject: [PATCH 077/495] refactor: Simplify sidebar and breadcrumbs code --- frappe/website/context.py | 303 +++--------------- .../website/page_controllers/template_page.py | 56 ++-- 2 files changed, 83 insertions(+), 276 deletions(-) diff --git a/frappe/website/context.py b/frappe/website/context.py index ed5c89f149..c8dade5b8b 100644 --- a/frappe/website/context.py +++ b/frappe/website/context.py @@ -1,206 +1,28 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt +import json +import os -from __future__ import unicode_literals -import frappe, os, json - -from frappe.website.doctype.website_settings.website_settings import get_website_settings -from frappe.website.router import get_page_context +import frappe from frappe.model.document import Document -def get_context(path, args=None): - if args and args.source: - context = args - else: - context = get_page_context(path) - if args: - context.update(args) - - if hasattr(frappe.local, 'request'): - # for (remove leading slash) - # path could be overriden in render.resolve_from_map - context["path"] = frappe.local.request.path.strip('/ ') - else: - context["path"] = path - - context.canonical = frappe.utils.get_url(frappe.utils.escape_html(context.path)) - context.route = context.path - context = build_context(context) - - # set using frappe.respond_as_web_page - if hasattr(frappe.local, 'response') and frappe.local.response.get('context'): - context.update(frappe.local.response.context) - - # to be able to inspect the context dict - # Use the macro "inspect" from macros.html - context._context_dict = context - - context.developer_mode = frappe.conf.developer_mode - - return context - -def update_controller_context(context, controller): - module = frappe.get_module(controller) - - if module: - # get config fields - for prop in ("base_template_path", "template", "no_cache", "sitemap", - "condition_field"): - if hasattr(module, prop): - context[prop] = getattr(module, prop) - - if hasattr(module, "get_context"): - import inspect - try: - if inspect.getfullargspec(module.get_context).args: - ret = module.get_context(context) - else: - ret = module.get_context() - if ret: - context.update(ret) - except (frappe.PermissionError, frappe.PageDoesNotExistError, frappe.Redirect): - raise - except: - if not any([frappe.flags.in_migrate, frappe.flags.in_website_search_build]): - frappe.errprint(frappe.utils.get_traceback()) - - if hasattr(module, "get_children"): - context.children = module.get_children(context) - - -def build_context(context): - """get_context method of doc or module is supposed to render - content templates and push it into context""" - context = frappe._dict(context) - - if not "url_prefix" in context: - context.url_prefix = "" - - if context.url_prefix and context.url_prefix[-1]!='/': - context.url_prefix += '/' - - # for backward compatibility - context.docs_base_url = '/docs' - - context.update(get_website_settings(context)) - context.update(frappe.local.conf.get("website_context") or {}) - - # provide doc - if context.doc: - context.update(context.doc.as_dict()) - context.update(context.doc.get_website_properties()) - - if not context.template: - context.template = context.doc.meta.get_web_template() - - if hasattr(context.doc, "get_context"): - ret = context.doc.get_context(context) - - if ret: - context.update(ret) - - for prop in ("no_cache", "sitemap"): - if not prop in context: - context[prop] = getattr(context.doc, prop, False) - - elif context.controller: - # controller based context - update_controller_context(context, context.controller) - - # controller context extensions - context_controller_hooks = frappe.get_hooks("extend_website_page_controller_context") or {} - for controller, extension in context_controller_hooks.items(): - if isinstance(extension, list): - for ext in extension: - if controller == context.controller: - update_controller_context(context, ext) - else: - update_controller_context(context, extension) - - add_metatags(context) - add_sidebar_and_breadcrumbs(context) - - # determine templates to be used - if not context.base_template_path: - app_base = frappe.get_hooks("base_template") - context.base_template_path = app_base[-1] if app_base else "templates/base.html" - - if context.title_prefix and context.title and not context.title.startswith(context.title_prefix): - context.title = '{0} - {1}'.format(context.title_prefix, context.title) - - # apply context from hooks - update_website_context = frappe.get_hooks('update_website_context') - for method in update_website_context: - values = frappe.get_attr(method)(context) - if values: - context.update(values) - - return context - -def load_sidebar(context, sidebar_json_path): - with open(sidebar_json_path, 'r') as sidebarfile: - try: - sidebar_json = sidebarfile.read() - context.sidebar_items = json.loads(sidebar_json) - context.show_sidebar = 1 - except json.decoder.JSONDecodeError: - frappe.throw('Invalid Sidebar JSON at ' + sidebar_json_path) - -def get_sidebar_json_path(path, look_for=False): - ''' - Get _sidebar.json path from directory path - - :param path: path of the current diretory - :param look_for: if True, look for _sidebar.json going upwards from given path - - :return: _sidebar.json path - ''' - if os.path.split(path)[1] == 'www' or path == '/' or not path: - return '' - - sidebar_json_path = os.path.join(path, '_sidebar.json') - if os.path.exists(sidebar_json_path): - return sidebar_json_path - else: - if look_for: - return get_sidebar_json_path(os.path.split(path)[0], look_for) - else: - return '' - -def add_sidebar_and_breadcrumbs(context): - '''Add sidebar and breadcrumbs to context''' - from frappe.website.router import get_page_info_from_template - if context.show_sidebar: - context.no_cache = 1 - add_sidebar_data(context) - else: - if context.basepath: - hooks = frappe.get_hooks('look_for_sidebar_json') - look_for_sidebar_json = hooks[0] if hooks else 0 - sidebar_json_path = get_sidebar_json_path( - context.basepath, - look_for_sidebar_json - ) - if sidebar_json_path: - load_sidebar(context, sidebar_json_path) - - if context.add_breadcrumbs and not context.parents: - if context.basepath: - parent_path = os.path.dirname(context.path).rstrip('/') - page_info = get_page_info_from_template(parent_path) - if page_info: - context.parents = [dict(route=parent_path, title=page_info.title)] - -def add_sidebar_data(context): - from frappe.utils.user import get_fullname_and_avatar +def get_sidebar_data(parent_sidebar, basepath): import frappe.www.list + sidebar_data = frappe._dict() - if context.show_sidebar and context.website_sidebar: - context.sidebar_items = frappe.get_all('Website Sidebar Item', - filters=dict(parent=context.website_sidebar), fields=['title', 'route', '`group`'], + hooks = frappe.get_hooks('look_for_sidebar_json') + look_for_sidebar_json = hooks[0] if hooks else 0 + + if basepath and look_for_sidebar_json: + sidebar_items = get_sidebar_items_from_sidebar_file(basepath, look_for_sidebar_json) + sidebar_data['sidebar_items'] = sidebar_items + + if not sidebar_data.sidebar_items and parent_sidebar: + sidebar_data.sidebar_items = frappe.get_all('Website Sidebar Item', + filters=dict(parent=parent_sidebar), fields=['title', 'route', '`group`'], order_by='idx asc') - if not context.sidebar_items: + if not sidebar_data.sidebar_items: sidebar_items = frappe.cache().hget('portal_menu_items', frappe.session.user) if sidebar_items == None: sidebar_items = [] @@ -224,74 +46,41 @@ def add_sidebar_data(context): add_items(sidebar_items, items_via_hooks) frappe.cache().hset('portal_menu_items', frappe.session.user, sidebar_items) + sidebar_data.sidebar_items = sidebar_items - context.sidebar_items = sidebar_items + return sidebar_data - info = get_fullname_and_avatar(frappe.session.user) - context["fullname"] = info.fullname - context["user_image"] = info.avatar - context["user"] = info.name +def get_sidebar_items_from_sidebar_file(basepath, look_for_sidebar_json): + sidebar_items = frappe._dict() + sidebar_json_path = get_sidebar_json_path(basepath, look_for_sidebar_json) + if not sidebar_json_path: return sidebar_items + with open(sidebar_json_path, 'r') as sidebarfile: + try: + sidebar_json = sidebarfile.read() + sidebar_items = json.loads(sidebar_json) + except json.decoder.JSONDecodeError: + frappe.throw('Invalid Sidebar JSON at ' + sidebar_json_path) -def add_metatags(context): - tags = frappe._dict(context.get("metatags") or {}) + return sidebar_items - if "og:type" not in tags: - tags["og:type"] = "article" +def get_sidebar_json_path(path, look_for=False): + ''' + Get _sidebar.json path from directory path - if "title" not in tags and context.title: - tags["title"] = context.title + :param path: path of the current diretory + :param look_for: if True, look for _sidebar.json going upwards from given path - title = tags.get("name") or tags.get("title") - if title: - tags["og:title"] = tags["twitter:title"] = title - tags["twitter:card"] = "summary" + :return: _sidebar.json path + ''' + if os.path.split(path)[1] == 'www' or path == '/' or not path: + return '' - if not tags.get('description') and context.description: - tags["description"] = context.description - - description = tags.get("description") - if description: - tags["og:description"] = tags["twitter:description"] = description - - if "image" not in tags and context.image: - tags["image"] = context.image - - image = tags.get("image") - if image: - tags["og:image"] = tags["twitter:image"] = tags["image"] = frappe.utils.get_url(image) - tags['twitter:card'] = "summary_large_image" - - if "author" not in tags and context.author: - tags["author"] = context.author - - tags["og:url"] = tags["url"] = frappe.utils.get_url(context.path) - - if "published_on" not in tags and context.published_on: - tags["published_on"] = context.published_on - - if "published_on" in tags: - tags["datePublished"] = tags["published_on"] - del tags["published_on"] - - tags["language"] = frappe.local.lang or "en" - - # Get meta tags from Website Route meta - # they can override the defaults set above - route = context.path - if route == '': - # homepage - route = frappe.db.get_single_value('Website Settings', 'home_page') - - route_exists = (route - and not route.endswith(('.js', '.css')) - and frappe.db.exists('Website Route Meta', route)) - - if route_exists: - website_route_meta = frappe.get_doc('Website Route Meta', route) - for meta_tag in website_route_meta.meta_tags: - d = meta_tag.get_meta_dict() - tags.update(d) - - # update tags in context - context.metatags = tags + sidebar_json_path = os.path.join(path, '_sidebar.json') + if os.path.exists(sidebar_json_path): + return sidebar_json_path + else: + if look_for: + return get_sidebar_json_path(os.path.split(path)[0], look_for) + else: + return '' diff --git a/frappe/website/page_controllers/template_page.py b/frappe/website/page_controllers/template_page.py index f2664fb928..5b001a55e7 100644 --- a/frappe/website/page_controllers/template_page.py +++ b/frappe/website/page_controllers/template_page.py @@ -3,7 +3,7 @@ import os import frappe from frappe.website.page_controllers.base_template_page import BaseTemplatePage -from frappe.website.context import add_sidebar_and_breadcrumbs +from frappe.website.context import get_sidebar_data from frappe.website.render import build_response from frappe.website.router import get_base_template from frappe.website.utils import (extract_comment_tag, @@ -12,29 +12,32 @@ from frappe.website.utils import (extract_comment_tag, WEBPAGE_PY_MODULE_PROPERTIES = ("base_template_path", "template", "no_cache", "sitemap", "condition_field") class TemplatePage(BaseTemplatePage): - def validate(self): - for app in frappe.get_installed_apps(frappe_last=True): - if self.find_page_in_app(app): - return True + def __init__(self, path, http_status_code=None): + super().__init__(path=path, http_status_code=http_status_code) + self.set_template_path() - def find_page_in_app(self, app): + def set_template_path(self): ''' Searches for file matching the path in the /www - and /templates/pages folders + and /templates/pages folders and sets path if match is found ''' - app_path = frappe.get_app_path(app) folders = get_start_folders() + for app in frappe.get_installed_apps(frappe_last=True): + app_path = frappe.get_app_path(app) - for dirname in folders: - search_path = os.path.join(app_path, dirname, self.path) - for p in self.get_index_path_options(search_path): - file_path = frappe.as_unicode(p) - if os.path.exists(file_path) and not os.path.isdir(file_path): - self.app = app - self.app_path = app_path - self.template_path = os.path.relpath(file_path, self.app_path) - self.basepath = os.path.dirname(file_path) - return True + for dirname in folders: + search_path = os.path.join(app_path, dirname, self.path) + for p in self.get_index_path_options(search_path): + file_path = frappe.as_unicode(p) + if os.path.exists(file_path) and not os.path.isdir(file_path): + self.app = app + self.app_path = app_path + self.template_path = os.path.relpath(file_path, self.app_path) + self.basepath = os.path.dirname(file_path) + return + + def validate(self): + return hasattr(self, 'template_path') and bool(self.template_path) def get_index_path_options(self, search_path): return (f'{search_path}{d}' for d in ('', '.html', '.md', '/index.html', '/index.md')) @@ -59,12 +62,20 @@ class TemplatePage(BaseTemplatePage): return html def post_process_context(self): + self.set_user_info() self.add_sidebar_and_breadcrumbs() self.set_missing_values() super(TemplatePage, self).post_process_context() def add_sidebar_and_breadcrumbs(self): - add_sidebar_and_breadcrumbs(self.context) + if self.basepath: + sidebar_data = get_sidebar_data(self.context.website_sidebar, self.basepath) or None + self.context.sidebar_items = sidebar_data.sidebar_items + + if self.context.add_breadcrumbs and not self.context.parents: + # TODO: set correct title and route for breadcrumbs + parent_path = os.path.dirname(self.path) + self.context.parents = [dict(route=parent_path, title=extract_title(source='', path=parent_path))] def set_pymodule(self): ''' @@ -229,6 +240,13 @@ class TemplatePage(BaseTemplatePage): # for backward compatibility self.context.docs_base_url = '/docs' + def set_user_info(self): + from frappe.utils.user import get_fullname_and_avatar + info = get_fullname_and_avatar(frappe.session.user) + self.context["fullname"] = info.fullname + self.context["user_image"] = info.avatar + self.context["user"] = info.name + def get_start_folders(): return frappe.local.flags.web_pages_folders or ('www', 'templates/pages') From 19879943102d669ad94b82a9ba89177f2430d8d2 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 17 May 2021 23:37:28 +0530 Subject: [PATCH 078/495] refactor: Move unwanted code from validate - Also, removed unwanted code in render.py --- frappe/website/page_controllers/error_page.py | 5 ++- frappe/website/page_controllers/list_page.py | 11 ++++--- .../page_controllers/not_found_page.py | 8 +++-- .../page_controllers/not_permitted_page.py | 9 +++-- frappe/website/page_controllers/print_page.py | 14 +++++--- .../website/page_controllers/static_page.py | 32 ++++++++++-------- frappe/website/render.py | 33 +------------------ 7 files changed, 50 insertions(+), 62 deletions(-) diff --git a/frappe/website/page_controllers/error_page.py b/frappe/website/page_controllers/error_page.py index 1f54a5f38c..554949684f 100644 --- a/frappe/website/page_controllers/error_page.py +++ b/frappe/website/page_controllers/error_page.py @@ -1,7 +1,10 @@ from frappe.website.page_controllers.template_page import TemplatePage class ErrorPage(TemplatePage): - def __init__(self, path, http_status_code, exception): + def __init__(self, path=None, http_status_code=None, exception=None): path = 'error' super().__init__(path=path, http_status_code=http_status_code) self.http_status_code = getattr(exception, 'http_status_code', None) or http_status_code or 500 + + def validate(self): + return True diff --git a/frappe/website/page_controllers/list_page.py b/frappe/website/page_controllers/list_page.py index 2cb6ed5f26..cc6f150dd2 100644 --- a/frappe/website/page_controllers/list_page.py +++ b/frappe/website/page_controllers/list_page.py @@ -3,8 +3,9 @@ from frappe.website.page_controllers.template_page import TemplatePage class ListPage(TemplatePage): def validate(self): - if frappe.db.get_value('DocType', self.path): - frappe.local.form_dict.doctype = self.path - self.set_standard_path('list') - return True - return False + return frappe.db.exists('DocType', self.path, True) + + def render(self): + frappe.local.form_dict.doctype = self.path + self.set_standard_path('list') + return super().render() diff --git a/frappe/website/page_controllers/not_found_page.py b/frappe/website/page_controllers/not_found_page.py index 1d15877e9d..aebc0006ae 100644 --- a/frappe/website/page_controllers/not_found_page.py +++ b/frappe/website/page_controllers/not_found_page.py @@ -2,7 +2,9 @@ from frappe.website.page_controllers.template_page import TemplatePage class NotFoundPage(TemplatePage): def __init__(self, path, http_status_code): + path = '404' + http_status_code = 404 super().__init__(path=path, http_status_code=http_status_code) - self.path = '404' - self.http_status_code = 404 - self.template_path = '404' + + def validate(self): + return True diff --git a/frappe/website/page_controllers/not_permitted_page.py b/frappe/website/page_controllers/not_permitted_page.py index 61c6b88e09..3ed732d80b 100644 --- a/frappe/website/page_controllers/not_permitted_page.py +++ b/frappe/website/page_controllers/not_permitted_page.py @@ -1,13 +1,18 @@ import frappe from frappe import _ from frappe.website.page_controllers.template_page import TemplatePage +from frappe.utils import cstr class NotPermittedPage(TemplatePage): - def __init__(self, path, http_status_code): + def __init__(self, path=None, http_status_code=None, exception=''): + frappe.local.message = cstr(exception) super().__init__(path=path, http_status_code=http_status_code) self.http_status_code = 403 def validate(self): + return True + + def render(self): frappe.local.message_title = _("Not Permitted") frappe.local.response['context'] = dict( indicator_color = 'red', @@ -16,4 +21,4 @@ class NotPermittedPage(TemplatePage): fullpage=True ) self.set_standard_path('message') - return True + return super().render() diff --git a/frappe/website/page_controllers/print_page.py b/frappe/website/page_controllers/print_page.py index 6daea7fcbc..574f335f5d 100644 --- a/frappe/website/page_controllers/print_page.py +++ b/frappe/website/page_controllers/print_page.py @@ -9,11 +9,15 @@ class PrintPage(TemplatePage): def validate(self): parts = self.path.split('/', 1) if len(parts)==2: - if (frappe.db.get_value('DocType', parts[0]) - and frappe.db.get_value(parts[0], parts[1])): - frappe.form_dict.doctype = parts[0] - frappe.form_dict.name = parts[1] - self.set_standard_path('printview') + if (frappe.db.exists('DocType', parts[0], True) + and frappe.db.exists(parts[0], parts[1], True)): return True return False + + def render(self): + parts = self.path.split('/', 1) + frappe.form_dict.doctype = parts[0] + frappe.form_dict.name = parts[1] + self.set_standard_path('printview') + return super().render() diff --git a/frappe/website/page_controllers/static_page.py b/frappe/website/page_controllers/static_page.py index 8f474e71be..8c07440ebb 100644 --- a/frappe/website/page_controllers/static_page.py +++ b/frappe/website/page_controllers/static_page.py @@ -7,27 +7,31 @@ from werkzeug.wsgi import wrap_file import frappe from frappe.website.page_controllers.web_page import WebPage +UNSUPPORTED_STATIC_PAGE_TYPES = ('html', 'md', 'js', 'xml', 'css', 'txt', 'py', 'json') class StaticPage(WebPage): - def validate(self): - if ('.' not in self.path): - return False - extension = self.path.rsplit('.', 1)[-1] - if extension in ('html', 'md', 'js', 'xml', 'css', 'txt', 'py', 'json'): - return False + def __init__(self, path, http_status_code=None): + super().__init__(path=path, http_status_code=http_status_code) + self.set_file_path() - if self.find_path_in_apps(): - return True - - return False - - def find_path_in_apps(self): + def set_file_path(self): + self.file_path = '' + if not self.is_valid_file_path(): return for app in frappe.get_installed_apps(): file_path = frappe.get_app_path(app, 'www') + '/' + self.path if os.path.exists(file_path): self.file_path = file_path - return True - return False + + def validate(self): + return self.is_valid_file_path() and self.file_path + + def is_valid_file_path(self): + if ('.' not in self.path): + return False + extension = self.path.rsplit('.', 1)[-1] + if extension in UNSUPPORTED_STATIC_PAGE_TYPES: + return False + return True def render(self): f = open(self.file_path, 'rb') diff --git a/frappe/website/render.py b/frappe/website/render.py index 87eddf3463..6a1d5a404e 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -11,9 +11,8 @@ from werkzeug.wrappers import Response import frappe import frappe.sessions -from frappe.website.context import get_context from frappe.website.router import evaluate_dynamic_routes -from frappe.website.utils import (can_cache, get_home_page, get_next_link, get_toc) +from frappe.website.utils import get_home_page def build_response(path, data, http_status_code, headers=None): @@ -54,36 +53,6 @@ def add_preload_headers(response): import traceback traceback.print_exc() - -def build_page(path): - if not getattr(frappe.local, "path", None): - frappe.local.path = path - - context = get_context(path) - - if context.source: - html = frappe.render_template(context.source, context) - elif context.template: - if path.endswith('min.js'): - html = frappe.get_jloader().get_source(frappe.get_jenv(), context.template)[0] - else: - html = frappe.get_template(context.template).render(context) - - if '{index}' in html: - html = html.replace('{index}', get_toc(context.route)) - - if '{next}' in html: - html = html.replace('{next}', get_next_link(context.route)) - - # html = frappe.get_template(context.base_template_path).render(context) - - if can_cache(context.no_cache): - page_cache = frappe.cache().hget("website_page", path) or {} - page_cache[frappe.local.lang] = html - frappe.cache().hset("website_page", path, page_cache) - - return html - def resolve_path(path): if not path: path = "index" From 5318697c0fd74d7fd534e1e645fdd6e34464cb08 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 18 May 2021 08:40:37 +0530 Subject: [PATCH 079/495] feat: Add redirect page --- frappe/website/page_controllers/redirect_page.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 frappe/website/page_controllers/redirect_page.py diff --git a/frappe/website/page_controllers/redirect_page.py b/frappe/website/page_controllers/redirect_page.py new file mode 100644 index 0000000000..f3a0ba6e7f --- /dev/null +++ b/frappe/website/page_controllers/redirect_page.py @@ -0,0 +1,16 @@ +import frappe +from frappe.website.render import build_response + +class RedirectPage(object): + def __init__(self, path, http_status_code=301): + self.path = path + self.http_status_code = http_status_code + + def validate(self): + return True + + def render(self): + return build_response(self.path, "", 301, { + "Location": frappe.flags.redirect_location or (frappe.local.response or {}).get('location'), + "Cache-Control": "no-store, no-cache, must-revalidate" + }) From a02e5f766a70258ab05e741afc64263f06970137 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 18 May 2021 08:43:49 +0530 Subject: [PATCH 080/495] refactor: Add path_resolver to separate route resolving part --- frappe/website/path_resolver.py | 67 +++++++++++++++++++++++++++++++++ frappe/website/serve.py | 54 +++++++++++--------------- 2 files changed, 88 insertions(+), 33 deletions(-) create mode 100644 frappe/website/path_resolver.py diff --git a/frappe/website/path_resolver.py b/frappe/website/path_resolver.py new file mode 100644 index 0000000000..d6e43f4f1f --- /dev/null +++ b/frappe/website/path_resolver.py @@ -0,0 +1,67 @@ +from frappe.website.redirect import resolve_redirect +from frappe.website.render import resolve_path +import frappe + +from frappe.website.page_controllers.document_page import DocumentPage +from frappe.website.page_controllers.list_page import ListPage +from frappe.website.page_controllers.not_found_page import NotFoundPage +from frappe.website.page_controllers.print_page import PrintPage +from frappe.website.page_controllers.template_page import TemplatePage +from frappe.website.page_controllers.static_page import StaticPage +from frappe.website.page_controllers.web_form import WebFormPage + +class PathResolver(): + def __init__(self, path, http_status_code=None): + self._path = path + self.http_status_code = http_status_code + # self.url_map = get_url_map() + + @property + def path(self): + return self._path.strip('/ ') + + def resolve(self): + '''Returns endpoint and a renderer instance that can render the endpoint''' + query_string = frappe.local.request.query_string + resolve_redirect(self.path, query_string) + endpoint = resolve_path(self.path) + # urls = self.url_map.bind_to_environ(frappe.local.request.environ) + + renderers = (StaticPage, WebFormPage, TemplatePage, ListPage, DocumentPage, PrintPage, NotFoundPage) + + for renderer in renderers: + renderer_instance = renderer(endpoint, self.http_status_code) + can_render = renderer_instance.validate() + if can_render: + return endpoint, renderer_instance + + return endpoint, None + +# #> Path > Path > resolve using url_map > path.endpoint +# # +# # +# # +# # pathclass url_map + +# def get_url_map(): +# Map([Rule('/', endpoint=get_home_page, defaults={'renderer': TemplatePage})]) + + +# query_string = None +# response = None + +# if not path: +# path = frappe.local.request.path +# query_string = frappe.local.request.query_string + +# try: +# path = path.strip('/ ') +# resolve_redirect(path, query_string) +# path = resolve_path(path) +# # there is no way to determine the type of the page based on the route +# # so evaluate each type of page sequentially +# renderers = [StaticPage, WebFormPage, TemplatePage, ListPage, DocumentPage, PrintPage, NotFoundPage] +# for renderer in renderers: +# response = renderer(path, http_status_code).get() +# if response: +# break diff --git a/frappe/website/serve.py b/frappe/website/serve.py index 47ca0aa1d2..bdec8a16ba 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -1,49 +1,37 @@ import frappe -from frappe.utils import cstr -from frappe.website.page_controllers.document_page import DocumentPage from frappe.website.page_controllers.error_page import ErrorPage -from frappe.website.page_controllers.list_page import ListPage -from frappe.website.page_controllers.not_found_page import NotFoundPage from frappe.website.page_controllers.not_permitted_page import NotPermittedPage -from frappe.website.page_controllers.print_page import PrintPage -from frappe.website.page_controllers.template_page import TemplatePage -from frappe.website.page_controllers.static_page import StaticPage -from frappe.website.page_controllers.web_form import WebFormPage - -from frappe.website.redirect import resolve_redirect -from frappe.website.render import build_response, resolve_path +from frappe.website.page_controllers.redirect_page import RedirectPage +from frappe.website.path_resolver import PathResolver +from frappe.website.utils import can_cache def get_response(path=None, http_status_code=200): - """render html page""" - query_string = None + """Resolves path and renders page""" response = None + path = path or frappe.local.request.path + endpoint = path + # if can_cache(): + # # return rendered page + # page_cache = frappe.cache().hget("website_page", path) + # if page_cache and frappe.local.lang in page_cache: + # out = page_cache[frappe.local.lang] - if not path: - path = frappe.local.request.path - query_string = frappe.local.request.query_string + # if out: + # frappe.local.response.from_cache = True + # return out try: - path = path.strip('/ ') - resolve_redirect(path, query_string) - path = resolve_path(path) - # there is no way to determine the type of the page based on the route - # so evaluate each type of page sequentially - renderers = [StaticPage, WebFormPage, TemplatePage, ListPage, DocumentPage, PrintPage, NotFoundPage] - for renderer in renderers: - response = renderer(path, http_status_code).get() - if response: - break + path_resolver = PathResolver(path, http_status_code) + endpoint, renderer_instance = path_resolver.resolve() + if renderer_instance: + response = renderer_instance.render() except frappe.Redirect: - return build_response(path, "", 301, { - "Location": frappe.flags.redirect_location or (frappe.local.response or {}).get('location'), - "Cache-Control": "no-store, no-cache, must-revalidate" - }) + return RedirectPage(endpoint or path, http_status_code).render() except frappe.PermissionError as e: - frappe.local.message = cstr(e) - response = NotPermittedPage(path, http_status_code).get() + response = NotPermittedPage(endpoint, http_status_code, exception=e).render() except Exception as e: - response = ErrorPage(path, http_status_code, exception=e).get() + response = ErrorPage(exception=e).render() return response From c8d588819f940b89e8a48a5190ecf22a188b5ece Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 18 May 2021 09:05:02 +0530 Subject: [PATCH 081/495] refactor: Move get_sidebar_data method to utils - Also, rename get_sidebar_data to get_sidebar_items & delete context.py - Remove commented text --- frappe/website/context.py | 86 ---------------- .../website/page_controllers/template_page.py | 5 +- frappe/website/path_resolver.py | 39 +------- frappe/website/serve.py | 4 +- frappe/website/utils.py | 97 +++++++++++++++++-- 5 files changed, 99 insertions(+), 132 deletions(-) delete mode 100644 frappe/website/context.py diff --git a/frappe/website/context.py b/frappe/website/context.py deleted file mode 100644 index c8dade5b8b..0000000000 --- a/frappe/website/context.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt -import json -import os - -import frappe -from frappe.model.document import Document - -def get_sidebar_data(parent_sidebar, basepath): - import frappe.www.list - sidebar_data = frappe._dict() - - hooks = frappe.get_hooks('look_for_sidebar_json') - look_for_sidebar_json = hooks[0] if hooks else 0 - - if basepath and look_for_sidebar_json: - sidebar_items = get_sidebar_items_from_sidebar_file(basepath, look_for_sidebar_json) - sidebar_data['sidebar_items'] = sidebar_items - - if not sidebar_data.sidebar_items and parent_sidebar: - sidebar_data.sidebar_items = frappe.get_all('Website Sidebar Item', - filters=dict(parent=parent_sidebar), fields=['title', 'route', '`group`'], - order_by='idx asc') - - if not sidebar_data.sidebar_items: - sidebar_items = frappe.cache().hget('portal_menu_items', frappe.session.user) - if sidebar_items == None: - sidebar_items = [] - roles = frappe.get_roles() - portal_settings = frappe.get_doc('Portal Settings', 'Portal Settings') - - def add_items(sidebar_items, items): - for d in items: - if d.get('enabled') and ((not d.get('role')) or d.get('role') in roles): - sidebar_items.append(d.as_dict() if isinstance(d, Document) else d) - - if not portal_settings.hide_standard_menu: - add_items(sidebar_items, portal_settings.get('menu')) - - if portal_settings.custom_menu: - add_items(sidebar_items, portal_settings.get('custom_menu')) - - items_via_hooks = frappe.get_hooks('portal_menu_items') - if items_via_hooks: - for i in items_via_hooks: i['enabled'] = 1 - add_items(sidebar_items, items_via_hooks) - - frappe.cache().hset('portal_menu_items', frappe.session.user, sidebar_items) - sidebar_data.sidebar_items = sidebar_items - - return sidebar_data - -def get_sidebar_items_from_sidebar_file(basepath, look_for_sidebar_json): - sidebar_items = frappe._dict() - sidebar_json_path = get_sidebar_json_path(basepath, look_for_sidebar_json) - if not sidebar_json_path: return sidebar_items - - with open(sidebar_json_path, 'r') as sidebarfile: - try: - sidebar_json = sidebarfile.read() - sidebar_items = json.loads(sidebar_json) - except json.decoder.JSONDecodeError: - frappe.throw('Invalid Sidebar JSON at ' + sidebar_json_path) - - return sidebar_items - -def get_sidebar_json_path(path, look_for=False): - ''' - Get _sidebar.json path from directory path - - :param path: path of the current diretory - :param look_for: if True, look for _sidebar.json going upwards from given path - - :return: _sidebar.json path - ''' - if os.path.split(path)[1] == 'www' or path == '/' or not path: - return '' - - sidebar_json_path = os.path.join(path, '_sidebar.json') - if os.path.exists(sidebar_json_path): - return sidebar_json_path - else: - if look_for: - return get_sidebar_json_path(os.path.split(path)[0], look_for) - else: - return '' diff --git a/frappe/website/page_controllers/template_page.py b/frappe/website/page_controllers/template_page.py index 5b001a55e7..12f459afd7 100644 --- a/frappe/website/page_controllers/template_page.py +++ b/frappe/website/page_controllers/template_page.py @@ -3,7 +3,7 @@ import os import frappe from frappe.website.page_controllers.base_template_page import BaseTemplatePage -from frappe.website.context import get_sidebar_data +from frappe.website.utils import get_sidebar_items from frappe.website.render import build_response from frappe.website.router import get_base_template from frappe.website.utils import (extract_comment_tag, @@ -69,8 +69,7 @@ class TemplatePage(BaseTemplatePage): def add_sidebar_and_breadcrumbs(self): if self.basepath: - sidebar_data = get_sidebar_data(self.context.website_sidebar, self.basepath) or None - self.context.sidebar_items = sidebar_data.sidebar_items + self.context.sidebar_items = get_sidebar_items(self.context.website_sidebar, self.basepath) if self.context.add_breadcrumbs and not self.context.parents: # TODO: set correct title and route for breadcrumbs diff --git a/frappe/website/path_resolver.py b/frappe/website/path_resolver.py index d6e43f4f1f..b9f8972380 100644 --- a/frappe/website/path_resolver.py +++ b/frappe/website/path_resolver.py @@ -1,20 +1,19 @@ -from frappe.website.redirect import resolve_redirect -from frappe.website.render import resolve_path import frappe - from frappe.website.page_controllers.document_page import DocumentPage from frappe.website.page_controllers.list_page import ListPage from frappe.website.page_controllers.not_found_page import NotFoundPage from frappe.website.page_controllers.print_page import PrintPage -from frappe.website.page_controllers.template_page import TemplatePage from frappe.website.page_controllers.static_page import StaticPage +from frappe.website.page_controllers.template_page import TemplatePage from frappe.website.page_controllers.web_form import WebFormPage +from frappe.website.redirect import resolve_redirect +from frappe.website.render import resolve_path + class PathResolver(): def __init__(self, path, http_status_code=None): self._path = path self.http_status_code = http_status_code - # self.url_map = get_url_map() @property def path(self): @@ -25,7 +24,6 @@ class PathResolver(): query_string = frappe.local.request.query_string resolve_redirect(self.path, query_string) endpoint = resolve_path(self.path) - # urls = self.url_map.bind_to_environ(frappe.local.request.environ) renderers = (StaticPage, WebFormPage, TemplatePage, ListPage, DocumentPage, PrintPage, NotFoundPage) @@ -36,32 +34,3 @@ class PathResolver(): return endpoint, renderer_instance return endpoint, None - -# #> Path > Path > resolve using url_map > path.endpoint -# # -# # -# # -# # pathclass url_map - -# def get_url_map(): -# Map([Rule('/', endpoint=get_home_page, defaults={'renderer': TemplatePage})]) - - -# query_string = None -# response = None - -# if not path: -# path = frappe.local.request.path -# query_string = frappe.local.request.query_string - -# try: -# path = path.strip('/ ') -# resolve_redirect(path, query_string) -# path = resolve_path(path) -# # there is no way to determine the type of the page based on the route -# # so evaluate each type of page sequentially -# renderers = [StaticPage, WebFormPage, TemplatePage, ListPage, DocumentPage, PrintPage, NotFoundPage] -# for renderer in renderers: -# response = renderer(path, http_status_code).get() -# if response: -# break diff --git a/frappe/website/serve.py b/frappe/website/serve.py index bdec8a16ba..9b28fd2e3b 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -1,10 +1,10 @@ import frappe - from frappe.website.page_controllers.error_page import ErrorPage from frappe.website.page_controllers.not_permitted_page import NotPermittedPage from frappe.website.page_controllers.redirect_page import RedirectPage from frappe.website.path_resolver import PathResolver -from frappe.website.utils import can_cache + +#from frappe.website.utils import can_cache def get_response(path=None, http_status_code=200): """Resolves path and renders page""" diff --git a/frappe/website/utils.py b/frappe/website/utils.py index a5fe9c3986..723d878fac 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -2,16 +2,21 @@ # MIT License. See license.txt from __future__ import unicode_literals -import functools -import re -import os -import frappe -import yaml -from six import iteritems +import functools +import json +import os +import re + +import yaml from past.builtins import cmp +from six import iteritems + +import frappe +from frappe.model.document import Document from frappe.utils import md_to_html + def delete_page_cache(path): cache = frappe.cache() cache.delete_value('full_index') @@ -439,3 +444,83 @@ def get_frontmatter(string): "attributes": yaml.safe_load(frontmatter), "body": body, } + +def get_sidebar_items(parent_sidebar, basepath): + import frappe.www.list + sidebar_items = [] + + hooks = frappe.get_hooks('look_for_sidebar_json') + look_for_sidebar_json = hooks[0] if hooks else 0 + + if basepath and look_for_sidebar_json: + sidebar_items = get_sidebar_items_from_sidebar_file(basepath, look_for_sidebar_json) + + if not sidebar_items and parent_sidebar: + sidebar_items = frappe.get_all('Website Sidebar Item', + filters=dict(parent=parent_sidebar), fields=['title', 'route', '`group`'], + order_by='idx asc') + + if not sidebar_items: + sidebar_items = get_portal_sidebar_items() + + return sidebar_items + + +def get_portal_sidebar_items(): + sidebar_items = frappe.cache().hget('portal_menu_items', frappe.session.user) + if sidebar_items == None: + sidebar_items = [] + roles = frappe.get_roles() + portal_settings = frappe.get_doc('Portal Settings', 'Portal Settings') + + def add_items(sidebar_items, items): + for d in items: + if d.get('enabled') and ((not d.get('role')) or d.get('role') in roles): + sidebar_items.append(d.as_dict() if isinstance(d, Document) else d) + + if not portal_settings.hide_standard_menu: + add_items(sidebar_items, portal_settings.get('menu')) + + if portal_settings.custom_menu: + add_items(sidebar_items, portal_settings.get('custom_menu')) + + items_via_hooks = frappe.get_hooks('portal_menu_items') + if items_via_hooks: + for i in items_via_hooks: i['enabled'] = 1 + add_items(sidebar_items, items_via_hooks) + + frappe.cache().hset('portal_menu_items', frappe.session.user, sidebar_items) + + return sidebar_items + +def get_sidebar_items_from_sidebar_file(basepath, look_for_sidebar_json): + sidebar_items = frappe._dict() + sidebar_json_path = get_sidebar_json_path(basepath, look_for_sidebar_json) + if not sidebar_json_path: return sidebar_items + + with open(sidebar_json_path, 'r') as sidebarfile: + try: + sidebar_json = sidebarfile.read() + sidebar_items = json.loads(sidebar_json) + except json.decoder.JSONDecodeError: + frappe.throw('Invalid Sidebar JSON at ' + sidebar_json_path) + + return sidebar_items + +def get_sidebar_json_path(path, look_for=False): + '''Get _sidebar.json path from directory path + :param path: path of the current diretory + :param look_for: if True, look for _sidebar.json going upwards from given path + :return: _sidebar.json path + ''' + if os.path.split(path)[1] == 'www' or path == '/' or not path: + return '' + + sidebar_json_path = os.path.join(path, '_sidebar.json') + if os.path.exists(sidebar_json_path): + return sidebar_json_path + else: + if look_for: + return get_sidebar_json_path(os.path.split(path)[0], look_for) + else: + return '' From d6a539362d1a3401f549f6788450116004d6b986 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 18 May 2021 09:13:13 +0530 Subject: [PATCH 082/495] style: Fix formatting issue --- frappe/website/page_controllers/static_page.py | 3 ++- frappe/website/utils.py | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/frappe/website/page_controllers/static_page.py b/frappe/website/page_controllers/static_page.py index 8c07440ebb..767cc872d1 100644 --- a/frappe/website/page_controllers/static_page.py +++ b/frappe/website/page_controllers/static_page.py @@ -16,7 +16,8 @@ class StaticPage(WebPage): def set_file_path(self): self.file_path = '' - if not self.is_valid_file_path(): return + if not self.is_valid_file_path(): + return for app in frappe.get_installed_apps(): file_path = frappe.get_app_path(app, 'www') + '/' + self.path if os.path.exists(file_path): diff --git a/frappe/website/utils.py b/frappe/website/utils.py index 723d878fac..2d16658efb 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -468,7 +468,7 @@ def get_sidebar_items(parent_sidebar, basepath): def get_portal_sidebar_items(): sidebar_items = frappe.cache().hget('portal_menu_items', frappe.session.user) - if sidebar_items == None: + if sidebar_items is None: sidebar_items = [] roles = frappe.get_roles() portal_settings = frappe.get_doc('Portal Settings', 'Portal Settings') @@ -486,7 +486,8 @@ def get_portal_sidebar_items(): items_via_hooks = frappe.get_hooks('portal_menu_items') if items_via_hooks: - for i in items_via_hooks: i['enabled'] = 1 + for i in items_via_hooks: + i['enabled'] = 1 add_items(sidebar_items, items_via_hooks) frappe.cache().hset('portal_menu_items', frappe.session.user, sidebar_items) @@ -494,9 +495,10 @@ def get_portal_sidebar_items(): return sidebar_items def get_sidebar_items_from_sidebar_file(basepath, look_for_sidebar_json): - sidebar_items = frappe._dict() + sidebar_items = [] sidebar_json_path = get_sidebar_json_path(basepath, look_for_sidebar_json) - if not sidebar_json_path: return sidebar_items + if not sidebar_json_path: + return sidebar_items with open(sidebar_json_path, 'r') as sidebarfile: try: From 0c575f02eae7c66861a12b9c264d8ae05c11e760 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 18 May 2021 11:43:42 +0530 Subject: [PATCH 083/495] fix: Handle encoding issue and some edge cases --- frappe/website/page_controllers/template_page.py | 2 +- frappe/website/path_resolver.py | 2 +- frappe/website/serve.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/website/page_controllers/template_page.py b/frappe/website/page_controllers/template_page.py index 12f459afd7..9f3bf66701 100644 --- a/frappe/website/page_controllers/template_page.py +++ b/frappe/website/page_controllers/template_page.py @@ -234,7 +234,7 @@ class TemplatePage(BaseTemplatePage): self.context.url_prefix += '/' self.context.path = self.path - self.context.pathname = frappe.local.path + self.context.pathname = frappe.local.path if hasattr(frappe, 'local') else self.path # for backward compatibility self.context.docs_base_url = '/docs' diff --git a/frappe/website/path_resolver.py b/frappe/website/path_resolver.py index b9f8972380..1368932792 100644 --- a/frappe/website/path_resolver.py +++ b/frappe/website/path_resolver.py @@ -21,7 +21,7 @@ class PathResolver(): def resolve(self): '''Returns endpoint and a renderer instance that can render the endpoint''' - query_string = frappe.local.request.query_string + query_string = frappe.local.request.query_string if hasattr(frappe.local, 'request') else None resolve_redirect(self.path, query_string) endpoint = resolve_path(self.path) diff --git a/frappe/website/serve.py b/frappe/website/serve.py index 9b28fd2e3b..7a93ada825 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -37,4 +37,4 @@ def get_response(path=None, http_status_code=200): def get_response_content(path=None, http_status_code=200): response = get_response(path, http_status_code) - return response.data + return str(response.data, 'utf-8') From c217b32fa91c1a8e252a343bcb21f9c7283ca2e5 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 18 May 2021 15:41:23 +0530 Subject: [PATCH 084/495] fix: Add cache_html decorator to cache HTML --- .../website/page_controllers/template_page.py | 5 +++-- frappe/website/serve.py | 10 --------- frappe/website/utils.py | 22 +++++++++++++++++++ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/frappe/website/page_controllers/template_page.py b/frappe/website/page_controllers/template_page.py index 9f3bf66701..635b8a56fe 100644 --- a/frappe/website/page_controllers/template_page.py +++ b/frappe/website/page_controllers/template_page.py @@ -6,8 +6,8 @@ from frappe.website.page_controllers.base_template_page import BaseTemplatePage from frappe.website.utils import get_sidebar_items from frappe.website.render import build_response from frappe.website.router import get_base_template -from frappe.website.utils import (extract_comment_tag, - extract_title, get_next_link, get_toc, get_frontmatter) +from frappe.website.utils import (extract_comment_tag, extract_title, + get_next_link, get_toc, get_frontmatter, cache_html) WEBPAGE_PY_MODULE_PROPERTIES = ("base_template_path", "template", "no_cache", "sitemap", "condition_field") @@ -45,6 +45,7 @@ class TemplatePage(BaseTemplatePage): def render(self): return build_response(self.path, self.get_html(), self.http_status_code, self.headers) + @cache_html def get_html(self): # context object should be separate from self for security # because it will be accessed via the user defined template diff --git a/frappe/website/serve.py b/frappe/website/serve.py index 7a93ada825..7010b768d8 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -4,22 +4,12 @@ from frappe.website.page_controllers.not_permitted_page import NotPermittedPage from frappe.website.page_controllers.redirect_page import RedirectPage from frappe.website.path_resolver import PathResolver -#from frappe.website.utils import can_cache def get_response(path=None, http_status_code=200): """Resolves path and renders page""" response = None path = path or frappe.local.request.path endpoint = path - # if can_cache(): - # # return rendered page - # page_cache = frappe.cache().hget("website_page", path) - # if page_cache and frappe.local.lang in page_cache: - # out = page_cache[frappe.local.lang] - - # if out: - # frappe.local.response.from_cache = True - # return out try: path_resolver = PathResolver(path, http_status_code) diff --git a/frappe/website/utils.py b/frappe/website/utils.py index 2d16658efb..576d1d911c 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -7,6 +7,7 @@ import functools import json import os import re +from functools import wraps import yaml from past.builtins import cmp @@ -526,3 +527,24 @@ def get_sidebar_json_path(path, look_for=False): return get_sidebar_json_path(os.path.split(path)[0], look_for) else: return '' + +def cache_html(func): + @wraps(func) + def cache_html_decorator(*args, **kwargs): + if can_cache(): + html = None + page_cache = frappe.cache().hget("website_page", args[0].path) + if page_cache and frappe.local.lang in page_cache: + html = page_cache[frappe.local.lang] + if html: + frappe.local.response.from_cache = True + return html + html = func(*args, **kwargs) + if can_cache(): + page_cache = frappe.cache().hget("website_page", args[0].path) or {} + page_cache[frappe.local.lang] = html + frappe.cache().hset("website_page", args[0].path, page_cache) + + return html + + return cache_html_decorator From 6375818c10d73e9ae02ba7ee6e1821e3b4143857 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 18 May 2021 21:57:20 +0530 Subject: [PATCH 085/495] fix: 404 caching --- .../page_controllers/not_found_page.py | 20 +++++++++++++++++++ frappe/website/serve.py | 6 ++++++ frappe/website/utils.py | 3 ++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/frappe/website/page_controllers/not_found_page.py b/frappe/website/page_controllers/not_found_page.py index aebc0006ae..1f6bbd4b02 100644 --- a/frappe/website/page_controllers/not_found_page.py +++ b/frappe/website/page_controllers/not_found_page.py @@ -1,10 +1,30 @@ +import frappe from frappe.website.page_controllers.template_page import TemplatePage +from frappe.website.utils import can_cache +from urllib.parse import urlparse + +HOMEPAGE_PATHS = ('/', '/index', '', 'index') class NotFoundPage(TemplatePage): def __init__(self, path, http_status_code): + self.resolved_path = path path = '404' http_status_code = 404 super().__init__(path=path, http_status_code=http_status_code) def validate(self): return True + + def render(self): + if can_cache_404(self.resolved_path): + frappe.cache().hset('website_404', frappe.request.url, True) + return super().render() + +def can_cache_404(path): + # do not cache 404 for custom homepages + return can_cache() and not is_custom_home_page(path) + +def is_custom_home_page(path): + url = frappe.request.url + url_parts = urlparse(url) + return url_parts.path in HOMEPAGE_PATHS and path not in HOMEPAGE_PATHS diff --git a/frappe/website/serve.py b/frappe/website/serve.py index 7010b768d8..56fed22469 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -1,8 +1,10 @@ import frappe from frappe.website.page_controllers.error_page import ErrorPage +from frappe.website.page_controllers.not_found_page import NotFoundPage from frappe.website.page_controllers.not_permitted_page import NotPermittedPage from frappe.website.page_controllers.redirect_page import RedirectPage from frappe.website.path_resolver import PathResolver +from frappe.website.utils import can_cache def get_response(path=None, http_status_code=200): @@ -11,6 +13,10 @@ def get_response(path=None, http_status_code=200): path = path or frappe.local.request.path endpoint = path + if can_cache() and frappe.cache().hget('website_404', frappe.request.url): + response = NotFoundPage(path=path).render() + return response + try: path_resolver = PathResolver(path, http_status_code) endpoint, renderer_instance = path_resolver.resolve() diff --git a/frappe/website/utils.py b/frappe/website/utils.py index 576d1d911c..be18466714 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -540,7 +540,8 @@ def cache_html(func): frappe.local.response.from_cache = True return html html = func(*args, **kwargs) - if can_cache(): + context = args[0].context + if can_cache(context.no_cache): page_cache = frappe.cache().hget("website_page", args[0].path) or {} page_cache[frappe.local.lang] = html frappe.cache().hset("website_page", args[0].path, page_cache) From 48b9198ebd717fe3e3ac1ae1598c78dc04e91266 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 18 May 2021 23:34:30 +0530 Subject: [PATCH 086/495] test: Add test case to validate base_template_path --- frappe/website/doctype/web_page/test_web_page.py | 9 ++++++++- frappe/www/_test/_test_folder/_test_page.html | 3 +++ frappe/www/_test/_test_folder/_test_page.py | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 frappe/www/_test/_test_folder/_test_page.html create mode 100644 frappe/www/_test/_test_folder/_test_page.py diff --git a/frappe/website/doctype/web_page/test_web_page.py b/frappe/website/doctype/web_page/test_web_page.py index daaa41f5a8..2c43acc226 100644 --- a/frappe/website/doctype/web_page/test_web_page.py +++ b/frappe/website/doctype/web_page/test_web_page.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import unittest import frappe from frappe.website.router import resolve_route -from frappe.website.serve import get_response +from frappe.website.serve import get_response, get_response_content from frappe.utils import set_request test_records = frappe.get_test_records('Web Page') @@ -73,4 +73,11 @@ class TestWebPage(unittest.TestCase): finally: web_page.delete() + def test_custom_base_template_path(self): + content = get_response_content('/_test/_test_folder/_test_page') + # assert the text in base template is rendered + self.assertTrue('

This is for testing

' in frappe.as_unicode(content)) + + # assert template block rendered + self.assertTrue('

Test content

' in frappe.as_unicode(content)) diff --git a/frappe/www/_test/_test_folder/_test_page.html b/frappe/www/_test/_test_folder/_test_page.html new file mode 100644 index 0000000000..7364235e10 --- /dev/null +++ b/frappe/www/_test/_test_folder/_test_page.html @@ -0,0 +1,3 @@ +{% block content %} +

Test content

+{% endblock %} diff --git a/frappe/www/_test/_test_folder/_test_page.py b/frappe/www/_test/_test_folder/_test_page.py new file mode 100644 index 0000000000..fa7b5f5727 --- /dev/null +++ b/frappe/www/_test/_test_folder/_test_page.py @@ -0,0 +1,2 @@ +def get_context(context): + context.base_template_path = 'frappe/templates/test/_test_base.html' From 1be1c25e587c98b62a6134496dac19cd9f4af88e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 19 May 2021 04:46:19 +0530 Subject: [PATCH 087/495] fix: 404 caching - Move 404 resolving code to path resolver --- .../page_controllers/not_found_page.py | 28 +++++++++++-------- frappe/website/path_resolver.py | 12 ++++++-- frappe/website/serve.py | 6 ---- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/frappe/website/page_controllers/not_found_page.py b/frappe/website/page_controllers/not_found_page.py index 1f6bbd4b02..13c6358512 100644 --- a/frappe/website/page_controllers/not_found_page.py +++ b/frappe/website/page_controllers/not_found_page.py @@ -1,13 +1,16 @@ +import os +from urllib.parse import urlparse + import frappe from frappe.website.page_controllers.template_page import TemplatePage from frappe.website.utils import can_cache -from urllib.parse import urlparse -HOMEPAGE_PATHS = ('/', '/index', '', 'index') +HOMEPAGE_PATHS = ('/', '/index', 'index') class NotFoundPage(TemplatePage): def __init__(self, path, http_status_code): - self.resolved_path = path + self.request_path = path + self.request_url = frappe.local.request.url if hasattr(frappe.local, 'request') else '' path = '404' http_status_code = 404 super().__init__(path=path, http_status_code=http_status_code) @@ -16,15 +19,16 @@ class NotFoundPage(TemplatePage): return True def render(self): - if can_cache_404(self.resolved_path): - frappe.cache().hset('website_404', frappe.request.url, True) + if self.can_cache_404(): + frappe.cache().hset('website_404', self.request_url, True) return super().render() -def can_cache_404(path): - # do not cache 404 for custom homepages - return can_cache() and not is_custom_home_page(path) + def can_cache_404(self): + # do not cache 404 for custom homepages + return can_cache() and self.request_url and not self.is_custom_home_page() -def is_custom_home_page(path): - url = frappe.request.url - url_parts = urlparse(url) - return url_parts.path in HOMEPAGE_PATHS and path not in HOMEPAGE_PATHS + def is_custom_home_page(self): + url_parts = urlparse(self.request_url) + request_url = os.path.splitext(url_parts.path)[0] + request_path = os.path.splitext(self.request_path)[0] + return request_url in HOMEPAGE_PATHS and request_path not in HOMEPAGE_PATHS diff --git a/frappe/website/path_resolver.py b/frappe/website/path_resolver.py index 1368932792..1a813fac60 100644 --- a/frappe/website/path_resolver.py +++ b/frappe/website/path_resolver.py @@ -8,6 +8,7 @@ from frappe.website.page_controllers.template_page import TemplatePage from frappe.website.page_controllers.web_form import WebFormPage from frappe.website.redirect import resolve_redirect from frappe.website.render import resolve_path +from frappe.website.utils import can_cache class PathResolver(): @@ -21,8 +22,15 @@ class PathResolver(): def resolve(self): '''Returns endpoint and a renderer instance that can render the endpoint''' - query_string = frappe.local.request.query_string if hasattr(frappe.local, 'request') else None - resolve_redirect(self.path, query_string) + request = frappe._dict() + if hasattr(frappe.local, 'request'): + request = frappe.local.request or request + + # check if the request url is in 404 list + if request.url and can_cache() and frappe.cache().hget('website_404', request.url): + return self.path, NotFoundPage(self.path) + + resolve_redirect(self.path, request.query_string) endpoint = resolve_path(self.path) renderers = (StaticPage, WebFormPage, TemplatePage, ListPage, DocumentPage, PrintPage, NotFoundPage) diff --git a/frappe/website/serve.py b/frappe/website/serve.py index 56fed22469..7010b768d8 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -1,10 +1,8 @@ import frappe from frappe.website.page_controllers.error_page import ErrorPage -from frappe.website.page_controllers.not_found_page import NotFoundPage from frappe.website.page_controllers.not_permitted_page import NotPermittedPage from frappe.website.page_controllers.redirect_page import RedirectPage from frappe.website.path_resolver import PathResolver -from frappe.website.utils import can_cache def get_response(path=None, http_status_code=200): @@ -13,10 +11,6 @@ def get_response(path=None, http_status_code=200): path = path or frappe.local.request.path endpoint = path - if can_cache() and frappe.cache().hget('website_404', frappe.request.url): - response = NotFoundPage(path=path).render() - return response - try: path_resolver = PathResolver(path, http_status_code) endpoint, renderer_instance = path_resolver.resolve() From b193666c71a32550ce61a2a9a7bc722ef1b2a2fd Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 19 May 2021 11:34:04 +0530 Subject: [PATCH 088/495] fix: Do not set content type for paths ending with .com To avoid unnecessary download --- frappe/website/render.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/website/render.py b/frappe/website/render.py index 6a1d5a404e..c14d8e0d48 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -103,7 +103,9 @@ def set_content_type(response, data, path): response.mimetype = 'text/html' response.charset = 'utf-8' - if "." in path: + # ignore paths ending with .com to avoid unnecessary download + # https://bugs.python.org/issue22347 + if "." in path and not path.endswith('.com'): content_type, encoding = mimetypes.guess_type(path) if content_type: response.mimetype = content_type From 0b37e0dbd7a4f82d0562685723757282304611f2 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 19 May 2021 13:55:45 +0530 Subject: [PATCH 089/495] fix: Check if resolved file_path in StaticPage is a file --- frappe/website/page_controllers/static_page.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/page_controllers/static_page.py b/frappe/website/page_controllers/static_page.py index 767cc872d1..b292a62922 100644 --- a/frappe/website/page_controllers/static_page.py +++ b/frappe/website/page_controllers/static_page.py @@ -20,7 +20,7 @@ class StaticPage(WebPage): return for app in frappe.get_installed_apps(): file_path = frappe.get_app_path(app, 'www') + '/' + self.path - if os.path.exists(file_path): + if os.path.isfile(file_path): self.file_path = file_path def validate(self): From 68aa1805aaca41476ea4d47cba9d88ab7cf17c18 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 19 May 2021 14:03:31 +0530 Subject: [PATCH 090/495] fix: Exception handling --- frappe/utils/global_search.py | 4 +-- .../website/doctype/web_page/test_web_page.py | 31 ++++++++++++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index 4c78d63918..a7d484a7ff 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -332,8 +332,8 @@ def add_route_to_global_search(route): route=route ) sync_value_in_queue(value) - except (frappe.PermissionError, frappe.DoesNotExistError, frappe.ValidationError, Exception) as e: - raise e + except Exception: + pass frappe.set_user('Administrator') diff --git a/frappe/website/doctype/web_page/test_web_page.py b/frappe/website/doctype/web_page/test_web_page.py index 2c43acc226..b40b63b0d5 100644 --- a/frappe/website/doctype/web_page/test_web_page.py +++ b/frappe/website/doctype/web_page/test_web_page.py @@ -63,7 +63,7 @@ class TestWebPage(unittest.TestCase): dynamic_route = 1, route = '/doctype-view/', content_type = 'HTML', - dymamic_template = 1, + dynamic_template = 1, main_section_html = '
{{ frappe.form_dict.doctype }}
' )).insert() @@ -81,3 +81,32 @@ class TestWebPage(unittest.TestCase): # assert template block rendered self.assertTrue('

Test content

' in frappe.as_unicode(content)) + def test_home_page(self): + content = get_response_content(path='/') + print(content) + content = get_response_content(path='/index') + print(content) + + def test_table_of_content(self): + page = get_response(path='/_test/_test_folder/_test_toc') + print(page) + + def test_sidebar_data(self): + pass + + def test_meta_tags(self): + pass + + def test_breadcrumbs(self): + pass + + def test_downloadable_file(self): + pass + + + + +# breadcrumb +# validate +# cache no-cache +# page context caching From c26c5d9ef027312ba695c962fe0427172a8db27d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 19 May 2021 19:04:16 +0530 Subject: [PATCH 091/495] feat: Add is_valid_path method to PathResolver --- .../website/doctype/web_page/test_web_page.py | 11 ++++---- frappe/website/path_resolver.py | 26 +++++++++++-------- frappe/website/serve.py | 2 +- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/frappe/website/doctype/web_page/test_web_page.py b/frappe/website/doctype/web_page/test_web_page.py index b40b63b0d5..108b36caaa 100644 --- a/frappe/website/doctype/web_page/test_web_page.py +++ b/frappe/website/doctype/web_page/test_web_page.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import unittest import frappe -from frappe.website.router import resolve_route +from frappe.website.path_resolver import PathResolver from frappe.website.serve import get_response, get_response_content from frappe.utils import set_request @@ -18,10 +18,11 @@ class TestWebPage(unittest.TestCase): for t in test_records: frappe.get_doc(t).insert() - def test_check_sitemap(self): - resolve_route("test-web-page-1") - resolve_route("test-web-page-1/test-web-page-2") - resolve_route("test-web-page-1/test-web-page-3") + def test_path_resolver(self): + self.assertTrue(PathResolver("test-web-page-1").is_valid_path()) + self.assertTrue(PathResolver("test-web-page-1/test-web-page-2").is_valid_path()) + self.assertTrue(PathResolver("test-web-page-1/test-web-page-3").is_valid_path()) + self.assertFalse(PathResolver("test-web-page-1/test-web-page-Random").is_valid_path()) def test_base_template(self): content = get_page_content('/_test/_test_custom_base.html') diff --git a/frappe/website/path_resolver.py b/frappe/website/path_resolver.py index 1a813fac60..b6f7dbe9f9 100644 --- a/frappe/website/path_resolver.py +++ b/frappe/website/path_resolver.py @@ -6,19 +6,15 @@ from frappe.website.page_controllers.print_page import PrintPage from frappe.website.page_controllers.static_page import StaticPage from frappe.website.page_controllers.template_page import TemplatePage from frappe.website.page_controllers.web_form import WebFormPage +from frappe.website.page_controllers.redirect_page import RedirectPage from frappe.website.redirect import resolve_redirect from frappe.website.render import resolve_path from frappe.website.utils import can_cache class PathResolver(): - def __init__(self, path, http_status_code=None): - self._path = path - self.http_status_code = http_status_code - - @property - def path(self): - return self._path.strip('/ ') + def __init__(self, path): + self.path = path.strip('/ ') def resolve(self): '''Returns endpoint and a renderer instance that can render the endpoint''' @@ -30,15 +26,23 @@ class PathResolver(): if request.url and can_cache() and frappe.cache().hget('website_404', request.url): return self.path, NotFoundPage(self.path) - resolve_redirect(self.path, request.query_string) - endpoint = resolve_path(self.path) + try: + resolve_redirect(self.path, request.query_string) + except frappe.Redirect: + return self.path, RedirectPage(self.path) + endpoint = resolve_path(self.path) renderers = (StaticPage, WebFormPage, TemplatePage, ListPage, DocumentPage, PrintPage, NotFoundPage) for renderer in renderers: - renderer_instance = renderer(endpoint, self.http_status_code) + renderer_instance = renderer(endpoint, 200) can_render = renderer_instance.validate() if can_render: return endpoint, renderer_instance - return endpoint, None + return endpoint, NotFoundPage(endpoint) + + def is_valid_path(self): + _endpoint, renderer_instance = self.resolve() + return not isinstance(renderer_instance, NotFoundPage) + diff --git a/frappe/website/serve.py b/frappe/website/serve.py index 7010b768d8..95a49cef1a 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -12,7 +12,7 @@ def get_response(path=None, http_status_code=200): endpoint = path try: - path_resolver = PathResolver(path, http_status_code) + path_resolver = PathResolver(path) endpoint, renderer_instance = path_resolver.resolve() if renderer_instance: response = renderer_instance.render() From adb4bc8e30bff16602556f8563d8a09759d58a87 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 19 May 2021 19:08:22 +0530 Subject: [PATCH 092/495] refactor: Delete unused code --- frappe/website/doctype/web_page/web_page.py | 42 +----- .../website_settings/website_settings.py | 13 +- frappe/website/router.py | 125 +----------------- frappe/website/utils.py | 19 --- 4 files changed, 14 insertions(+), 185 deletions(-) diff --git a/frappe/website/doctype/web_page/web_page.py b/frappe/website/doctype/web_page/web_page.py index d3c7d19b9a..cc41e4e339 100644 --- a/frappe/website/doctype/web_page/web_page.py +++ b/frappe/website/doctype/web_page/web_page.py @@ -1,23 +1,19 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import print_function, unicode_literals - import re -import requests -import requests.exceptions from jinja2.exceptions import TemplateSyntaxError import frappe -from frappe.utils import get_datetime, now, strip_html, quoted +from frappe import _ +from frappe.utils import get_datetime, now, quoted, strip_html from frappe.utils.jinja import render_template -from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow -from frappe.website.router import resolve_route -from frappe.website.utils import (extract_title, find_first_image, get_comment_list, - get_html_content_based_on_type) -from frappe.website.website_generator import WebsiteGenerator from frappe.utils.safe_exec import safe_exec +from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow +from frappe.website.utils import (extract_title, find_first_image, + get_comment_list, get_html_content_based_on_type) +from frappe.website.website_generator import WebsiteGenerator class WebPage(WebsiteGenerator): @@ -184,32 +180,6 @@ def check_publish_status(): frappe.db.set_value("Web Page", page.name, "published", 1) - -def check_broken_links(): - cnt = 0 - for p in frappe.db.sql("select name, main_section from `tabWeb Page`", as_dict=True): - for link in re.findall('href=["\']([^"\']*)["\']', p.main_section): - if link.startswith("http"): - try: - res = requests.get(link) - except requests.exceptions.SSLError: - res = frappe._dict({"status_code": "SSL Error"}) - except requests.exceptions.ConnectionError: - res = frappe._dict({"status_code": "Connection Error"}) - - if res.status_code!=200: - print("[{0}] {1}: {2}".format(res.status_code, p.name, link)) - cnt += 1 - else: - link = link[1:] # remove leading / - link = link.split("#")[0] - - if not resolve_route(link): - print(p.name + ":" + link) - cnt += 1 - - print("{0} links broken".format(cnt)) - def get_web_blocks_html(blocks): '''Converts a list of blocks into Raw HTML and extracts out their scripts for deduplication''' diff --git a/frappe/website/doctype/website_settings/website_settings.py b/frappe/website/doctype/website_settings/website_settings.py index f7f22aa2df..ae8e6bd7e1 100644 --- a/frappe/website/doctype/website_settings/website_settings.py +++ b/frappe/website/doctype/website_settings/website_settings.py @@ -1,15 +1,13 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt +from six.moves.urllib.parse import quote -from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import get_request_site_address, encode -from frappe.model.document import Document -from six.moves.urllib.parse import quote -from frappe.website.router import resolve_route -from frappe.website.doctype.website_theme.website_theme import add_website_theme from frappe.integrations.doctype.google_settings.google_settings import get_auth_url +from frappe.model.document import Document +from frappe.utils import encode, get_request_site_address +from frappe.website.doctype.website_theme.website_theme import add_website_theme INDEXING_SCOPES = "https://www.googleapis.com/auth/indexing" @@ -23,7 +21,8 @@ class WebsiteSettings(Document): def validate_home_page(self): if frappe.flags.in_install: return - if self.home_page and not resolve_route(self.home_page): + from frappe.website.path_resolver import PathResolver + if self.home_page and not PathResolver(self.home_page).is_valid_path(): frappe.msgprint(_("Invalid Home Page") + " (Standard pages - index, login, products, blog, about, contact)") self.home_page = '' diff --git a/frappe/website/router.py b/frappe/website/router.py index 2e27b4c690..9922647ebf 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -5,130 +5,9 @@ import io import os import re -from werkzeug.routing import Map, NotFound, Rule - import frappe -from frappe.model.document import get_controller -from frappe.website.utils import can_cache, extract_comment_tag, extract_title - - -def resolve_route(path): - """Returns the page route object based on searching in pages and generators. - The `www` folder is also a part of generator **Web Page**. - - The only exceptions are `/about` and `/contact` these will be searched in Web Pages - first before checking the standard pages.""" - - if path not in ("about", "contact"): - context = get_page_info_from_template(path) - if context: - return context - return get_page_context_from_doctype(path) - else: - context = get_page_context_from_doctype(path) - if context: - return context - return get_page_info_from_template(path) - -def get_page_context(path): - page_context = None - if can_cache(): - page_context_cache = frappe.cache().hget("page_context", path) or {} - page_context = page_context_cache.get(frappe.local.lang, None) - - if not page_context: - page_context = make_page_context(path) - if can_cache(page_context.no_cache): - page_context_cache[frappe.local.lang] = page_context - frappe.cache().hset("page_context", path, page_context_cache) - - return page_context - -def make_page_context(path): - context = resolve_route(path) - if not context: - raise frappe.PageDoesNotExistError - - context.doctype = context.ref_doctype - - if context.page_title: - context.title = context.page_title - - context.pathname = frappe.local.path - - return context - -def get_page_info_from_template(path): - '''Return page_info from path''' - for app in frappe.get_installed_apps(frappe_last=True): - app_path = frappe.get_app_path(app) - - folders = get_start_folders() - - for start in folders: - search_path = os.path.join(app_path, start, path) - options = (search_path, search_path + '.html', search_path + '.md', - search_path + '/index.html', search_path + '/index.md') - for o in options: - option = frappe.as_unicode(o) - if os.path.exists(option) and not os.path.isdir(option): - return get_page_info(option, app, start, app_path=app_path) - - return None - -def get_page_context_from_doctype(path): - page_info = get_page_info_from_doctypes(path) - if not page_info: - page_info = get_page_info_from_web_page_with_dynamic_routes(path) - - if page_info: - return frappe.get_doc(page_info.get("doctype"), - page_info.get("name")).get_page_info() - -def get_all_page_context_from_doctypes(): - ''' - Get all doctype generated routes (for sitemap.xml) - ''' - routes = frappe.cache().get_value("website_generator_routes") - if not routes: - routes = get_page_info_from_doctypes() - frappe.cache().set_value("website_generator_routes", routes) - - return routes - -def get_page_info_from_doctypes(path=None): - ''' - Find a document with matching `route` from all doctypes with `has_web_view`=1 - ''' - routes = {} - for doctype in get_doctypes_with_web_view(): - filters = {} - controller = get_controller(doctype) - meta = frappe.get_meta(doctype) - - condition_field = (meta.is_published_field or - # custom doctypes dont have controllers and no website attribute - (controller.website.condition_field if not meta.custom else None)) - - if condition_field: - filters[condition_field] = 1 - - if path: - filters['route'] = path - - try: - for r in frappe.get_all(doctype, fields = ['name', 'route', 'modified'], - filters = filters, limit = 1): - - routes[r.route] = {"doctype": doctype, "name": r.name, "modified": r.modified} - - # just want one path, return it! - if path: - return routes[r.route] - except Exception as e: - if not frappe.db.is_missing_column(e): raise e - - return routes +from frappe.website.utils import extract_comment_tag, extract_title +from werkzeug.routing import Map, Rule, NotFound def get_page_info_from_web_page_with_dynamic_routes(path): ''' diff --git a/frappe/website/utils.py b/frappe/website/utils.py index be18466714..4f19267a7d 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -371,25 +371,6 @@ def extract_comment_tag(source, tag): return None -def add_missing_headers(): - '''Walk and add missing headers in docs (to be called from bench execute)''' - path = frappe.get_app_path('erpnext', 'docs') - for basepath, folders, files in os.walk(path): - for fname in files: - if fname.endswith('.md'): - with open(os.path.join(basepath, fname), 'r') as f: - content = frappe.as_unicode(f.read()) - - if not content.startswith('# ') and not '

' in content: - with open(os.path.join(basepath, fname), 'w') as f: - if fname=='index.md': - fname = os.path.basename(basepath) - else: - fname = fname[:-3] - h = fname.replace('_', ' ').replace('-', ' ').title() - content = '# {0}\n\n'.format(h) + content - f.write(content.encode('utf-8')) - def get_html_content_based_on_type(doc, fieldname, content_type): ''' Set content based on content_type From 132434cc9476254e2abc5df2b0408f90d2b9bb76 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 20 May 2021 01:23:54 +0530 Subject: [PATCH 093/495] refactor: Delete unused code --- frappe/website/doctype/web_page/web_page.py | 42 +------ .../website_settings/website_settings.py | 13 +-- frappe/website/utils.py | 104 +----------------- 3 files changed, 15 insertions(+), 144 deletions(-) diff --git a/frappe/website/doctype/web_page/web_page.py b/frappe/website/doctype/web_page/web_page.py index d3c7d19b9a..cc41e4e339 100644 --- a/frappe/website/doctype/web_page/web_page.py +++ b/frappe/website/doctype/web_page/web_page.py @@ -1,23 +1,19 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import print_function, unicode_literals - import re -import requests -import requests.exceptions from jinja2.exceptions import TemplateSyntaxError import frappe -from frappe.utils import get_datetime, now, strip_html, quoted +from frappe import _ +from frappe.utils import get_datetime, now, quoted, strip_html from frappe.utils.jinja import render_template -from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow -from frappe.website.router import resolve_route -from frappe.website.utils import (extract_title, find_first_image, get_comment_list, - get_html_content_based_on_type) -from frappe.website.website_generator import WebsiteGenerator from frappe.utils.safe_exec import safe_exec +from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow +from frappe.website.utils import (extract_title, find_first_image, + get_comment_list, get_html_content_based_on_type) +from frappe.website.website_generator import WebsiteGenerator class WebPage(WebsiteGenerator): @@ -184,32 +180,6 @@ def check_publish_status(): frappe.db.set_value("Web Page", page.name, "published", 1) - -def check_broken_links(): - cnt = 0 - for p in frappe.db.sql("select name, main_section from `tabWeb Page`", as_dict=True): - for link in re.findall('href=["\']([^"\']*)["\']', p.main_section): - if link.startswith("http"): - try: - res = requests.get(link) - except requests.exceptions.SSLError: - res = frappe._dict({"status_code": "SSL Error"}) - except requests.exceptions.ConnectionError: - res = frappe._dict({"status_code": "Connection Error"}) - - if res.status_code!=200: - print("[{0}] {1}: {2}".format(res.status_code, p.name, link)) - cnt += 1 - else: - link = link[1:] # remove leading / - link = link.split("#")[0] - - if not resolve_route(link): - print(p.name + ":" + link) - cnt += 1 - - print("{0} links broken".format(cnt)) - def get_web_blocks_html(blocks): '''Converts a list of blocks into Raw HTML and extracts out their scripts for deduplication''' diff --git a/frappe/website/doctype/website_settings/website_settings.py b/frappe/website/doctype/website_settings/website_settings.py index f7f22aa2df..ae8e6bd7e1 100644 --- a/frappe/website/doctype/website_settings/website_settings.py +++ b/frappe/website/doctype/website_settings/website_settings.py @@ -1,15 +1,13 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt +from six.moves.urllib.parse import quote -from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import get_request_site_address, encode -from frappe.model.document import Document -from six.moves.urllib.parse import quote -from frappe.website.router import resolve_route -from frappe.website.doctype.website_theme.website_theme import add_website_theme from frappe.integrations.doctype.google_settings.google_settings import get_auth_url +from frappe.model.document import Document +from frappe.utils import encode, get_request_site_address +from frappe.website.doctype.website_theme.website_theme import add_website_theme INDEXING_SCOPES = "https://www.googleapis.com/auth/indexing" @@ -23,7 +21,8 @@ class WebsiteSettings(Document): def validate_home_page(self): if frappe.flags.in_install: return - if self.home_page and not resolve_route(self.home_page): + from frappe.website.path_resolver import PathResolver + if self.home_page and not PathResolver(self.home_page).is_valid_path(): frappe.msgprint(_("Invalid Home Page") + " (Standard pages - index, login, products, blog, about, contact)") self.home_page = '' diff --git a/frappe/website/utils.py b/frappe/website/utils.py index be18466714..9e98b9da9c 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -1,8 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals - import functools import json import os @@ -156,91 +153,15 @@ def cleanup_page_name(title): name = title.lower() name = re.sub(r'[~!@#$%^&*+()<>,."\'\?]', '', name) name = re.sub('[:/]', '-', name) - name = '-'.join(name.split()) - # replace repeating hyphens name = re.sub(r"(-)\1+", r"\1", name) - return name[:140] -def get_shade(color, percent): - color, color_format = detect_color_format(color) - r, g, b, a = color - - avg = (float(int(r) + int(g) + int(b)) / 3) - # switch dark and light shades - if avg > 128: - percent = -percent - - # stronger diff for darker shades - if percent < 25 and avg < 64: - percent = percent * 2 - - new_color = [] - for channel_value in (r, g, b): - new_color.append(get_shade_for_channel(channel_value, percent)) - - r, g, b = new_color - - return format_color(r, g, b, a, color_format) - - -def detect_color_format(color): - if color.startswith("rgba"): - color_format = "rgba" - color = [c.strip() for c in color[5:-1].split(",")] - - elif color.startswith("rgb"): - color_format = "rgb" - color = [c.strip() for c in color[4:-1].split(",")] + [1] - - else: - # assume hex - color_format = "hex" - - if color.startswith("#"): - color = color[1:] - - if len(color) == 3: - # hex in short form like #fff - color = "{0}{0}{1}{1}{2}{2}".format(*tuple(color)) - - color = [int(color[0:2], 16), int(color[2:4], 16), int(color[4:6], 16), 1] - - return color, color_format - - -def get_shade_for_channel(channel_value, percent): - v = int(channel_value) + int(int('ff', 16) * (float(percent)/100)) - if v < 0: - v=0 - if v > 255: - v=255 - - return v - - -def format_color(r, g, b, a, color_format): - if color_format == "rgba": - return "rgba({0}, {1}, {2}, {3})".format(r, g, b, a) - - elif color_format == "rgb": - return "rgb({0}, {1}, {2})".format(r, g, b) - - else: - # assume hex - return "#{0}{1}{2}".format(convert_to_hex(r), convert_to_hex(g), convert_to_hex(b)) - - -def convert_to_hex(channel_value): - h = hex(channel_value)[2:] - - if len(h) < 2: - h = "0" + h - - return h +def get_shade(color, percent=None): + frappe.msgprint('get_shade method has been deprecated!') + return color def abs_url(path): """Deconstructs and Reconstructs a URL into an absolute URL or a URL relative from root '/'""" @@ -371,25 +292,6 @@ def extract_comment_tag(source, tag): return None -def add_missing_headers(): - '''Walk and add missing headers in docs (to be called from bench execute)''' - path = frappe.get_app_path('erpnext', 'docs') - for basepath, folders, files in os.walk(path): - for fname in files: - if fname.endswith('.md'): - with open(os.path.join(basepath, fname), 'r') as f: - content = frappe.as_unicode(f.read()) - - if not content.startswith('# ') and not '

' in content: - with open(os.path.join(basepath, fname), 'w') as f: - if fname=='index.md': - fname = os.path.basename(basepath) - else: - fname = fname[:-3] - h = fname.replace('_', ' ').replace('-', ' ').title() - content = '# {0}\n\n'.format(h) + content - f.write(content.encode('utf-8')) - def get_html_content_based_on_type(doc, fieldname, content_type): ''' Set content based on content_type From 862b5320b6931a8f31164f6e234f4e71af17aea6 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 20 May 2021 01:29:02 +0530 Subject: [PATCH 094/495] refactor: Move code to appropriate files - Delete unused resolve_route method - move resolve_path & resolve_redirect to path_resolver - Remove redirect.py --- frappe/core/doctype/user/user.py | 4 +- frappe/website/path_resolver.py | 93 ++++++++++++++++++++++- frappe/website/redirect.py | 45 ----------- frappe/website/render.py | 84 +++++---------------- frappe/website/router.py | 125 +------------------------------ frappe/website/utils.py | 12 +-- frappe/www/list.py | 2 +- 7 files changed, 117 insertions(+), 248 deletions(-) delete mode 100644 frappe/website/redirect.py diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index a4d13a57e0..e202711b07 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -17,7 +17,7 @@ from frappe.utils.password import update_password as _update_password, check_pas from frappe.desk.notifications import clear_notifications from frappe.desk.doctype.notification_settings.notification_settings import create_notification_settings, toggle_notifications from frappe.utils.user import get_system_managers -from frappe.website.utils import is_signup_enabled +from frappe.website.utils import is_signup_disabled from frappe.rate_limiter import rate_limit from frappe.utils.background_jobs import enqueue from frappe.core.doctype.user_type.user_type import user_linked_with_permission_on_doctype @@ -843,7 +843,7 @@ def verify_password(password): @frappe.whitelist(allow_guest=True) def sign_up(email, full_name, redirect_to): - if not is_signup_enabled(): + if is_signup_disabled(): frappe.throw(_('Sign Up is disabled'), title='Not Allowed') user = frappe.db.get("User", {"email": email}) diff --git a/frappe/website/path_resolver.py b/frappe/website/path_resolver.py index b6f7dbe9f9..39edf0d9a8 100644 --- a/frappe/website/path_resolver.py +++ b/frappe/website/path_resolver.py @@ -1,15 +1,18 @@ +import re + +from werkzeug.routing import Rule + import frappe from frappe.website.page_controllers.document_page import DocumentPage from frappe.website.page_controllers.list_page import ListPage from frappe.website.page_controllers.not_found_page import NotFoundPage from frappe.website.page_controllers.print_page import PrintPage +from frappe.website.page_controllers.redirect_page import RedirectPage from frappe.website.page_controllers.static_page import StaticPage from frappe.website.page_controllers.template_page import TemplatePage from frappe.website.page_controllers.web_form import WebFormPage -from frappe.website.page_controllers.redirect_page import RedirectPage -from frappe.website.redirect import resolve_redirect -from frappe.website.render import resolve_path -from frappe.website.utils import can_cache +from frappe.website.router import evaluate_dynamic_routes +from frappe.website.utils import can_cache, get_home_page class PathResolver(): @@ -46,3 +49,85 @@ class PathResolver(): _endpoint, renderer_instance = self.resolve() return not isinstance(renderer_instance, NotFoundPage) + +def resolve_redirect(path, query_string=None): + ''' + Resolve redirects from hooks + + Example: + + website_redirect = [ + # absolute location + {"source": "/from", "target": "https://mysite/from"}, + + # relative location + {"source": "/from", "target": "/main"}, + + # use regex + {"source": r"/from/(.*)", "target": r"/main/\1"} + # use r as a string prefix if you use regex groups or want to escape any string literal + ] + ''' + redirects = frappe.get_hooks('website_redirects') + redirects += frappe.db.get_all('Website Route Redirect', ['source', 'target']) + + if not redirects: return + + redirect_to = frappe.cache().hget('website_redirects', path) + + if redirect_to: + frappe.flags.redirect_location = redirect_to + raise frappe.Redirect + + for rule in redirects: + pattern = rule['source'].strip('/ ') + '$' + path_to_match = path + if rule.get('match_with_query_string'): + path_to_match = path + '?' + frappe.safe_decode(query_string) + + if re.match(pattern, path_to_match): + redirect_to = re.sub(pattern, rule['target'], path_to_match) + frappe.flags.redirect_location = redirect_to + frappe.cache().hset('website_redirects', path_to_match, redirect_to) + raise frappe.Redirect + + +def resolve_path(path): + if not path: + path = "index" + + if path.endswith('.html'): + path = path[:-5] + + if path == "index": + path = get_home_page() + + frappe.local.path = path + + if path != "index": + path = resolve_from_map(path) + + return path + +def resolve_from_map(path): + '''transform dynamic route to a static one from hooks and route defined in doctype''' + rules = [Rule(r["from_route"], endpoint=r["to_route"], defaults=r.get("defaults")) + for r in get_website_rules()] + + return evaluate_dynamic_routes(rules, path) or path + +def get_website_rules(): + '''Get website route rules from hooks and DocType route''' + def _get(): + rules = frappe.get_hooks("website_route_rules") + for d in frappe.get_all('DocType', 'name, route', dict(has_web_view=1)): + if d.route: + rules.append(dict(from_route = '/' + d.route.strip('/'), to_route=d.name)) + + return rules + + if frappe.local.dev_server: + # dont cache in development + return _get() + + return frappe.cache().get_value('website_route_rules', _get) diff --git a/frappe/website/redirect.py b/frappe/website/redirect.py deleted file mode 100644 index e66c0a3b7b..0000000000 --- a/frappe/website/redirect.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import unicode_literals - -import re, frappe - -def resolve_redirect(path, query_string=None): - ''' - Resolve redirects from hooks - - Example: - - website_redirect = [ - # absolute location - {"source": "/from", "target": "https://mysite/from"}, - - # relative location - {"source": "/from", "target": "/main"}, - - # use regex - {"source": r"/from/(.*)", "target": r"/main/\1"} - # use r as a string prefix if you use regex groups or want to escape any string literal - ] - ''' - redirects = frappe.get_hooks('website_redirects') - redirects += frappe.db.get_all('Website Route Redirect', ['source', 'target']) - - if not redirects: return - - redirect_to = frappe.cache().hget('website_redirects', path) - - if redirect_to: - frappe.flags.redirect_location = redirect_to - raise frappe.Redirect - - for rule in redirects: - pattern = rule['source'].strip('/ ') + '$' - path_to_match = path - if rule.get('match_with_query_string'): - path_to_match = path + '?' + frappe.safe_decode(query_string) - - if re.match(pattern, path_to_match): - redirect_to = re.sub(pattern, rule['target'], path_to_match) - frappe.flags.redirect_location = redirect_to - frappe.cache().hset('website_redirects', path_to_match, redirect_to) - raise frappe.Redirect - diff --git a/frappe/website/render.py b/frappe/website/render.py index c14d8e0d48..d5587ad98b 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -6,13 +6,10 @@ import mimetypes import re from six import iteritems -from werkzeug.routing import Rule from werkzeug.wrappers import Response import frappe import frappe.sessions -from frappe.website.router import evaluate_dynamic_routes -from frappe.website.utils import get_home_page def build_response(path, data, http_status_code, headers=None): @@ -30,6 +27,26 @@ def build_response(path, data, http_status_code, headers=None): return response +def set_content_type(response, data, path): + if isinstance(data, dict): + response.mimetype = 'application/json' + response.charset = 'utf-8' + data = json.dumps(data) + return data + + response.mimetype = 'text/html' + response.charset = 'utf-8' + + # ignore paths ending with .com to avoid unnecessary download + # https://bugs.python.org/issue22347 + if "." in path and not path.endswith('.com'): + content_type, encoding = mimetypes.guess_type(path) + if content_type: + response.mimetype = content_type + if encoding: + response.charset = encoding + + return data def add_preload_headers(response): from bs4 import BeautifulSoup @@ -53,67 +70,6 @@ def add_preload_headers(response): import traceback traceback.print_exc() -def resolve_path(path): - if not path: - path = "index" - - if path.endswith('.html'): - path = path[:-5] - - if path == "index": - path = get_home_page() - - frappe.local.path = path - - if path != "index": - path = resolve_from_map(path) - - return path - -def resolve_from_map(path): - '''transform dynamic route to a static one from hooks and route defined in doctype''' - rules = [Rule(r["from_route"], endpoint=r["to_route"], defaults=r.get("defaults")) - for r in get_website_rules()] - - return evaluate_dynamic_routes(rules, path) or path - -def get_website_rules(): - '''Get website route rules from hooks and DocType route''' - def _get(): - rules = frappe.get_hooks("website_route_rules") - for d in frappe.get_all('DocType', 'name, route', dict(has_web_view=1)): - if d.route: - rules.append(dict(from_route = '/' + d.route.strip('/'), to_route=d.name)) - - return rules - - if frappe.local.dev_server: - # dont cache in development - return _get() - - return frappe.cache().get_value('website_route_rules', _get) - -def set_content_type(response, data, path): - if isinstance(data, dict): - response.mimetype = 'application/json' - response.charset = 'utf-8' - data = json.dumps(data) - return data - - response.mimetype = 'text/html' - response.charset = 'utf-8' - - # ignore paths ending with .com to avoid unnecessary download - # https://bugs.python.org/issue22347 - if "." in path and not path.endswith('.com'): - content_type, encoding = mimetypes.guess_type(path) - if content_type: - response.mimetype = content_type - if encoding: - response.charset = encoding - - return data - def clear_cache(path=None): # TODO: Remove this from frappe.website.utils import clear_cache diff --git a/frappe/website/router.py b/frappe/website/router.py index 2e27b4c690..9922647ebf 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -5,130 +5,9 @@ import io import os import re -from werkzeug.routing import Map, NotFound, Rule - import frappe -from frappe.model.document import get_controller -from frappe.website.utils import can_cache, extract_comment_tag, extract_title - - -def resolve_route(path): - """Returns the page route object based on searching in pages and generators. - The `www` folder is also a part of generator **Web Page**. - - The only exceptions are `/about` and `/contact` these will be searched in Web Pages - first before checking the standard pages.""" - - if path not in ("about", "contact"): - context = get_page_info_from_template(path) - if context: - return context - return get_page_context_from_doctype(path) - else: - context = get_page_context_from_doctype(path) - if context: - return context - return get_page_info_from_template(path) - -def get_page_context(path): - page_context = None - if can_cache(): - page_context_cache = frappe.cache().hget("page_context", path) or {} - page_context = page_context_cache.get(frappe.local.lang, None) - - if not page_context: - page_context = make_page_context(path) - if can_cache(page_context.no_cache): - page_context_cache[frappe.local.lang] = page_context - frappe.cache().hset("page_context", path, page_context_cache) - - return page_context - -def make_page_context(path): - context = resolve_route(path) - if not context: - raise frappe.PageDoesNotExistError - - context.doctype = context.ref_doctype - - if context.page_title: - context.title = context.page_title - - context.pathname = frappe.local.path - - return context - -def get_page_info_from_template(path): - '''Return page_info from path''' - for app in frappe.get_installed_apps(frappe_last=True): - app_path = frappe.get_app_path(app) - - folders = get_start_folders() - - for start in folders: - search_path = os.path.join(app_path, start, path) - options = (search_path, search_path + '.html', search_path + '.md', - search_path + '/index.html', search_path + '/index.md') - for o in options: - option = frappe.as_unicode(o) - if os.path.exists(option) and not os.path.isdir(option): - return get_page_info(option, app, start, app_path=app_path) - - return None - -def get_page_context_from_doctype(path): - page_info = get_page_info_from_doctypes(path) - if not page_info: - page_info = get_page_info_from_web_page_with_dynamic_routes(path) - - if page_info: - return frappe.get_doc(page_info.get("doctype"), - page_info.get("name")).get_page_info() - -def get_all_page_context_from_doctypes(): - ''' - Get all doctype generated routes (for sitemap.xml) - ''' - routes = frappe.cache().get_value("website_generator_routes") - if not routes: - routes = get_page_info_from_doctypes() - frappe.cache().set_value("website_generator_routes", routes) - - return routes - -def get_page_info_from_doctypes(path=None): - ''' - Find a document with matching `route` from all doctypes with `has_web_view`=1 - ''' - routes = {} - for doctype in get_doctypes_with_web_view(): - filters = {} - controller = get_controller(doctype) - meta = frappe.get_meta(doctype) - - condition_field = (meta.is_published_field or - # custom doctypes dont have controllers and no website attribute - (controller.website.condition_field if not meta.custom else None)) - - if condition_field: - filters[condition_field] = 1 - - if path: - filters['route'] = path - - try: - for r in frappe.get_all(doctype, fields = ['name', 'route', 'modified'], - filters = filters, limit = 1): - - routes[r.route] = {"doctype": doctype, "name": r.name, "modified": r.modified} - - # just want one path, return it! - if path: - return routes[r.route] - except Exception as e: - if not frappe.db.is_missing_column(e): raise e - - return routes +from frappe.website.utils import extract_comment_tag, extract_title +from werkzeug.routing import Map, Rule, NotFound def get_page_info_from_web_page_with_dynamic_routes(path): ''' diff --git a/frappe/website/utils.py b/frappe/website/utils.py index 9e98b9da9c..bfe7d5867d 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -136,14 +136,8 @@ def get_home_page_via_hooks(): return home_page -def is_signup_enabled(): - if getattr(frappe.local, "is_signup_enabled", None) is None: - frappe.local.is_signup_enabled = True - if frappe.utils.cint(frappe.db.get_value("Website Settings", - "Website Settings", "disable_signup")): - frappe.local.is_signup_enabled = False - - return frappe.local.is_signup_enabled +def is_signup_disabled(): + return frappe.db.get_single_value('Website Settings', 'disable_signup', True) def cleanup_page_name(title): """make page name from title""" @@ -353,7 +347,7 @@ def get_sidebar_items(parent_sidebar, basepath): sidebar_items = [] hooks = frappe.get_hooks('look_for_sidebar_json') - look_for_sidebar_json = hooks[0] if hooks else 0 + look_for_sidebar_json = hooks[0] if hooks else frappe.flags.look_for_sidebar if basepath and look_for_sidebar_json: sidebar_items = get_sidebar_items_from_sidebar_file(basepath, look_for_sidebar_json) diff --git a/frappe/www/list.py b/frappe/www/list.py index fc4dc602c3..28bf73e575 100644 --- a/frappe/www/list.py +++ b/frappe/www/list.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, json from frappe.utils import cint, quoted -from frappe.website.render import resolve_path +from frappe.website.path_resolver import resolve_path from frappe.model.document import get_controller, Document from frappe import _ From 948d7d2b564f4b2d198d38e23a774ccca7604ba1 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 20 May 2021 13:20:05 +0530 Subject: [PATCH 095/495] test: Add sidebar test --- frappe/website/doctype/web_page/test_web_page.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frappe/website/doctype/web_page/test_web_page.py b/frappe/website/doctype/web_page/test_web_page.py index 108b36caaa..ada42f7a75 100644 --- a/frappe/website/doctype/web_page/test_web_page.py +++ b/frappe/website/doctype/web_page/test_web_page.py @@ -82,6 +82,15 @@ class TestWebPage(unittest.TestCase): # assert template block rendered self.assertTrue('

Test content

' in frappe.as_unicode(content)) + def test_json_sidebar_data(self): + frappe.flags.look_for_sidebar = False + content = get_response_content('/_test/_test_folder/_test_page') + self.assertTrue('Test Sidebar' not in frappe.as_unicode(content)) + frappe.flags.look_for_sidebar = True + content = get_response_content('/_test/_test_folder/_test_page') + self.assertTrue('Test Sidebar' in frappe.as_unicode(content)) + frappe.flags.look_for_sidebar = False + def test_home_page(self): content = get_response_content(path='/') print(content) From 5bd80106813398f4ad4ca7d0315519d3fdbdb941 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 20 May 2021 13:24:44 +0530 Subject: [PATCH 096/495] style: Translate string --- frappe/website/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/website/utils.py b/frappe/website/utils.py index bfe7d5867d..814bd0101e 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -11,6 +11,7 @@ from past.builtins import cmp from six import iteritems import frappe +from frappe import _ from frappe.model.document import Document from frappe.utils import md_to_html @@ -154,7 +155,7 @@ def cleanup_page_name(title): def get_shade(color, percent=None): - frappe.msgprint('get_shade method has been deprecated!') + frappe.msgprint(_('get_shade method has been deprecated.')) return color def abs_url(path): From 0a89c9e32e625e53cbe5ea151aff42031fb833a5 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 20 May 2021 13:25:04 +0530 Subject: [PATCH 097/495] fix: Add canonical link --- frappe/website/page_controllers/base_template_page.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/website/page_controllers/base_template_page.py b/frappe/website/page_controllers/base_template_page.py index c40afb7b1c..af7fc43293 100644 --- a/frappe/website/page_controllers/base_template_page.py +++ b/frappe/website/page_controllers/base_template_page.py @@ -32,6 +32,7 @@ class BaseTemplatePage(WebPage): # to be able to inspect the context dict # Use the macro "inspect" from macros.html self.context._context_dict = self.context + self.context.canonical = frappe.utils.get_url(frappe.utils.escape_html(self.path)) # context sends us a new template path if self.context.template: From d7696f5f95de82fac1a429e71d3c705d37725b32 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 20 May 2021 13:44:35 +0530 Subject: [PATCH 098/495] test: Add test files --- frappe/website/doctype/web_page/test_web_page.py | 7 ------- frappe/www/_test/_sidebar.json | 6 ++++++ frappe/www/_test/_test_folder/__init__.py | 0 frappe/www/_test/_test_folder/_test_page.html | 1 + frappe/www/_test/_test_folder/_test_toc.md | 9 +++++++++ frappe/www/_test/_test_folder/new.csv/index.html | 12 ++++++++++++ 6 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 frappe/www/_test/_sidebar.json create mode 100644 frappe/www/_test/_test_folder/__init__.py create mode 100644 frappe/www/_test/_test_folder/_test_toc.md create mode 100644 frappe/www/_test/_test_folder/new.csv/index.html diff --git a/frappe/website/doctype/web_page/test_web_page.py b/frappe/website/doctype/web_page/test_web_page.py index ada42f7a75..c5d250a4a0 100644 --- a/frappe/website/doctype/web_page/test_web_page.py +++ b/frappe/website/doctype/web_page/test_web_page.py @@ -93,16 +93,10 @@ class TestWebPage(unittest.TestCase): def test_home_page(self): content = get_response_content(path='/') - print(content) content = get_response_content(path='/index') - print(content) def test_table_of_content(self): page = get_response(path='/_test/_test_folder/_test_toc') - print(page) - - def test_sidebar_data(self): - pass def test_meta_tags(self): pass @@ -118,5 +112,4 @@ class TestWebPage(unittest.TestCase): # breadcrumb # validate -# cache no-cache # page context caching diff --git a/frappe/www/_test/_sidebar.json b/frappe/www/_test/_sidebar.json new file mode 100644 index 0000000000..a2567e94da --- /dev/null +++ b/frappe/www/_test/_sidebar.json @@ -0,0 +1,6 @@ +[ + { + "route": "/_test/_test_folder", + "title": "Test Sidebar" + } +] diff --git a/frappe/www/_test/_test_folder/__init__.py b/frappe/www/_test/_test_folder/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/www/_test/_test_folder/_test_page.html b/frappe/www/_test/_test_folder/_test_page.html index 7364235e10..900f07459d 100644 --- a/frappe/www/_test/_test_folder/_test_page.html +++ b/frappe/www/_test/_test_folder/_test_page.html @@ -1,3 +1,4 @@ {% block content %} +{% include "templates/includes/web_sidebar.html" %}

Test content

{% endblock %} diff --git a/frappe/www/_test/_test_folder/_test_toc.md b/frappe/www/_test/_test_folder/_test_toc.md new file mode 100644 index 0000000000..8d05b120a5 --- /dev/null +++ b/frappe/www/_test/_test_folder/_test_toc.md @@ -0,0 +1,9 @@ +# Level 1 + +## Level 1.1 + +## Level 1.2 + +## Level 1.3 + +### Level 1.3.1 diff --git a/frappe/www/_test/_test_folder/new.csv/index.html b/frappe/www/_test/_test_folder/new.csv/index.html new file mode 100644 index 0000000000..7a1bb69558 --- /dev/null +++ b/frappe/www/_test/_test_folder/new.csv/index.html @@ -0,0 +1,12 @@ + + + + + + + Document + + + Test Page + + From 1a0a095b34f33ddaba7615b9bddb1d4d8a1df30a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 24 May 2021 14:55:42 +0530 Subject: [PATCH 099/495] fix: Template path for document_page --- frappe/website/page_controllers/document_page.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/website/page_controllers/document_page.py b/frappe/website/page_controllers/document_page.py index 537b8ac658..c79e385a68 100644 --- a/frappe/website/page_controllers/document_page.py +++ b/frappe/website/page_controllers/document_page.py @@ -53,8 +53,8 @@ class DocumentPage(BaseTemplatePage): self.init_context() self.update_context() self.post_process_context() - - html = frappe.get_template(self.context.template_path).render(self.context) + template_path = self.context.template_path or self.context.template or '' + html = frappe.get_template(template_path).render(self.context) html = self.add_csrf_token(html) return build_response(self.path, html, self.http_status_code or 200, self.headers) From 15f5b697d312c4ee6174fd4e40828c41d3eb581c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 24 May 2021 19:52:31 +0530 Subject: [PATCH 100/495] fix: Website template path --- frappe/website/page_controllers/base_template_page.py | 7 +++++-- frappe/website/page_controllers/document_page.py | 11 ++++++----- frappe/website/website_generator.py | 2 ++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/frappe/website/page_controllers/base_template_page.py b/frappe/website/page_controllers/base_template_page.py index af7fc43293..4d282dab9d 100644 --- a/frappe/website/page_controllers/base_template_page.py +++ b/frappe/website/page_controllers/base_template_page.py @@ -5,6 +5,10 @@ from frappe.website.website_components.metatags import MetaTags class BaseTemplatePage(WebPage): + def __init__(self, path, http_status_code): + super().__init__(path=path, http_status_code=http_status_code) + self.template_path = '' + def init_context(self): self.context = frappe._dict() self.context.update(get_website_settings()) @@ -35,8 +39,7 @@ class BaseTemplatePage(WebPage): self.context.canonical = frappe.utils.get_url(frappe.utils.escape_html(self.path)) # context sends us a new template path - if self.context.template: - self.template_path = self.context.template + self.template_path = self.context.template or '' def set_base_template_if_missing(self): if not self.context.base_template_path: diff --git a/frappe/website/page_controllers/document_page.py b/frappe/website/page_controllers/document_page.py index c79e385a68..6ab384e77d 100644 --- a/frappe/website/page_controllers/document_page.py +++ b/frappe/website/page_controllers/document_page.py @@ -53,8 +53,7 @@ class DocumentPage(BaseTemplatePage): self.init_context() self.update_context() self.post_process_context() - template_path = self.context.template_path or self.context.template or '' - html = frappe.get_template(template_path).render(self.context) + html = frappe.get_template(self.template_path).render(self.context) html = self.add_csrf_token(html) return build_response(self.path, html, self.http_status_code or 200, self.headers) @@ -62,10 +61,12 @@ class DocumentPage(BaseTemplatePage): def update_context(self): self.context.doc = self.doc self.context.update(self.context.doc.as_dict()) - self.context.update(self.context.doc.get_website_properties()) + self.context.update(self.context.doc.get_page_info()) - if not self.context.template_path: - self.context.template_path = self.context.doc.meta.get_web_template() + self.template_path = self.context.template or self.template_path + + if not self.template_path: + self.template_path = self.context.doc.meta.get_web_template() if hasattr(self.doc, "get_context"): ret = self.doc.get_context(self.context) diff --git a/frappe/website/website_generator.py b/frappe/website/website_generator.py index fc08abeed9..f2638fd86d 100644 --- a/frappe/website/website_generator.py +++ b/frappe/website/website_generator.py @@ -129,6 +129,8 @@ class WebsiteGenerator(Document): if not route.page_title: route.page_title = self.get(self.get_title_field()) + route.title = route.page_title + return route def send_indexing_request(self, operation_type='URL_UPDATED'): From 63ce0c51361422c3386f6a62a3e3334560f5a71d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 24 May 2021 21:53:09 +0530 Subject: [PATCH 101/495] fix: Fallback to template_path if template is not set --- frappe/website/page_controllers/base_template_page.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/page_controllers/base_template_page.py b/frappe/website/page_controllers/base_template_page.py index 4d282dab9d..012f9fccc9 100644 --- a/frappe/website/page_controllers/base_template_page.py +++ b/frappe/website/page_controllers/base_template_page.py @@ -39,7 +39,7 @@ class BaseTemplatePage(WebPage): self.context.canonical = frappe.utils.get_url(frappe.utils.escape_html(self.path)) # context sends us a new template path - self.template_path = self.context.template or '' + self.template_path = self.context.template or self.template_path def set_base_template_if_missing(self): if not self.context.base_template_path: From cbd00179cb16e0f08b8283d6f1d0f0e815cefef1 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 24 May 2021 22:31:23 +0530 Subject: [PATCH 102/495] fix: Move set_missing_values to base_template_page - Since it is common for all inherited pages from base_template_page --- .../page_controllers/base_template_page.py | 30 ++++++++++++------- .../website/page_controllers/template_page.py | 11 +------ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/frappe/website/page_controllers/base_template_page.py b/frappe/website/page_controllers/base_template_page.py index 012f9fccc9..f06869fd5f 100644 --- a/frappe/website/page_controllers/base_template_page.py +++ b/frappe/website/page_controllers/base_template_page.py @@ -28,18 +28,10 @@ class BaseTemplatePage(WebPage): self.set_base_template_if_missing() self.set_title_with_prefix() self.update_website_context() - - # set using frappe.respond_as_web_page - if hasattr(frappe.local, 'response') and frappe.local.response.get('context'): - self.context.update(frappe.local.response.context) - - # to be able to inspect the context dict - # Use the macro "inspect" from macros.html - self.context._context_dict = self.context - self.context.canonical = frappe.utils.get_url(frappe.utils.escape_html(self.path)) - # context sends us a new template path self.template_path = self.context.template or self.template_path + self.context._context_dict = self.context + self.set_missing_values() def set_base_template_if_missing(self): if not self.context.base_template_path: @@ -51,6 +43,24 @@ class BaseTemplatePage(WebPage): and not self.context.title.startswith(self.context.title_prefix)): self.context.title = '{0} - {1}'.format(self.context.title_prefix, self.context.title) + def set_missing_values(self): + # set using frappe.respond_as_web_page + if hasattr(frappe.local, 'response') and frappe.local.response.get('context'): + self.context.update(frappe.local.response.context) + + # to be able to inspect the context dict + # Use the macro "inspect" from macros.html + self.context.canonical = frappe.utils.get_url(frappe.utils.escape_html(self.path)) + + if "url_prefix" not in self.context: + self.context.url_prefix = "" + + if self.context.url_prefix and self.context.url_prefix[-1]!='/': + self.context.url_prefix += '/' + + self.context.path = self.path + self.context.pathname = frappe.local.path if hasattr(frappe, 'local') else self.path + def update_website_context(self): # apply context from hooks update_website_context = frappe.get_hooks('update_website_context') diff --git a/frappe/website/page_controllers/template_page.py b/frappe/website/page_controllers/template_page.py index 635b8a56fe..cd30fa02c8 100644 --- a/frappe/website/page_controllers/template_page.py +++ b/frappe/website/page_controllers/template_page.py @@ -65,7 +65,6 @@ class TemplatePage(BaseTemplatePage): def post_process_context(self): self.set_user_info() self.add_sidebar_and_breadcrumbs() - self.set_missing_values() super(TemplatePage, self).post_process_context() def add_sidebar_and_breadcrumbs(self): @@ -228,15 +227,7 @@ class TemplatePage(BaseTemplatePage): self.template_path = 'www/{path}.html'.format(path=path) def set_missing_values(self): - if "url_prefix" not in self.context: - self.context.url_prefix = "" - - if self.context.url_prefix and self.context.url_prefix[-1]!='/': - self.context.url_prefix += '/' - - self.context.path = self.path - self.context.pathname = frappe.local.path if hasattr(frappe, 'local') else self.path - + super().set_missing_values() # for backward compatibility self.context.docs_base_url = '/docs' From 70a8bd5945da8db42838cc61efe8312237d9e91d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 25 May 2021 08:21:06 +0530 Subject: [PATCH 103/495] refactor: Rename validate function to can_render --- frappe/website/page_controllers/document_page.py | 2 +- frappe/website/page_controllers/error_page.py | 2 +- frappe/website/page_controllers/list_page.py | 2 +- frappe/website/page_controllers/not_found_page.py | 2 +- frappe/website/page_controllers/not_permitted_page.py | 2 +- frappe/website/page_controllers/print_page.py | 2 +- frappe/website/page_controllers/redirect_page.py | 2 +- frappe/website/page_controllers/static_page.py | 2 +- frappe/website/page_controllers/template_page.py | 2 +- frappe/website/page_controllers/web_form.py | 2 +- frappe/website/page_controllers/web_page.py | 2 +- frappe/website/path_resolver.py | 5 ++--- 12 files changed, 13 insertions(+), 14 deletions(-) diff --git a/frappe/website/page_controllers/document_page.py b/frappe/website/page_controllers/document_page.py index 6ab384e77d..1a42b5debb 100644 --- a/frappe/website/page_controllers/document_page.py +++ b/frappe/website/page_controllers/document_page.py @@ -7,7 +7,7 @@ from frappe.website.router import (get_doctypes_with_web_view, class DocumentPage(BaseTemplatePage): - def validate(self): + def can_render(self): ''' Find a document with matching `route` from all doctypes with `has_web_view`=1 ''' diff --git a/frappe/website/page_controllers/error_page.py b/frappe/website/page_controllers/error_page.py index 554949684f..52903d076b 100644 --- a/frappe/website/page_controllers/error_page.py +++ b/frappe/website/page_controllers/error_page.py @@ -6,5 +6,5 @@ class ErrorPage(TemplatePage): super().__init__(path=path, http_status_code=http_status_code) self.http_status_code = getattr(exception, 'http_status_code', None) or http_status_code or 500 - def validate(self): + def can_render(self): return True diff --git a/frappe/website/page_controllers/list_page.py b/frappe/website/page_controllers/list_page.py index cc6f150dd2..e3b92a2771 100644 --- a/frappe/website/page_controllers/list_page.py +++ b/frappe/website/page_controllers/list_page.py @@ -2,7 +2,7 @@ import frappe from frappe.website.page_controllers.template_page import TemplatePage class ListPage(TemplatePage): - def validate(self): + def can_render(self): return frappe.db.exists('DocType', self.path, True) def render(self): diff --git a/frappe/website/page_controllers/not_found_page.py b/frappe/website/page_controllers/not_found_page.py index 13c6358512..1f687ddb98 100644 --- a/frappe/website/page_controllers/not_found_page.py +++ b/frappe/website/page_controllers/not_found_page.py @@ -15,7 +15,7 @@ class NotFoundPage(TemplatePage): http_status_code = 404 super().__init__(path=path, http_status_code=http_status_code) - def validate(self): + def can_render(self): return True def render(self): diff --git a/frappe/website/page_controllers/not_permitted_page.py b/frappe/website/page_controllers/not_permitted_page.py index 3ed732d80b..75a8b756d8 100644 --- a/frappe/website/page_controllers/not_permitted_page.py +++ b/frappe/website/page_controllers/not_permitted_page.py @@ -9,7 +9,7 @@ class NotPermittedPage(TemplatePage): super().__init__(path=path, http_status_code=http_status_code) self.http_status_code = 403 - def validate(self): + def can_render(self): return True def render(self): diff --git a/frappe/website/page_controllers/print_page.py b/frappe/website/page_controllers/print_page.py index 574f335f5d..790ee75585 100644 --- a/frappe/website/page_controllers/print_page.py +++ b/frappe/website/page_controllers/print_page.py @@ -6,7 +6,7 @@ class PrintPage(TemplatePage): default path returns a printable object (based on permission) /Quotation/Q-0001 ''' - def validate(self): + def can_render(self): parts = self.path.split('/', 1) if len(parts)==2: if (frappe.db.exists('DocType', parts[0], True) diff --git a/frappe/website/page_controllers/redirect_page.py b/frappe/website/page_controllers/redirect_page.py index f3a0ba6e7f..0bfa8897a3 100644 --- a/frappe/website/page_controllers/redirect_page.py +++ b/frappe/website/page_controllers/redirect_page.py @@ -6,7 +6,7 @@ class RedirectPage(object): self.path = path self.http_status_code = http_status_code - def validate(self): + def can_render(self): return True def render(self): diff --git a/frappe/website/page_controllers/static_page.py b/frappe/website/page_controllers/static_page.py index b292a62922..0c75d6feb3 100644 --- a/frappe/website/page_controllers/static_page.py +++ b/frappe/website/page_controllers/static_page.py @@ -23,7 +23,7 @@ class StaticPage(WebPage): if os.path.isfile(file_path): self.file_path = file_path - def validate(self): + def can_render(self): return self.is_valid_file_path() and self.file_path def is_valid_file_path(self): diff --git a/frappe/website/page_controllers/template_page.py b/frappe/website/page_controllers/template_page.py index cd30fa02c8..c9797b88c9 100644 --- a/frappe/website/page_controllers/template_page.py +++ b/frappe/website/page_controllers/template_page.py @@ -36,7 +36,7 @@ class TemplatePage(BaseTemplatePage): self.basepath = os.path.dirname(file_path) return - def validate(self): + def can_render(self): return hasattr(self, 'template_path') and bool(self.template_path) def get_index_path_options(self, search_path): diff --git a/frappe/website/page_controllers/web_form.py b/frappe/website/page_controllers/web_form.py index d7ab02d880..c1afaafbe5 100644 --- a/frappe/website/page_controllers/web_form.py +++ b/frappe/website/page_controllers/web_form.py @@ -2,5 +2,5 @@ from frappe.website.page_controllers.web_page import WebPage import frappe class WebFormPage(WebPage): - def validate(self): + def can_render(self): return bool(frappe.get_all("Web Form", filters={'route': self.path})) diff --git a/frappe/website/page_controllers/web_page.py b/frappe/website/page_controllers/web_page.py index 4d9e642b4b..0407afe532 100644 --- a/frappe/website/page_controllers/web_page.py +++ b/frappe/website/page_controllers/web_page.py @@ -13,7 +13,7 @@ class WebPage(object): if self.validate(): return self.render() - def validate(self): + def can_render(self): pass def render(self): diff --git a/frappe/website/path_resolver.py b/frappe/website/path_resolver.py index 39edf0d9a8..854948740c 100644 --- a/frappe/website/path_resolver.py +++ b/frappe/website/path_resolver.py @@ -32,15 +32,14 @@ class PathResolver(): try: resolve_redirect(self.path, request.query_string) except frappe.Redirect: - return self.path, RedirectPage(self.path) + return frappe.flags.redirect_location, RedirectPage(self.path) endpoint = resolve_path(self.path) renderers = (StaticPage, WebFormPage, TemplatePage, ListPage, DocumentPage, PrintPage, NotFoundPage) for renderer in renderers: renderer_instance = renderer(endpoint, 200) - can_render = renderer_instance.validate() - if can_render: + if renderer_instance.can_render(): return endpoint, renderer_instance return endpoint, NotFoundPage(endpoint) From d0915a709bc9984bf205be66193bfd767c598944 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 25 May 2021 08:22:45 +0530 Subject: [PATCH 104/495] fix: Make http_status_code non mandatory --- frappe/website/page_controllers/base_template_page.py | 2 +- frappe/website/serve.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/frappe/website/page_controllers/base_template_page.py b/frappe/website/page_controllers/base_template_page.py index f06869fd5f..d7770ccd93 100644 --- a/frappe/website/page_controllers/base_template_page.py +++ b/frappe/website/page_controllers/base_template_page.py @@ -5,7 +5,7 @@ from frappe.website.website_components.metatags import MetaTags class BaseTemplatePage(WebPage): - def __init__(self, path, http_status_code): + def __init__(self, path, http_status_code=None): super().__init__(path=path, http_status_code=http_status_code) self.template_path = '' diff --git a/frappe/website/serve.py b/frappe/website/serve.py index 95a49cef1a..a761f479f3 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -14,8 +14,7 @@ def get_response(path=None, http_status_code=200): try: path_resolver = PathResolver(path) endpoint, renderer_instance = path_resolver.resolve() - if renderer_instance: - response = renderer_instance.render() + response = renderer_instance.render() except frappe.Redirect: return RedirectPage(endpoint or path, http_status_code).render() except frappe.PermissionError as e: From bb68dd7323974771fec691c212a954e4f2683999 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 25 May 2021 09:24:32 +0530 Subject: [PATCH 105/495] fix: Add comment properties back with deprecation warning --- .../website/page_controllers/template_page.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/frappe/website/page_controllers/template_page.py b/frappe/website/page_controllers/template_page.py index c9797b88c9..69e9cceb9f 100644 --- a/frappe/website/page_controllers/template_page.py +++ b/frappe/website/page_controllers/template_page.py @@ -1,5 +1,6 @@ import io import os +import click import frappe from frappe.website.page_controllers.base_template_page import BaseTemplatePage @@ -11,6 +12,17 @@ from frappe.website.utils import (extract_comment_tag, extract_title, WEBPAGE_PY_MODULE_PROPERTIES = ("base_template_path", "template", "no_cache", "sitemap", "condition_field") +COMMENT_PROPERTY_KEY_VALUE_MAP = { + "no-breadcrumbs": ("no_breadcrumbs", 1), + "show-sidebar": ("show_sidebar", 1), + "add-breadcrumbs": ("add_breadcrumbs", 1), + "no-header": ("no_header", 1), + "add-next-prev-links": ("add_next_prev_links", 1), + "no-cache": ("no_cache", 1), + "no-sitemap": ("sitemap", 0), + "sitemap": ("sitemap", 1) +} + class TemplatePage(BaseTemplatePage): def __init__(self, path, http_status_code=None): super().__init__(path=path, http_status_code=http_status_code) @@ -147,6 +159,15 @@ class TemplatePage(BaseTemplatePage): self.source = '''{{% extends "{0}" %}} {{% block page_content %}}{1}{{% endblock %}}'''.format(context.base_template, self.source) + self.set_properties_via_comments() + + def set_properties_via_comments(self): + for comment, (context_key, value) in COMMENT_PROPERTY_KEY_VALUE_MAP.items(): + comment_tag = f"" + if comment_tag in self.source: + self.context[context_key] = value + click.echo(f'⚠️ DEPRECATION WARNING: {comment_tag} will be deprecated on 2021-12-31.') + def run_pymodule_method(self, method): if hasattr(self.pymodule, method): try: From 8317e00712210a802e363c96d3f446f084ddb061 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 25 May 2021 09:27:26 +0530 Subject: [PATCH 106/495] refactor: Rename page_controllers folder to page_renderers --- .../base_template_page.py | 2 +- .../document_page.py | 2 +- .../error_page.py | 2 +- .../list_page.py | 2 +- .../not_found_page.py | 2 +- .../not_permitted_page.py | 2 +- .../print_page.py | 2 +- .../redirect_page.py | 0 .../static_page.py | 2 +- .../template_page.py | 2 +- .../web_form.py | 2 +- .../web_page.py | 0 frappe/website/path_resolver.py | 16 ++++++++-------- frappe/website/serve.py | 6 +++--- 14 files changed, 21 insertions(+), 21 deletions(-) rename frappe/website/{page_controllers => page_renderers}/base_template_page.py (97%) rename frappe/website/{page_controllers => page_renderers}/document_page.py (96%) rename frappe/website/{page_controllers => page_renderers}/error_page.py (81%) rename frappe/website/{page_controllers => page_renderers}/list_page.py (77%) rename frappe/website/{page_controllers => page_renderers}/not_found_page.py (93%) rename frappe/website/{page_controllers => page_renderers}/not_permitted_page.py (89%) rename frappe/website/{page_controllers => page_renderers}/print_page.py (88%) rename frappe/website/{page_controllers => page_renderers}/redirect_page.py (100%) rename frappe/website/{page_controllers => page_renderers}/static_page.py (95%) rename frappe/website/{page_controllers => page_renderers}/template_page.py (99%) rename frappe/website/{page_controllers => page_renderers}/web_form.py (69%) rename frappe/website/{page_controllers => page_renderers}/web_page.py (100%) diff --git a/frappe/website/page_controllers/base_template_page.py b/frappe/website/page_renderers/base_template_page.py similarity index 97% rename from frappe/website/page_controllers/base_template_page.py rename to frappe/website/page_renderers/base_template_page.py index d7770ccd93..8405270a57 100644 --- a/frappe/website/page_controllers/base_template_page.py +++ b/frappe/website/page_renderers/base_template_page.py @@ -1,6 +1,6 @@ import frappe from frappe.website.doctype.website_settings.website_settings import get_website_settings -from frappe.website.page_controllers.web_page import WebPage +from frappe.website.page_renderers.web_page import WebPage from frappe.website.website_components.metatags import MetaTags diff --git a/frappe/website/page_controllers/document_page.py b/frappe/website/page_renderers/document_page.py similarity index 96% rename from frappe/website/page_controllers/document_page.py rename to frappe/website/page_renderers/document_page.py index 1a42b5debb..34dc42e203 100644 --- a/frappe/website/page_controllers/document_page.py +++ b/frappe/website/page_renderers/document_page.py @@ -1,6 +1,6 @@ import frappe from frappe.model.document import get_controller -from frappe.website.page_controllers.base_template_page import BaseTemplatePage +from frappe.website.page_renderers.base_template_page import BaseTemplatePage from frappe.website.render import build_response from frappe.website.router import (get_doctypes_with_web_view, get_page_info_from_web_page_with_dynamic_routes) diff --git a/frappe/website/page_controllers/error_page.py b/frappe/website/page_renderers/error_page.py similarity index 81% rename from frappe/website/page_controllers/error_page.py rename to frappe/website/page_renderers/error_page.py index 52903d076b..3501c77765 100644 --- a/frappe/website/page_controllers/error_page.py +++ b/frappe/website/page_renderers/error_page.py @@ -1,4 +1,4 @@ -from frappe.website.page_controllers.template_page import TemplatePage +from frappe.website.page_renderers.template_page import TemplatePage class ErrorPage(TemplatePage): def __init__(self, path=None, http_status_code=None, exception=None): diff --git a/frappe/website/page_controllers/list_page.py b/frappe/website/page_renderers/list_page.py similarity index 77% rename from frappe/website/page_controllers/list_page.py rename to frappe/website/page_renderers/list_page.py index e3b92a2771..61c781ea14 100644 --- a/frappe/website/page_controllers/list_page.py +++ b/frappe/website/page_renderers/list_page.py @@ -1,5 +1,5 @@ import frappe -from frappe.website.page_controllers.template_page import TemplatePage +from frappe.website.page_renderers.template_page import TemplatePage class ListPage(TemplatePage): def can_render(self): diff --git a/frappe/website/page_controllers/not_found_page.py b/frappe/website/page_renderers/not_found_page.py similarity index 93% rename from frappe/website/page_controllers/not_found_page.py rename to frappe/website/page_renderers/not_found_page.py index 1f687ddb98..af510fecfc 100644 --- a/frappe/website/page_controllers/not_found_page.py +++ b/frappe/website/page_renderers/not_found_page.py @@ -2,7 +2,7 @@ import os from urllib.parse import urlparse import frappe -from frappe.website.page_controllers.template_page import TemplatePage +from frappe.website.page_renderers.template_page import TemplatePage from frappe.website.utils import can_cache HOMEPAGE_PATHS = ('/', '/index', 'index') diff --git a/frappe/website/page_controllers/not_permitted_page.py b/frappe/website/page_renderers/not_permitted_page.py similarity index 89% rename from frappe/website/page_controllers/not_permitted_page.py rename to frappe/website/page_renderers/not_permitted_page.py index 75a8b756d8..e69299f5c5 100644 --- a/frappe/website/page_controllers/not_permitted_page.py +++ b/frappe/website/page_renderers/not_permitted_page.py @@ -1,6 +1,6 @@ import frappe from frappe import _ -from frappe.website.page_controllers.template_page import TemplatePage +from frappe.website.page_renderers.template_page import TemplatePage from frappe.utils import cstr class NotPermittedPage(TemplatePage): diff --git a/frappe/website/page_controllers/print_page.py b/frappe/website/page_renderers/print_page.py similarity index 88% rename from frappe/website/page_controllers/print_page.py rename to frappe/website/page_renderers/print_page.py index 790ee75585..05d4026e2b 100644 --- a/frappe/website/page_controllers/print_page.py +++ b/frappe/website/page_renderers/print_page.py @@ -1,5 +1,5 @@ import frappe -from frappe.website.page_controllers.template_page import TemplatePage +from frappe.website.page_renderers.template_page import TemplatePage class PrintPage(TemplatePage): ''' diff --git a/frappe/website/page_controllers/redirect_page.py b/frappe/website/page_renderers/redirect_page.py similarity index 100% rename from frappe/website/page_controllers/redirect_page.py rename to frappe/website/page_renderers/redirect_page.py diff --git a/frappe/website/page_controllers/static_page.py b/frappe/website/page_renderers/static_page.py similarity index 95% rename from frappe/website/page_controllers/static_page.py rename to frappe/website/page_renderers/static_page.py index 0c75d6feb3..fc3edbe216 100644 --- a/frappe/website/page_controllers/static_page.py +++ b/frappe/website/page_renderers/static_page.py @@ -5,7 +5,7 @@ from werkzeug.wrappers import Response from werkzeug.wsgi import wrap_file import frappe -from frappe.website.page_controllers.web_page import WebPage +from frappe.website.page_renderers.web_page import WebPage UNSUPPORTED_STATIC_PAGE_TYPES = ('html', 'md', 'js', 'xml', 'css', 'txt', 'py', 'json') diff --git a/frappe/website/page_controllers/template_page.py b/frappe/website/page_renderers/template_page.py similarity index 99% rename from frappe/website/page_controllers/template_page.py rename to frappe/website/page_renderers/template_page.py index 69e9cceb9f..467d74b283 100644 --- a/frappe/website/page_controllers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -3,7 +3,7 @@ import os import click import frappe -from frappe.website.page_controllers.base_template_page import BaseTemplatePage +from frappe.website.page_renderers.base_template_page import BaseTemplatePage from frappe.website.utils import get_sidebar_items from frappe.website.render import build_response from frappe.website.router import get_base_template diff --git a/frappe/website/page_controllers/web_form.py b/frappe/website/page_renderers/web_form.py similarity index 69% rename from frappe/website/page_controllers/web_form.py rename to frappe/website/page_renderers/web_form.py index c1afaafbe5..094ae78d71 100644 --- a/frappe/website/page_controllers/web_form.py +++ b/frappe/website/page_renderers/web_form.py @@ -1,4 +1,4 @@ -from frappe.website.page_controllers.web_page import WebPage +from frappe.website.page_renderers.web_page import WebPage import frappe class WebFormPage(WebPage): diff --git a/frappe/website/page_controllers/web_page.py b/frappe/website/page_renderers/web_page.py similarity index 100% rename from frappe/website/page_controllers/web_page.py rename to frappe/website/page_renderers/web_page.py diff --git a/frappe/website/path_resolver.py b/frappe/website/path_resolver.py index 854948740c..9816900677 100644 --- a/frappe/website/path_resolver.py +++ b/frappe/website/path_resolver.py @@ -3,14 +3,14 @@ import re from werkzeug.routing import Rule import frappe -from frappe.website.page_controllers.document_page import DocumentPage -from frappe.website.page_controllers.list_page import ListPage -from frappe.website.page_controllers.not_found_page import NotFoundPage -from frappe.website.page_controllers.print_page import PrintPage -from frappe.website.page_controllers.redirect_page import RedirectPage -from frappe.website.page_controllers.static_page import StaticPage -from frappe.website.page_controllers.template_page import TemplatePage -from frappe.website.page_controllers.web_form import WebFormPage +from frappe.website.page_renderers.document_page import DocumentPage +from frappe.website.page_renderers.list_page import ListPage +from frappe.website.page_renderers.not_found_page import NotFoundPage +from frappe.website.page_renderers.print_page import PrintPage +from frappe.website.page_renderers.redirect_page import RedirectPage +from frappe.website.page_renderers.static_page import StaticPage +from frappe.website.page_renderers.template_page import TemplatePage +from frappe.website.page_renderers.web_form import WebFormPage from frappe.website.router import evaluate_dynamic_routes from frappe.website.utils import can_cache, get_home_page diff --git a/frappe/website/serve.py b/frappe/website/serve.py index a761f479f3..fe7fc77064 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -1,7 +1,7 @@ import frappe -from frappe.website.page_controllers.error_page import ErrorPage -from frappe.website.page_controllers.not_permitted_page import NotPermittedPage -from frappe.website.page_controllers.redirect_page import RedirectPage +from frappe.website.page_renderers.error_page import ErrorPage +from frappe.website.page_renderers.not_permitted_page import NotPermittedPage +from frappe.website.page_renderers.redirect_page import RedirectPage from frappe.website.path_resolver import PathResolver From 86897c1808063efccbc8929250c3d2ca203b9c9c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 25 May 2021 10:53:35 +0530 Subject: [PATCH 107/495] refactor: Remove render.py and move all utility functions to utils.py - Replace or remove all render imports --- frappe/cache_manager.py | 2 +- frappe/commands/utils.py | 8 +- frappe/core/doctype/comment/comment.py | 2 +- frappe/core/doctype/doctype/doctype.py | 9 +-- frappe/installer.py | 4 +- frappe/migrate.py | 4 +- frappe/patches.txt | 2 +- .../templates/includes/comments/comments.py | 2 +- .../about_us_settings/about_us_settings.py | 6 +- .../doctype/blog_category/blog_category.py | 2 +- frappe/website/doctype/blog_post/blog_post.py | 2 +- .../doctype/blog_settings/blog_settings.py | 4 +- .../contact_us_settings.py | 2 +- .../doctype/help_article/help_article.py | 2 +- .../portal_settings/portal_settings.py | 2 +- .../doctype/web_template/web_template.py | 2 +- .../doctype/website_script/website_script.py | 2 +- .../website_settings/website_settings.py | 2 +- .../website_slideshow/website_slideshow.py | 2 +- .../website/page_renderers/document_page.py | 2 +- .../website/page_renderers/redirect_page.py | 2 +- .../website/page_renderers/template_page.py | 6 +- frappe/website/page_renderers/web_page.py | 4 - frappe/website/render.py | 76 ------------------- frappe/website/router.py | 49 +----------- frappe/website/utils.py | 60 +++++++++++++++ frappe/website/website_generator.py | 2 +- .../_test/_test_folder/new.csv/__init__.py | 0 28 files changed, 96 insertions(+), 166 deletions(-) delete mode 100644 frappe/website/render.py create mode 100644 frappe/www/_test/_test_folder/new.csv/__init__.py diff --git a/frappe/cache_manager.py b/frappe/cache_manager.py index 7330c83102..516f13de39 100644 --- a/frappe/cache_manager.py +++ b/frappe/cache_manager.py @@ -55,7 +55,7 @@ def clear_domain_cache(user=None): cache.delete_value(domain_cache_keys) def clear_global_cache(): - from frappe.website.render import clear_cache as clear_website_cache + from frappe.website.utils import clear_cache as clear_website_cache clear_doctype_cache() clear_website_cache() diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 4da0f6bb78..a6937a5dc4 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -69,14 +69,14 @@ def watch(apps=None): def clear_cache(context): "Clear cache, doctype cache and defaults" import frappe.sessions - import frappe.website.render + from frappe.website.utils import clear_cache as clear_website_cache from frappe.desk.notifications import clear_notifications for site in context.sites: try: frappe.connect(site) frappe.clear_cache() clear_notifications() - frappe.website.render.clear_cache() + clear_website_cache() finally: frappe.destroy() if not context.sites: @@ -86,12 +86,12 @@ def clear_cache(context): @pass_context def clear_website_cache(context): "Clear website cache" - import frappe.website.render + from frappe.website.utils import clear_cache as clear_website_cache for site in context.sites: try: frappe.init(site=site) frappe.connect() - frappe.website.render.clear_cache() + clear_website_cache() finally: frappe.destroy() if not context.sites: diff --git a/frappe/core/doctype/comment/comment.py b/frappe/core/doctype/comment/comment.py index ad5d60500b..6090947393 100644 --- a/frappe/core/doctype/comment/comment.py +++ b/frappe/core/doctype/comment/comment.py @@ -11,7 +11,7 @@ from frappe.core.doctype.user.user import extract_mentions from frappe.desk.doctype.notification_log.notification_log import enqueue_create_notification,\ get_title, get_title_html from frappe.utils import get_fullname -from frappe.website.render import clear_cache +from frappe.website.utils import clear_cache from frappe.database.schema import add_column from frappe.exceptions import ImplicitCommitError diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 84673f990a..8186aac2b0 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -13,7 +13,6 @@ from six import iteritems # imports - module imports import frappe -import frappe.website.render from frappe import _ from frappe.utils import now, cint from frappe.model import no_value_fields, default_fields, data_fieldtypes, table_fields, data_field_options @@ -27,6 +26,7 @@ from frappe.model.docfield import supports_translation from frappe.modules.import_file import get_file_path from frappe.model.meta import Meta from frappe.desk.utils import validate_route_conflict +from frappe.website.utils import clear_cache class InvalidFieldNameError(frappe.ValidationError): pass class UniqueFieldnameError(frappe.ValidationError): pass @@ -251,7 +251,7 @@ class DocType(Document): frappe.throw(_('Field "route" is mandatory for Web Views'), title='Missing Field') # clear website cache - frappe.website.render.clear_cache() + clear_cache() def change_modified_of_parent(self): """Change the timestamp of parent DocType if the current one is a child to clear caches.""" @@ -553,11 +553,6 @@ class DocType(Document): from frappe.modules.export_file import export_to_files export_to_files(record_list=[['DocType', self.name]], create_init=True) - def import_doc(self): - """Import from standard folder `[module]/doctype/[name]/[name].json`.""" - from frappe.modules.import_module import import_from_files - import_from_files(record_list=[[self.module, 'doctype', self.name]]) - def make_controller_template(self): """Make boilerplate controller template.""" make_boilerplate("controller._py", self) diff --git a/frappe/installer.py b/frappe/installer.py index d7d885d60e..138d5abc1b 100755 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -282,10 +282,10 @@ def remove_app(app_name, dry_run=False, yes=False, no_backup=False, force=False) def post_install(rebuild_website=False): - from frappe.website import render + from frappe.website.utils import clear_cache as clear_website_cache if rebuild_website: - render.clear_cache() + clear_website_cache() init_singles() frappe.db.commit() diff --git a/frappe/migrate.py b/frappe/migrate.py index 619510fe5e..0fe7c2e034 100644 --- a/frappe/migrate.py +++ b/frappe/migrate.py @@ -15,7 +15,7 @@ from frappe.utils.connections import check_connection from frappe.utils.dashboard import sync_dashboards from frappe.cache_manager import clear_global_cache from frappe.desk.notifications import clear_notifications -from frappe.website import render +from frappe.website.utils import clear_cache as clear_website_cache from frappe.core.doctype.language.language import sync_languages from frappe.modules.utils import sync_customizations from frappe.core.doctype.scheduled_job_type.scheduled_job_type import sync_jobs @@ -78,7 +78,7 @@ Otherwise, check the server logs and ensure that all the required services are r frappe.get_doc('Portal Settings', 'Portal Settings').sync_menu() # syncs statics - render.clear_cache() + clear_website_cache() # updating installed applications data frappe.get_single('Installed Applications').update_versions() diff --git a/frappe/patches.txt b/frappe/patches.txt index e70be0a37b..a2a3f73592 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -93,7 +93,7 @@ frappe.patches.v4_0.create_custom_field_for_owner_match frappe.patches.v4_0.enable_scheduler_in_system_settings execute:frappe.db.sql("update tabReport set apply_user_permissions=1") #2014-06-03 frappe.patches.v4_0.replace_deprecated_timezones -execute:import frappe.website.render; frappe.website.render.clear_cache("login"); #2014-06-10 +execute:import frappe.website.utils; frappe.website.utils.clear_cache("login"); #2014-06-10 frappe.patches.v4_0.fix_attach_field_file_url execute:frappe.permissions.reset_perms("User") #2015-03-24 execute:frappe.db.sql("""delete from `tabUserRole` where ifnull(parentfield, '')='' or ifnull(`role`, '')=''""") #2014-08-18 diff --git a/frappe/templates/includes/comments/comments.py b/frappe/templates/includes/comments/comments.py index d08eb12ba8..b1cd98f2b9 100644 --- a/frappe/templates/includes/comments/comments.py +++ b/frappe/templates/includes/comments/comments.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe import re -from frappe.website.render import clear_cache +from frappe.website.utils import clear_cache from frappe.utils import add_to_date, now from frappe import _ diff --git a/frappe/website/doctype/about_us_settings/about_us_settings.py b/frappe/website/doctype/about_us_settings/about_us_settings.py index 5b93cdcede..1ef832ba99 100644 --- a/frappe/website/doctype/about_us_settings/about_us_settings.py +++ b/frappe/website/doctype/about_us_settings/about_us_settings.py @@ -9,11 +9,11 @@ import frappe from frappe.model.document import Document class AboutUsSettings(Document): - + def on_update(self): - from frappe.website.render import clear_cache + from frappe.website.utils import clear_cache clear_cache("about") - + def get_args(): obj = frappe.get_doc("About Us Settings") return { diff --git a/frappe/website/doctype/blog_category/blog_category.py b/frappe/website/doctype/blog_category/blog_category.py index 375ba5b6a3..b8252c993f 100644 --- a/frappe/website/doctype/blog_category/blog_category.py +++ b/frappe/website/doctype/blog_category/blog_category.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from frappe.website.website_generator import WebsiteGenerator -from frappe.website.render import clear_cache +from frappe.website.utils import clear_cache class BlogCategory(WebsiteGenerator): def autoname(self): diff --git a/frappe/website/doctype/blog_post/blog_post.py b/frappe/website/doctype/blog_post/blog_post.py index 5671540682..b8a443525f 100644 --- a/frappe/website/doctype/blog_post/blog_post.py +++ b/frappe/website/doctype/blog_post/blog_post.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.website.website_generator import WebsiteGenerator -from frappe.website.render import clear_cache +from frappe.website.utils import clear_cache from frappe.utils import today, cint, global_date_format, get_fullname, strip_html_tags, markdown, sanitize_html from math import ceil from frappe.website.utils import (find_first_image, get_html_content_based_on_type, diff --git a/frappe/website/doctype/blog_settings/blog_settings.py b/frappe/website/doctype/blog_settings/blog_settings.py index 0ed98b9b87..2580806ef1 100644 --- a/frappe/website/doctype/blog_settings/blog_settings.py +++ b/frappe/website/doctype/blog_settings/blog_settings.py @@ -9,8 +9,8 @@ import frappe from frappe.model.document import Document class BlogSettings(Document): - + def on_update(self): - from frappe.website.render import clear_cache + from frappe.website.utils import clear_cache clear_cache("blog") clear_cache("writers") \ No newline at end of file diff --git a/frappe/website/doctype/contact_us_settings/contact_us_settings.py b/frappe/website/doctype/contact_us_settings/contact_us_settings.py index 24e9811a47..c237a13bda 100644 --- a/frappe/website/doctype/contact_us_settings/contact_us_settings.py +++ b/frappe/website/doctype/contact_us_settings/contact_us_settings.py @@ -11,5 +11,5 @@ from frappe.model.document import Document class ContactUsSettings(Document): def on_update(self): - from frappe.website.render import clear_cache + from frappe.website.utils import clear_cache clear_cache("contact") \ No newline at end of file diff --git a/frappe/website/doctype/help_article/help_article.py b/frappe/website/doctype/help_article/help_article.py index fa26cfef99..555d6c9268 100644 --- a/frappe/website/doctype/help_article/help_article.py +++ b/frappe/website/doctype/help_article/help_article.py @@ -93,7 +93,7 @@ def get_sidebar_items(): def clear_cache(): clear_website_cache() - from frappe.website.render import clear_cache + from frappe.website.utils import clear_cache clear_cache() def clear_website_cache(path=None): diff --git a/frappe/website/doctype/portal_settings/portal_settings.py b/frappe/website/doctype/portal_settings/portal_settings.py index 1bfbc70d60..84ee678c1e 100644 --- a/frappe/website/doctype/portal_settings/portal_settings.py +++ b/frappe/website/doctype/portal_settings/portal_settings.py @@ -45,7 +45,7 @@ class PortalSettings(Document): # clear web cache (for menus!) frappe.clear_cache(user='Guest') - from frappe.website.render import clear_cache + from frappe.website.utils import clear_cache clear_cache() # clears role based home pages diff --git a/frappe/website/doctype/web_template/web_template.py b/frappe/website/doctype/web_template/web_template.py index 2fd5bfa179..712cf3c1e4 100644 --- a/frappe/website/doctype/web_template/web_template.py +++ b/frappe/website/doctype/web_template/web_template.py @@ -9,7 +9,7 @@ from shutil import rmtree import frappe from frappe.model.document import Document -from frappe.website.render import clear_cache +from frappe.website.utils import clear_cache from frappe import _ from frappe.modules.export_file import ( write_document_file, diff --git a/frappe/website/doctype/website_script/website_script.py b/frappe/website/doctype/website_script/website_script.py index 5648c27fd6..d1f9f595ed 100644 --- a/frappe/website/doctype/website_script/website_script.py +++ b/frappe/website/doctype/website_script/website_script.py @@ -14,5 +14,5 @@ class WebsiteScript(Document): """clear cache""" frappe.clear_cache(user = 'Guest') - from frappe.website.render import clear_cache + from frappe.website.utils import clear_cache clear_cache() \ No newline at end of file diff --git a/frappe/website/doctype/website_settings/website_settings.py b/frappe/website/doctype/website_settings/website_settings.py index ae8e6bd7e1..e5dec24fbb 100644 --- a/frappe/website/doctype/website_settings/website_settings.py +++ b/frappe/website/doctype/website_settings/website_settings.py @@ -68,7 +68,7 @@ class WebsiteSettings(Document): # clear web cache (for menus!) frappe.clear_cache(user = 'Guest') - from frappe.website.render import clear_cache + from frappe.website.utils import clear_cache clear_cache() # clears role based home pages diff --git a/frappe/website/doctype/website_slideshow/website_slideshow.py b/frappe/website/doctype/website_slideshow/website_slideshow.py index 90f62d1bb1..5e6353c5cd 100644 --- a/frappe/website/doctype/website_slideshow/website_slideshow.py +++ b/frappe/website/doctype/website_slideshow/website_slideshow.py @@ -15,7 +15,7 @@ class WebsiteSlideshow(Document): def on_update(self): # a slide show can be in use and any change in it should get reflected - from frappe.website.render import clear_cache + from frappe.website.utils import clear_cache clear_cache() def validate_images(self): diff --git a/frappe/website/page_renderers/document_page.py b/frappe/website/page_renderers/document_page.py index 34dc42e203..2f272ffd15 100644 --- a/frappe/website/page_renderers/document_page.py +++ b/frappe/website/page_renderers/document_page.py @@ -1,7 +1,7 @@ import frappe from frappe.model.document import get_controller from frappe.website.page_renderers.base_template_page import BaseTemplatePage -from frappe.website.render import build_response +from frappe.website.utils import build_response from frappe.website.router import (get_doctypes_with_web_view, get_page_info_from_web_page_with_dynamic_routes) diff --git a/frappe/website/page_renderers/redirect_page.py b/frappe/website/page_renderers/redirect_page.py index 0bfa8897a3..2049c375e8 100644 --- a/frappe/website/page_renderers/redirect_page.py +++ b/frappe/website/page_renderers/redirect_page.py @@ -1,5 +1,5 @@ import frappe -from frappe.website.render import build_response +from frappe.website.utils import build_response class RedirectPage(object): def __init__(self, path, http_status_code=301): diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index 467d74b283..d8032c6b9c 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -4,11 +4,9 @@ import click import frappe from frappe.website.page_renderers.base_template_page import BaseTemplatePage -from frappe.website.utils import get_sidebar_items -from frappe.website.render import build_response from frappe.website.router import get_base_template -from frappe.website.utils import (extract_comment_tag, extract_title, - get_next_link, get_toc, get_frontmatter, cache_html) +from frappe.website.utils import (extract_comment_tag, extract_title, get_next_link, + get_toc, get_frontmatter, cache_html, get_sidebar_items, build_response) WEBPAGE_PY_MODULE_PROPERTIES = ("base_template_path", "template", "no_cache", "sitemap", "condition_field") diff --git a/frappe/website/page_renderers/web_page.py b/frappe/website/page_renderers/web_page.py index 0407afe532..5c3807c0ee 100644 --- a/frappe/website/page_renderers/web_page.py +++ b/frappe/website/page_renderers/web_page.py @@ -9,10 +9,6 @@ class WebPage(object): self.path = path.strip('/ ') self.basepath = '' - def get(self): - if self.validate(): - return self.render() - def can_render(self): pass diff --git a/frappe/website/render.py b/frappe/website/render.py deleted file mode 100644 index d5587ad98b..0000000000 --- a/frappe/website/render.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import json -import mimetypes -import re - -from six import iteritems -from werkzeug.wrappers import Response - -import frappe -import frappe.sessions - - -def build_response(path, data, http_status_code, headers=None): - # build response - response = Response() - response.data = set_content_type(response, data, path) - response.status_code = http_status_code - response.headers["X-Page-Name"] = path.encode("ascii", errors="xmlcharrefreplace") - response.headers["X-From-Cache"] = frappe.local.response.from_cache or False - - add_preload_headers(response) - if headers: - for key, val in iteritems(headers): - response.headers[key] = val.encode("ascii", errors="xmlcharrefreplace") - - return response - -def set_content_type(response, data, path): - if isinstance(data, dict): - response.mimetype = 'application/json' - response.charset = 'utf-8' - data = json.dumps(data) - return data - - response.mimetype = 'text/html' - response.charset = 'utf-8' - - # ignore paths ending with .com to avoid unnecessary download - # https://bugs.python.org/issue22347 - if "." in path and not path.endswith('.com'): - content_type, encoding = mimetypes.guess_type(path) - if content_type: - response.mimetype = content_type - if encoding: - response.charset = encoding - - return data - -def add_preload_headers(response): - from bs4 import BeautifulSoup - - try: - preload = [] - soup = BeautifulSoup(response.data, "lxml") - for elem in soup.find_all('script', src=re.compile(".*")): - preload.append(("script", elem.get("src"))) - - for elem in soup.find_all('link', rel="stylesheet"): - preload.append(("style", elem.get("href"))) - - links = [] - for _type, link in preload: - links.append("<{}>; rel=preload; as={}".format(link, _type)) - - if links: - response.headers["Link"] = ",".join(links) - except Exception: - import traceback - traceback.print_exc() - -def clear_cache(path=None): - # TODO: Remove this - from frappe.website.utils import clear_cache - return clear_cache(path) diff --git a/frappe/website/router.py b/frappe/website/router.py index 9922647ebf..23fd5cb21f 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -6,7 +6,7 @@ import os import re import frappe -from frappe.website.utils import extract_comment_tag, extract_title +from frappe.website.utils import extract_title from werkzeug.routing import Map, Rule, NotFound def get_page_info_from_web_page_with_dynamic_routes(path): @@ -143,14 +143,12 @@ def get_page_info(path, app, start, basepath=None, app_path=None, fname=None): # get the source setup_source(page_info) - # extract properties from HTML comments - load_properties_from_source(page_info) + if not page_info.title: + page_info.title = extract_title(page_info.source, page_info.route) # extract properties from controller attributes load_properties_from_controller(page_info) - page_info.build_version = frappe.utils.get_build_version() - return page_info def get_frontmatter(string): @@ -252,47 +250,6 @@ def setup_index(page_info): if os.path.exists(index_txt_path): page_info.index = open(index_txt_path, 'r').read().splitlines() -def load_properties_from_source(page_info): - '''Load properties like no_cache, title from source html''' - - if not page_info.title: - page_info.title = extract_title(page_info.source, page_info.route) - - base_template = extract_comment_tag(page_info.source, 'base_template') - if base_template: - page_info.base_template = base_template - - if (page_info.base_template - and "{%- extends" not in page_info.source - and "{% extends" not in page_info.source - and "" not in page_info.source): - page_info.source = '''{{% extends "{0}" %}} - {{% block page_content %}}{1}{{% endblock %}}'''.format(page_info.base_template, page_info.source) - - if "" in page_info.source: - page_info.no_breadcrumbs = 1 - - if "" in page_info.source: - page_info.show_sidebar = 1 - - if "" in page_info.source: - page_info.add_breadcrumbs = 1 - - if "" in page_info.source: - page_info.no_header = 1 - - if "" in page_info.source: - page_info.add_next_prev_links = 1 - - if "" in page_info.source: - page_info.no_cache = 1 - - if "" in page_info.source: - page_info.sitemap = 0 - - if "" in page_info.source: - page_info.sitemap = 1 - def load_properties_from_controller(page_info): if not page_info.controller: return diff --git a/frappe/website/utils.py b/frappe/website/utils.py index 814bd0101e..f57cfa7ec5 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -2,6 +2,7 @@ # MIT License. See license.txt import functools import json +import mimetypes import os import re from functools import wraps @@ -9,6 +10,7 @@ from functools import wraps import yaml from past.builtins import cmp from six import iteritems +from werkzeug.wrappers import Response import frappe from frappe import _ @@ -446,3 +448,61 @@ def cache_html(func): return html return cache_html_decorator + +def build_response(path, data, http_status_code, headers=None): + # build response + response = Response() + response.data = set_content_type(response, data, path) + response.status_code = http_status_code + response.headers["X-Page-Name"] = path.encode("ascii", errors="xmlcharrefreplace") + response.headers["X-From-Cache"] = frappe.local.response.from_cache or False + + add_preload_headers(response) + if headers: + for key, val in iteritems(headers): + response.headers[key] = val.encode("ascii", errors="xmlcharrefreplace") + + return response + +def set_content_type(response, data, path): + if isinstance(data, dict): + response.mimetype = 'application/json' + response.charset = 'utf-8' + data = json.dumps(data) + return data + + response.mimetype = 'text/html' + response.charset = 'utf-8' + + # ignore paths ending with .com to avoid unnecessary download + # https://bugs.python.org/issue22347 + if "." in path and not path.endswith('.com'): + content_type, encoding = mimetypes.guess_type(path) + if content_type: + response.mimetype = content_type + if encoding: + response.charset = encoding + + return data + +def add_preload_headers(response): + from bs4 import BeautifulSoup + + try: + preload = [] + soup = BeautifulSoup(response.data, "lxml") + for elem in soup.find_all('script', src=re.compile(".*")): + preload.append(("script", elem.get("src"))) + + for elem in soup.find_all('link', rel="stylesheet"): + preload.append(("style", elem.get("href"))) + + links = [] + for _type, link in preload: + links.append("<{}>; rel=preload; as={}".format(link, _type)) + + if links: + response.headers["Link"] = ",".join(links) + except Exception: + import traceback + traceback.print_exc() diff --git a/frappe/website/website_generator.py b/frappe/website/website_generator.py index f2638fd86d..8b0a1bfd2d 100644 --- a/frappe/website/website_generator.py +++ b/frappe/website/website_generator.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.website.utils import cleanup_page_name -from frappe.website.render import clear_cache +from frappe.website.utils import clear_cache from frappe.modules import get_module_name from frappe.search.website_search import update_index_for_path, remove_document_from_index diff --git a/frappe/www/_test/_test_folder/new.csv/__init__.py b/frappe/www/_test/_test_folder/new.csv/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From 889e8aacb73f9c059cd5d39d88c3a612c44fbae8 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 25 May 2021 12:51:10 +0530 Subject: [PATCH 108/495] ci(semgrep): false +ve translation on templates separated JS and python rules for granuarilty. Ignore matches with microtemplating that have this structure: `{{.*_.*}}` in string. --- .github/helper/semgrep_rules/ux.js | 9 +++++++++ .github/helper/semgrep_rules/ux.py | 18 +++++++++--------- .github/helper/semgrep_rules/ux.yml | 23 +++++++++++++++++++---- 3 files changed, 37 insertions(+), 13 deletions(-) create mode 100644 .github/helper/semgrep_rules/ux.js diff --git a/.github/helper/semgrep_rules/ux.js b/.github/helper/semgrep_rules/ux.js new file mode 100644 index 0000000000..ae73f9cc60 --- /dev/null +++ b/.github/helper/semgrep_rules/ux.js @@ -0,0 +1,9 @@ + +// ok: frappe-missing-translate-function-js +frappe.msgprint('{{ _("Both login and password required") }}'); + +// ruleid: frappe-missing-translate-function-js +frappe.msgprint('What'); + +// ok: frappe-missing-translate-function-js +frappe.throw(' {{ _("Both login and password required") }}. '); diff --git a/.github/helper/semgrep_rules/ux.py b/.github/helper/semgrep_rules/ux.py index 4a74457435..a00d3cd8ae 100644 --- a/.github/helper/semgrep_rules/ux.py +++ b/.github/helper/semgrep_rules/ux.py @@ -2,30 +2,30 @@ import frappe from frappe import msgprint, throw, _ -# ruleid: frappe-missing-translate-function +# ruleid: frappe-missing-translate-function-python throw("Error Occured") -# ruleid: frappe-missing-translate-function +# ruleid: frappe-missing-translate-function-python frappe.throw("Error Occured") -# ruleid: frappe-missing-translate-function +# ruleid: frappe-missing-translate-function-python frappe.msgprint("Useful message") -# ruleid: frappe-missing-translate-function +# ruleid: frappe-missing-translate-function-python msgprint("Useful message") -# ok: frappe-missing-translate-function +# ok: frappe-missing-translate-function-python translatedmessage = _("Hello") -# ok: frappe-missing-translate-function +# ok: frappe-missing-translate-function-python throw(translatedmessage) -# ok: frappe-missing-translate-function +# ok: frappe-missing-translate-function-python msgprint(translatedmessage) -# ok: frappe-missing-translate-function +# ok: frappe-missing-translate-function-python msgprint(_("Helpful message")) -# ok: frappe-missing-translate-function +# ok: frappe-missing-translate-function-python frappe.throw(_("Error occured")) diff --git a/.github/helper/semgrep_rules/ux.yml b/.github/helper/semgrep_rules/ux.yml index ed06a6a80c..dd667f36c0 100644 --- a/.github/helper/semgrep_rules/ux.yml +++ b/.github/helper/semgrep_rules/ux.yml @@ -1,15 +1,30 @@ rules: -- id: frappe-missing-translate-function +- id: frappe-missing-translate-function-python pattern-either: - patterns: - pattern: frappe.msgprint("...", ...) - pattern-not: frappe.msgprint(_("..."), ...) - - pattern-not: frappe.msgprint(__("..."), ...) - patterns: - pattern: frappe.throw("...", ...) - pattern-not: frappe.throw(_("..."), ...) - - pattern-not: frappe.throw(__("..."), ...) message: | All user facing text must be wrapped in translate function. Please refer to translation documentation. https://frappeframework.com/docs/user/en/guides/basics/translations - languages: [python, javascript, json] + languages: [python] + severity: ERROR + +- id: frappe-missing-translate-function-js + pattern-either: + - patterns: + - pattern: frappe.msgprint("...", ...) + - pattern-not: frappe.msgprint(__("..."), ...) + # ignore microtemplating e.g. msgprint("{{ _("server side translation") }}") + - pattern-not: frappe.msgprint("=~/\{\{.*\_.*\}\}/i", ...) + - patterns: + - pattern: frappe.throw("...", ...) + - pattern-not: frappe.throw(__("..."), ...) + # ignore microtemplating + - pattern-not: frappe.throw("=~/\{\{.*\_.*\}\}/i", ...) + message: | + All user facing text must be wrapped in translate function. Please refer to translation documentation. https://frappeframework.com/docs/user/en/guides/basics/translations + languages: [javascript] severity: ERROR From faa347461d7e09136803d82931f179b90aabcfb7 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 25 May 2021 13:00:02 +0530 Subject: [PATCH 109/495] chore: fix all findings flaged by ux rule --- frappe/desk/doctype/dashboard/dashboard.py | 2 +- frappe/desk/doctype/dashboard_chart/dashboard_chart.py | 2 +- frappe/templates/includes/contact.js | 10 ++++------ frappe/tests/ui_test_helpers.py | 3 ++- frappe/utils/jinja.py | 4 ++-- frappe/utils/password.py | 2 +- frappe/utils/safe_exec.py | 3 ++- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/frappe/desk/doctype/dashboard/dashboard.py b/frappe/desk/doctype/dashboard/dashboard.py index 4e66318769..90620ed3c5 100644 --- a/frappe/desk/doctype/dashboard/dashboard.py +++ b/frappe/desk/doctype/dashboard/dashboard.py @@ -22,7 +22,7 @@ class Dashboard(Document): def validate(self): if not frappe.conf.developer_mode and self.is_standard: - frappe.throw('Cannot edit Standard Dashboards') + frappe.throw(_('Cannot edit Standard Dashboards')) if self.is_standard: non_standard_docs_map = { diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py index 48b34e6cd9..cd934c54e6 100644 --- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py @@ -326,7 +326,7 @@ class DashboardChart(Document): def validate(self): if not frappe.conf.developer_mode and self.is_standard: - frappe.throw('Cannot edit Standard charts') + frappe.throw(_('Cannot edit Standard charts')) if self.chart_type != 'Custom' and self.chart_type != 'Report': self.check_required_field() self.check_document_type() diff --git a/frappe/templates/includes/contact.js b/frappe/templates/includes/contact.js index 130d8bfc0c..fb0b73ff80 100644 --- a/frappe/templates/includes/contact.js +++ b/frappe/templates/includes/contact.js @@ -12,14 +12,12 @@ frappe.ready(function() { var message = $('[name="message"]').val(); if(!(email && message)) { - frappe.msgprint("{{ _("Please enter both your email and message so that we \ - can get back to you. Thanks!") }}"); + frappe.msgprint('{{ _("Please enter both your email and message so that we can get back to you. Thanks!") }}'); return false; } if(!validate_email(email)) { - frappe.msgprint("{{ _("You seem to have written your name instead of your email. \ - Please enter a valid email address so that we can get back.") }}"); + frappe.msgprint('{{ _("You seem to have written your name instead of your email. Please enter a valid email address so that we can get back.") }}'); $('[name="email"]').focus(); return false; } @@ -31,9 +29,9 @@ frappe.ready(function() { message: message, callback: function(r) { if(r.message==="okay") { - frappe.msgprint("{{ _("Thank you for your message") }}"); + frappe.msgprint('{{ _("Thank you for your message") }}'); } else { - frappe.msgprint("{{ _("There were errors") }}"); + frappe.msgprint('{{ _("There were errors") }}'); console.log(r.exc); } $(':input').val(''); diff --git a/frappe/tests/ui_test_helpers.py b/frappe/tests/ui_test_helpers.py index f56311b2e3..7670f99698 100644 --- a/frappe/tests/ui_test_helpers.py +++ b/frappe/tests/ui_test_helpers.py @@ -1,4 +1,5 @@ import frappe +from frappe import _ from frappe.utils import add_to_date, now @frappe.whitelist() @@ -10,7 +11,7 @@ def create_if_not_exists(doc): ''' if not frappe.local.dev_server: - frappe.throw('This method can only be accessed in development', frappe.PermissionError) + frappe.throw(_('This method can only be accessed in development'), frappe.PermissionError) doc = frappe.parse_json(doc) diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py index a77eca4977..c276526c52 100644 --- a/frappe/utils/jinja.py +++ b/frappe/utils/jinja.py @@ -67,7 +67,7 @@ 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 frappe import get_traceback, throw, _ from jinja2 import TemplateError if not template: @@ -77,7 +77,7 @@ def render_template(template, context, is_path=None, safe_render=True): return get_jenv().get_template(template).render(context) else: if safe_render and ".__" in template: - throw("Illegal template") + throw(_("Illegal template")) try: return get_jenv().from_string(template).render(context) except TemplateError: diff --git a/frappe/utils/password.py b/frappe/utils/password.py index fbed3cd8e7..fef0bab8af 100644 --- a/frappe/utils/password.py +++ b/frappe/utils/password.py @@ -61,7 +61,7 @@ def set_encrypted_password(doctype, name, pwd, fieldname='password'): except frappe.db.DataError as e: if ((frappe.db.db_type == 'mariadb' and e.args[0] == DATA_TOO_LONG) or (frappe.db.db_type == 'postgres' and e.pgcode == STRING_DATA_RIGHT_TRUNCATION)): - frappe.throw("Most probably your password is too long.", exc=e) + frappe.throw(_("Most probably your password is too long."), exc=e) raise e diff --git a/frappe/utils/safe_exec.py b/frappe/utils/safe_exec.py index 643812b226..21508706c9 100644 --- a/frappe/utils/safe_exec.py +++ b/frappe/utils/safe_exec.py @@ -5,6 +5,7 @@ from html2text import html2text from RestrictedPython import compile_restricted, safe_globals import RestrictedPython.Guards import frappe +from frappe import _ import frappe.utils import frappe.utils.data from frappe.website.utils import (get_shade, get_toc, get_next_link) @@ -31,7 +32,7 @@ class NamespaceDict(frappe._dict): def safe_exec(script, _globals=None, _locals=None): # script reports must be enabled via site_config.json if not frappe.conf.server_script_enabled: - frappe.throw('Please Enable Server Scripts', ServerScriptNotEnabled) + frappe.throw(_('Please Enable Server Scripts'), ServerScriptNotEnabled) # build globals exec_globals = get_safe_globals() From d88ce8e807fe7acbc5e8c1cb9bec8fd6c52bb432 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 25 May 2021 13:32:20 +0530 Subject: [PATCH 110/495] test: Add test to check replacement of {next} & {index} identifiers --- frappe/website/doctype/web_page/test_web_page.py | 16 +++++++++++----- frappe/www/_test/_test_folder/_test_page.html | 1 + frappe/www/_test/_test_folder/index.md | 3 +++ 3 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 frappe/www/_test/_test_folder/index.md diff --git a/frappe/website/doctype/web_page/test_web_page.py b/frappe/website/doctype/web_page/test_web_page.py index c5d250a4a0..b9bd425781 100644 --- a/frappe/website/doctype/web_page/test_web_page.py +++ b/frappe/website/doctype/web_page/test_web_page.py @@ -91,12 +91,18 @@ class TestWebPage(unittest.TestCase): self.assertTrue('Test Sidebar' in frappe.as_unicode(content)) frappe.flags.look_for_sidebar = False - def test_home_page(self): - content = get_response_content(path='/') - content = get_response_content(path='/index') + def test_index_and_next_comment(self): + content = get_response_content('/_test/_test_folder') + # test if {index} was rendered + self.assertTrue(' Test Page' \ + in frappe.as_unicode(content)) + self.assertTrue(' Test Toc' \ + in frappe.as_unicode(content)) - def test_table_of_content(self): - page = get_response(path='/_test/_test_folder/_test_toc') + content = get_response_content('/_test/_test_folder/_test_page') + # test if {next} was rendered + self.assertTrue('Next: Test Toc' \ + in frappe.as_unicode(content)) def test_meta_tags(self): pass diff --git a/frappe/www/_test/_test_folder/_test_page.html b/frappe/www/_test/_test_folder/_test_page.html index 900f07459d..123d619e38 100644 --- a/frappe/www/_test/_test_folder/_test_page.html +++ b/frappe/www/_test/_test_folder/_test_page.html @@ -1,4 +1,5 @@ {% block content %} {% include "templates/includes/web_sidebar.html" %}

Test content

+{next} {% endblock %} diff --git a/frappe/www/_test/_test_folder/index.md b/frappe/www/_test/_test_folder/index.md new file mode 100644 index 0000000000..d0b531a642 --- /dev/null +++ b/frappe/www/_test/_test_folder/index.md @@ -0,0 +1,3 @@ +# Index + +{index} \ No newline at end of file From ca56b21290b52a1fde8c34db718ee264756274ef Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 25 May 2021 15:01:20 +0530 Subject: [PATCH 111/495] test: Check all cases of setting homepage --- frappe/tests/test_website.py | 63 ++++++++++++++++++++++++----- frappe/website/utils.py | 3 ++ frappe/www/_test/_test_home_page.py | 2 + 3 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 frappe/www/_test/_test_home_page.py diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index cc500d1c02..6b9530a8ce 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -4,7 +4,7 @@ import unittest import frappe from frappe.website.serve import get_response -from frappe.website.utils import get_home_page +from frappe.website.utils import get_home_page, clear_website_cache from frappe.utils import set_request class TestWebsite(unittest.TestCase): @@ -14,32 +14,29 @@ class TestWebsite(unittest.TestCase): def tearDown(self): frappe.set_user('Administrator') - def test_home_page_for_role(self): + def test_home_page(self): frappe.set_user('Administrator') - frappe.delete_doc_if_exists('User', 'test-user-for-home-page@example.com') - frappe.delete_doc_if_exists('Role', 'home-page-test') - frappe.delete_doc_if_exists('Web Page', 'home-page-test') + # test home page via role user = frappe.get_doc(dict( doctype='User', email='test-user-for-home-page@example.com', - first_name='test')).insert() + first_name='test')).insert(ignore_if_duplicate=True) role = frappe.get_doc(dict( doctype = 'Role', role_name = 'home-page-test', desk_access = 0, - home_page = '/home-page-test' - )).insert() + )).insert(ignore_if_duplicate=True) user.add_roles(role.name) user.save() + frappe.db.set_value('Role', 'home-page-test', 'home_page', 'home-page-test') frappe.set_user('test-user-for-home-page@example.com') self.assertEqual(get_home_page(), 'home-page-test') frappe.set_user('Administrator') - role.home_page = '' - role.save() + frappe.db.set_value('Role', 'home-page-test', 'home_page', '') # home page via portal settings frappe.db.set_value('Portal Settings', None, 'default_portal_home', 'test-portal-home') @@ -48,6 +45,42 @@ class TestWebsite(unittest.TestCase): frappe.cache().hdel('home_page', frappe.session.user) self.assertEqual(get_home_page(), 'test-portal-home') + frappe.db.set_value("Portal Settings", None, "default_portal_home", '') + clear_website_cache() + + # home page via website settings + frappe.db.set_value("Website Settings", None, "home_page", 'contact') + self.assertEqual(get_home_page(), 'contact') + + frappe.db.set_value("Website Settings", None, "home_page", None) + clear_website_cache() + + # fallback homepage + self.assertEqual(get_home_page(), 'me') + + # fallback homepage for guest + frappe.set_user('Guest') + self.assertEqual(get_home_page(), 'login') + frappe.set_user('Administrator') + + # test homepage via hooks + clear_website_cache() + set_home_page_hook('get_website_user_home_page', 'frappe.www._test._test_home_page.get_website_user_home_page') + self.assertEqual(get_home_page(), '_test/_test_folder') + + clear_website_cache() + set_home_page_hook('website_user_home_page', 'login') + self.assertEqual(get_home_page(), 'login') + + clear_website_cache() + set_home_page_hook('home_page', 'about') + self.assertEqual(get_home_page(), 'about') + + clear_website_cache() + set_home_page_hook('role_home_page', {'home-page-test': 'home-page-test'}) + self.assertEqual(get_home_page(), 'home-page-test') + + def test_page_load(self): set_request(method='POST', path='login') response = get_response() @@ -147,3 +180,13 @@ class TestWebsite(unittest.TestCase): delattr(frappe.hooks, 'website_redirects') frappe.cache().delete_key('app_hooks') + + +def set_home_page_hook(key, value): + from frappe import hooks + # reset home_page hooks + for hook in ('get_website_user_home_page','website_user_home_page','role_home_page','home_page'): + if hasattr(hooks, hook): + delattr(hooks, hook) + + setattr(hooks, key, value) diff --git a/frappe/website/utils.py b/frappe/website/utils.py index f57cfa7ec5..a6d9535ec6 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -328,6 +328,9 @@ def clear_cache(path=None): for method in frappe.get_hooks("website_clear_cache"): frappe.get_attr(method)(path) +def clear_website_cache(path=None): + clear_cache(path) + def clear_sitemap(): delete_page_cache("*") diff --git a/frappe/www/_test/_test_home_page.py b/frappe/www/_test/_test_home_page.py new file mode 100644 index 0000000000..936399c700 --- /dev/null +++ b/frappe/www/_test/_test_home_page.py @@ -0,0 +1,2 @@ +def get_website_user_home_page(user): + return '/_test/_test_folder' \ No newline at end of file From e6d701b038545fbdba72d7d968776bcb0f5f7356 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 25 May 2021 15:02:18 +0530 Subject: [PATCH 112/495] refactor: Use clear_website_cache instead of clear_cache --- frappe/cache_manager.py | 2 +- frappe/commands/utils.py | 4 ++-- frappe/installer.py | 2 +- frappe/migrate.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/cache_manager.py b/frappe/cache_manager.py index 516f13de39..6b937731b0 100644 --- a/frappe/cache_manager.py +++ b/frappe/cache_manager.py @@ -55,7 +55,7 @@ def clear_domain_cache(user=None): cache.delete_value(domain_cache_keys) def clear_global_cache(): - from frappe.website.utils import clear_cache as clear_website_cache + from frappe.website.utils import clear_website_cache clear_doctype_cache() clear_website_cache() diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index a6937a5dc4..b45f36ad0b 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -69,7 +69,7 @@ def watch(apps=None): def clear_cache(context): "Clear cache, doctype cache and defaults" import frappe.sessions - from frappe.website.utils import clear_cache as clear_website_cache + from frappe.website.utils import clear_website_cache from frappe.desk.notifications import clear_notifications for site in context.sites: try: @@ -86,7 +86,7 @@ def clear_cache(context): @pass_context def clear_website_cache(context): "Clear website cache" - from frappe.website.utils import clear_cache as clear_website_cache + from frappe.website.utils import clear_website_cache for site in context.sites: try: frappe.init(site=site) diff --git a/frappe/installer.py b/frappe/installer.py index 138d5abc1b..b81d0a03b4 100755 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -282,7 +282,7 @@ def remove_app(app_name, dry_run=False, yes=False, no_backup=False, force=False) def post_install(rebuild_website=False): - from frappe.website.utils import clear_cache as clear_website_cache + from frappe.website.utils import clear_website_cache if rebuild_website: clear_website_cache() diff --git a/frappe/migrate.py b/frappe/migrate.py index 0fe7c2e034..ada0860ae4 100644 --- a/frappe/migrate.py +++ b/frappe/migrate.py @@ -15,7 +15,7 @@ from frappe.utils.connections import check_connection from frappe.utils.dashboard import sync_dashboards from frappe.cache_manager import clear_global_cache from frappe.desk.notifications import clear_notifications -from frappe.website.utils import clear_cache as clear_website_cache +from frappe.website.utils import clear_website_cache from frappe.core.doctype.language.language import sync_languages from frappe.modules.utils import sync_customizations from frappe.core.doctype.scheduled_job_type.scheduled_job_type import sync_jobs From 78206bea8fb2894138f0c9b6076c2cedf976c827 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 25 May 2021 16:39:48 +0530 Subject: [PATCH 113/495] test: Clear hook cache after updating hook --- frappe/tests/test_website.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index 6b9530a8ce..69425cdc45 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -146,9 +146,6 @@ class TestWebsite(unittest.TestCase): }) website_settings.save() - frappe.cache().delete_key('app_hooks') - frappe.cache().delete_key('website_redirects') - set_request(method='GET', path='/testfrom') response = get_response() self.assertEqual(response.status_code, 301) @@ -190,3 +187,5 @@ def set_home_page_hook(key, value): delattr(hooks, hook) setattr(hooks, key, value) + frappe.cache().delete_key('app_hooks') + From 886d16c87177d4a2954616ae6dcd1799be1db876 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 25 May 2021 19:45:53 +0200 Subject: [PATCH 114/495] feat: add context to confirm dailog --- frappe/public/js/frappe/ui/messages.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/ui/messages.js b/frappe/public/js/frappe/ui/messages.js index 2e8ba7d206..f465250af9 100644 --- a/frappe/public/js/frappe/ui/messages.js +++ b/frappe/public/js/frappe/ui/messages.js @@ -26,13 +26,13 @@ frappe.throw = function(msg) { frappe.confirm = function(message, confirm_action, reject_action) { var d = new frappe.ui.Dialog({ - title: __("Confirm"), - primary_action_label: __("Yes"), + title: __("Confirm", null, "Title of confirmation dialog"), + primary_action_label: __("Yes", null, "Approve confirmation dialog"), primary_action: () => { confirm_action && confirm_action(); d.hide(); }, - secondary_action_label: __("No"), + secondary_action_label: __("No", null, "Dismiss confirmation dialog"), secondary_action: () => d.hide(), }); From d3121d753ead5511ecc6a0e57e6e6825901c68ad Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 25 May 2021 19:47:15 +0200 Subject: [PATCH 115/495] feat: add context to prompt dialog --- frappe/public/js/frappe/ui/messages.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/ui/messages.js b/frappe/public/js/frappe/ui/messages.js index f465250af9..067fed233c 100644 --- a/frappe/public/js/frappe/ui/messages.js +++ b/frappe/public/js/frappe/ui/messages.js @@ -88,9 +88,9 @@ frappe.prompt = function(fields, callback, title, primary_label) { if(!$.isArray(fields)) fields = [fields]; var d = new frappe.ui.Dialog({ fields: fields, - title: title || __("Enter Value"), + title: title || __("Enter Value", null, "Title of prompt dialog"), }); - d.set_primary_action(primary_label || __("Submit"), function() { + d.set_primary_action(primary_label || __("Submit", null, "Primary action of prompt dialog"), function() { var values = d.get_values(); if(!values) { return; From d7d0ddf6adee391714e5b4f31ab559fe61c322c5 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Wed, 26 May 2021 02:54:04 +0000 Subject: [PATCH 116/495] fix: package.json & yarn.lock to reduce vulnerabilities The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-NORMALIZEURL-1296539 --- package.json | 2 +- yarn.lock | 1022 ++++++++++++++++++++------------------------------ 2 files changed, 418 insertions(+), 606 deletions(-) diff --git a/package.json b/package.json index e666a2e1ce..25bf0b0695 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "bootstrap": "4.5.0", "cliui": "^7.0.4", "cookie": "^0.4.0", - "cssnano": "^4.1.10", + "cssnano": "^5.0.0", "driver.js": "^0.9.8", "express": "^4.17.1", "fast-deep-equal": "^2.0.1", diff --git a/yarn.lock b/yarn.lock index 86719d81f4..96a6059ecc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,27 @@ resolved "https://registry.yarnpkg.com/@assemblyscript/loader/-/loader-0.10.1.tgz#70e45678f06c72fa2e350e8553ec4a4d72b92e06" integrity sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg== +"@babel/code-frame@^7.0.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" + integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== + dependencies: + "@babel/highlight" "^7.12.13" + +"@babel/helper-validator-identifier@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" + integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== + +"@babel/highlight@^7.12.13": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf" + integrity sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.0" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@deepcode/dcignore@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@deepcode/dcignore/-/dcignore-1.0.2.tgz#39e4a3df7dde8811925330506e4bb3fbf3c288d8" @@ -361,6 +382,11 @@ dependencies: defer-to-connect "^2.0.0" +"@trysound/sax@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.1.1.tgz#3348564048e7a2d7398c935d466c0414ebb6a669" + integrity sha512-Z6DoceYb/1xSg5+e+ZlPZ9v0N16ZvZ+wYMraFue4HYrE4ttONKtsvruIRf6t9TBR0YvSOfi1hUU0fJfBLCDYow== + "@types/babel-types@*", "@types/babel-types@^7.0.0": version "7.0.9" resolved "https://registry.yarnpkg.com/@types/babel-types/-/babel-types-7.0.9.tgz#01d7b86949f455402a94c788883fe4ba574cad41" @@ -463,10 +489,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.4.tgz#76c3cb3a12909510f52e5dc04a6298cdf9504ffd" integrity sha512-oVeL12C6gQS/GAExndigSaLxTrKpQPxewx9bOcwfvJiJge4rr7wNaph4J+ns5hrmIV2as5qxqN8YKthn9qh0jw== -"@types/q@^1.5.1": - version "1.5.1" - resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.1.tgz#48fd98c1561fe718b61733daed46ff115b496e18" - integrity sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA== +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/responselike@*", "@types/responselike@^1.0.0": version "1.0.0" @@ -595,7 +621,7 @@ align-text@^0.1.1, align-text@^0.1.3: longest "^1.0.1" repeat-string "^1.5.2" -alphanum-sort@^1.0.0: +alphanum-sort@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= @@ -958,7 +984,7 @@ body-parser@1.19.0: raw-body "2.4.0" type-is "~1.6.17" -boolbase@^1.0.0, boolbase@~1.0.0: +boolbase@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= @@ -1009,7 +1035,7 @@ browserify-zlib@^0.1.4: dependencies: pako "~0.2.0" -browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.16.3: +browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.16.0, browserslist@^4.16.3: version "4.16.6" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== @@ -1077,24 +1103,10 @@ call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= - dependencies: - caller-callsite "^2.0.0" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase-keys@^2.0.0: version "2.1.0" @@ -1166,7 +1178,7 @@ chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1313,21 +1325,12 @@ clone@^2.1.1, clone@^2.1.2: resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= -coa@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" - integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== - dependencies: - "@types/q" "^1.5.1" - chalk "^2.4.1" - q "^1.1.2" - code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -color-convert@^1.9.0, color-convert@^1.9.1: +color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== @@ -1346,26 +1349,15 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -color-name@^1.0.0, color-name@~1.1.4: +color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^1.5.2: - version "1.5.3" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" - integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/color/-/color-3.1.0.tgz#d8e9fb096732875774c84bf922815df0308d0ffc" - integrity sha512-CwyopLkuRYO5ei2EpzpIh6LqJMt6Mt+jZhO5VI5f/wJLZriXQE32/SSqzmrh+QB+AZT81Cj8yv+7zwToW8ahZg== - dependencies: - color-convert "^1.9.1" - color-string "^1.5.2" +colord@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.0.0.tgz#f8c19f2526b7dc5b22d6e57ef102f03a2a43a3d8" + integrity sha512-WMDFJfoY3wqPZNpKUFdse3HhD5BHCbE9JCdxRzoVH+ywRITGOeWAHNkGEmyxLlErEpN9OLMWgdM9dWQtDk5dog== colorette@^1.2.1, colorette@^1.2.2: version "1.2.2" @@ -1379,6 +1371,11 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +commander@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + component-bind@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" @@ -1492,16 +1489,16 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cosmiconfig@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.1.0.tgz#6c5c35e97f37f985061cdf653f114784231185cf" - integrity sha512-kCNPvthka8gvLtzAxQXvWo4FxqRB+ftRZyPZNuab5ngvM9Y7yw7hbEysglptLgpkGX9nAOKTBVkHUAe8xtYR6Q== +cosmiconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" + integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.9.0" - lodash.get "^4.4.2" - parse-json "^4.0.0" + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" cross-spawn@^3.0.0: version "3.0.1" @@ -1532,17 +1529,21 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== -css-color-names@0.0.4, css-color-names@^0.0.4: +css-color-names@^0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= -css-declaration-sorter@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" - integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== +css-color-names@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-1.0.1.tgz#6ff7ee81a823ad46e020fa2fd6ab40a887e2ba67" + integrity sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA== + +css-declaration-sorter@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.0.3.tgz#9dfd8ea0df4cc7846827876fafb52314890c21a9" + integrity sha512-52P95mvW1SMzuRZegvpluT6yEv0FqQusydKQPZsNN5Q7hh8EwQvN8E2nwuJ16BBvNN6LcoIZXu/Bk58DAhrrxw== dependencies: - postcss "^7.0.1" timsort "^0.3.0" css-parse@~2.0.0: @@ -1552,20 +1553,16 @@ css-parse@~2.0.0: dependencies: css "^2.0.0" -css-select-base-adapter@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" - integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== - -css-select@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.0.2.tgz#ab4386cec9e1f668855564b17c3733b43b2a5ede" - integrity sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ== +css-select@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-3.1.2.tgz#d52cbdc6fee379fba97fb0d3925abbd18af2d9d8" + integrity sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA== dependencies: boolbase "^1.0.0" - css-what "^2.1.2" - domutils "^1.7.0" - nth-check "^1.0.2" + css-what "^4.0.0" + domhandler "^4.0.0" + domutils "^2.4.3" + nth-check "^2.0.0" css-selector-tokenizer@^0.7.0: version "0.7.1" @@ -1576,36 +1573,18 @@ css-selector-tokenizer@^0.7.0: fastparse "^1.1.1" regexpu-core "^1.0.0" -css-tree@1.0.0-alpha.28: - version "1.0.0-alpha.28" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.28.tgz#8e8968190d886c9477bc8d61e96f61af3f7ffa7f" - integrity sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w== +css-tree@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== dependencies: - mdn-data "~1.1.0" - source-map "^0.5.3" + mdn-data "2.0.14" + source-map "^0.6.1" -css-tree@1.0.0-alpha.29: - version "1.0.0-alpha.29" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.29.tgz#3fa9d4ef3142cbd1c301e7664c1f352bd82f5a39" - integrity sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg== - dependencies: - mdn-data "~1.1.0" - source-map "^0.5.3" - -css-unit-converter@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996" - integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY= - -css-url-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/css-url-regex/-/css-url-regex-1.1.0.tgz#83834230cc9f74c457de59eebd1543feeb83b7ec" - integrity sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w= - -css-what@^2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" - integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== +css-what@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-4.0.0.tgz#35e73761cab2eeb3d3661126b23d7aa0e8432233" + integrity sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A== css@^2.0.0: version "2.2.4" @@ -1622,90 +1601,66 @@ cssesc@^0.1.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" integrity sha1-yBSQPkViM3GgR3tAEJqq++6t27Q= -cssesc@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" - integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== - cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -cssnano-preset-default@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76" - integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== +cssnano-preset-default@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.1.tgz#5cd783caed942cc94159aeb10583af4691445b8c" + integrity sha512-kAhR71Tascmnjlhl4UegGA3KGGbMLXHkkqVpA9idsRT1JmIhIsz1C3tDpBeQMUw5EX5Rfb1HGc/PRqD2AFk3Vg== dependencies: - css-declaration-sorter "^4.0.1" - cssnano-util-raw-cache "^4.0.1" - postcss "^7.0.0" - postcss-calc "^7.0.1" - postcss-colormin "^4.0.3" - postcss-convert-values "^4.0.1" - postcss-discard-comments "^4.0.2" - postcss-discard-duplicates "^4.0.2" - postcss-discard-empty "^4.0.1" - postcss-discard-overridden "^4.0.1" - postcss-merge-longhand "^4.0.11" - postcss-merge-rules "^4.0.3" - postcss-minify-font-values "^4.0.2" - postcss-minify-gradients "^4.0.2" - postcss-minify-params "^4.0.2" - postcss-minify-selectors "^4.0.2" - postcss-normalize-charset "^4.0.1" - postcss-normalize-display-values "^4.0.2" - postcss-normalize-positions "^4.0.2" - postcss-normalize-repeat-style "^4.0.2" - postcss-normalize-string "^4.0.2" - postcss-normalize-timing-functions "^4.0.2" - postcss-normalize-unicode "^4.0.1" - postcss-normalize-url "^4.0.1" - postcss-normalize-whitespace "^4.0.2" - postcss-ordered-values "^4.1.2" - postcss-reduce-initial "^4.0.3" - postcss-reduce-transforms "^4.0.2" - postcss-svgo "^4.0.2" - postcss-unique-selectors "^4.0.1" + css-declaration-sorter "^6.0.3" + cssnano-utils "^2.0.1" + postcss-calc "^8.0.0" + postcss-colormin "^5.1.1" + postcss-convert-values "^5.0.1" + postcss-discard-comments "^5.0.1" + postcss-discard-duplicates "^5.0.1" + postcss-discard-empty "^5.0.1" + postcss-discard-overridden "^5.0.1" + postcss-merge-longhand "^5.0.2" + postcss-merge-rules "^5.0.1" + postcss-minify-font-values "^5.0.1" + postcss-minify-gradients "^5.0.1" + postcss-minify-params "^5.0.1" + postcss-minify-selectors "^5.1.0" + postcss-normalize-charset "^5.0.1" + postcss-normalize-display-values "^5.0.1" + postcss-normalize-positions "^5.0.1" + postcss-normalize-repeat-style "^5.0.1" + postcss-normalize-string "^5.0.1" + postcss-normalize-timing-functions "^5.0.1" + postcss-normalize-unicode "^5.0.1" + postcss-normalize-url "^5.0.1" + postcss-normalize-whitespace "^5.0.1" + postcss-ordered-values "^5.0.1" + postcss-reduce-initial "^5.0.1" + postcss-reduce-transforms "^5.0.1" + postcss-svgo "^5.0.1" + postcss-unique-selectors "^5.0.1" -cssnano-util-get-arguments@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" - integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= +cssnano-utils@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-2.0.1.tgz#8660aa2b37ed869d2e2f22918196a9a8b6498ce2" + integrity sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ== -cssnano-util-get-match@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" - integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= - -cssnano-util-raw-cache@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" - integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== +cssnano@^5.0.0: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.0.4.tgz#5ca90729c94c71c4bc3d45abb543be10740bf381" + integrity sha512-I+fDW74CJ4yb31765ov9xXe70XLZvFTXjwhmA//VgAAuSAU34Oblbe94Q9zffiCX1VhcSfQWARQnwhz+Nqgb4Q== dependencies: - postcss "^7.0.0" + cosmiconfig "^7.0.0" + cssnano-preset-default "^5.1.1" + is-resolvable "^1.1.0" -cssnano-util-same-parent@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" - integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== - -cssnano@^4.1.10: - version "4.1.10" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" - integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== +csso@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" + integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== dependencies: - cosmiconfig "^5.0.0" - cssnano-preset-default "^4.0.7" - is-resolvable "^1.0.0" - postcss "^7.0.0" - -csso@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/csso/-/csso-3.5.1.tgz#7b9eb8be61628973c1b261e169d2f024008e758b" - integrity sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg== - dependencies: - css-tree "1.0.0-alpha.29" + css-tree "^1.1.2" currently-unhandled@^0.4.1: version "0.4.1" @@ -1928,33 +1883,35 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk= -dom-serializer@0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" - integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== +dom-serializer@^1.0.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" + integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== dependencies: - domelementtype "^1.3.0" - entities "^1.1.1" + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" -domelementtype@1, domelementtype@^1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== -domutils@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== +domhandler@^4.0.0, domhandler@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" + integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA== dependencies: - dom-serializer "0" - domelementtype "1" + domelementtype "^2.2.0" -dot-prop@^4.1.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.1.tgz#45884194a71fc2cda71cbb4bceb3a4dd2f433ba4" - integrity sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ== +domutils@^2.4.3: + version "2.6.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.6.0.tgz#2e15c04185d43fb16ae7057cb76433c6edb938b7" + integrity sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA== dependencies: - is-obj "^1.0.0" + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" dot-prop@^5.2.0: version "5.2.0" @@ -2102,10 +2059,10 @@ engine.io@~3.5.0: engine.io-parser "~2.2.0" ws "~7.4.2" -entities@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" - integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== errno@^0.1.1: version "0.1.7" @@ -2121,18 +2078,6 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.12.0, es-abstract@^1.5.1: - version "1.13.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" - integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== - dependencies: - es-to-primitive "^1.2.0" - function-bind "^1.1.1" - has "^1.0.3" - is-callable "^1.1.4" - is-regex "^1.0.4" - object-keys "^1.0.12" - es-abstract@^1.17.0-next.1, es-abstract@^1.17.4, es-abstract@^1.17.5: version "1.17.5" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" @@ -2163,15 +2108,6 @@ es-get-iterator@^1.1.0: is-string "^1.0.5" isarray "^2.0.5" -es-to-primitive@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" - integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -2914,7 +2850,7 @@ has-yarn@^2.1.0: resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== -has@^1.0.0, has@^1.0.1, has@^1.0.3: +has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== @@ -2993,11 +2929,6 @@ hsla-regex@^1.0.0: resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= -html-comment-regex@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" - integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== - http-cache-semantics@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -3084,13 +3015,13 @@ immediate@~3.0.5: resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" + parent-module "^1.0.0" + resolve-from "^4.0.0" import-lazy@^2.1.0: version "2.1.0" @@ -3162,10 +3093,10 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -is-absolute-url@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" - integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= +is-absolute-url@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== is-arguments@^1.0.4: version "1.0.4" @@ -3177,11 +3108,6 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - is-bigint@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4" @@ -3221,7 +3147,7 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-color-stop@^1.0.0: +is-color-stop@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= @@ -3255,11 +3181,6 @@ is-deflate@^1.0.0: resolved "https://registry.yarnpkg.com/is-deflate/-/is-deflate-1.0.0.tgz#c862901c3c161fb09dac7cdc7e784f80e98f2f14" integrity sha1-yGKQHDwWH7CdrHzcfnhPgOmPLxQ= -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= - is-docker@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.0.0.tgz#2cb0df0e75e2d064fe1864c37cdeacb7b2dcf25b" @@ -3350,11 +3271,6 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-obj@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= - is-obj@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" @@ -3378,13 +3294,6 @@ is-regex@^1.0.3: call-bind "^1.0.2" has-symbols "^1.0.1" -is-regex@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" - integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= - dependencies: - has "^1.0.1" - is-regex@^1.0.5: version "1.1.0" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff" @@ -3392,7 +3301,7 @@ is-regex@^1.0.5: dependencies: has-symbols "^1.0.1" -is-resolvable@^1.0.0: +is-resolvable@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== @@ -3412,13 +3321,6 @@ is-string@^1.0.4, is-string@^1.0.5: resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== -is-svg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" - integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== - dependencies: - html-comment-regex "^1.1.0" - is-symbol@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" @@ -3543,7 +3445,12 @@ js-stringify@^1.0.1: resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db" integrity sha1-Fzb939lyTyijaCrcYjCufk6Weds= -js-yaml@^3.12.0, js-yaml@^3.13.1, js-yaml@^3.9.0: +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: version "3.14.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== @@ -3576,10 +3483,10 @@ json-buffer@3.0.1: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== -json-parse-better-errors@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" @@ -3740,6 +3647,11 @@ lie@~3.3.0: dependencies: immediate "~3.0.5" +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -4129,10 +4041,10 @@ md5@^2.3.0: crypt "0.0.2" is-buffer "~1.1.6" -mdn-data@~1.1.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-1.1.4.tgz#50b5d4ffc4575276573c4eedb8780812a8419f01" - integrity sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA== +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== media-typer@0.3.0: version "0.3.0" @@ -4248,11 +4160,6 @@ minimatch@^3.0.4, minimatch@~3.0.2: dependencies: brace-expansion "^1.1.7" -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - minimist@^1.1.3, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -4290,13 +4197,6 @@ mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@~0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - moment-timezone@^0.5.28: version "0.5.28" resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.28.tgz#f093d789d091ed7b055d82aa81a82467f72e4338" @@ -4505,16 +4405,16 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= -normalize-url@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" - integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== - normalize-url@^4.1.0: version "4.5.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== +normalize-url@^4.5.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -4532,12 +4432,12 @@ npm-run-path@^2.0.0: gauge "~2.7.3" set-blocking "~2.0.0" -nth-check@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== +nth-check@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125" + integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q== dependencies: - boolbase "~1.0.0" + boolbase "^1.0.0" num2fraction@^1.2.2: version "1.2.2" @@ -4597,24 +4497,6 @@ object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.getownpropertydescriptors@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" - integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= - dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.1" - -object.values@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9" - integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.12.0" - function-bind "^1.1.1" - has "^1.0.3" - omggif@^1.0.5: version "1.0.10" resolved "https://registry.yarnpkg.com/omggif/-/omggif-1.0.10.tgz#ddaaf90d4a42f532e9e7cb3a95ecdd47f17c7b19" @@ -4765,6 +4647,13 @@ parchment@^1.1.4: resolved "https://registry.yarnpkg.com/parchment/-/parchment-1.1.4.tgz#aeded7ab938fe921d4c34bc339ce1168bc2ffde5" integrity sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg== +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parse-data-uri@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/parse-data-uri/-/parse-data-uri-0.2.0.tgz#bf04d851dd5c87b0ab238e5d01ace494b604b4c9" @@ -4779,13 +4668,15 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: + "@babel/code-frame" "^7.0.0" error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" parse-link-header@^1.0.1: version "1.0.1" @@ -4860,6 +4751,11 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + peek-stream@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/peek-stream/-/peek-stream-1.1.3.tgz#3b35d84b7ccbbd262fff31dc10da56856ead6d67" @@ -4938,124 +4834,104 @@ popper.js@^1.16.0: resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== -postcss-calc@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.1.tgz#36d77bab023b0ecbb9789d84dcb23c4941145436" - integrity sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ== +postcss-calc@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.0.0.tgz#a05b87aacd132740a5db09462a3612453e5df90a" + integrity sha512-5NglwDrcbiy8XXfPM11F3HeC6hoT9W7GUH/Zi5U/p7u3Irv4rHhdDcIZwG0llHXV4ftsBjpfWMXAnXNl4lnt8g== dependencies: - css-unit-converter "^1.1.1" - postcss "^7.0.5" - postcss-selector-parser "^5.0.0-rc.4" - postcss-value-parser "^3.3.1" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.0.2" -postcss-colormin@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" - integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== +postcss-colormin@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.1.1.tgz#834d262f6021f832d9085e355f08ade288a92a1d" + integrity sha512-SyTmqKKN6PyYNeeKEC0hqIP5CDuprO1hHurdW1aezDyfofDUOn7y7MaxcolbsW3oazPwFiGiY30XRiW1V4iZpA== dependencies: - browserslist "^4.0.0" - color "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" + browserslist "^4.16.0" + colord "^2.0.0" + postcss-value-parser "^4.1.0" -postcss-convert-values@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" - integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== +postcss-convert-values@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.0.1.tgz#4ec19d6016534e30e3102fdf414e753398645232" + integrity sha512-C3zR1Do2BkKkCgC0g3sF8TS0koF2G+mN8xxayZx3f10cIRmTaAnpgpRQZjNekTZxM2ciSPoh2IWJm0VZx8NoQg== dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" + postcss-value-parser "^4.1.0" -postcss-discard-comments@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" - integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== - dependencies: - postcss "^7.0.0" +postcss-discard-comments@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz#9eae4b747cf760d31f2447c27f0619d5718901fe" + integrity sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg== -postcss-discard-duplicates@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" - integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== - dependencies: - postcss "^7.0.0" +postcss-discard-duplicates@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz#68f7cc6458fe6bab2e46c9f55ae52869f680e66d" + integrity sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA== -postcss-discard-empty@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" - integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== - dependencies: - postcss "^7.0.0" +postcss-discard-empty@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz#ee136c39e27d5d2ed4da0ee5ed02bc8a9f8bf6d8" + integrity sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw== -postcss-discard-overridden@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" - integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== - dependencies: - postcss "^7.0.0" +postcss-discard-overridden@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz#454b41f707300b98109a75005ca4ab0ff2743ac6" + integrity sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q== -postcss-merge-longhand@^4.0.11: - version "4.0.11" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" - integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== +postcss-merge-longhand@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.0.2.tgz#277ada51d9a7958e8ef8cf263103c9384b322a41" + integrity sha512-BMlg9AXSI5G9TBT0Lo/H3PfUy63P84rVz3BjCFE9e9Y9RXQZD3+h3YO1kgTNsNJy7bBc1YQp8DmSnwLIW5VPcw== dependencies: - css-color-names "0.0.4" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - stylehacks "^4.0.0" + css-color-names "^1.0.1" + postcss-value-parser "^4.1.0" + stylehacks "^5.0.1" -postcss-merge-rules@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" - integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== +postcss-merge-rules@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.0.1.tgz#4ff61c5089d86845184a0f149e88d687028bef7e" + integrity sha512-UR6R5Ph0c96QB9TMBH3ml8/kvPCThPHepdhRqAbvMRDRHQACPC8iM5NpfIC03+VRMZTGXy4L/BvFzcDFCgb+fA== dependencies: - browserslist "^4.0.0" + browserslist "^4.16.0" caniuse-api "^3.0.0" - cssnano-util-same-parent "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - vendors "^1.0.0" + cssnano-utils "^2.0.1" + postcss-selector-parser "^6.0.5" + vendors "^1.0.3" -postcss-minify-font-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" - integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== +postcss-minify-font-values@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.0.1.tgz#a90cefbfdaa075bd3dbaa1b33588bb4dc268addf" + integrity sha512-7JS4qIsnqaxk+FXY1E8dHBDmraYFWmuL6cgt0T1SWGRO5bzJf8sUoelwa4P88LEWJZweHevAiDKxHlofuvtIoA== dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" + postcss-value-parser "^4.1.0" -postcss-minify-gradients@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" - integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== +postcss-minify-gradients@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.0.1.tgz#2dc79fd1a1afcb72a9e727bc549ce860f93565d2" + integrity sha512-odOwBFAIn2wIv+XYRpoN2hUV3pPQlgbJ10XeXPq8UY2N+9ZG42xu45lTn/g9zZ+d70NKSQD6EOi6UiCMu3FN7g== dependencies: - cssnano-util-get-arguments "^4.0.0" - is-color-stop "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" + cssnano-utils "^2.0.1" + is-color-stop "^1.1.0" + postcss-value-parser "^4.1.0" -postcss-minify-params@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" - integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== +postcss-minify-params@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.0.1.tgz#371153ba164b9d8562842fdcd929c98abd9e5b6c" + integrity sha512-4RUC4k2A/Q9mGco1Z8ODc7h+A0z7L7X2ypO1B6V8057eVK6mZ6xwz6QN64nHuHLbqbclkX1wyzRnIrdZehTEHw== dependencies: - alphanum-sort "^1.0.0" - browserslist "^4.0.0" - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" + alphanum-sort "^1.0.2" + browserslist "^4.16.0" + cssnano-utils "^2.0.1" + postcss-value-parser "^4.1.0" uniqs "^2.0.0" -postcss-minify-selectors@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" - integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== +postcss-minify-selectors@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.1.0.tgz#4385c845d3979ff160291774523ffa54eafd5a54" + integrity sha512-NzGBXDa7aPsAcijXZeagnJBKBPMYLaJJzB8CQh6ncvyl2sIndLVWfbcDi0SBjRWk5VqEjXvf8tYwzoKf4Z07og== dependencies: - alphanum-sort "^1.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" + alphanum-sort "^1.0.2" + postcss-selector-parser "^6.0.5" postcss-modules-extract-imports@^3.0.0: version "3.0.0" @@ -5127,133 +5003,96 @@ postcss-modules@^4.0.0: postcss-modules-values "^4.0.0" string-hash "^1.1.1" -postcss-normalize-charset@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" - integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== - dependencies: - postcss "^7.0.0" +postcss-normalize-charset@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz#121559d1bebc55ac8d24af37f67bd4da9efd91d0" + integrity sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg== -postcss-normalize-display-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" - integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== +postcss-normalize-display-values@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.1.tgz#62650b965981a955dffee83363453db82f6ad1fd" + integrity sha512-uupdvWk88kLDXi5HEyI9IaAJTE3/Djbcrqq8YgjvAVuzgVuqIk3SuJWUisT2gaJbZm1H9g5k2w1xXilM3x8DjQ== dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" + cssnano-utils "^2.0.1" + postcss-value-parser "^4.1.0" -postcss-normalize-positions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" - integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== +postcss-normalize-positions@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.0.1.tgz#868f6af1795fdfa86fbbe960dceb47e5f9492fe5" + integrity sha512-rvzWAJai5xej9yWqlCb1OWLd9JjW2Ex2BCPzUJrbaXmtKtgfL8dBMOOMTX6TnvQMtjk3ei1Lswcs78qKO1Skrg== dependencies: - cssnano-util-get-arguments "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" + postcss-value-parser "^4.1.0" -postcss-normalize-repeat-style@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" - integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== +postcss-normalize-repeat-style@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.1.tgz#cbc0de1383b57f5bb61ddd6a84653b5e8665b2b5" + integrity sha512-syZ2itq0HTQjj4QtXZOeefomckiV5TaUO6ReIEabCh3wgDs4Mr01pkif0MeVwKyU/LHEkPJnpwFKRxqWA/7O3w== dependencies: - cssnano-util-get-arguments "^4.0.0" - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" + cssnano-utils "^2.0.1" + postcss-value-parser "^4.1.0" -postcss-normalize-string@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" - integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== +postcss-normalize-string@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.0.1.tgz#d9eafaa4df78c7a3b973ae346ef0e47c554985b0" + integrity sha512-Ic8GaQ3jPMVl1OEn2U//2pm93AXUcF3wz+OriskdZ1AOuYV25OdgS7w9Xu2LO5cGyhHCgn8dMXh9bO7vi3i9pA== dependencies: - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" + postcss-value-parser "^4.1.0" -postcss-normalize-timing-functions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" - integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== +postcss-normalize-timing-functions@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.1.tgz#8ee41103b9130429c6cbba736932b75c5e2cb08c" + integrity sha512-cPcBdVN5OsWCNEo5hiXfLUnXfTGtSFiBU9SK8k7ii8UD7OLuznzgNRYkLZow11BkQiiqMcgPyh4ZqXEEUrtQ1Q== dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" + cssnano-utils "^2.0.1" + postcss-value-parser "^4.1.0" -postcss-normalize-unicode@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" - integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== +postcss-normalize-unicode@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.1.tgz#82d672d648a411814aa5bf3ae565379ccd9f5e37" + integrity sha512-kAtYD6V3pK0beqrU90gpCQB7g6AOfP/2KIPCVBKJM2EheVsBQmx/Iof+9zR9NFKLAx4Pr9mDhogB27pmn354nA== dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" + browserslist "^4.16.0" + postcss-value-parser "^4.1.0" -postcss-normalize-url@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" - integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== +postcss-normalize-url@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.0.1.tgz#ffa9fe545935d8b57becbbb7934dd5e245513183" + integrity sha512-hkbG0j58Z1M830/CJ73VsP7gvlG1yF+4y7Fd1w4tD2c7CaA2Psll+pQ6eQhth9y9EaqZSLzamff/D0MZBMbYSg== dependencies: - is-absolute-url "^2.0.0" - normalize-url "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" + is-absolute-url "^3.0.3" + normalize-url "^4.5.0" + postcss-value-parser "^4.1.0" -postcss-normalize-whitespace@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" - integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== +postcss-normalize-whitespace@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.1.tgz#b0b40b5bcac83585ff07ead2daf2dcfbeeef8e9a" + integrity sha512-iPklmI5SBnRvwceb/XH568yyzK0qRVuAG+a1HFUsFRf11lEJTiQQa03a4RSCQvLKdcpX7XsI1Gen9LuLoqwiqA== dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" + postcss-value-parser "^4.1.0" -postcss-ordered-values@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" - integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== +postcss-ordered-values@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.0.1.tgz#79ef6e2bd267ccad3fc0c4f4a586dfd01c131f64" + integrity sha512-6mkCF5BQ25HvEcDfrMHCLLFHlraBSlOXFnQMHYhSpDO/5jSR1k8LdEXOkv+7+uzW6o6tBYea1Km0wQSRkPJkwA== dependencies: - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" + cssnano-utils "^2.0.1" + postcss-value-parser "^4.1.0" -postcss-reduce-initial@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" - integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== +postcss-reduce-initial@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.0.1.tgz#9d6369865b0f6f6f6b165a0ef5dc1a4856c7e946" + integrity sha512-zlCZPKLLTMAqA3ZWH57HlbCjkD55LX9dsRyxlls+wfuRfqCi5mSlZVan0heX5cHr154Dq9AfbH70LyhrSAezJw== dependencies: - browserslist "^4.0.0" + browserslist "^4.16.0" caniuse-api "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" -postcss-reduce-transforms@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" - integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== +postcss-reduce-transforms@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.1.tgz#93c12f6a159474aa711d5269923e2383cedcf640" + integrity sha512-a//FjoPeFkRuAguPscTVmRQUODP+f3ke2HqFNgGPwdYnpeC29RZdCBvGRGTsKpMURb/I3p6jdKoBQ2zI+9Q7kA== dependencies: - cssnano-util-get-match "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-selector-parser@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865" - integrity sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU= - dependencies: - dot-prop "^4.1.1" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-selector-parser@^5.0.0-rc.4: - version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c" - integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== - dependencies: - cssesc "^2.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" + cssnano-utils "^2.0.1" + postcss-value-parser "^4.1.0" postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: version "6.0.4" @@ -5265,31 +5104,32 @@ postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: uniq "^1.0.1" util-deprecate "^1.0.2" -postcss-svgo@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" - integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw== +postcss-selector-parser@^6.0.5: + version "6.0.6" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea" + integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg== dependencies: - is-svg "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - svgo "^1.0.0" + cssesc "^3.0.0" + util-deprecate "^1.0.2" -postcss-unique-selectors@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" - integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== +postcss-svgo@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.0.1.tgz#6ed5e01e164e59204978994d844c653a331a8100" + integrity sha512-cD7DFo6tF9i5eWvwtI4irKOHCpmASFS0xvZ5EQIgEdA1AWfM/XiHHY/iss0gcKHhkqwgYmuo2M0KhJLd5Us6mg== dependencies: - alphanum-sort "^1.0.0" - postcss "^7.0.0" + postcss-value-parser "^4.1.0" + svgo "^2.3.0" + +postcss-unique-selectors@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.0.1.tgz#3be5c1d7363352eff838bd62b0b07a0abad43bfc" + integrity sha512-gwi1NhHV4FMmPn+qwBNuot1sG1t2OmacLQ/AX29lzyggnjd+MnVD5uqQmpXO3J17KGL2WAxQruj1qTd3H0gG/w== + dependencies: + alphanum-sort "^1.0.2" + postcss-selector-parser "^6.0.5" uniqs "^2.0.0" -postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" - integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== - -postcss-value-parser@^4.1.0: +postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== @@ -5322,7 +5162,7 @@ postcss@^6.0.1: source-map "^0.6.1" supports-color "^5.4.0" -postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.5: +postcss@^7.0.14: version "7.0.14" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.14.tgz#4527ed6b1ca0d82c53ce5ec1a2041c2346bbd6e5" integrity sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg== @@ -5554,11 +5394,6 @@ pupa@^2.0.1: dependencies: escape-goat "^2.0.0" -q@^1.1.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= - qs@6.7.0, qs@^6.5.1: version "6.7.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" @@ -5924,10 +5759,10 @@ resolve-file@^0.3.0: lazy-cache "^2.0.2" resolve "^1.2.0" -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha1-six699nWiBvItuZTM17rywoYh0g= +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-url@^0.2.1: version "0.2.1" @@ -6217,13 +6052,6 @@ signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= - dependencies: - is-arrayish "^0.3.1" - snyk-config@4.0.0, snyk-config@^4.0.0-rc.2: version "4.0.0" resolved "https://registry.yarnpkg.com/snyk-config/-/snyk-config-4.0.0.tgz#21d459f19087991246cc07a7ffb4501dce6f4159" @@ -6677,7 +6505,7 @@ source-map@^0.4.2: dependencies: amdefine ">=0.0.4" -source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: +source-map@^0.5.6, source-map@~0.5.1: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -6935,14 +6763,13 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -stylehacks@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" - integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== +stylehacks@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.0.1.tgz#323ec554198520986806388c7fdaebc38d2c06fb" + integrity sha512-Es0rVnHIqbWzveU1b24kbw92HsebBepxfcqe5iix7t9j0PQqhs0IxXVXv0pY2Bxa08CgMkzD6OWql7kbGOuEdA== dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" + browserslist "^4.16.0" + postcss-selector-parser "^6.0.4" stylus@^0.54.5, stylus@^0.x: version "0.54.8" @@ -7007,25 +6834,18 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -svgo@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.2.0.tgz#305a8fc0f4f9710828c65039bb93d5793225ffc3" - integrity sha512-xBfxJxfk4UeVN8asec9jNxHiv3UAMv/ujwBWGYvQhhMb2u3YTGKkiybPcLFDLq7GLLWE9wa73e0/m8L5nTzQbw== +svgo@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.3.0.tgz#6b3af81d0cbd1e19c83f5f63cec2cb98c70b5373" + integrity sha512-fz4IKjNO6HDPgIQxu4IxwtubtbSfGEAJUq/IXyTPIkGhWck/faiiwfkvsB8LnBkKLvSoyNNIY6d13lZprJMc9Q== dependencies: - chalk "^2.4.1" - coa "^2.0.2" - css-select "^2.0.0" - css-select-base-adapter "^0.1.1" - css-tree "1.0.0-alpha.28" - css-url-regex "^1.1.0" - csso "^3.5.1" - js-yaml "^3.12.0" - mkdirp "~0.5.1" - object.values "^1.1.0" - sax "~1.2.4" + "@trysound/sax" "0.1.1" + chalk "^4.1.0" + commander "^7.1.0" + css-select "^3.1.2" + css-tree "^1.1.2" + csso "^4.2.0" stable "^0.1.8" - unquote "~1.1.1" - util.promisify "~1.0.0" tar-stream@^2.1.0: version "2.1.0" @@ -7332,11 +7152,6 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= -unquote@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" - integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= - update-notifier@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.1.tgz#895fc8562bbe666179500f9f2cebac4f26323746" @@ -7390,14 +7205,6 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util.promisify@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" - integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== - dependencies: - define-properties "^1.1.2" - object.getownpropertydescriptors "^2.0.3" - utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" @@ -7436,10 +7243,10 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= -vendors@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.2.tgz#7fcb5eef9f5623b156bcea89ec37d63676f21801" - integrity sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ== +vendors@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" + integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== verror@1.10.0: version "1.10.0" @@ -7672,6 +7479,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@^1.10.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + yaml@^1.9.2: version "1.10.0" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" From 646612be09439eed5d05a3d66ac081553f139733 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 26 May 2021 10:40:16 +0530 Subject: [PATCH 117/495] fix: Colocated asset load --- frappe/templates/test/_test_base.html | 13 ++++++++++++- frappe/website/doctype/web_page/test_web_page.py | 15 ++++++--------- frappe/website/page_renderers/template_page.py | 6 ++++-- frappe/www/_test/_test_folder/_test_page.css | 3 +++ frappe/www/_test/_test_folder/_test_page.js | 1 + 5 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 frappe/www/_test/_test_folder/_test_page.css create mode 100644 frappe/www/_test/_test_folder/_test_page.js diff --git a/frappe/templates/test/_test_base.html b/frappe/templates/test/_test_base.html index a0b1a83c97..5a88584a5d 100644 --- a/frappe/templates/test/_test_base.html +++ b/frappe/templates/test/_test_base.html @@ -1,8 +1,19 @@ - + + {%- block style %} + {% if colocated_css -%} + + {%- endif %} + {%- endblock -%} +

This is for testing

{% block content %}{% endblock %} + {%- block script %} + {% if colocated_js -%} + + {%- endif %} + {%- endblock %} diff --git a/frappe/website/doctype/web_page/test_web_page.py b/frappe/website/doctype/web_page/test_web_page.py index b9bd425781..b5da51cca6 100644 --- a/frappe/website/doctype/web_page/test_web_page.py +++ b/frappe/website/doctype/web_page/test_web_page.py @@ -104,18 +104,15 @@ class TestWebPage(unittest.TestCase): self.assertTrue('Next: Test Toc' \ in frappe.as_unicode(content)) - def test_meta_tags(self): - pass + def test_colocated_assets(self): + content = get_response_content('/_test/_test_folder/_test_page') + self.assertTrue("" \ + in frappe.as_unicode(content)) + self.assertTrue("background-color: var(--bg-color);" \ + in frappe.as_unicode(content)) def test_breadcrumbs(self): pass def test_downloadable_file(self): pass - - - - -# breadcrumb -# validate -# page context caching diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index d8032c6b9c..682c3cda39 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -42,6 +42,7 @@ class TemplatePage(BaseTemplatePage): if os.path.exists(file_path) and not os.path.isdir(file_path): self.app = app self.app_path = app_path + self.basename = os.path.splitext(file_path)[0] self.template_path = os.path.relpath(file_path, self.app_path) self.basepath = os.path.dirname(file_path) return @@ -110,6 +111,7 @@ class TemplatePage(BaseTemplatePage): def update_context(self): self.context.base_template = self.context.base_template or get_base_template(self.path) self.context.basepath = self.basepath + self.context.basename = self.basename self.context.path = self.path self.set_page_properties() self.set_properties_from_source() @@ -197,11 +199,11 @@ class TemplatePage(BaseTemplatePage): def load_colocated_files(self): '''load co-located css/js files with the same name''' - js_path = self.basepath + '.js' + js_path = self.basename + '.js' if os.path.exists(js_path) and '{% block script %}' not in self.source: self.context.colocated_js = self.get_colocated_file(js_path) - css_path = self.basepath + '.css' + css_path = self.basename + '.css' if os.path.exists(css_path) and '{% block style %}' not in self.source: self.context.colocated_css = self.get_colocated_file(css_path) diff --git a/frappe/www/_test/_test_folder/_test_page.css b/frappe/www/_test/_test_folder/_test_page.css new file mode 100644 index 0000000000..e42b809085 --- /dev/null +++ b/frappe/www/_test/_test_folder/_test_page.css @@ -0,0 +1,3 @@ +body { + background-color: var(--bg-color); +} \ No newline at end of file diff --git a/frappe/www/_test/_test_folder/_test_page.js b/frappe/www/_test/_test_folder/_test_page.js new file mode 100644 index 0000000000..6e0c1f3a87 --- /dev/null +++ b/frappe/www/_test/_test_folder/_test_page.js @@ -0,0 +1 @@ +console.log('test data'); \ No newline at end of file From 40bd818c0b6e29c3b682db45fe083d1abd60d42c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 26 May 2021 10:40:43 +0530 Subject: [PATCH 118/495] test: Add attributes via frontmatter --- frappe/www/_test/_test_folder/_test_toc.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frappe/www/_test/_test_folder/_test_toc.md b/frappe/www/_test/_test_folder/_test_toc.md index 8d05b120a5..02cc3c82be 100644 --- a/frappe/www/_test/_test_folder/_test_toc.md +++ b/frappe/www/_test/_test_folder/_test_toc.md @@ -1,3 +1,13 @@ +--- +title: Test TOC +add_breadcrumbs: 1 +show_sidebar: 0 + +metatags: + description: Test Description. + keywords: Frappe Framework. +--- + # Level 1 ## Level 1.1 From e19c18e5a9840ed68c4c29ccf5ecb09df03af137 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 26 May 2021 11:03:33 +0530 Subject: [PATCH 119/495] refactor: Move context value setting code to a separate function --- frappe/website/page_renderers/template_page.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index 682c3cda39..2b326d7383 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -45,6 +45,8 @@ class TemplatePage(BaseTemplatePage): self.basename = os.path.splitext(file_path)[0] self.template_path = os.path.relpath(file_path, self.app_path) self.basepath = os.path.dirname(file_path) + self.filename = os.path.basename(file_path) + self.name = os.path.splitext(self.filename)[0] return def can_render(self): @@ -109,10 +111,6 @@ class TemplatePage(BaseTemplatePage): self.convert_from_markdown() def update_context(self): - self.context.base_template = self.context.base_template or get_base_template(self.path) - self.context.basepath = self.basepath - self.context.basename = self.basename - self.context.path = self.path self.set_page_properties() self.set_properties_from_source() self.load_colocated_files() @@ -138,8 +136,15 @@ class TemplatePage(BaseTemplatePage): self.context[prop] = getattr(self.pymodule, prop) def set_page_properties(self): + self.context.base_template = self.context.base_template \ + or get_base_template(self.path) \ + or 'templates/web.html' + self.context.basepath = self.basepath + self.context.basename = self.basename + self.context.name = self.name + self.context.path = self.path + self.context.route = self.path self.context.template = self.template_path - self.context.base_template = self.context.base_template or 'templates/web.html' def set_properties_from_source(self): if not self.source: From 38fdfc369a1a1dd0b22bda2f2eb03e10543e2b6a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 26 May 2021 11:51:12 +0530 Subject: [PATCH 120/495] test: Use assertIn for better error message --- .../website/doctype/web_page/test_web_page.py | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/frappe/website/doctype/web_page/test_web_page.py b/frappe/website/doctype/web_page/test_web_page.py index b5da51cca6..d213fd57a8 100644 --- a/frappe/website/doctype/web_page/test_web_page.py +++ b/frappe/website/doctype/web_page/test_web_page.py @@ -28,10 +28,10 @@ class TestWebPage(unittest.TestCase): content = get_page_content('/_test/_test_custom_base.html') # assert the text in base template is rendered - self.assertTrue('

This is for testing

' in frappe.as_unicode(content)) + self.assertIn('

This is for testing

', frappe.as_unicode(content)) # assert template block rendered - self.assertTrue('

Test content

' in frappe.as_unicode(content)) + self.assertIn('

Test content

', frappe.as_unicode(content)) def test_content_type(self): web_page = frappe.get_doc(dict( @@ -44,15 +44,15 @@ class TestWebPage(unittest.TestCase): main_section_html = '
html content
' )).insert() - self.assertTrue('rich text' in get_page_content('/test-content-type')) + self.assertIn('rich text', get_page_content('/test-content-type')) web_page.content_type = 'Markdown' web_page.save() - self.assertTrue('markdown content' in get_page_content('/test-content-type')) + self.assertIn('markdown content', get_page_content('/test-content-type')) web_page.content_type = 'HTML' web_page.save() - self.assertTrue('html content' in get_page_content('/test-content-type')) + self.assertIn('html content', get_page_content('/test-content-type')) web_page.delete() @@ -70,46 +70,47 @@ class TestWebPage(unittest.TestCase): try: content = get_page_content('/doctype-view/DocField') - self.assertTrue('
DocField
' in content) + self.assertIn('
DocField
', content) finally: web_page.delete() def test_custom_base_template_path(self): content = get_response_content('/_test/_test_folder/_test_page') # assert the text in base template is rendered - self.assertTrue('

This is for testing

' in frappe.as_unicode(content)) + self.assertIn('

This is for testing

', frappe.as_unicode(content)) # assert template block rendered - self.assertTrue('

Test content

' in frappe.as_unicode(content)) + self.assertIn('

Test content

', frappe.as_unicode(content)) def test_json_sidebar_data(self): frappe.flags.look_for_sidebar = False content = get_response_content('/_test/_test_folder/_test_page') - self.assertTrue('Test Sidebar' not in frappe.as_unicode(content)) + self.assertNotIn('Test Sidebar', frappe.as_unicode(content)) frappe.flags.look_for_sidebar = True content = get_response_content('/_test/_test_folder/_test_page') - self.assertTrue('Test Sidebar' in frappe.as_unicode(content)) + self.assertIn('Test Sidebar', frappe.as_unicode(content)) frappe.flags.look_for_sidebar = False def test_index_and_next_comment(self): content = get_response_content('/_test/_test_folder') # test if {index} was rendered - self.assertTrue(' Test Page' \ - in frappe.as_unicode(content)) - self.assertTrue(' Test Toc' \ - in frappe.as_unicode(content)) + self.assertIn(' Test Page', + frappe.as_unicode(content)) + + self.assertIn('Test TOC', + frappe.as_unicode(content)) content = get_response_content('/_test/_test_folder/_test_page') # test if {next} was rendered - self.assertTrue('Next: Test Toc' \ - in frappe.as_unicode(content)) + self.assertIn('Next: Test TOC', + frappe.as_unicode(content)) def test_colocated_assets(self): content = get_response_content('/_test/_test_folder/_test_page') - self.assertTrue("" \ - in frappe.as_unicode(content)) - self.assertTrue("background-color: var(--bg-color);" \ - in frappe.as_unicode(content)) + self.assertIn("", + frappe.as_unicode(content)) + self.assertIn("background-color: var(--bg-color);", + frappe.as_unicode(content)) def test_breadcrumbs(self): pass From 33a97196e141e42bf99116f336fe8f65c7672a30 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 26 May 2021 11:51:34 +0530 Subject: [PATCH 121/495] fix: Init page properties in webpage --- frappe/website/page_renderers/web_page.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/website/page_renderers/web_page.py b/frappe/website/page_renderers/web_page.py index 5c3807c0ee..6e738e8df9 100644 --- a/frappe/website/page_renderers/web_page.py +++ b/frappe/website/page_renderers/web_page.py @@ -8,6 +8,9 @@ class WebPage(object): path = frappe.local.request.path self.path = path.strip('/ ') self.basepath = '' + self.basename = '' + self.name = '' + self.route = '' def can_render(self): pass From e8cf96d1d3718a874fabed676f07be20703c7876 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 26 May 2021 13:21:37 +0530 Subject: [PATCH 122/495] fix: Replace cmp with different logic cmp was being used from past.builtins library since it was deprecated in PY2. It's hard to understand behaviour of their usages, so this is an attempt to replicate behaviour with simpler logic, making this more readable. Also, removed usages of iteritems and string_types, compatibility imports --- frappe/__init__.py | 1 - frappe/contacts/doctype/address/address.py | 17 +++++++---------- frappe/contacts/doctype/contact/contact.py | 15 +++++++-------- frappe/website/utils.py | 9 ++------- 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 9b208f7c2d..c915ce70a0 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -21,7 +21,6 @@ if _dev_server: from werkzeug.local import Local, release_local import sys, importlib, inspect, json import typing -from past.builtins import cmp import click # Local application imports diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py index 84b925d50e..9aced0dacb 100644 --- a/frappe/contacts/doctype/address/address.py +++ b/frappe/contacts/doctype/address/address.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import throw, _ @@ -10,15 +9,10 @@ from frappe.utils import cstr from frappe.model.document import Document from jinja2 import TemplateSyntaxError -from frappe.utils.user import is_website_user from frappe.model.naming import make_autoname from frappe.core.doctype.dynamic_link.dynamic_link import deduplicate_dynamic_links -from six import iteritems, string_types -from past.builtins import cmp from frappe.contacts.address_and_contact import set_link_title -import functools - class Address(Document): def __setup__(self): @@ -112,10 +106,13 @@ def get_default_address(doctype, name, sort_key='is_primary_address'): WHERE dl.parent = addr.name and dl.link_doctype = %s and dl.link_name = %s and ifnull(addr.disabled, 0) = 0 - """ %(sort_key, '%s', '%s'), (doctype, name)) + """ %(sort_key, '%s', '%s'), (doctype, name), as_dict=True) if out: - return sorted(out, key = functools.cmp_to_key(lambda x,y: cmp(y[1], x[1])))[0][0] + for contact in out: + if contact.get(sort_key): + return contact.parent + return out[0].parent else: return None @@ -141,7 +138,7 @@ def get_territory_from_address(address): if not address: return - if isinstance(address, string_types): + if isinstance(address, str): address = frappe.get_cached_doc("Address", address) territory = None @@ -214,7 +211,7 @@ def address_query(doctype, txt, searchfield, start, page_len, filters): condition = "" meta = frappe.get_meta("Address") - for fieldname, value in iteritems(filters): + for fieldname, value in filters.items(): if meta.get_field(fieldname) or fieldname in frappe.db.DEFAULT_COLUMNS: condition += " and {field}={value}".format( field=fieldname, diff --git a/frappe/contacts/doctype/contact/contact.py b/frappe/contacts/doctype/contact/contact.py index b3d4c6fc5c..c8af99bebc 100644 --- a/frappe/contacts/doctype/contact/contact.py +++ b/frappe/contacts/doctype/contact/contact.py @@ -1,18 +1,14 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe -from frappe.utils import cstr, has_gravatar, cint +from frappe.utils import cstr, has_gravatar from frappe import _ from frappe.model.document import Document from frappe.core.doctype.dynamic_link.dynamic_link import deduplicate_dynamic_links -from six import iteritems -from past.builtins import cmp from frappe.model.naming import append_number_if_name_exists from frappe.contacts.address_and_contact import set_link_title -import functools class Contact(Document): def autoname(self): @@ -120,7 +116,7 @@ class Contact(Document): if len(is_primary) > 1: frappe.throw(_("Only one {0} can be set as primary.").format(frappe.bold(frappe.unscrub(fieldname)))) - primary_number_exists = False + primary_number_exists = False for d in self.phone_nos: if d.get(field_name) == 1: primary_number_exists = True @@ -140,10 +136,13 @@ def get_default_contact(doctype, name): where dl.link_doctype=%s and dl.link_name=%s and - dl.parenttype = "Contact"''', (doctype, name)) + dl.parenttype = "Contact"''', (doctype, name), as_dict=True) if out: - return sorted(out, key = functools.cmp_to_key(lambda x,y: cmp(cint(y[1]), cint(x[1]))))[0][0] + for contact in out: + if contact.is_primary_contact: + return contact.parent + return out[0].parent else: return None diff --git a/frappe/website/utils.py b/frappe/website/utils.py index efa15bdb14..0738c836f3 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -1,14 +1,10 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals -import functools import re import os import frappe -from six import iteritems -from past.builtins import cmp from frappe.utils import md_to_html @@ -293,7 +289,7 @@ def get_full_index(route=None, app=None): pages = get_pages(app=app) # make children map - for route, page_info in iteritems(pages): + for route, page_info in pages.items(): parent_route = os.path.dirname(route) if parent_route not in added: children_map.setdefault(parent_route, []).append(page_info) @@ -316,8 +312,7 @@ def get_full_index(route=None, app=None): added.append(child_route) # add remaining pages not in index.txt - _children = sorted(children, key = functools.cmp_to_key(lambda a, b: cmp( - os.path.basename(a.route), os.path.basename(b.route)))) + _children = sorted(children, key=lambda x: os.path.basename(x.route)) for child_route in _children: if child_route not in new_children: From e407b785065873c2566a5f07ffde6d6e806079b1 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 26 May 2021 15:31:29 +0530 Subject: [PATCH 123/495] chore: Drop dead and deprecated code * Remove six for PY2 compatability since our dependencies are not, PY2 is legacy. * Removed usages of utils from future/past libraries since they are deprecated. This includes 'from __future__ ...' and 'from past...' statements. * Removed compatibility imports for PY2, switched from six imports to standard library imports. * Removed utils code blocks that handle operations depending on PY2/3 versions. * Removed 'from __future__ ...' lines from templates/code generators * Used PY3 syntaxes in place of PY2 compatible blocks. eg: metaclass --- frappe/api.py | 1 - frappe/app.py | 7 ++- frappe/auth.py | 6 +-- .../assignment_rule/assignment_rule.py | 2 - .../assignment_rule/test_assignment_rule.py | 2 - .../assignment_rule_day.py | 1 - .../assignment_rule_user.py | 1 - .../doctype/auto_repeat/auto_repeat.py | 1 - .../doctype/auto_repeat/test_auto_repeat.py | 2 - .../auto_repeat_day/auto_repeat_day.py | 1 - .../automation/doctype/milestone/milestone.py | 2 - .../doctype/milestone/test_milestone.py | 2 - .../milestone_tracker/milestone_tracker.py | 2 - .../test_milestone_tracker.py | 2 - frappe/boot.py | 9 +--- frappe/build.py | 4 +- frappe/cache_manager.py | 2 - frappe/chat/__init__.py | 2 +- .../chat/doctype/chat_message/chat_message.py | 2 - .../chat/doctype/chat_profile/chat_profile.py | 2 - frappe/chat/doctype/chat_room/chat_room.py | 2 - .../doctype/chat_room_user/chat_room_user.py | 2 - frappe/chat/doctype/chat_token/chat_token.py | 1 - frappe/chat/util/__init__.py | 2 - frappe/chat/util/test_util.py | 5 +-- frappe/chat/util/util.py | 2 - frappe/chat/website/__init__.py | 2 +- frappe/client.py | 19 ++++---- frappe/commands/__init__.py | 3 +- frappe/commands/scheduler.py | 1 - frappe/commands/translate.py | 1 - frappe/commands/utils.py | 2 +- frappe/config/__init__.py | 3 -- frappe/contacts/address_and_contact.py | 1 - .../contacts/doctype/address/test_address.py | 2 - .../address_template/address_template.py | 1 - .../address_template/test_address_template.py | 4 +- frappe/contacts/doctype/contact/contact.py | 1 - .../contacts/doctype/contact/test_contact.py | 2 - .../doctype/contact_email/contact_email.py | 1 - .../doctype/contact_phone/contact_phone.py | 1 - frappe/contacts/doctype/gender/gender.py | 1 - frappe/contacts/doctype/gender/test_gender.py | 2 - .../contacts/doctype/salutation/salutation.py | 1 - .../doctype/salutation/test_salutation.py | 2 - .../addresses_and_contacts.py | 5 +-- .../test_addresses_and_contacts.py | 2 +- frappe/core/__init__.py | 2 - frappe/core/doctype/__init__.py | 1 - frappe/core/doctype/access_log/access_log.py | 2 - .../core/doctype/activity_log/activity_log.py | 1 - frappe/core/doctype/activity_log/feed.py | 4 +- .../doctype/activity_log/test_activity_log.py | 2 - .../core/doctype/block_module/block_module.py | 1 - frappe/core/doctype/comment/comment.py | 2 - frappe/core/doctype/comment/test_comment.py | 2 - .../doctype/communication/communication.py | 3 +- frappe/core/doctype/communication/email.py | 11 ++--- .../communication/test_communication.py | 4 +- .../communication_link/communication_link.py | 1 - .../doctype/custom_docperm/custom_docperm.py | 1 - .../custom_docperm/test_custom_docperm.py | 2 - .../core/doctype/custom_role/custom_role.py | 1 - .../doctype/custom_role/test_custom_role.py | 2 - .../core/doctype/data_export/data_export.py | 1 - frappe/core/doctype/data_export/exporter.py | 5 +-- frappe/core/doctype/data_import/importer.py | 1 - .../doctype/data_import/test_data_import.py | 2 - .../core/doctype/data_import/test_exporter.py | 2 - .../core/doctype/data_import/test_importer.py | 2 - .../doctype/data_import_legacy/importer.py | 8 +--- .../test_data_import_legacy.py | 2 - frappe/core/doctype/defaultvalue/__init__.py | 1 - .../core/doctype/defaultvalue/defaultvalue.py | 1 - .../deleted_document/deleted_document.py | 1 - .../deleted_document/test_deleted_document.py | 2 - frappe/core/doctype/docfield/__init__.py | 1 - frappe/core/doctype/docfield/docfield.py | 2 - frappe/core/doctype/docperm/__init__.py | 1 - frappe/core/doctype/docperm/docperm.py | 1 - frappe/core/doctype/docshare/docshare.py | 1 - frappe/core/doctype/docshare/test_docshare.py | 1 - frappe/core/doctype/doctype/__init__.py | 1 - frappe/core/doctype/doctype/doctype.py | 9 +--- frappe/core/doctype/doctype/test_doctype.py | 2 - .../doctype/doctype_action/doctype_action.py | 1 - .../core/doctype/doctype_link/doctype_link.py | 1 - .../document_naming_rule.py | 1 - .../test_document_naming_rule.py | 2 - .../document_naming_rule_condition.py | 1 - .../test_document_naming_rule_condition.py | 2 - frappe/core/doctype/domain/domain.py | 1 - frappe/core/doctype/domain/test_domain.py | 2 - .../domain_settings/domain_settings.py | 1 - .../core/doctype/dynamic_link/dynamic_link.py | 1 - frappe/core/doctype/error_log/error_log.py | 1 - .../core/doctype/error_log/test_error_log.py | 2 - .../doctype/error_snapshot/error_snapshot.py | 1 - .../error_snapshot/test_error_snapshot.py | 2 - frappe/core/doctype/file/file.py | 42 ++++++++--------- frappe/core/doctype/file/test_file.py | 2 - frappe/core/doctype/has_domain/has_domain.py | 1 - frappe/core/doctype/has_role/has_role.py | 1 - .../installed_application.py | 1 - .../installed_applications.py | 1 - .../test_installed_applications.py | 2 - frappe/core/doctype/language/language.py | 1 - frappe/core/doctype/language/test_language.py | 2 - .../log_setting_user/log_setting_user.py | 1 - .../log_setting_user/test_log_setting_user.py | 2 - .../core/doctype/log_settings/log_settings.py | 1 - .../doctype/log_settings/test_log_settings.py | 2 - frappe/core/doctype/module_def/__init__.py | 1 - frappe/core/doctype/module_def/module_def.py | 1 - .../doctype/module_def/test_module_def.py | 2 - .../doctype/module_profile/module_profile.py | 1 - .../module_profile/test_module_profile.py | 1 - .../core/doctype/navbar_item/navbar_item.py | 1 - .../doctype/navbar_item/test_navbar_item.py | 2 - .../navbar_settings/navbar_settings.py | 1 - .../navbar_settings/test_navbar_settings.py | 2 - frappe/core/doctype/page/__init__.py | 1 - frappe/core/doctype/page/page.py | 2 - frappe/core/doctype/page/test_page.py | 2 - frappe/core/doctype/patch_log/patch_log.py | 1 - .../core/doctype/patch_log/test_patch_log.py | 2 - .../payment_gateway/payment_gateway.py | 1 - .../payment_gateway/test_payment_gateway.py | 2 - .../prepared_report/prepared_report.py | 2 - .../prepared_report/test_prepared_report.py | 2 - frappe/core/doctype/report/__init__.py | 1 - frappe/core/doctype/report/report.py | 5 +-- frappe/core/doctype/report/test_report.py | 1 - .../doctype/report_column/report_column.py | 1 - .../doctype/report_filter/report_filter.py | 1 - frappe/core/doctype/role/__init__.py | 1 - frappe/core/doctype/role/role.py | 1 - frappe/core/doctype/role/test_role.py | 2 - .../role_permission_for_page_and_report.py | 1 - .../core/doctype/role_profile/role_profile.py | 1 - .../doctype/role_profile/test_role_profile.py | 1 - .../scheduled_job_log/scheduled_job_log.py | 1 - .../test_scheduled_job_log.py | 2 - .../test_scheduled_job_type.py | 2 - .../doctype/server_script/server_script.py | 2 - .../server_script/test_server_script.py | 2 - .../session_default/session_default.py | 1 - .../session_default_settings.py | 1 - .../test_session_default_settings.py | 2 - frappe/core/doctype/sms_parameter/__init__.py | 2 +- .../doctype/sms_parameter/sms_parameter.py | 1 - frappe/core/doctype/sms_settings/__init__.py | 2 +- .../core/doctype/sms_settings/sms_settings.py | 11 ++--- .../doctype/sms_settings/test_sms_settings.py | 2 - .../doctype/success_action/success_action.py | 1 - .../system_settings/system_settings.py | 1 - .../system_settings/test_system_settings.py | 2 - frappe/core/doctype/test/test.py | 1 - frappe/core/doctype/test/test_test.py | 2 - .../transaction_log/test_transaction_log.py | 4 +- .../transaction_log/transaction_log.py | 1 - .../doctype/translation/test_translation.py | 2 - .../core/doctype/translation/translation.py | 1 - frappe/core/doctype/user/test_user.py | 2 - frappe/core/doctype/user/user.py | 4 -- .../user_document_type/user_document_type.py | 1 - frappe/core/doctype/user_email/user_email.py | 1 - .../doctype/user_group/test_user_group.py | 2 - frappe/core/doctype/user_group/user_group.py | 1 - .../test_user_group_member.py | 2 - .../user_group_member/user_group_member.py | 1 - .../user_permission/test_user_permission.py | 1 - .../user_permission/user_permission.py | 1 - .../user_select_document_type.py | 1 - .../user_social_login/user_social_login.py | 1 - .../core/doctype/user_type/test_user_type.py | 2 - frappe/core/doctype/user_type/user_type.py | 4 +- .../doctype/user_type/user_type_dashboard.py | 2 +- .../user_type_module/user_type_module.py | 1 - frappe/core/doctype/version/test_version.py | 2 - frappe/core/doctype/version/version.py | 1 - frappe/core/doctype/view_log/test_view_log.py | 6 +-- frappe/core/doctype/view_log/view_log.py | 1 - frappe/core/notifications.py | 1 - frappe/core/page/__init__.py | 1 - .../permission_manager/permission_manager.py | 1 - .../permitted_documents_for_user.py | 1 - .../transaction_log_report.py | 1 - frappe/core/utils.py | 1 - .../web_form/edit_profile/edit_profile.py | 2 - .../custom/doctype/client_script/__init__.py | 1 - .../doctype/client_script/client_script.py | 1 - .../client_script/test_client_script.py | 2 - .../custom/doctype/custom_field/__init__.py | 1 - .../doctype/custom_field/custom_field.py | 1 - .../doctype/custom_field/test_custom_field.py | 2 - .../custom/doctype/customize_form/__init__.py | 1 - .../doctype/customize_form/customize_form.py | 1 - .../customize_form/test_customize_form.py | 1 - .../doctype/customize_form_field/__init__.py | 1 - .../customize_form_field.py | 1 - .../doctype/doctype_layout/doctype_layout.py | 2 - .../doctype_layout/test_doctype_layout.py | 2 - .../doctype_layout_field.py | 1 - .../doctype/property_setter/__init__.py | 1 - .../property_setter/property_setter.py | 1 - .../property_setter/test_property_setter.py | 2 - .../test_rename_new/test_rename_new.py | 1 - .../test_rename_new/test_test_rename_new.py | 2 - .../connectors/base.py | 5 +-- .../connectors/frappe_connection.py | 2 +- .../data_migration_connector.py | 4 +- .../test_data_migration_connector.py | 1 - .../data_migration_mapping.py | 1 - .../test_data_migration_mapping.py | 1 - .../data_migration_mapping_detail.py | 1 - .../data_migration_plan.py | 1 - .../test_data_migration_plan.py | 1 - .../data_migration_plan_mapping.py | 1 - .../data_migration_run/data_migration_run.py | 1 - .../test_data_migration_run.py | 1 - frappe/database/__init__.py | 2 - frappe/database/database.py | 27 ++++------- frappe/database/mariadb/schema.py | 2 - frappe/database/mariadb/setup_db.py | 2 - frappe/database/postgres/database.py | 3 +- frappe/database/postgres/setup_db.py | 1 - frappe/database/schema.py | 2 - frappe/defaults.py | 1 - frappe/desk/__init__.py | 1 - frappe/desk/calendar.py | 2 - frappe/desk/desk_page.py | 1 - frappe/desk/desktop.py | 4 +- .../desk/doctype/bulk_update/bulk_update.py | 1 - .../doctype/calendar_view/calendar_view.py | 1 - .../desk/doctype/console_log/console_log.py | 1 - .../doctype/console_log/test_console_log.py | 2 - frappe/desk/doctype/dashboard/dashboard.py | 1 - .../desk/doctype/dashboard/test_dashboard.py | 2 - .../dashboard_chart/dashboard_chart.py | 1 - .../dashboard_chart/test_dashboard_chart.py | 2 - .../dashboard_chart_field.py | 1 - .../dashboard_chart_link.py | 1 - .../dashboard_chart_source.py | 1 - .../test_dashboard_chart_source.py | 2 - .../dashboard_settings/dashboard_settings.py | 1 - .../desk/doctype/desktop_icon/desktop_icon.py | 9 ++-- frappe/desk/doctype/event/__init__.py | 1 - frappe/desk/doctype/event/event.py | 8 ++-- frappe/desk/doctype/event/test_event.py | 2 - .../event_participants/event_participants.py | 1 - .../global_search_doctype.py | 1 - .../global_search_settings.py | 1 - .../desk/doctype/kanban_board/kanban_board.py | 4 +- .../doctype/kanban_board/test_kanban_board.py | 2 - .../kanban_board_column.py | 1 - .../desk/doctype/list_filter/list_filter.py | 1 - .../list_view_settings/list_view_settings.py | 1 - .../test_list_view_settings.py | 2 - .../module_onboarding/module_onboarding.py | 1 - .../test_module_onboarding.py | 2 - frappe/desk/doctype/note/note.py | 1 - frappe/desk/doctype/note/test_note.py | 1 - .../desk/doctype/note_seen_by/note_seen_by.py | 1 - .../notification_log/notification_log.py | 1 - .../notification_log/test_notification_log.py | 2 - .../notification_settings.py | 1 - .../notification_subscribed_document.py | 1 - .../desk/doctype/number_card/number_card.py | 1 - .../doctype/number_card/test_number_card.py | 2 - .../number_card_link/number_card_link.py | 1 - .../onboarding_permission.py | 1 - .../test_onboarding_permission.py | 2 - .../onboarding_step/onboarding_step.py | 1 - .../onboarding_step/test_onboarding_step.py | 2 - .../onboarding_step_map.py | 1 - .../doctype/route_history/route_history.py | 1 - .../doctype/system_console/system_console.py | 2 - .../system_console/test_system_console.py | 2 - frappe/desk/doctype/tag/tag.py | 1 - frappe/desk/doctype/tag/test_tag.py | 2 - frappe/desk/doctype/tag_link/tag_link.py | 1 - frappe/desk/doctype/tag_link/test_tag_link.py | 2 - frappe/desk/doctype/todo/__init__.py | 1 - frappe/desk/doctype/todo/test_todo.py | 2 - frappe/desk/doctype/todo/todo.py | 5 +-- .../desk/doctype/workspace/test_workspace.py | 2 - frappe/desk/doctype/workspace/workspace.py | 1 - .../workspace_chart/workspace_chart.py | 1 - .../doctype/workspace_link/workspace_link.py | 1 - .../workspace_shortcut/workspace_shortcut.py | 1 - frappe/desk/form/__init__.py | 1 - frappe/desk/form/assign_to.py | 1 - frappe/desk/form/document_follow.py | 1 - frappe/desk/form/linked_with.py | 8 ++-- frappe/desk/form/load.py | 3 +- frappe/desk/form/meta.py | 9 +--- frappe/desk/form/save.py | 1 - frappe/desk/form/test_form.py | 1 - frappe/desk/form/utils.py | 4 +- frappe/desk/gantt.py | 2 - frappe/desk/leaderboard.py | 2 - frappe/desk/like.py | 2 - frappe/desk/listview.py | 2 - frappe/desk/moduleview.py | 1 - frappe/desk/notifications.py | 7 +-- frappe/desk/page/activity/__init__.py | 2 +- frappe/desk/page/activity/activity.py | 1 - frappe/desk/page/backups/backups.py | 2 +- frappe/desk/page/leaderboard/leaderboard.py | 2 - .../page/setup_wizard/install_fixtures.py | 2 - frappe/desk/page/setup_wizard/setup_wizard.py | 9 ++-- frappe/desk/query_report.py | 21 ++++----- frappe/desk/report/todo/todo.py | 1 - frappe/desk/report_dump.py | 3 +- frappe/desk/reportview.py | 22 +++++---- frappe/desk/search.py | 4 +- frappe/desk/treeview.py | 1 - frappe/email/__init__.py | 1 - .../auto_email_report/auto_email_report.py | 2 - .../test_auto_email_report.py | 2 - .../document_follow/document_follow.py | 1 - .../document_follow/test_document_follow.py | 2 - .../doctype/email_account/email_account.py | 2 - .../email_account/test_email_account.py | 1 - .../doctype/email_domain/email_domain.py | 1 - .../doctype/email_domain/test_email_domain.py | 2 - .../email_flag_queue/email_flag_queue.py | 1 - .../email_flag_queue/test_email_flag_queue.py | 2 - .../email/doctype/email_group/email_group.py | 1 - .../doctype/email_group/test_email_group.py | 2 - .../email_group_member/email_group_member.py | 1 - .../test_email_group_member.py | 2 - .../doctype/email_queue/test_email_queue.py | 2 - .../email_queue_recipient.py | 1 - frappe/email/doctype/email_rule/email_rule.py | 1 - .../doctype/email_rule/test_email_rule.py | 2 - .../doctype/email_template/email_template.py | 6 +-- .../email_template/test_email_template.py | 2 - .../email_unsubscribe/email_unsubscribe.py | 1 - .../test_email_unsubscribe.py | 2 - frappe/email/doctype/newsletter/newsletter.py | 2 - .../doctype/newsletter/test_newsletter.py | 2 - .../newsletter_email_group.py | 1 - .../doctype/notification/notification.py | 8 +--- .../doctype/notification/test_notification.py | 2 - .../notification_recipient.py | 1 - .../unhandled_email/test_unhandled_email.py | 2 - .../unhandled_email/unhandled_email.py | 1 - frappe/email/email_body.py | 12 +++-- frappe/email/inbox.py | 2 +- frappe/email/queue.py | 10 ++--- frappe/email/receive.py | 10 ++--- frappe/email/smtp.py | 1 - frappe/email/test_email_body.py | 18 ++------ frappe/email/utils.py | 2 - .../document_type_field_mapping.py | 1 - .../document_type_mapping.py | 9 ++-- .../test_document_type_mapping.py | 2 - .../doctype/event_consumer/event_consumer.py | 3 +- .../event_consumer/test_event_consumer.py | 2 - .../event_consumer_document_type.py | 1 - .../event_producer/test_event_producer.py | 6 +-- .../event_producer_document_type.py | 1 - .../event_producer_last_update.py | 1 - .../test_event_producer_last_update.py | 2 - .../doctype/event_sync_log/event_sync_log.py | 1 - .../event_sync_log/test_event_sync_log.py | 2 - .../event_update_log/event_update_log.py | 3 +- .../event_update_log/test_event_update_log.py | 2 - .../event_update_log_consumer.py | 1 - frappe/exceptions.py | 1 - frappe/frappeclient.py | 6 +-- frappe/geo/country_info.py | 2 - frappe/geo/doctype/country/__init__.py | 2 +- frappe/geo/doctype/country/country.py | 1 - frappe/geo/doctype/country/test_country.py | 1 - frappe/geo/doctype/currency/__init__.py | 2 +- frappe/geo/doctype/currency/currency.py | 1 - frappe/geo/doctype/currency/test_currency.py | 1 - frappe/geo/utils.py | 2 - frappe/handler.py | 2 - frappe/hooks.py | 2 +- .../braintree_settings/braintree_settings.py | 3 +- .../test_braintree_settings.py | 2 - .../connected_app/test_connected_app.py | 2 - .../dropbox_settings/test_dropbox_settings.py | 2 - .../doctype/google_drive/test_google_drive.py | 2 - .../google_settings/google_settings.py | 1 - .../integration_request.py | 6 +-- .../test_integration_request.py | 2 - .../ldap_group_mapping/ldap_group_mapping.py | 1 - .../doctype/ldap_settings/ldap_settings.py | 1 - .../ldap_settings/test_ldap_settings.py | 2 - .../oauth_authorization_code.py | 1 - .../test_oauth_authorization_code.py | 2 - .../oauth_bearer_token/oauth_bearer_token.py | 1 - .../test_oauth_bearer_token.py | 2 - .../doctype/oauth_client/oauth_client.py | 1 - .../doctype/oauth_client/test_oauth_client.py | 2 - .../oauth_provider_settings.py | 1 - .../doctype/oauth_scope/oauth_scope.py | 1 - .../paypal_settings/paypal_settings.py | 3 +- .../doctype/paytm_settings/paytm_settings.py | 5 +-- .../paytm_settings/test_paytm_settings.py | 2 - .../query_parameters/query_parameters.py | 1 - .../razorpay_settings/razorpay_settings.py | 3 +- .../s3_backup_settings/s3_backup_settings.py | 2 - .../test_s3_backup_settings.py | 2 - .../slack_webhook_url/slack_webhook_url.py | 1 - .../test_slack_webhook_url.py | 2 - .../social_login_key/social_login_key.py | 1 - .../social_login_key/test_social_login_key.py | 2 - .../social_login_keys/social_login_keys.py | 1 - .../stripe_settings/stripe_settings.py | 3 +- .../stripe_settings/test_stripe_settings.py | 2 - .../doctype/token_cache/test_token_cache.py | 2 - .../doctype/token_cache/token_cache.py | 1 - .../integrations/doctype/webhook/__init__.py | 4 +- .../doctype/webhook/test_webhook.py | 6 +-- .../integrations/doctype/webhook/webhook.py | 4 +- .../doctype/webhook_data/webhook_data.py | 1 - .../doctype/webhook_header/webhook_header.py | 1 - frappe/integrations/oauth2_logins.py | 3 +- frappe/integrations/offsite_backup_utils.py | 1 - frappe/integrations/utils.py | 10 ++--- frappe/middlewares.py | 2 - frappe/migrate.py | 2 - frappe/model/__init__.py | 1 - frappe/model/base_document.py | 20 ++++----- frappe/model/create_new.py | 1 - frappe/model/db_query.py | 23 ++++------ frappe/model/delete_doc.py | 4 +- frappe/model/docfield.py | 1 - frappe/model/document.py | 11 ++--- frappe/model/dynamic_links.py | 1 - frappe/model/mapper.py | 4 +- frappe/model/meta.py | 3 -- frappe/model/naming.py | 14 +++--- frappe/model/rename_doc.py | 3 -- frappe/model/sync.py | 2 - frappe/model/utils/__init__.py | 5 --- frappe/model/utils/link_count.py | 5 +-- frappe/model/utils/rename_field.py | 3 -- frappe/model/utils/user_settings.py | 7 ++- frappe/model/workflow.py | 4 +- frappe/modules/__init__.py | 2 +- frappe/modules/export_file.py | 2 - frappe/modules/import_file.py | 3 -- frappe/modules/patch_handler.py | 5 --- frappe/modules/utils.py | 2 - frappe/monitor.py | 2 - ..._chat_by_default_within_system_settings.py | 2 +- frappe/patches/v10_0/enhance_security.py | 2 - .../increase_single_table_column_length.py | 2 +- .../v10_0/migrate_passwords_passlib.py | 2 +- .../v10_0/modify_naming_series_table.py | 2 - .../modify_smallest_currency_fraction.py | 1 - .../v10_0/refactor_social_login_keys.py | 2 - .../v10_0/reload_countries_and_currencies.py | 2 +- ...remove_custom_field_for_disabled_domain.py | 2 +- .../patches/v10_0/set_default_locking_time.py | 1 - .../v10_0/set_no_copy_to_workflow_state.py | 2 +- .../v11_0/change_email_signature_fieldtype.py | 1 - .../v11_0/copy_fetch_data_from_options.py | 2 +- .../patches/v11_0/create_contact_for_user.py | 2 +- .../v11_0/delete_all_prepared_reports.py | 2 +- .../delete_duplicate_user_permissions.py | 2 +- .../drop_column_apply_user_permissions.py | 2 +- .../v11_0/fix_order_by_in_reports_json.py | 2 +- ...all_prepared_report_attachments_private.py | 2 +- ...igrate_report_settings_for_new_listview.py | 2 +- .../v11_0/multiple_references_in_events.py | 2 +- .../v11_0/reload_and_rename_view_log.py | 2 +- ...pe_user_permissions_for_page_and_report.py | 1 - .../patches/v11_0/remove_skip_for_doctype.py | 2 +- .../rename_email_alert_to_notification.py | 2 +- .../v11_0/rename_google_maps_doctype.py | 2 +- ...rename_standard_reply_to_email_template.py | 2 +- ...rkflow_action_to_workflow_action_master.py | 2 +- .../v11_0/replicate_old_user_permissions.py | 2 +- .../set_allow_self_approval_in_workflow.py | 2 +- .../v11_0/set_default_letter_head_source.py | 2 - .../patches/v11_0/set_dropbox_file_backup.py | 2 +- .../sync_stripe_settings_before_migrate.py | 2 +- ..._user_permission_doctype_before_migrate.py | 2 +- .../v11_0/update_list_user_settings.py | 2 +- .../create_notification_settings_for_user.py | 2 +- frappe/patches/v12_0/init_desk_settings.py | 2 - .../move_timeline_links_to_dynamic_links.py | 2 - .../setup_comments_from_communications.py | 2 - frappe/patches/v12_0/setup_email_linking.py | 2 - ..._auto_repeat_status_and_not_submittable.py | 2 +- .../v13_0/add_standard_navbar_items.py | 2 +- .../add_switch_theme_to_navbar_settings.py | 2 +- .../add_toggle_width_in_navbar_settings.py | 2 +- frappe/patches/v13_0/cleanup_desk_cards.py | 13 +++--- ...delete_event_producer_and_consumer_keys.py | 1 - .../v13_0/delete_package_publish_tool.py | 1 - frappe/patches/v13_0/enable_custom_script.py | 1 - .../generate_theme_files_in_public_folder.py | 1 - frappe/patches/v13_0/jinja_hook.py | 1 - frappe/patches/v13_0/queryreport_columns.py | 1 - .../v13_0/remove_duplicate_navbar_items.py | 2 +- .../remove_tailwind_from_page_builder.py | 1 - ...list_view_setting_to_list_view_settings.py | 1 - .../v13_0/rename_notification_fields.py | 1 - frappe/patches/v13_0/rename_onboarding.py | 1 - .../patches/v13_0/replace_old_data_import.py | 1 - .../update_date_filters_in_user_settings.py | 2 +- .../patches/v13_0/update_duration_options.py | 1 - .../update_icons_in_customized_desk_pages.py | 2 +- .../v13_0/update_newsletter_content_type.py | 1 - .../update_notification_channel_if_empty.py | 1 - .../patches/v13_0/web_template_set_module.py | 1 - frappe/patches/v4_0/add_delete_permission.py | 8 ++-- frappe/patches/v4_0/change_varchar_length.py | 1 - .../create_custom_field_for_owner_match.py | 2 - .../patches/v4_0/deprecate_control_panel.py | 1 - frappe/patches/v4_0/deprecate_link_selects.py | 1 - .../enable_scheduler_in_system_settings.py | 1 - frappe/patches/v4_0/file_manager_hooks.py | 4 -- .../patches/v4_0/fix_attach_field_file_url.py | 1 - frappe/patches/v4_0/private_backups.py | 1 - frappe/patches/v4_0/remove_index_sitemap.py | 2 +- frappe/patches/v4_0/remove_old_parent.py | 1 - .../v4_0/remove_user_owner_custom_field.py | 1 - frappe/patches/v4_0/rename_profile_to_user.py | 2 +- .../patches/v4_0/rename_sitemap_to_route.py | 2 +- .../v4_0/replace_deprecated_timezones.py | 1 - frappe/patches/v4_0/set_module_in_report.py | 1 - .../v4_0/set_todo_checked_as_closed.py | 2 +- frappe/patches/v4_0/set_user_gravatar.py | 1 - frappe/patches/v4_0/set_user_permissions.py | 1 - frappe/patches/v4_0/set_website_route_idx.py | 2 +- .../v4_0/update_custom_field_insert_after.py | 1 - frappe/patches/v4_0/update_datetime.py | 2 +- frappe/patches/v4_0/webnotes_to_frappe.py | 2 +- .../patches/v4_0/website_sitemap_hierarchy.py | 2 - .../v4_1/enable_outgoing_email_settings.py | 1 - frappe/patches/v4_1/enable_print_as_pdf.py | 1 - frappe/patches/v4_1/file_manager_fix.py | 8 +--- frappe/patches/v4_2/print_with_letterhead.py | 1 - .../patches/v4_2/refactor_website_routing.py | 2 +- frappe/patches/v4_2/set_assign_in_doc.py | 2 +- .../remove_allow_on_submit_customization.py | 1 - frappe/patches/v5_0/bookmarks_to_stars.py | 5 +-- .../clear_website_group_and_notifications.py | 2 +- frappe/patches/v5_0/communication_parent.py | 2 +- .../v5_0/convert_to_barracuda_and_utf8mb4.py | 2 +- .../patches/v5_0/expire_old_scheduler_logs.py | 2 +- frappe/patches/v5_0/fix_email_alert.py | 2 - frappe/patches/v5_0/fix_null_date_datetime.py | 2 +- .../patches/v5_0/fix_text_editor_file_urls.py | 1 - frappe/patches/v5_0/force_sync_website.py | 2 +- frappe/patches/v5_0/modify_session.py | 2 +- ...scheduler_last_event_to_system_settings.py | 2 +- .../patches/v5_0/remove_shopping_cart_app.py | 2 - .../v5_0/rename_ref_type_fieldnames.py | 1 - .../patches/v5_0/rename_table_fieldnames.py | 1 - .../v5_0/style_settings_to_website_theme.py | 2 +- frappe/patches/v5_0/update_shared.py | 2 +- frappe/patches/v5_0/v4_to_v5.py | 1 - .../patches/v5_2/change_checks_to_not_null.py | 2 +- .../patches/v5_3/rename_chinese_languages.py | 1 - .../communication_status_and_permission.py | 2 +- frappe/patches/v6_0/document_type_rename.py | 4 +- frappe/patches/v6_0/fix_ghana_currency.py | 2 - frappe/patches/v6_0/make_task_log_folder.py | 2 +- frappe/patches/v6_1/rename_file_data.py | 1 - .../v6_11/rename_field_in_email_account.py | 2 +- ...move_property_setter_for_previous_field.py | 1 - frappe/patches/v6_15/set_username.py | 2 +- frappe/patches/v6_16/feed_doc_owner.py | 2 +- frappe/patches/v6_16/star_to_like.py | 2 +- .../v6_19/comment_feed_communication.py | 2 +- .../ignore_user_permissions_if_missing.py | 2 +- frappe/patches/v6_2/rename_backup_manager.py | 2 +- .../v6_20x/remove_roles_from_website_user.py | 2 +- .../v6_20x/set_allow_draft_for_print.py | 2 +- frappe/patches/v6_20x/update_insert_after.py | 2 +- .../print_settings_repeat_header_footer.py | 2 +- frappe/patches/v6_24/set_language_as_code.py | 2 +- frappe/patches/v6_4/reduce_varchar_length.py | 1 - .../patches/v6_4/rename_bengali_language.py | 1 - frappe/patches/v6_6/fix_file_url.py | 2 +- frappe/patches/v6_6/rename_slovak_language.py | 1 - frappe/patches/v6_6/user_last_active.py | 2 +- frappe/patches/v6_9/int_float_not_null.py | 2 +- .../patches/v6_9/rename_burmese_language.py | 1 - .../patches/v7_0/add_communication_in_doc.py | 2 +- frappe/patches/v7_0/cleanup_list_settings.py | 12 ++--- .../v7_0/create_private_file_folder.py | 2 +- frappe/patches/v7_0/re_route.py | 2 +- .../v7_0/rename_bulk_email_to_email_queue.py | 2 +- .../rename_newsletter_list_to_email_group.py | 2 +- frappe/patches/v7_0/set_email_group.py | 3 +- frappe/patches/v7_0/set_user_fullname.py | 4 +- frappe/patches/v7_0/update_auth.py | 2 +- .../v7_0/update_report_builder_json.py | 1 - .../v7_0/update_send_after_in_bulk_email.py | 2 +- ..._print_settings_for_custom_print_format.py | 13 +++--- .../v7_1/refactor_integration_broker.py | 9 ++-- .../v7_1/rename_chinese_language_codes.py | 2 +- .../v7_1/rename_scheduler_log_to_error_log.py | 2 +- frappe/patches/v7_1/set_backup_limit.py | 2 +- .../v7_1/setup_integration_services.py | 2 +- frappe/patches/v7_1/sync_language_doctype.py | 2 +- .../patches/v7_2/fix_email_queue_recipient.py | 2 +- frappe/patches/v7_2/merge_knowledge_base.py | 2 +- frappe/patches/v7_2/remove_in_filter.py | 2 +- frappe/patches/v7_2/set_doctype_engine.py | 2 +- .../v7_2/set_in_standard_filter_property.py | 2 +- frappe/patches/v7_2/setup_custom_perms.py | 2 +- frappe/patches/v7_2/setup_ldap_config.py | 2 +- frappe/patches/v7_2/update_communications.py | 2 +- .../patches/v7_2/update_feedback_request.py | 2 +- .../v8_0/deprecate_integration_broker.py | 6 +-- frappe/patches/v8_0/drop_in_dialog.py | 2 +- .../v8_0/drop_is_custom_from_docperm.py | 2 +- frappe/patches/v8_0/drop_unwanted_indexes.py | 5 +-- .../install_new_build_system_requirements.py | 1 - .../v8_0/newsletter_childtable_migrate.py | 3 +- .../rename_listsettings_to_usersettings.py | 5 +-- .../v8_0/rename_page_role_to_has_role.py | 3 +- .../patches/v8_0/rename_print_to_printing.py | 4 +- frappe/patches/v8_0/set_allow_traceback.py | 2 +- .../v8_0/set_currency_field_precision.py | 1 - .../v8_0/set_doctype_values_in_custom_role.py | 5 +-- ...set_user_permission_for_page_and_report.py | 9 ++-- frappe/patches/v8_0/setup_email_inbox.py | 2 +- .../v8_0/update_gender_and_salutation.py | 1 - .../v8_0/update_global_search_table.py | 2 +- .../v8_0/update_published_in_global_search.py | 2 +- .../v8_0/update_records_in_global_search.py | 4 +- ...te_custom_docperm_if_doctype_not_exists.py | 2 +- ...llow_error_traceback_in_system_settings.py | 1 - ...ate_format_options_in_auto_email_report.py | 1 - ...lete_static_web_page_from_global_search.py | 2 +- ..._email_group_member_with_invalid_emails.py | 1 - frappe/patches/v8_5/patch_event_colors.py | 2 +- frappe/patches/v8_x/update_user_permission.py | 2 +- frappe/patches/v9_1/__init__.py | 2 +- .../v9_1/add_sms_sender_name_as_parameters.py | 1 - .../patches/v9_1/move_feed_to_activity_log.py | 2 +- frappe/patches/v9_1/resave_domain_settings.py | 2 +- frappe/patches/v9_1/revert_domain_settings.py | 2 +- frappe/permissions.py | 12 +++-- .../doctype/letter_head/letter_head.py | 3 +- .../doctype/letter_head/test_letter_head.py | 2 - .../doctype/print_format/print_format.py | 1 - .../doctype/print_format/test_print_format.py | 2 - .../doctype/print_heading/print_heading.py | 1 - .../print_heading/test_print_heading.py | 2 - .../doctype/print_settings/print_settings.py | 1 - .../print_settings/test_print_settings.py | 2 - .../doctype/print_style/print_style.py | 1 - .../doctype/print_style/test_print_style.py | 2 - frappe/pythonrc.py | 2 - frappe/rate_limiter.py | 2 - frappe/recorder.py | 2 - frappe/sessions.py | 7 +-- frappe/share.py | 1 - .../energy_point_log/energy_point_log.py | 1 - .../energy_point_log/test_energy_point_log.py | 2 - .../energy_point_rule/energy_point_rule.py | 1 - .../energy_point_settings.py | 1 - frappe/social/doctype/post/post.py | 1 - .../doctype/post_comment/post_comment.py | 1 - .../doctype/review_level/review_level.py | 1 - .../templates/includes/comments/comments.py | 2 - .../pages/integrations/braintree_checkout.py | 1 - .../pages/integrations/payment_cancel.py | 1 - .../pages/integrations/payment_success.py | 2 - .../pages/integrations/paytm_checkout.py | 1 - .../pages/integrations/razorpay_checkout.py | 4 +- .../pages/integrations/stripe_checkout.py | 1 - frappe/test_runner.py | 9 ++-- frappe/tests/test_assign.py | 2 - frappe/tests/test_auth.py | 2 - frappe/tests/test_bot.py | 1 - frappe/tests/test_client.py | 2 - frappe/tests/test_cors.py | 2 - frappe/tests/test_db.py | 2 - frappe/tests/test_db_query.py | 2 - frappe/tests/test_defaults.py | 2 - frappe/tests/test_document.py | 2 - frappe/tests/test_document_locks.py | 2 - frappe/tests/test_domainification.py | 2 - frappe/tests/test_dynamic_links.py | 2 - frappe/tests/test_email.py | 8 +--- frappe/tests/test_exporter_fixtures.py | 2 - frappe/tests/test_fmt_datetime.py | 2 - frappe/tests/test_fmt_money.py | 2 - frappe/tests/test_form_load.py | 2 - frappe/tests/test_geo_ip.py | 2 - frappe/tests/test_global_search.py | 1 - frappe/tests/test_goal.py | 1 - frappe/tests/test_hooks.py | 5 +-- frappe/tests/test_listview.py | 2 - frappe/tests/test_monitor.py | 1 - frappe/tests/test_naming.py | 1 - frappe/tests/test_password.py | 1 - frappe/tests/test_patches.py | 2 +- frappe/tests/test_pdf.py | 6 +-- frappe/tests/test_permissions.py | 2 - frappe/tests/test_query_report.py | 2 - frappe/tests/test_rate_limiter.py | 2 - frappe/tests/test_recorder.py | 1 - frappe/tests/test_safe_exec.py | 2 +- frappe/tests/test_scheduler.py | 2 - frappe/tests/test_search.py | 1 - frappe/tests/test_seen.py | 2 - frappe/tests/test_sitemap.py | 2 - frappe/tests/test_translate.py | 2 - frappe/tests/test_translation.py | 2 - frappe/tests/test_twofactor.py | 2 - frappe/tests/test_utils.py | 24 +++++----- frappe/tests/test_website.py | 2 - frappe/tests/tests_geo_utils.py | 2 - frappe/translate.py | 37 +++++---------- frappe/twofactor.py | 12 ++--- frappe/utils/__init__.py | 2 +- frappe/utils/background_jobs.py | 7 +-- frappe/utils/bench_helper.py | 1 - frappe/utils/boilerplate.py | 3 -- frappe/utils/bot.py | 2 - frappe/utils/change_log.py | 1 - frappe/utils/connections.py | 2 +- frappe/utils/csvutils.py | 18 +++----- frappe/utils/dashboard.py | 1 - frappe/utils/data.py | 45 +++++++++---------- frappe/utils/dateutils.py | 4 +- frappe/utils/doctor.py | 4 +- frappe/utils/error.py | 8 ++-- frappe/utils/file_lock.py | 2 - frappe/utils/file_manager.py | 38 +++++++--------- frappe/utils/formatters.py | 4 +- frappe/utils/global_search.py | 7 +-- frappe/utils/goal.py | 1 - frappe/utils/html_utils.py | 9 ++-- frappe/utils/identicon.py | 4 +- frappe/utils/image.py | 8 ++-- frappe/utils/install.py | 3 -- frappe/utils/jinja.py | 2 - frappe/utils/jinja_globals.py | 1 - frappe/utils/logger.py | 8 +--- frappe/utils/make_random.py | 6 +-- frappe/utils/minify.py | 4 +- frappe/utils/momentjs.py | 1 - frappe/utils/nestedset.py | 2 - frappe/utils/oauth.py | 6 +-- frappe/utils/password.py | 1 - frappe/utils/password_strength.py | 2 - frappe/utils/pdf.py | 5 --- frappe/utils/print_format.py | 2 - frappe/utils/redis_wrapper.py | 12 +++-- frappe/utils/reset_doc.py | 4 +- frappe/utils/response.py | 8 ++-- frappe/utils/scheduler.py | 2 - frappe/utils/testutils.py | 2 - frappe/utils/user.py | 2 - frappe/utils/verified_command.py | 7 +-- frappe/website/context.py | 1 - frappe/website/doctype/__init__.py | 2 +- .../about_us_settings/about_us_settings.py | 5 +-- .../test_about_us_settings.py | 2 - .../about_us_team_member.py | 1 - .../doctype/blog_category/blog_category.py | 1 - .../blog_category/test_blog_category.py | 2 - frappe/website/doctype/blog_post/blog_post.py | 2 - .../doctype/blog_post/test_blog_post.py | 1 - .../doctype/blog_settings/blog_settings.py | 3 +- .../blog_settings/test_blog_settings.py | 2 - frappe/website/doctype/blogger/blogger.py | 1 - .../website/doctype/blogger/test_blogger.py | 2 - frappe/website/doctype/color/color.py | 1 - frappe/website/doctype/color/test_color.py | 2 - .../company_history/company_history.py | 1 - .../contact_us_settings.py | 1 - .../doctype/help_article/help_article.py | 1 - .../doctype/help_article/test_help_article.py | 2 - .../doctype/help_category/help_category.py | 1 - .../help_category/test_help_category.py | 2 - .../test_personal_data_deletion_request.py | 2 - .../personal_data_deletion_step.py | 1 - .../personal_data_download_request.py | 1 - .../test_personal_data_download_request.py | 2 - .../portal_menu_item/portal_menu_item.py | 1 - .../portal_settings/portal_settings.py | 1 - .../portal_settings/test_portal_settings.py | 2 - .../social_link_settings.py | 1 - .../website/doctype/top_bar_item/__init__.py | 2 +- .../doctype/top_bar_item/top_bar_item.py | 1 - .../website/doctype/web_form/test_web_form.py | 2 - frappe/website/doctype/web_form/web_form.py | 12 +---- .../doctype/web_form_field/web_form_field.py | 1 - frappe/website/doctype/web_page/__init__.py | 2 +- .../website/doctype/web_page/test_web_page.py | 2 +- frappe/website/doctype/web_page/web_page.py | 2 - .../doctype/web_page_block/web_page_block.py | 2 - .../web_page_view/test_web_page_view.py | 2 - .../doctype/web_page_view/web_page_view.py | 1 - .../doctype/web_template/test_web_template.py | 2 - .../doctype/web_template/web_template.py | 2 - .../test_web_template_field.py | 2 - .../web_template_field/web_template_field.py | 1 - .../website_meta_tag/website_meta_tag.py | 1 - .../test_website_route_meta.py | 2 - .../website_route_meta/website_route_meta.py | 1 - .../website_route_redirect.py | 1 - .../doctype/website_script/website_script.py | 1 - .../doctype/website_settings/__init__.py | 2 +- .../website_settings/test_website_settings.py | 2 - .../website_settings/website_settings.py | 3 +- .../website_sidebar/test_website_sidebar.py | 2 - .../website_sidebar/website_sidebar.py | 1 - .../website_sidebar_item.py | 1 - .../test_website_slideshow.py | 2 - .../website_slideshow/website_slideshow.py | 1 - .../website_slideshow_item.py | 1 - .../website_theme/test_website_theme.py | 2 - .../doctype/website_theme/website_theme.py | 1 - .../website_theme_ignore_app.py | 1 - frappe/website/page/__init__.py | 2 +- frappe/website/purifycss.py | 1 - frappe/website/redirect.py | 2 - frappe/website/render.py | 7 +-- .../website_analytics/website_analytics.py | 1 - frappe/website/router.py | 2 - frappe/website/utils.py | 1 - .../web_form/request_data/request_data.py | 2 - .../request_to_delete_data.py | 2 - frappe/website/website_generator.py | 1 - .../doctype/workflow/test_workflow.py | 2 - frappe/workflow/doctype/workflow/workflow.py | 1 - .../workflow_action/workflow_action.py | 1 - .../workflow_action_master.py | 1 - .../workflow_document_state.py | 1 - .../workflow_state/test_workflow_state.py | 2 - .../doctype/workflow_state/workflow_state.py | 1 - .../workflow_transition.py | 1 - frappe/www/404.py | 2 - frappe/www/__init__.py | 2 +- frappe/www/about.py | 1 - frappe/www/app.py | 3 -- frappe/www/complete_signup.py | 2 - frappe/www/contact.py | 2 - frappe/www/error.py | 2 - frappe/www/list.py | 1 - frappe/www/login.py | 1 - frappe/www/me.py | 1 - frappe/www/message.py | 1 - frappe/www/printview.py | 17 +++---- frappe/www/profile.py | 2 - frappe/www/qrcode.py | 4 +- frappe/www/robots.py | 2 +- frappe/www/rss.py | 3 +- frappe/www/search.py | 2 +- frappe/www/sitemap.py | 9 ++-- frappe/www/third_party_apps.py | 2 +- frappe/www/unsubscribe.py | 2 +- frappe/www/update_password.py | 2 - frappe/www/website_script.py | 1 - setup.py | 2 - 865 files changed, 527 insertions(+), 1718 deletions(-) diff --git a/frappe/api.py b/frappe/api.py index 6427cbfbd8..36d51e894c 100644 --- a/frappe/api.py +++ b/frappe/api.py @@ -1,6 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - import base64 import binascii import json diff --git a/frappe/app.py b/frappe/app.py index 64befdf531..6f5023be93 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import os -from six import iteritems import logging from werkzeug.local import LocalManager @@ -191,8 +189,9 @@ def make_form_dict(request): frappe.throw(_("Invalid request arguments")) try: - frappe.local.form_dict = frappe._dict({ k:v[0] if isinstance(v, (list, tuple)) else v \ - for k, v in iteritems(args) }) + frappe.local.form_dict = frappe._dict({ + k: v[0] if isinstance(v, (list, tuple)) else v for k, v in args.items() + }) except IndexError: frappe.local.form_dict = frappe._dict(args) diff --git a/frappe/auth.py b/frappe/auth.py index 73cb8e8c15..ef79d96ddb 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -1,9 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals import datetime - from frappe import _ import frappe import frappe.database @@ -19,8 +16,7 @@ from frappe.core.doctype.activity_log.activity_log import add_authentication_log from frappe.twofactor import (should_run_2fa, authenticate_for_2factor, confirm_otp_token, get_cached_user_pass) from frappe.website.utils import get_home_page - -from six.moves.urllib.parse import quote +from urllib.parse import quote class HTTPRequest: diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.py b/frappe/automation/doctype/assignment_rule/assignment_rule.py index c673d5ceeb..ef579aca01 100644 --- a/frappe/automation/doctype/assignment_rule/assignment_rule.py +++ b/frappe/automation/doctype/assignment_rule/assignment_rule.py @@ -2,8 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import frappe from frappe.model.document import Document from frappe.desk.form import assign_to diff --git a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py index cb1e0ff8f4..e287b83965 100644 --- a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py +++ b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from frappe.utils import random_string diff --git a/frappe/automation/doctype/assignment_rule_day/assignment_rule_day.py b/frappe/automation/doctype/assignment_rule_day/assignment_rule_day.py index 27f9aa40e1..c734495c39 100644 --- a/frappe/automation/doctype/assignment_rule_day/assignment_rule_day.py +++ b/frappe/automation/doctype/assignment_rule_day/assignment_rule_day.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/automation/doctype/assignment_rule_user/assignment_rule_user.py b/frappe/automation/doctype/assignment_rule_user/assignment_rule_user.py index ee8081c6d8..4d65efd5c1 100644 --- a/frappe/automation/doctype/assignment_rule_user/assignment_rule_user.py +++ b/frappe/automation/doctype/assignment_rule_user/assignment_rule_user.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py index bf05baf5b6..998e73a42c 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from datetime import timedelta diff --git a/frappe/automation/doctype/auto_repeat/test_auto_repeat.py b/frappe/automation/doctype/auto_repeat/test_auto_repeat.py index 6ceb4dba72..567c1161af 100644 --- a/frappe/automation/doctype/auto_repeat/test_auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/test_auto_repeat.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest import frappe diff --git a/frappe/automation/doctype/auto_repeat_day/auto_repeat_day.py b/frappe/automation/doctype/auto_repeat_day/auto_repeat_day.py index 3a7ced1370..8af3284cde 100644 --- a/frappe/automation/doctype/auto_repeat_day/auto_repeat_day.py +++ b/frappe/automation/doctype/auto_repeat_day/auto_repeat_day.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/automation/doctype/milestone/milestone.py b/frappe/automation/doctype/milestone/milestone.py index 64c073a378..6ea6d7544a 100644 --- a/frappe/automation/doctype/milestone/milestone.py +++ b/frappe/automation/doctype/milestone/milestone.py @@ -2,8 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import frappe from frappe.model.document import Document diff --git a/frappe/automation/doctype/milestone/test_milestone.py b/frappe/automation/doctype/milestone/test_milestone.py index 75602d48db..175c56e552 100644 --- a/frappe/automation/doctype/milestone/test_milestone.py +++ b/frappe/automation/doctype/milestone/test_milestone.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - #import frappe import unittest diff --git a/frappe/automation/doctype/milestone_tracker/milestone_tracker.py b/frappe/automation/doctype/milestone_tracker/milestone_tracker.py index 388620bfb4..125cad7fa8 100644 --- a/frappe/automation/doctype/milestone_tracker/milestone_tracker.py +++ b/frappe/automation/doctype/milestone_tracker/milestone_tracker.py @@ -2,8 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import frappe from frappe.model.document import Document import frappe.cache_manager diff --git a/frappe/automation/doctype/milestone_tracker/test_milestone_tracker.py b/frappe/automation/doctype/milestone_tracker/test_milestone_tracker.py index 05db3b025e..21b2779018 100644 --- a/frappe/automation/doctype/milestone_tracker/test_milestone_tracker.py +++ b/frappe/automation/doctype/milestone_tracker/test_milestone_tracker.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import frappe.cache_manager import unittest diff --git a/frappe/boot.py b/frappe/boot.py index 0dfcb8d1b4..0589e32ac8 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -1,10 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals - -from six import iteritems, text_type - """ bootstrap client session """ @@ -75,7 +70,7 @@ def get_bootinfo(): frappe.get_attr(method)(bootinfo) if bootinfo.lang: - bootinfo.lang = text_type(bootinfo.lang) + bootinfo.lang = str(bootinfo.lang) bootinfo.versions = {k: v['version'] for k, v in get_versions().items()} bootinfo.error_report_email = frappe.conf.error_report_email @@ -220,7 +215,7 @@ def load_translations(bootinfo): messages[name] = frappe._(name) # only untranslated - messages = {k:v for k, v in iteritems(messages) if k!=v} + messages = {k: v for k, v in messages.items() if k!=v} bootinfo["__messages"] = messages diff --git a/frappe/build.py b/frappe/build.py index c970ae3a28..6e4bf2b533 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -1,11 +1,11 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - import os import re import json import shutil import subprocess +from io import StringIO from tempfile import mkdtemp, mktemp from distutils.spawn import find_executable @@ -402,8 +402,6 @@ def get_build_maps(): def pack(target, sources, no_compress, verbose): - from six import StringIO - outtype, outtxt = target.split(".")[-1], "" jsm = JavascriptMinify() diff --git a/frappe/cache_manager.py b/frappe/cache_manager.py index 7330c83102..52fba4568d 100644 --- a/frappe/cache_manager.py +++ b/frappe/cache_manager.py @@ -1,8 +1,6 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, json from frappe.model.document import Document from frappe.desk.notifications import (delete_notification_count_for, diff --git a/frappe/chat/__init__.py b/frappe/chat/__init__.py index dea0030839..4c9b1c5db7 100644 --- a/frappe/chat/__init__.py +++ b/frappe/chat/__init__.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe import _ diff --git a/frappe/chat/doctype/chat_message/chat_message.py b/frappe/chat/doctype/chat_message/chat_message.py index 5549aaa657..bc470a5e9c 100644 --- a/frappe/chat/doctype/chat_message/chat_message.py +++ b/frappe/chat/doctype/chat_message/chat_message.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - # imports - standard imports import json diff --git a/frappe/chat/doctype/chat_profile/chat_profile.py b/frappe/chat/doctype/chat_profile/chat_profile.py index 698d992d35..da10a836c4 100644 --- a/frappe/chat/doctype/chat_profile/chat_profile.py +++ b/frappe/chat/doctype/chat_profile/chat_profile.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - # imports - module imports from frappe.model.document import Document from frappe import _ diff --git a/frappe/chat/doctype/chat_room/chat_room.py b/frappe/chat/doctype/chat_room/chat_room.py index 609acaef7d..bdbee44d7a 100644 --- a/frappe/chat/doctype/chat_room/chat_room.py +++ b/frappe/chat/doctype/chat_room/chat_room.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - # imports - module imports from frappe.model.document import Document from frappe import _ diff --git a/frappe/chat/doctype/chat_room_user/chat_room_user.py b/frappe/chat/doctype/chat_room_user/chat_room_user.py index f8e13add82..f6dbdc7659 100644 --- a/frappe/chat/doctype/chat_room_user/chat_room_user.py +++ b/frappe/chat/doctype/chat_room_user/chat_room_user.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - # imports - module imports from frappe.model.document import Document import frappe diff --git a/frappe/chat/doctype/chat_token/chat_token.py b/frappe/chat/doctype/chat_token/chat_token.py index 30a76ef5bd..63d69a58be 100644 --- a/frappe/chat/doctype/chat_token/chat_token.py +++ b/frappe/chat/doctype/chat_token/chat_token.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/chat/util/__init__.py b/frappe/chat/util/__init__.py index 15977af566..383df581cd 100644 --- a/frappe/chat/util/__init__.py +++ b/frappe/chat/util/__init__.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - # imports - module imports from frappe.chat.util.util import ( get_user_doc, diff --git a/frappe/chat/util/test_util.py b/frappe/chat/util/test_util.py index 6d44a63d31..e2d05a4024 100644 --- a/frappe/chat/util/test_util.py +++ b/frappe/chat/util/test_util.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - # imports - standard imports import unittest @@ -9,7 +7,6 @@ from frappe.chat.util import ( safe_json_loads ) import frappe -import six class TestChatUtil(unittest.TestCase): def test_safe_json_loads(self): @@ -20,7 +17,7 @@ class TestChatUtil(unittest.TestCase): self.assertEqual(type(number), float) string = safe_json_loads("foobar") - self.assertEqual(type(string), six.text_type) + self.assertEqual(type(string), str) array = safe_json_loads('[{ "foo": "bar" }]') self.assertEqual(type(array), list) diff --git a/frappe/chat/util/util.py b/frappe/chat/util/util.py index 82df6dd127..b7e7991c2b 100644 --- a/frappe/chat/util/util.py +++ b/frappe/chat/util/util.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - # imports - standard imports import json from collections.abc import MutableMapping, MutableSequence, Sequence diff --git a/frappe/chat/website/__init__.py b/frappe/chat/website/__init__.py index f33f531cbf..12affd2782 100644 --- a/frappe/chat/website/__init__.py +++ b/frappe/chat/website/__init__.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.chat.util import filter_dict, safe_json_loads diff --git a/frappe/client.py b/frappe/client.py index a2e04452ff..66c457e893 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals import frappe from frappe import _ import frappe.model @@ -11,7 +9,6 @@ from frappe.utils import get_safe_filters from frappe.desk.reportview import validate_args from frappe.model.db_query import check_parent_permission -from six import iteritems, string_types, integer_types ''' Handle RESTful requests that are mapped to the `/api/resource` route. @@ -86,7 +83,7 @@ def get_value(doctype, fieldname, filters=None, as_dict=True, debug=False, paren frappe.throw(_("No permission for {0}").format(doctype), frappe.PermissionError) filters = get_safe_filters(filters) - if isinstance(filters, string_types): + if isinstance(filters, str): filters = {"name": filters} try: @@ -135,7 +132,7 @@ def set_value(doctype, name, fieldname, value=None): if not value: values = fieldname - if isinstance(fieldname, string_types): + if isinstance(fieldname, str): try: values = json.loads(fieldname) except ValueError: @@ -161,7 +158,7 @@ def insert(doc=None): '''Insert a document :param doc: JSON or dict object to be inserted''' - if isinstance(doc, string_types): + if isinstance(doc, str): doc = json.loads(doc) if doc.get("parent") and doc.get("parenttype"): @@ -179,7 +176,7 @@ def insert_many(docs=None): '''Insert multiple documents :param docs: JSON or list of dict objects to be inserted in one request''' - if isinstance(docs, string_types): + if isinstance(docs, str): docs = json.loads(docs) out = [] @@ -205,7 +202,7 @@ def save(doc): '''Update (save) an existing document :param doc: JSON or dict object with the properties of the document to be updated''' - if isinstance(doc, string_types): + if isinstance(doc, str): doc = json.loads(doc) doc = frappe.get_doc(doc) @@ -228,7 +225,7 @@ def submit(doc): '''Submit a document :param doc: JSON or dict object to be submitted remotely''' - if isinstance(doc, string_types): + if isinstance(doc, str): doc = json.loads(doc) doc = frappe.get_doc(doc) @@ -266,7 +263,7 @@ def make_width_property_setter(doc): '''Set width Property Setter :param doc: Property Setter document with `width` property''' - if isinstance(doc, string_types): + if isinstance(doc, str): doc = json.loads(doc) if doc["doctype"]=="Property Setter" and doc["property"]=="width": frappe.get_doc(doc).insert(ignore_permissions = True) @@ -280,7 +277,7 @@ def bulk_update(docs): failed_docs = [] for doc in docs: try: - ddoc = {key: val for key, val in iteritems(doc) if key not in ['doctype', 'docname']} + ddoc = {key: val for key, val in doc.items() if key not in ['doctype', 'docname']} doctype = doc['doctype'] docname = doc['docname'] doc = frappe.get_doc(doctype, docname) diff --git a/frappe/commands/__init__.py b/frappe/commands/__init__.py index e521acc9ad..be9d107025 100644 --- a/frappe/commands/__init__.py +++ b/frappe/commands/__init__.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals, absolute_import, print_function import sys import click import cProfile @@ -10,7 +9,7 @@ import frappe import frappe.utils import subprocess # nosec from functools import wraps -from six import StringIO +from io import StringIO from os import environ click.disable_unicode_literals_warning = True diff --git a/frappe/commands/scheduler.py b/frappe/commands/scheduler.py index e9638800cd..d69ebb3024 100755 --- a/frappe/commands/scheduler.py +++ b/frappe/commands/scheduler.py @@ -1,4 +1,3 @@ -from __future__ import unicode_literals, absolute_import, print_function import click import sys import frappe diff --git a/frappe/commands/translate.py b/frappe/commands/translate.py index 48a7fd1db7..68d210eaaa 100644 --- a/frappe/commands/translate.py +++ b/frappe/commands/translate.py @@ -1,4 +1,3 @@ -from __future__ import unicode_literals, absolute_import, print_function import click from frappe.commands import pass_context, get_site from frappe.exceptions import SiteNotSpecifiedError diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 4da0f6bb78..bcb1749644 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -222,7 +222,7 @@ def execute(context, method, args=None, kwargs=None, profile=False): if profile: import pstats - from six import StringIO + from io import StringIO pr.disable() s = StringIO() diff --git a/frappe/config/__init__.py b/frappe/config/__init__.py index 30be82d0df..62a877be24 100644 --- a/frappe/config/__init__.py +++ b/frappe/config/__init__.py @@ -1,6 +1,3 @@ -from __future__ import unicode_literals -import json -from six import iteritems import frappe from frappe import _ from frappe.desk.moduleview import (get_data, get_onboard_items, config_exists, get_module_link_items_from_list) diff --git a/frappe/contacts/address_and_contact.py b/frappe/contacts/address_and_contact.py index 3ca9547188..f21819ad98 100644 --- a/frappe/contacts/address_and_contact.py +++ b/frappe/contacts/address_and_contact.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ diff --git a/frappe/contacts/doctype/address/test_address.py b/frappe/contacts/doctype/address/test_address.py index d6d4e50491..ed61b6f0ee 100644 --- a/frappe/contacts/doctype/address/test_address.py +++ b/frappe/contacts/doctype/address/test_address.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe, unittest from frappe.contacts.doctype.address.address import get_address_display diff --git a/frappe/contacts/doctype/address_template/address_template.py b/frappe/contacts/doctype/address_template/address_template.py index 2ca9aebff5..2d69a792ab 100644 --- a/frappe/contacts/doctype/address_template/address_template.py +++ b/frappe/contacts/doctype/address_template/address_template.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils import cint diff --git a/frappe/contacts/doctype/address_template/test_address_template.py b/frappe/contacts/doctype/address_template/test_address_template.py index f40b56e7d9..6b519a3bb7 100644 --- a/frappe/contacts/doctype/address_template/test_address_template.py +++ b/frappe/contacts/doctype/address_template/test_address_template.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe, unittest class TestAddressTemplate(unittest.TestCase): @@ -42,4 +40,4 @@ class TestAddressTemplate(unittest.TestCase): "doctype": "Address Template", "country": 'Brazil', "template": template - }).insert() \ No newline at end of file + }).insert() \ No newline at end of file diff --git a/frappe/contacts/doctype/contact/contact.py b/frappe/contacts/doctype/contact/contact.py index c8af99bebc..d1dd1f1010 100644 --- a/frappe/contacts/doctype/contact/contact.py +++ b/frappe/contacts/doctype/contact/contact.py @@ -1,6 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt - import frappe from frappe.utils import cstr, has_gravatar from frappe import _ diff --git a/frappe/contacts/doctype/contact/test_contact.py b/frappe/contacts/doctype/contact/test_contact.py index b131428696..6c6089edeb 100644 --- a/frappe/contacts/doctype/contact/test_contact.py +++ b/frappe/contacts/doctype/contact/test_contact.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/contacts/doctype/contact_email/contact_email.py b/frappe/contacts/doctype/contact_email/contact_email.py index 04e8b22989..5fc2fef316 100644 --- a/frappe/contacts/doctype/contact_email/contact_email.py +++ b/frappe/contacts/doctype/contact_email/contact_email.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/contacts/doctype/contact_phone/contact_phone.py b/frappe/contacts/doctype/contact_phone/contact_phone.py index fe2f86a4bd..63f5f73cf1 100644 --- a/frappe/contacts/doctype/contact_phone/contact_phone.py +++ b/frappe/contacts/doctype/contact_phone/contact_phone.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/contacts/doctype/gender/gender.py b/frappe/contacts/doctype/gender/gender.py index bfca5830c1..319800de7e 100644 --- a/frappe/contacts/doctype/gender/gender.py +++ b/frappe/contacts/doctype/gender/gender.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class Gender(Document): diff --git a/frappe/contacts/doctype/gender/test_gender.py b/frappe/contacts/doctype/gender/test_gender.py index fbe3473bc3..071ed47df0 100644 --- a/frappe/contacts/doctype/gender/test_gender.py +++ b/frappe/contacts/doctype/gender/test_gender.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestGender(unittest.TestCase): diff --git a/frappe/contacts/doctype/salutation/salutation.py b/frappe/contacts/doctype/salutation/salutation.py index d9e4528c7d..d79ad66845 100644 --- a/frappe/contacts/doctype/salutation/salutation.py +++ b/frappe/contacts/doctype/salutation/salutation.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class Salutation(Document): diff --git a/frappe/contacts/doctype/salutation/test_salutation.py b/frappe/contacts/doctype/salutation/test_salutation.py index 63d603e6a4..e2e9075855 100644 --- a/frappe/contacts/doctype/salutation/test_salutation.py +++ b/frappe/contacts/doctype/salutation/test_salutation.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestSalutation(unittest.TestCase): diff --git a/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py b/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py index 1b3982f251..bf48b6b185 100644 --- a/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py +++ b/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py @@ -1,8 +1,5 @@ # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt - -from __future__ import unicode_literals -from six import iteritems import frappe from frappe import _ @@ -58,7 +55,7 @@ def get_reference_addresses_and_contact(reference_doctype, reference_name): reference_details = get_reference_details(reference_doctype, "Address", reference_list, reference_details) reference_details = get_reference_details(reference_doctype, "Contact", reference_list, reference_details) - for reference_name, details in iteritems(reference_details): + for reference_name, details in reference_details.items(): addresses = details.get("address", []) contacts = details.get("contact", []) if not any([addresses, contacts]): diff --git a/frappe/contacts/report/addresses_and_contacts/test_addresses_and_contacts.py b/frappe/contacts/report/addresses_and_contacts/test_addresses_and_contacts.py index 9e98dcf6f6..f539722175 100644 --- a/frappe/contacts/report/addresses_and_contacts/test_addresses_and_contacts.py +++ b/frappe/contacts/report/addresses_and_contacts/test_addresses_and_contacts.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe import frappe.defaults import unittest diff --git a/frappe/core/__init__.py b/frappe/core/__init__.py index 998a299158..f064a66c17 100644 --- a/frappe/core/__init__.py +++ b/frappe/core/__init__.py @@ -1,4 +1,2 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals \ No newline at end of file diff --git a/frappe/core/doctype/__init__.py b/frappe/core/doctype/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/__init__.py +++ b/frappe/core/doctype/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/access_log/access_log.py b/frappe/core/doctype/access_log/access_log.py index 43381e7f2e..d2fbee108b 100644 --- a/frappe/core/doctype/access_log/access_log.py +++ b/frappe/core/doctype/access_log/access_log.py @@ -3,8 +3,6 @@ # For license information, please see license.txt # imports - standard imports -from __future__ import unicode_literals - # imports - module imports import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/activity_log/activity_log.py b/frappe/core/doctype/activity_log/activity_log.py index 98dc91806d..efec0dc217 100644 --- a/frappe/core/doctype/activity_log/activity_log.py +++ b/frappe/core/doctype/activity_log/activity_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe import _ from frappe.utils import get_fullname, now from frappe.model.document import Document diff --git a/frappe/core/doctype/activity_log/feed.py b/frappe/core/doctype/activity_log/feed.py index f51692fe9f..caa3cae613 100644 --- a/frappe/core/doctype/activity_log/feed.py +++ b/frappe/core/doctype/activity_log/feed.py @@ -1,13 +1,11 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: See license.txt -from __future__ import unicode_literals import frappe import frappe.permissions from frappe.utils import get_fullname from frappe import _ from frappe.core.doctype.activity_log.activity_log import add_authentication_log -from six import string_types def update_feed(doc, method=None): if frappe.flags.in_patch or frappe.flags.in_install or frappe.flags.in_import: @@ -23,7 +21,7 @@ def update_feed(doc, method=None): feed = doc.get_feed() if feed: - if isinstance(feed, string_types): + if isinstance(feed, str): feed = {"subject": feed} feed = frappe._dict(feed) diff --git a/frappe/core/doctype/activity_log/test_activity_log.py b/frappe/core/doctype/activity_log/test_activity_log.py index f33c7a1c85..ed7b70cca1 100644 --- a/frappe/core/doctype/activity_log/test_activity_log.py +++ b/frappe/core/doctype/activity_log/test_activity_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest import time diff --git a/frappe/core/doctype/block_module/block_module.py b/frappe/core/doctype/block_module/block_module.py index e7bb3cf045..d9723f9170 100644 --- a/frappe/core/doctype/block_module/block_module.py +++ b/frappe/core/doctype/block_module/block_module.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/comment/comment.py b/frappe/core/doctype/comment/comment.py index ad5d60500b..e29bae25a2 100644 --- a/frappe/core/doctype/comment/comment.py +++ b/frappe/core/doctype/comment/comment.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt - -from __future__ import unicode_literals, absolute_import import frappe from frappe import _ import json diff --git a/frappe/core/doctype/comment/test_comment.py b/frappe/core/doctype/comment/test_comment.py index 3cf8fbaa3f..13db92e7a8 100644 --- a/frappe/core/doctype/comment/test_comment.py +++ b/frappe/core/doctype/comment/test_comment.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe, json import unittest diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 5ebf714645..ed17ffc0aa 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals, absolute_import from collections import Counter import frappe from frappe import _ @@ -13,7 +12,7 @@ from frappe.utils.bot import BotReply from frappe.utils import parse_addr from frappe.core.doctype.comment.comment import update_comment_in_doc from email.utils import parseaddr -from six.moves.urllib.parse import unquote +from urllib.parse import unquote from frappe.utils.user import is_system_user from frappe.contacts.doctype.contact.contact import get_contact_name from frappe.automation.doctype.assignment_rule.assignment_rule import apply as apply_assignment_rule diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index d3017055cf..c28956b41f 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -1,9 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals, absolute_import -from six.moves import range -from six import string_types import frappe import json from email.utils import formataddr @@ -77,7 +74,7 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = comm.save(ignore_permissions=True) - if isinstance(attachments, string_types): + if isinstance(attachments, str): attachments = json.loads(attachments) # if not committed, delayed task doesn't find the communication @@ -249,11 +246,11 @@ def prepare_to_notify(doc, print_html=None, print_format=None, attachments=None) "name":doc.reference_name, "print_format":print_format, "html":print_html}) if attachments: - if isinstance(attachments, string_types): + if isinstance(attachments, str): attachments = json.loads(attachments) for a in attachments: - if isinstance(a, string_types): + if isinstance(a, str): # is it a filename? try: # check for both filename and file id @@ -355,7 +352,7 @@ def add_attachments(name, attachments): '''Add attachments to the given Communication''' # loop through attachments for a in attachments: - if isinstance(a, string_types): + if isinstance(a, str): attach = frappe.db.get_value("File", {"name":a}, ["file_name", "file_url", "is_private"], as_dict=1) diff --git a/frappe/core/doctype/communication/test_communication.py b/frappe/core/doctype/communication/test_communication.py index 6df90baaae..5b400398a5 100644 --- a/frappe/core/doctype/communication/test_communication.py +++ b/frappe/core/doctype/communication/test_communication.py @@ -1,10 +1,8 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest -from six.moves.urllib.parse import quote +from urllib.parse import quote test_records = frappe.get_test_records('Communication') diff --git a/frappe/core/doctype/communication_link/communication_link.py b/frappe/core/doctype/communication_link/communication_link.py index d1612ef57e..d3307d1d32 100644 --- a/frappe/core/doctype/communication_link/communication_link.py +++ b/frappe/core/doctype/communication_link/communication_link.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/custom_docperm/custom_docperm.py b/frappe/core/doctype/custom_docperm/custom_docperm.py index cce9788b73..225f5db79b 100644 --- a/frappe/core/doctype/custom_docperm/custom_docperm.py +++ b/frappe/core/doctype/custom_docperm/custom_docperm.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/custom_docperm/test_custom_docperm.py b/frappe/core/doctype/custom_docperm/test_custom_docperm.py index bd6e17ccc9..6e0c82d1db 100644 --- a/frappe/core/doctype/custom_docperm/test_custom_docperm.py +++ b/frappe/core/doctype/custom_docperm/test_custom_docperm.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/custom_role/custom_role.py b/frappe/core/doctype/custom_role/custom_role.py index 25257e1a23..89e478dd38 100644 --- a/frappe/core/doctype/custom_role/custom_role.py +++ b/frappe/core/doctype/custom_role/custom_role.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/custom_role/test_custom_role.py b/frappe/core/doctype/custom_role/test_custom_role.py index 670b494b10..0ad77524fa 100644 --- a/frappe/core/doctype/custom_role/test_custom_role.py +++ b/frappe/core/doctype/custom_role/test_custom_role.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/data_export/data_export.py b/frappe/core/doctype/data_export/data_export.py index fb4fae26d5..c376b25230 100644 --- a/frappe/core/doctype/data_export/data_export.py +++ b/frappe/core/doctype/data_export/data_export.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class DataExport(Document): diff --git a/frappe/core/doctype/data_export/exporter.py b/frappe/core/doctype/data_export/exporter.py index 5d600cc0db..389948449e 100644 --- a/frappe/core/doctype/data_export/exporter.py +++ b/frappe/core/doctype/data_export/exporter.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe from frappe import _ import frappe.permissions @@ -10,7 +8,6 @@ import re, csv, os from frappe.utils.csvutils import UnicodeWriter from frappe.utils import cstr, formatdate, format_datetime, parse_json, cint, format_duration from frappe.core.doctype.data_import_legacy.importer import get_data_keys -from six import string_types from frappe.core.doctype.access_log.access_log import make_access_log reflags = { @@ -57,7 +54,7 @@ class DataExporter: self.docs_to_export = {} if self.doctype: - if isinstance(self.doctype, string_types): + if isinstance(self.doctype, str): self.doctype = [self.doctype] if len(self.doctype) > 1: diff --git a/frappe/core/doctype/data_import/importer.py b/frappe/core/doctype/data_import/importer.py index d3f981add4..fed90b75ce 100644 --- a/frappe/core/doctype/data_import/importer.py +++ b/frappe/core/doctype/data_import/importer.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import os import io import frappe diff --git a/frappe/core/doctype/data_import/test_data_import.py b/frappe/core/doctype/data_import/test_data_import.py index 15fd57744a..c9366a97ba 100644 --- a/frappe/core/doctype/data_import/test_data_import.py +++ b/frappe/core/doctype/data_import/test_data_import.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/data_import/test_exporter.py b/frappe/core/doctype/data_import/test_exporter.py index 8415af2e63..dfe9926906 100644 --- a/frappe/core/doctype/data_import/test_exporter.py +++ b/frappe/core/doctype/data_import/test_exporter.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest import frappe from frappe.core.doctype.data_import.exporter import Exporter diff --git a/frappe/core/doctype/data_import/test_importer.py b/frappe/core/doctype/data_import/test_importer.py index f76d4504a4..54a7788a2d 100644 --- a/frappe/core/doctype/data_import/test_importer.py +++ b/frappe/core/doctype/data_import/test_importer.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest import frappe from frappe.core.doctype.data_import.importer import Importer diff --git a/frappe/core/doctype/data_import_legacy/importer.py b/frappe/core/doctype/data_import_legacy/importer.py index 35569c7186..4080e70418 100644 --- a/frappe/core/doctype/data_import_legacy/importer.py +++ b/frappe/core/doctype/data_import_legacy/importer.py @@ -3,9 +3,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals, print_function - -from six.moves import range import requests import frappe, json import frappe.permissions @@ -16,7 +13,6 @@ from frappe.utils.csvutils import getlink from frappe.utils.dateutils import parse_date from frappe.utils import cint, cstr, flt, getdate, get_datetime, get_url, get_absolute_url, duration_to_seconds -from six import string_types @frappe.whitelist() @@ -42,7 +38,7 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, frappe.cache().hdel("lang", user) frappe.set_user_lang(user) - if data_import_doc and isinstance(data_import_doc, string_types): + if data_import_doc and isinstance(data_import_doc, str): data_import_doc = frappe.get_doc("Data Import Legacy", data_import_doc) if data_import_doc and from_data_import == "Yes": no_email = data_import_doc.no_email @@ -152,7 +148,7 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, elif fieldtype in ("Float", "Currency", "Percent"): d[fieldname] = flt(d[fieldname]) elif fieldtype == "Date": - if d[fieldname] and isinstance(d[fieldname], string_types): + if d[fieldname] and isinstance(d[fieldname], str): d[fieldname] = getdate(parse_date(d[fieldname])) elif fieldtype == "Datetime": if d[fieldname]: diff --git a/frappe/core/doctype/data_import_legacy/test_data_import_legacy.py b/frappe/core/doctype/data_import_legacy/test_data_import_legacy.py index e5b244e6a0..6f9964e8f5 100644 --- a/frappe/core/doctype/data_import_legacy/test_data_import_legacy.py +++ b/frappe/core/doctype/data_import_legacy/test_data_import_legacy.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/defaultvalue/__init__.py b/frappe/core/doctype/defaultvalue/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/defaultvalue/__init__.py +++ b/frappe/core/doctype/defaultvalue/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/defaultvalue/defaultvalue.py b/frappe/core/doctype/defaultvalue/defaultvalue.py index d9cc145053..0ae088ee96 100644 --- a/frappe/core/doctype/defaultvalue/defaultvalue.py +++ b/frappe/core/doctype/defaultvalue/defaultvalue.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/deleted_document/deleted_document.py b/frappe/core/doctype/deleted_document/deleted_document.py index 116fc5caf5..f4109c8197 100644 --- a/frappe/core/doctype/deleted_document/deleted_document.py +++ b/frappe/core/doctype/deleted_document/deleted_document.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import json from frappe.desk.doctype.bulk_update.bulk_update import show_progress diff --git a/frappe/core/doctype/deleted_document/test_deleted_document.py b/frappe/core/doctype/deleted_document/test_deleted_document.py index c45a2bd180..d9dc2bb2d1 100644 --- a/frappe/core/doctype/deleted_document/test_deleted_document.py +++ b/frappe/core/doctype/deleted_document/test_deleted_document.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/docfield/__init__.py b/frappe/core/doctype/docfield/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/docfield/__init__.py +++ b/frappe/core/doctype/docfield/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/docfield/docfield.py b/frappe/core/doctype/docfield/docfield.py index b6e2d9b67d..175cba3c7c 100644 --- a/frappe/core/doctype/docfield/docfield.py +++ b/frappe/core/doctype/docfield/docfield.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/docperm/__init__.py b/frappe/core/doctype/docperm/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/docperm/__init__.py +++ b/frappe/core/doctype/docperm/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/docperm/docperm.py b/frappe/core/doctype/docperm/docperm.py index 36ed9acbe6..9732cde920 100644 --- a/frappe/core/doctype/docperm/docperm.py +++ b/frappe/core/doctype/docperm/docperm.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/docshare/docshare.py b/frappe/core/doctype/docshare/docshare.py index 26ed53a87d..2d7b6b9e48 100644 --- a/frappe/core/doctype/docshare/docshare.py +++ b/frappe/core/doctype/docshare/docshare.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ diff --git a/frappe/core/doctype/docshare/test_docshare.py b/frappe/core/doctype/docshare/test_docshare.py index 9c424eb4d7..6551dabbea 100644 --- a/frappe/core/doctype/docshare/test_docshare.py +++ b/frappe/core/doctype/docshare/test_docshare.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals import frappe import frappe.share import unittest diff --git a/frappe/core/doctype/doctype/__init__.py b/frappe/core/doctype/doctype/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/doctype/__init__.py +++ b/frappe/core/doctype/doctype/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index f9dbeb0907..8a96fc89f6 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -2,15 +2,10 @@ # MIT License. See license.txt # imports - standard imports -from __future__ import unicode_literals import re, copy, os, shutil import json from frappe.cache_manager import clear_user_cache, clear_controller_cache -# imports - third party imports -import six -from six import iteritems - # imports - module imports import frappe import frappe.website.render @@ -486,7 +481,7 @@ class DocType(Document): # remove null and empty fields def remove_null_fields(o): to_remove = [] - for attr, value in iteritems(o): + for attr, value in o.items(): if isinstance(value, list): for v in value: remove_null_fields(v) @@ -670,7 +665,7 @@ class DocType(Document): if not name: name = self.name - flags = {"flags": re.ASCII} if six.PY3 else {} + flags = {"flags": re.ASCII} # a DocType name should not start or end with an empty space if re.search(r"^[ \t\n\r]+|[ \t\n\r]+$", name, **flags): diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index 9c492d2c36..1e1a01a685 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from frappe.core.doctype.doctype.doctype import (UniqueFieldnameError, diff --git a/frappe/core/doctype/doctype_action/doctype_action.py b/frappe/core/doctype/doctype_action/doctype_action.py index a745c7da40..203b06ec1b 100644 --- a/frappe/core/doctype/doctype_action/doctype_action.py +++ b/frappe/core/doctype/doctype_action/doctype_action.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/doctype_link/doctype_link.py b/frappe/core/doctype/doctype_link/doctype_link.py index efe8b09809..07e0efdace 100644 --- a/frappe/core/doctype/doctype_link/doctype_link.py +++ b/frappe/core/doctype/doctype_link/doctype_link.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/document_naming_rule/document_naming_rule.py b/frappe/core/doctype/document_naming_rule/document_naming_rule.py index 4b34293af6..4f500a2abf 100644 --- a/frappe/core/doctype/document_naming_rule/document_naming_rule.py +++ b/frappe/core/doctype/document_naming_rule/document_naming_rule.py @@ -2,7 +2,6 @@ # 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 from frappe.utils.data import evaluate_filters diff --git a/frappe/core/doctype/document_naming_rule/test_document_naming_rule.py b/frappe/core/doctype/document_naming_rule/test_document_naming_rule.py index 1b91f6a0cf..2206d173d7 100644 --- a/frappe/core/doctype/document_naming_rule/test_document_naming_rule.py +++ b/frappe/core/doctype/document_naming_rule/test_document_naming_rule.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/document_naming_rule_condition/document_naming_rule_condition.py b/frappe/core/doctype/document_naming_rule_condition/document_naming_rule_condition.py index 0895c9f93f..dfca052d95 100644 --- a/frappe/core/doctype/document_naming_rule_condition/document_naming_rule_condition.py +++ b/frappe/core/doctype/document_naming_rule_condition/document_naming_rule_condition.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py b/frappe/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py index 6f1376dc62..643e963bd7 100644 --- a/frappe/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py +++ b/frappe/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/domain/domain.py b/frappe/core/doctype/domain/domain.py index a4e9f503ab..681824bb02 100644 --- a/frappe/core/doctype/domain/domain.py +++ b/frappe/core/doctype/domain/domain.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/domain/test_domain.py b/frappe/core/doctype/domain/test_domain.py index 8e0bc65c54..c2686a7566 100644 --- a/frappe/core/doctype/domain/test_domain.py +++ b/frappe/core/doctype/domain/test_domain.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/domain_settings/domain_settings.py b/frappe/core/doctype/domain_settings/domain_settings.py index d4d394a5cb..7ad0aeff21 100644 --- a/frappe/core/doctype/domain_settings/domain_settings.py +++ b/frappe/core/doctype/domain_settings/domain_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/dynamic_link/dynamic_link.py b/frappe/core/doctype/dynamic_link/dynamic_link.py index 30e0ef1f1f..a7adb9ae72 100644 --- a/frappe/core/doctype/dynamic_link/dynamic_link.py +++ b/frappe/core/doctype/dynamic_link/dynamic_link.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/error_log/error_log.py b/frappe/core/doctype/error_log/error_log.py index ec02aaf446..8223238c57 100644 --- a/frappe/core/doctype/error_log/error_log.py +++ b/frappe/core/doctype/error_log/error_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/error_log/test_error_log.py b/frappe/core/doctype/error_log/test_error_log.py index d93fe07c61..d7444ab2a7 100644 --- a/frappe/core/doctype/error_log/test_error_log.py +++ b/frappe/core/doctype/error_log/test_error_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/error_snapshot/error_snapshot.py b/frappe/core/doctype/error_snapshot/error_snapshot.py index 5badaad63f..247a796a6b 100644 --- a/frappe/core/doctype/error_snapshot/error_snapshot.py +++ b/frappe/core/doctype/error_snapshot/error_snapshot.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/error_snapshot/test_error_snapshot.py b/frappe/core/doctype/error_snapshot/test_error_snapshot.py index b6438eae1d..135136294a 100644 --- a/frappe/core/doctype/error_snapshot/test_error_snapshot.py +++ b/frappe/core/doctype/error_snapshot/test_error_snapshot.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index c4c37e6d13..b4bfe1d21b 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -7,8 +7,6 @@ record of files naming for same name files: file.gif, file-1.gif, file-2.gif etc """ -from __future__ import unicode_literals - import base64 import hashlib import imghdr @@ -23,8 +21,8 @@ import zipfile import requests import requests.exceptions from PIL import Image, ImageFile, ImageOps -from six import PY2, StringIO, string_types, text_type -from six.moves.urllib.parse import quote, unquote +from io import StringIO +from urllib.parse import quote, unquote import frappe from frappe import _, conf @@ -382,18 +380,14 @@ class File(Document): file_path = self.get_full_path() # read the file - if PY2: - with open(encode(file_path)) as f: - content = f.read() - else: - with io.open(encode(file_path), mode='rb') as f: - content = f.read() - try: - # for plain text files - content = content.decode() - except UnicodeDecodeError: - # for .png, .jpg, etc - pass + with io.open(encode(file_path), mode='rb') as f: + content = f.read() + try: + # for plain text files + content = content.decode() + except UnicodeDecodeError: + # for .png, .jpg, etc + pass return content @@ -430,7 +424,7 @@ class File(Document): frappe.create_folder(file_path) # write the file self.content = self.get_content() - if isinstance(self.content, text_type): + if isinstance(self.content, str): self.content = self.content.encode() with open(os.path.join(file_path.encode('utf-8'), self.file_name.encode('utf-8')), 'wb+') as f: f.write(self.content) @@ -483,7 +477,7 @@ class File(Document): self.content = content if decode: - if isinstance(content, text_type): + if isinstance(content, str): self.content = content.encode("utf-8") if b"," in self.content: @@ -632,7 +626,7 @@ def create_new_folder(file_name, folder): @frappe.whitelist() def move_file(file_list, new_parent, old_parent): - if isinstance(file_list, string_types): + if isinstance(file_list, str): file_list = json.loads(file_list) for file_obj in file_list: @@ -834,7 +828,7 @@ def remove_file_by_url(file_url, doctype=None, name=None): def get_content_hash(content): - if isinstance(content, text_type): + if isinstance(content, str): content = content.encode() return hashlib.md5(content).hexdigest() #nosec @@ -887,8 +881,8 @@ def extract_images_from_html(doc, content): filename = headers.split("filename=")[-1] # decode filename - if not isinstance(filename, text_type): - filename = text_type(filename, 'utf-8') + if not isinstance(filename, str): + filename = str(filename, 'utf-8') else: mtype = headers.split(";")[0] filename = get_random_filename(content_type=mtype) @@ -911,7 +905,7 @@ def extract_images_from_html(doc, content): return ']*src\s*=\s*["\'](?=data:)(.*?)["\']', _save_file, content) return content @@ -941,7 +935,7 @@ def get_attached_images(doctype, names): '''get list of image urls attached in form returns {name: ['image.jpg', 'image.png']}''' - if isinstance(names, string_types): + if isinstance(names, str): names = json.loads(names) img_urls = frappe.db.get_list('File', filters={ diff --git a/frappe/core/doctype/file/test_file.py b/frappe/core/doctype/file/test_file.py index 2596fe94d0..649010c468 100644 --- a/frappe/core/doctype/file/test_file.py +++ b/frappe/core/doctype/file/test_file.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import base64 import frappe import os diff --git a/frappe/core/doctype/has_domain/has_domain.py b/frappe/core/doctype/has_domain/has_domain.py index 6381996035..2220656a2e 100644 --- a/frappe/core/doctype/has_domain/has_domain.py +++ b/frappe/core/doctype/has_domain/has_domain.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/has_role/has_role.py b/frappe/core/doctype/has_role/has_role.py index 45e76c85a1..51d86c7b0a 100644 --- a/frappe/core/doctype/has_role/has_role.py +++ b/frappe/core/doctype/has_role/has_role.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/installed_application/installed_application.py b/frappe/core/doctype/installed_application/installed_application.py index 6bb12afc49..f53a6424eb 100644 --- a/frappe/core/doctype/installed_application/installed_application.py +++ b/frappe/core/doctype/installed_application/installed_application.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/core/doctype/installed_applications/installed_applications.py b/frappe/core/doctype/installed_applications/installed_applications.py index 4e6eadf07e..b61555f57e 100644 --- a/frappe/core/doctype/installed_applications/installed_applications.py +++ b/frappe/core/doctype/installed_applications/installed_applications.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/core/doctype/installed_applications/test_installed_applications.py b/frappe/core/doctype/installed_applications/test_installed_applications.py index ab9b849fa1..1d57fd2cd8 100644 --- a/frappe/core/doctype/installed_applications/test_installed_applications.py +++ b/frappe/core/doctype/installed_applications/test_installed_applications.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/language/language.py b/frappe/core/doctype/language/language.py index fb18abdf5e..01c8553e10 100644 --- a/frappe/core/doctype/language/language.py +++ b/frappe/core/doctype/language/language.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, json, re from frappe import _ from frappe.model.document import Document diff --git a/frappe/core/doctype/language/test_language.py b/frappe/core/doctype/language/test_language.py index a4f35dd77b..837594247f 100644 --- a/frappe/core/doctype/language/test_language.py +++ b/frappe/core/doctype/language/test_language.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/log_setting_user/log_setting_user.py b/frappe/core/doctype/log_setting_user/log_setting_user.py index df6d55f0a9..64728b2c2b 100644 --- a/frappe/core/doctype/log_setting_user/log_setting_user.py +++ b/frappe/core/doctype/log_setting_user/log_setting_user.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/core/doctype/log_setting_user/test_log_setting_user.py b/frappe/core/doctype/log_setting_user/test_log_setting_user.py index 507c02d87d..c58b8faa66 100644 --- a/frappe/core/doctype/log_setting_user/test_log_setting_user.py +++ b/frappe/core/doctype/log_setting_user/test_log_setting_user.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/log_settings/log_settings.py b/frappe/core/doctype/log_settings/log_settings.py index 08e61d3289..e73aa8dac1 100644 --- a/frappe/core/doctype/log_settings/log_settings.py +++ b/frappe/core/doctype/log_settings/log_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document diff --git a/frappe/core/doctype/log_settings/test_log_settings.py b/frappe/core/doctype/log_settings/test_log_settings.py index 2824c71c88..8e0c9c3f23 100644 --- a/frappe/core/doctype/log_settings/test_log_settings.py +++ b/frappe/core/doctype/log_settings/test_log_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/module_def/__init__.py b/frappe/core/doctype/module_def/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/module_def/__init__.py +++ b/frappe/core/doctype/module_def/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/module_def/module_def.py b/frappe/core/doctype/module_def/module_def.py index 7e63572162..68025c83bb 100644 --- a/frappe/core/doctype/module_def/module_def.py +++ b/frappe/core/doctype/module_def/module_def.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, os, json from frappe.model.document import Document diff --git a/frappe/core/doctype/module_def/test_module_def.py b/frappe/core/doctype/module_def/test_module_def.py index 1f9bea4768..3a3ceb4b57 100644 --- a/frappe/core/doctype/module_def/test_module_def.py +++ b/frappe/core/doctype/module_def/test_module_def.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/module_profile/module_profile.py b/frappe/core/doctype/module_profile/module_profile.py index 4f392353ac..373e5078d0 100644 --- a/frappe/core/doctype/module_profile/module_profile.py +++ b/frappe/core/doctype/module_profile/module_profile.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class ModuleProfile(Document): diff --git a/frappe/core/doctype/module_profile/test_module_profile.py b/frappe/core/doctype/module_profile/test_module_profile.py index 400053d22c..e0d9c13371 100644 --- a/frappe/core/doctype/module_profile/test_module_profile.py +++ b/frappe/core/doctype/module_profile/test_module_profile.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals import frappe import unittest diff --git a/frappe/core/doctype/navbar_item/navbar_item.py b/frappe/core/doctype/navbar_item/navbar_item.py index 614aee8eaf..a8fa611374 100644 --- a/frappe/core/doctype/navbar_item/navbar_item.py +++ b/frappe/core/doctype/navbar_item/navbar_item.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/core/doctype/navbar_item/test_navbar_item.py b/frappe/core/doctype/navbar_item/test_navbar_item.py index 192e8fe42a..85852a45e8 100644 --- a/frappe/core/doctype/navbar_item/test_navbar_item.py +++ b/frappe/core/doctype/navbar_item/test_navbar_item.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/navbar_settings/navbar_settings.py b/frappe/core/doctype/navbar_settings/navbar_settings.py index 2244bc9e4e..60aec67a00 100644 --- a/frappe/core/doctype/navbar_settings/navbar_settings.py +++ b/frappe/core/doctype/navbar_settings/navbar_settings.py @@ -2,7 +2,6 @@ # 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 from frappe import _ diff --git a/frappe/core/doctype/navbar_settings/test_navbar_settings.py b/frappe/core/doctype/navbar_settings/test_navbar_settings.py index ed423b0f27..4d1ee72815 100644 --- a/frappe/core/doctype/navbar_settings/test_navbar_settings.py +++ b/frappe/core/doctype/navbar_settings/test_navbar_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/page/__init__.py b/frappe/core/doctype/page/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/page/__init__.py +++ b/frappe/core/doctype/page/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/page/page.py b/frappe/core/doctype/page/page.py index bdec350efd..0ba0e309dd 100644 --- a/frappe/core/doctype/page/page.py +++ b/frappe/core/doctype/page/page.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import os from frappe.model.document import Document @@ -11,7 +10,6 @@ from frappe import conf, _, safe_decode from frappe.desk.form.meta import get_code_files_via_hooks, get_js from frappe.desk.utils import validate_route_conflict from frappe.core.doctype.custom_role.custom_role import get_custom_allowed_roles -from six import text_type class Page(Document): def autoname(self): diff --git a/frappe/core/doctype/page/test_page.py b/frappe/core/doctype/page/test_page.py index f7b3952a5b..18b4aea2c8 100644 --- a/frappe/core/doctype/page/test_page.py +++ b/frappe/core/doctype/page/test_page.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/patch_log/patch_log.py b/frappe/core/doctype/patch_log/patch_log.py index 3103d44af4..cc66955eb8 100644 --- a/frappe/core/doctype/patch_log/patch_log.py +++ b/frappe/core/doctype/patch_log/patch_log.py @@ -3,7 +3,6 @@ # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/patch_log/test_patch_log.py b/frappe/core/doctype/patch_log/test_patch_log.py index 0a7f22a78b..d0690ecee0 100644 --- a/frappe/core/doctype/patch_log/test_patch_log.py +++ b/frappe/core/doctype/patch_log/test_patch_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/payment_gateway/payment_gateway.py b/frappe/core/doctype/payment_gateway/payment_gateway.py index 80799e311b..1459635b01 100644 --- a/frappe/core/doctype/payment_gateway/payment_gateway.py +++ b/frappe/core/doctype/payment_gateway/payment_gateway.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/payment_gateway/test_payment_gateway.py b/frappe/core/doctype/payment_gateway/test_payment_gateway.py index 2faf1a7fb4..66f899bd27 100644 --- a/frappe/core/doctype/payment_gateway/test_payment_gateway.py +++ b/frappe/core/doctype/payment_gateway/test_payment_gateway.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/prepared_report/prepared_report.py b/frappe/core/doctype/prepared_report/prepared_report.py index c27853f460..c68bb6a4f1 100644 --- a/frappe/core/doctype/prepared_report/prepared_report.py +++ b/frappe/core/doctype/prepared_report/prepared_report.py @@ -3,8 +3,6 @@ # For license information, please see license.txt -from __future__ import unicode_literals - import json import frappe diff --git a/frappe/core/doctype/prepared_report/test_prepared_report.py b/frappe/core/doctype/prepared_report/test_prepared_report.py index 17845be521..ef324dd01a 100644 --- a/frappe/core/doctype/prepared_report/test_prepared_report.py +++ b/frappe/core/doctype/prepared_report/test_prepared_report.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest import json diff --git a/frappe/core/doctype/report/__init__.py b/frappe/core/doctype/report/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/report/__init__.py +++ b/frappe/core/doctype/report/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/report/report.py b/frappe/core/doctype/report/report.py index 8a0f9a99f5..a5c61fa436 100644 --- a/frappe/core/doctype/report/report.py +++ b/frappe/core/doctype/report/report.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals import frappe import json, datetime from frappe import _, scrub @@ -13,7 +11,6 @@ from frappe.modules import make_boilerplate from frappe.core.doctype.page.page import delete_custom_role from frappe.core.doctype.custom_role.custom_role import get_custom_allowed_roles from frappe.desk.reportview import append_totals_row -from six import iteritems from frappe.utils.safe_exec import safe_exec @@ -238,7 +235,7 @@ class Report(Document): _filters = params.get('filters') or [] if filters: - for key, value in iteritems(filters): + for key, value in filters.items(): condition, _value = '=', value if isinstance(value, (list, tuple)): condition, _value = value diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py index d09799ca69..9d0c0b9af0 100644 --- a/frappe/core/doctype/report/test_report.py +++ b/frappe/core/doctype/report/test_report.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals import frappe, json, os import unittest from frappe.desk.query_report import run, save_report diff --git a/frappe/core/doctype/report_column/report_column.py b/frappe/core/doctype/report_column/report_column.py index 69c88b7bda..f9078d820d 100644 --- a/frappe/core/doctype/report_column/report_column.py +++ b/frappe/core/doctype/report_column/report_column.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/core/doctype/report_filter/report_filter.py b/frappe/core/doctype/report_filter/report_filter.py index d85a1a5a65..ccdcc0eb6f 100644 --- a/frappe/core/doctype/report_filter/report_filter.py +++ b/frappe/core/doctype/report_filter/report_filter.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/core/doctype/role/__init__.py b/frappe/core/doctype/role/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/doctype/role/__init__.py +++ b/frappe/core/doctype/role/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/doctype/role/role.py b/frappe/core/doctype/role/role.py index a1523db0dd..02482c75ca 100644 --- a/frappe/core/doctype/role/role.py +++ b/frappe/core/doctype/role/role.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/role/test_role.py b/frappe/core/doctype/role/test_role.py index 6459a72c98..471f6cac43 100644 --- a/frappe/core/doctype/role/test_role.py +++ b/frappe/core/doctype/role/test_role.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py b/frappe/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py index 77b523987c..59f34a1483 100644 --- a/frappe/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py +++ b/frappe/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.core.doctype.report.report import is_prepared_report_disabled from frappe.model.document import Document diff --git a/frappe/core/doctype/role_profile/role_profile.py b/frappe/core/doctype/role_profile/role_profile.py index 4def834adb..0f58da5b5e 100644 --- a/frappe/core/doctype/role_profile/role_profile.py +++ b/frappe/core/doctype/role_profile/role_profile.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class RoleProfile(Document): diff --git a/frappe/core/doctype/role_profile/test_role_profile.py b/frappe/core/doctype/role_profile/test_role_profile.py index 975453e8d1..53e0a1b043 100644 --- a/frappe/core/doctype/role_profile/test_role_profile.py +++ b/frappe/core/doctype/role_profile/test_role_profile.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals import frappe import unittest diff --git a/frappe/core/doctype/scheduled_job_log/scheduled_job_log.py b/frappe/core/doctype/scheduled_job_log/scheduled_job_log.py index 26871c9adf..7f54a3b6ae 100644 --- a/frappe/core/doctype/scheduled_job_log/scheduled_job_log.py +++ b/frappe/core/doctype/scheduled_job_log/scheduled_job_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/scheduled_job_log/test_scheduled_job_log.py b/frappe/core/doctype/scheduled_job_log/test_scheduled_job_log.py index 1e5290425b..85471d0d71 100644 --- a/frappe/core/doctype/scheduled_job_log/test_scheduled_job_log.py +++ b/frappe/core/doctype/scheduled_job_log/test_scheduled_job_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py index d0a65defa4..a071cfe9a9 100644 --- a/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/test_scheduled_job_type.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from frappe.utils import get_datetime diff --git a/frappe/core/doctype/server_script/server_script.py b/frappe/core/doctype/server_script/server_script.py index f80a067cf1..d26fe5a188 100644 --- a/frappe/core/doctype/server_script/server_script.py +++ b/frappe/core/doctype/server_script/server_script.py @@ -2,8 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import ast from types import FunctionType, MethodType, ModuleType from typing import Dict, List diff --git a/frappe/core/doctype/server_script/test_server_script.py b/frappe/core/doctype/server_script/test_server_script.py index aac8b3deed..c39fcfa0d0 100644 --- a/frappe/core/doctype/server_script/test_server_script.py +++ b/frappe/core/doctype/server_script/test_server_script.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest import requests diff --git a/frappe/core/doctype/session_default/session_default.py b/frappe/core/doctype/session_default/session_default.py index 8a8db46ff1..70ff103111 100644 --- a/frappe/core/doctype/session_default/session_default.py +++ b/frappe/core/doctype/session_default/session_default.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/session_default_settings/session_default_settings.py b/frappe/core/doctype/session_default_settings/session_default_settings.py index 7b4bd19e9a..25f7522c86 100644 --- a/frappe/core/doctype/session_default_settings/session_default_settings.py +++ b/frappe/core/doctype/session_default_settings/session_default_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ import json diff --git a/frappe/core/doctype/session_default_settings/test_session_default_settings.py b/frappe/core/doctype/session_default_settings/test_session_default_settings.py index 12aa14d343..7d20015b66 100644 --- a/frappe/core/doctype/session_default_settings/test_session_default_settings.py +++ b/frappe/core/doctype/session_default_settings/test_session_default_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from frappe.core.doctype.session_default_settings.session_default_settings import set_session_default_values, clear_session_defaults diff --git a/frappe/core/doctype/sms_parameter/__init__.py b/frappe/core/doctype/sms_parameter/__init__.py index baffc48825..8b13789179 100755 --- a/frappe/core/doctype/sms_parameter/__init__.py +++ b/frappe/core/doctype/sms_parameter/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/core/doctype/sms_parameter/sms_parameter.py b/frappe/core/doctype/sms_parameter/sms_parameter.py index 08b220b61a..d1fb1c53db 100644 --- a/frappe/core/doctype/sms_parameter/sms_parameter.py +++ b/frappe/core/doctype/sms_parameter/sms_parameter.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/sms_settings/__init__.py b/frappe/core/doctype/sms_settings/__init__.py index baffc48825..8b13789179 100755 --- a/frappe/core/doctype/sms_settings/__init__.py +++ b/frappe/core/doctype/sms_settings/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/core/doctype/sms_settings/sms_settings.py b/frappe/core/doctype/sms_settings/sms_settings.py index ac835108c1..58a0ff08f6 100644 --- a/frappe/core/doctype/sms_settings/sms_settings.py +++ b/frappe/core/doctype/sms_settings/sms_settings.py @@ -2,15 +2,12 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe import _, throw, msgprint from frappe.utils import nowdate from frappe.model.document import Document -import six -from six import string_types class SMSSettings(Document): pass @@ -35,20 +32,20 @@ def validate_receiver_nos(receiver_list): @frappe.whitelist() def get_contact_number(contact_name, ref_doctype, ref_name): "returns mobile number of the contact" - number = frappe.db.sql("""select mobile_no, phone from tabContact - where name=%s + number = frappe.db.sql("""select mobile_no, phone from tabContact + where name=%s and exists( select name from `tabDynamic Link` where link_doctype=%s and link_name=%s ) """, (contact_name, ref_doctype, ref_name)) - + return number and (number[0][0] or number[0][1]) or '' @frappe.whitelist() def send_sms(receiver_list, msg, sender_name = '', success_msg = True): import json - if isinstance(receiver_list, string_types): + if isinstance(receiver_list, str): receiver_list = json.loads(receiver_list) if not isinstance(receiver_list, list): receiver_list = [receiver_list] diff --git a/frappe/core/doctype/sms_settings/test_sms_settings.py b/frappe/core/doctype/sms_settings/test_sms_settings.py index b14fd3e4a0..862f5e3965 100644 --- a/frappe/core/doctype/sms_settings/test_sms_settings.py +++ b/frappe/core/doctype/sms_settings/test_sms_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/success_action/success_action.py b/frappe/core/doctype/success_action/success_action.py index f8b99f1fea..4ebd3d250b 100644 --- a/frappe/core/doctype/success_action/success_action.py +++ b/frappe/core/doctype/success_action/success_action.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class SuccessAction(Document): diff --git a/frappe/core/doctype/system_settings/system_settings.py b/frappe/core/doctype/system_settings/system_settings.py index 05aaca81de..466914569f 100644 --- a/frappe/core/doctype/system_settings/system_settings.py +++ b/frappe/core/doctype/system_settings/system_settings.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document diff --git a/frappe/core/doctype/system_settings/test_system_settings.py b/frappe/core/doctype/system_settings/test_system_settings.py index 82d0ddbd7c..a65c602abe 100644 --- a/frappe/core/doctype/system_settings/test_system_settings.py +++ b/frappe/core/doctype/system_settings/test_system_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/test/test.py b/frappe/core/doctype/test/test.py index 7e91b1cd4a..98e36e6a30 100644 --- a/frappe/core/doctype/test/test.py +++ b/frappe/core/doctype/test/test.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document import json diff --git a/frappe/core/doctype/test/test_test.py b/frappe/core/doctype/test/test_test.py index 2a9b43bf95..d8ca975d63 100644 --- a/frappe/core/doctype/test/test_test.py +++ b/frappe/core/doctype/test/test_test.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/transaction_log/test_transaction_log.py b/frappe/core/doctype/transaction_log/test_transaction_log.py index 164a683c38..0d9b9353d0 100644 --- a/frappe/core/doctype/transaction_log/test_transaction_log.py +++ b/frappe/core/doctype/transaction_log/test_transaction_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest import hashlib @@ -35,7 +33,7 @@ class TestTransactionLog(unittest.TestCase): sha = hashlib.sha256() sha.update( - frappe.safe_encode(str(third_log.transaction_hash)) + + frappe.safe_encode(str(third_log.transaction_hash)) + frappe.safe_encode(str(second_log.chaining_hash)) ) diff --git a/frappe/core/doctype/transaction_log/transaction_log.py b/frappe/core/doctype/transaction_log/transaction_log.py index b7ea6cac60..58d0b3d176 100644 --- a/frappe/core/doctype/transaction_log/transaction_log.py +++ b/frappe/core/doctype/transaction_log/transaction_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document diff --git a/frappe/core/doctype/translation/test_translation.py b/frappe/core/doctype/translation/test_translation.py index 12899dddf7..ae1293b38f 100644 --- a/frappe/core/doctype/translation/test_translation.py +++ b/frappe/core/doctype/translation/test_translation.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/core/doctype/translation/translation.py b/frappe/core/doctype/translation/translation.py index 177dea401f..b1f4642791 100644 --- a/frappe/core/doctype/translation/translation.py +++ b/frappe/core/doctype/translation/translation.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils import strip_html_tags, is_html diff --git a/frappe/core/doctype/user/test_user.py b/frappe/core/doctype/user/test_user.py index 5bea767934..392128834d 100644 --- a/frappe/core/doctype/user/test_user.py +++ b/frappe/core/doctype/user/test_user.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, unittest, uuid from frappe.model.delete_doc import delete_doc diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index a4d13a57e0..3fa31cbf80 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -1,10 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function - from bs4 import BeautifulSoup - import frappe import frappe.share import frappe.defaults diff --git a/frappe/core/doctype/user_document_type/user_document_type.py b/frappe/core/doctype/user_document_type/user_document_type.py index 979bfcb250..48dbf87b3d 100644 --- a/frappe/core/doctype/user_document_type/user_document_type.py +++ b/frappe/core/doctype/user_document_type/user_document_type.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/user_email/user_email.py b/frappe/core/doctype/user_email/user_email.py index a0ce2e169d..729aa03444 100644 --- a/frappe/core/doctype/user_email/user_email.py +++ b/frappe/core/doctype/user_email/user_email.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/user_group/test_user_group.py b/frappe/core/doctype/user_group/test_user_group.py index c7e28f3d31..2f89d032e1 100644 --- a/frappe/core/doctype/user_group/test_user_group.py +++ b/frappe/core/doctype/user_group/test_user_group.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/user_group/user_group.py b/frappe/core/doctype/user_group/user_group.py index b1d0fede4c..178775d407 100644 --- a/frappe/core/doctype/user_group/user_group.py +++ b/frappe/core/doctype/user_group/user_group.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document import frappe diff --git a/frappe/core/doctype/user_group_member/test_user_group_member.py b/frappe/core/doctype/user_group_member/test_user_group_member.py index 38aade4608..8dbaed9e65 100644 --- a/frappe/core/doctype/user_group_member/test_user_group_member.py +++ b/frappe/core/doctype/user_group_member/test_user_group_member.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/user_group_member/user_group_member.py b/frappe/core/doctype/user_group_member/user_group_member.py index 4d0656913d..f85ddc3209 100644 --- a/frappe/core/doctype/user_group_member/user_group_member.py +++ b/frappe/core/doctype/user_group_member/user_group_member.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/user_permission/test_user_permission.py b/frappe/core/doctype/user_permission/test_user_permission.py index 47651fee72..1a442b53e7 100644 --- a/frappe/core/doctype/user_permission/test_user_permission.py +++ b/frappe/core/doctype/user_permission/test_user_permission.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals from frappe.core.doctype.user_permission.user_permission import add_user_permissions, remove_applicable from frappe.permissions import has_user_permission from frappe.core.doctype.doctype.test_doctype import new_doctype diff --git a/frappe/core/doctype/user_permission/user_permission.py b/frappe/core/doctype/user_permission/user_permission.py index fec5019ca9..42ca4d7a14 100644 --- a/frappe/core/doctype/user_permission/user_permission.py +++ b/frappe/core/doctype/user_permission/user_permission.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, json from frappe.model.document import Document from frappe.permissions import (get_valid_perms, update_permission_property) diff --git a/frappe/core/doctype/user_select_document_type/user_select_document_type.py b/frappe/core/doctype/user_select_document_type/user_select_document_type.py index 373eaf7aa3..13e3f0d351 100644 --- a/frappe/core/doctype/user_select_document_type/user_select_document_type.py +++ b/frappe/core/doctype/user_select_document_type/user_select_document_type.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/user_social_login/user_social_login.py b/frappe/core/doctype/user_social_login/user_social_login.py index cc6c3d0e05..4a34006d2b 100644 --- a/frappe/core/doctype/user_social_login/user_social_login.py +++ b/frappe/core/doctype/user_social_login/user_social_login.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class UserSocialLogin(Document): diff --git a/frappe/core/doctype/user_type/test_user_type.py b/frappe/core/doctype/user_type/test_user_type.py index de61e0f476..1c47f02bbb 100644 --- a/frappe/core/doctype/user_type/test_user_type.py +++ b/frappe/core/doctype/user_type/test_user_type.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/core/doctype/user_type/user_type.py b/frappe/core/doctype/user_type/user_type.py index 0e8b692416..e7d06c45f2 100644 --- a/frappe/core/doctype/user_type/user_type.py +++ b/frappe/core/doctype/user_type/user_type.py @@ -2,10 +2,8 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ -from six import iteritems from frappe.utils import get_link_to_form from frappe.config import get_modules_from_app from frappe.permissions import add_permission, add_user_permission @@ -247,7 +245,7 @@ def apply_permissions_for_non_standard_user_type(doc, method=None): if not user_types: return - for user_type, data in iteritems(user_types): + for user_type, data in user_types.items(): if (not doc.get(data[1]) or doc.doctype != data[0]): continue diff --git a/frappe/core/doctype/user_type/user_type_dashboard.py b/frappe/core/doctype/user_type/user_type_dashboard.py index 7e14198bca..6cdd2f82a5 100644 --- a/frappe/core/doctype/user_type/user_type_dashboard.py +++ b/frappe/core/doctype/user_type/user_type_dashboard.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + from frappe import _ def get_data(): diff --git a/frappe/core/doctype/user_type_module/user_type_module.py b/frappe/core/doctype/user_type_module/user_type_module.py index 6cd2cbacdb..9afbcd294d 100644 --- a/frappe/core/doctype/user_type_module/user_type_module.py +++ b/frappe/core/doctype/user_type_module/user_type_module.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/core/doctype/version/test_version.py b/frappe/core/doctype/version/test_version.py index 51b3c21f58..f6c099c4ea 100644 --- a/frappe/core/doctype/version/test_version.py +++ b/frappe/core/doctype/version/test_version.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest, copy from frappe.test_runner import make_test_objects diff --git a/frappe/core/doctype/version/version.py b/frappe/core/doctype/version/version.py index 7654db4ae5..a1bd851346 100644 --- a/frappe/core/doctype/version/version.py +++ b/frappe/core/doctype/version/version.py @@ -3,7 +3,6 @@ # For license information, please see license.txt -from __future__ import unicode_literals import frappe, json from frappe.model.document import Document diff --git a/frappe/core/doctype/view_log/test_view_log.py b/frappe/core/doctype/view_log/test_view_log.py index 83967a39a4..025f3d8ad9 100644 --- a/frappe/core/doctype/view_log/test_view_log.py +++ b/frappe/core/doctype/view_log/test_view_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest @@ -25,11 +23,11 @@ class TestViewLog(unittest.TestCase): # load the form getdoc('Event', ev.name) a = frappe.get_value( - doctype="View Log", + doctype="View Log", filters={ "reference_doctype": "Event", "reference_name": ev.name - }, + }, fieldname=['viewed_by'] ) diff --git a/frappe/core/doctype/view_log/view_log.py b/frappe/core/doctype/view_log/view_log.py index 45e98e37c7..242250be8b 100644 --- a/frappe/core/doctype/view_log/view_log.py +++ b/frappe/core/doctype/view_log/view_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/core/notifications.py b/frappe/core/notifications.py index 771a15a2e7..707de43f28 100644 --- a/frappe/core/notifications.py +++ b/frappe/core/notifications.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def get_notification_config(): diff --git a/frappe/core/page/__init__.py b/frappe/core/page/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/core/page/__init__.py +++ b/frappe/core/page/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/core/page/permission_manager/permission_manager.py b/frappe/core/page/permission_manager/permission_manager.py index 1c215eb6e1..15c7cb55ae 100644 --- a/frappe/core/page/permission_manager/permission_manager.py +++ b/frappe/core/page/permission_manager/permission_manager.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ import frappe.defaults diff --git a/frappe/core/report/permitted_documents_for_user/permitted_documents_for_user.py b/frappe/core/report/permitted_documents_for_user/permitted_documents_for_user.py index c928939119..13602ca777 100644 --- a/frappe/core/report/permitted_documents_for_user/permitted_documents_for_user.py +++ b/frappe/core/report/permitted_documents_for_user/permitted_documents_for_user.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _, throw import frappe.utils.user diff --git a/frappe/core/report/transaction_log_report/transaction_log_report.py b/frappe/core/report/transaction_log_report/transaction_log_report.py index 9d84901f22..ff8d8345d6 100644 --- a/frappe/core/report/transaction_log_report/transaction_log_report.py +++ b/frappe/core/report/transaction_log_report/transaction_log_report.py @@ -1,7 +1,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import hashlib from frappe import _ diff --git a/frappe/core/utils.py b/frappe/core/utils.py index 55cfbc34d7..9b8ee3a326 100644 --- a/frappe/core/utils.py +++ b/frappe/core/utils.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe diff --git a/frappe/core/web_form/edit_profile/edit_profile.py b/frappe/core/web_form/edit_profile/edit_profile.py index 2334f8b26d..e1ada61927 100644 --- a/frappe/core/web_form/edit_profile/edit_profile.py +++ b/frappe/core/web_form/edit_profile/edit_profile.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe def get_context(context): diff --git a/frappe/custom/doctype/client_script/__init__.py b/frappe/custom/doctype/client_script/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/custom/doctype/client_script/__init__.py +++ b/frappe/custom/doctype/client_script/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/custom/doctype/client_script/client_script.py b/frappe/custom/doctype/client_script/client_script.py index 049f979263..9c098fe8c9 100644 --- a/frappe/custom/doctype/client_script/client_script.py +++ b/frappe/custom/doctype/client_script/client_script.py @@ -1,6 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ diff --git a/frappe/custom/doctype/client_script/test_client_script.py b/frappe/custom/doctype/client_script/test_client_script.py index de113c1ce7..b8358468b9 100644 --- a/frappe/custom/doctype/client_script/test_client_script.py +++ b/frappe/custom/doctype/client_script/test_client_script.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/custom/doctype/custom_field/__init__.py b/frappe/custom/doctype/custom_field/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/custom/doctype/custom_field/__init__.py +++ b/frappe/custom/doctype/custom_field/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py index 39aff8b4a7..7e6ea1875a 100644 --- a/frappe/custom/doctype/custom_field/custom_field.py +++ b/frappe/custom/doctype/custom_field/custom_field.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import json from frappe.utils import cstr diff --git a/frappe/custom/doctype/custom_field/test_custom_field.py b/frappe/custom/doctype/custom_field/test_custom_field.py index 819917050a..3196b66ee8 100644 --- a/frappe/custom/doctype/custom_field/test_custom_field.py +++ b/frappe/custom/doctype/custom_field/test_custom_field.py @@ -3,8 +3,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/custom/doctype/customize_form/__init__.py b/frappe/custom/doctype/customize_form/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/custom/doctype/customize_form/__init__.py +++ b/frappe/custom/doctype/customize_form/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index be0dded99c..8bcc6cf059 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals """ Customize Form is a Single DocType used to mask the Property Setter Thus providing a better UI from user perspective diff --git a/frappe/custom/doctype/customize_form/test_customize_form.py b/frappe/custom/doctype/customize_form/test_customize_form.py index 75555a8205..58bdcf9a18 100644 --- a/frappe/custom/doctype/customize_form/test_customize_form.py +++ b/frappe/custom/doctype/customize_form/test_customize_form.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, unittest, json from frappe.test_runner import make_test_records_for_doctype from frappe.core.doctype.doctype.doctype import InvalidFieldNameError diff --git a/frappe/custom/doctype/customize_form_field/__init__.py b/frappe/custom/doctype/customize_form_field/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/custom/doctype/customize_form_field/__init__.py +++ b/frappe/custom/doctype/customize_form_field/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/custom/doctype/customize_form_field/customize_form_field.py b/frappe/custom/doctype/customize_form_field/customize_form_field.py index 20c206328c..f288e70754 100644 --- a/frappe/custom/doctype/customize_form_field/customize_form_field.py +++ b/frappe/custom/doctype/customize_form_field/customize_form_field.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/custom/doctype/doctype_layout/doctype_layout.py b/frappe/custom/doctype/doctype_layout/doctype_layout.py index a4fe9a9bce..0dc320353d 100644 --- a/frappe/custom/doctype/doctype_layout/doctype_layout.py +++ b/frappe/custom/doctype/doctype_layout/doctype_layout.py @@ -2,8 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - from frappe.model.document import Document from frappe.desk.utils import slug diff --git a/frappe/custom/doctype/doctype_layout/test_doctype_layout.py b/frappe/custom/doctype/doctype_layout/test_doctype_layout.py index 5765c86262..dcde3c00a4 100644 --- a/frappe/custom/doctype/doctype_layout/test_doctype_layout.py +++ b/frappe/custom/doctype/doctype_layout/test_doctype_layout.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/custom/doctype/doctype_layout_field/doctype_layout_field.py b/frappe/custom/doctype/doctype_layout_field/doctype_layout_field.py index 7f8c8edfce..c1e963602f 100644 --- a/frappe/custom/doctype/doctype_layout_field/doctype_layout_field.py +++ b/frappe/custom/doctype/doctype_layout_field/doctype_layout_field.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/custom/doctype/property_setter/__init__.py b/frappe/custom/doctype/property_setter/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/custom/doctype/property_setter/__init__.py +++ b/frappe/custom/doctype/property_setter/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/custom/doctype/property_setter/property_setter.py b/frappe/custom/doctype/property_setter/property_setter.py index 56e5829271..2a6c06b70a 100644 --- a/frappe/custom/doctype/property_setter/property_setter.py +++ b/frappe/custom/doctype/property_setter/property_setter.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ diff --git a/frappe/custom/doctype/property_setter/test_property_setter.py b/frappe/custom/doctype/property_setter/test_property_setter.py index 33e7d288a4..4d4de66d51 100644 --- a/frappe/custom/doctype/property_setter/test_property_setter.py +++ b/frappe/custom/doctype/property_setter/test_property_setter.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/custom/doctype/test_rename_new/test_rename_new.py b/frappe/custom/doctype/test_rename_new/test_rename_new.py index aa5984e466..32d2396b2b 100644 --- a/frappe/custom/doctype/test_rename_new/test_rename_new.py +++ b/frappe/custom/doctype/test_rename_new/test_rename_new.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/custom/doctype/test_rename_new/test_test_rename_new.py b/frappe/custom/doctype/test_rename_new/test_test_rename_new.py index 554efbae45..b3ea4818de 100644 --- a/frappe/custom/doctype/test_rename_new/test_test_rename_new.py +++ b/frappe/custom/doctype/test_rename_new/test_test_rename_new.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/data_migration/doctype/data_migration_connector/connectors/base.py b/frappe/data_migration/doctype/data_migration_connector/connectors/base.py index 97f9f5f4a3..5eca7cfac5 100644 --- a/frappe/data_migration/doctype/data_migration_connector/connectors/base.py +++ b/frappe/data_migration/doctype/data_migration_connector/connectors/base.py @@ -1,10 +1,7 @@ -from __future__ import unicode_literals -from six import with_metaclass from abc import ABCMeta, abstractmethod from frappe.utils.password import get_decrypted_password -class BaseConnection(with_metaclass(ABCMeta)): - +class BaseConnection(metaclass=ABCMeta): @abstractmethod def get(self, remote_objectname, fields=None, filters=None, start=0, page_length=10): pass diff --git a/frappe/data_migration/doctype/data_migration_connector/connectors/frappe_connection.py b/frappe/data_migration/doctype/data_migration_connector/connectors/frappe_connection.py index 6ee41afdf2..473a15c2dc 100644 --- a/frappe/data_migration/doctype/data_migration_connector/connectors/frappe_connection.py +++ b/frappe/data_migration/doctype/data_migration_connector/connectors/frappe_connection.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.frappeclient import FrappeClient from .base import BaseConnection diff --git a/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.py b/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.py index 793dfe6694..d1137f2e67 100644 --- a/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.py +++ b/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, os from frappe.model.document import Document from frappe import _ @@ -76,8 +75,7 @@ def get_connection_class(python_module): return _class -connection_boilerplate = """from __future__ import unicode_literals -from frappe.data_migration.doctype.data_migration_connector.connectors.base import BaseConnection +connection_boilerplate = """from frappe.data_migration.doctype.data_migration_connector.connectors.base import BaseConnection class {connection_class}(BaseConnection): def __init__(self, connector): diff --git a/frappe/data_migration/doctype/data_migration_connector/test_data_migration_connector.py b/frappe/data_migration/doctype/data_migration_connector/test_data_migration_connector.py index a6e30fbe44..fd45f86ec1 100644 --- a/frappe/data_migration/doctype/data_migration_connector/test_data_migration_connector.py +++ b/frappe/data_migration/doctype/data_migration_connector/test_data_migration_connector.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals import unittest class TestDataMigrationConnector(unittest.TestCase): diff --git a/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py b/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py index 1cc54a0d1a..5cb20ba56c 100644 --- a/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py +++ b/frappe/data_migration/doctype/data_migration_mapping/data_migration_mapping.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils.safe_exec import get_safe_globals diff --git a/frappe/data_migration/doctype/data_migration_mapping/test_data_migration_mapping.py b/frappe/data_migration/doctype/data_migration_mapping/test_data_migration_mapping.py index e6f0ce2796..df11fc0522 100644 --- a/frappe/data_migration/doctype/data_migration_mapping/test_data_migration_mapping.py +++ b/frappe/data_migration/doctype/data_migration_mapping/test_data_migration_mapping.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals import unittest class TestDataMigrationMapping(unittest.TestCase): diff --git a/frappe/data_migration/doctype/data_migration_mapping_detail/data_migration_mapping_detail.py b/frappe/data_migration/doctype/data_migration_mapping_detail/data_migration_mapping_detail.py index 1ccdf76eed..6d3ef50937 100644 --- a/frappe/data_migration/doctype/data_migration_mapping_detail/data_migration_mapping_detail.py +++ b/frappe/data_migration/doctype/data_migration_mapping_detail/data_migration_mapping_detail.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class DataMigrationMappingDetail(Document): diff --git a/frappe/data_migration/doctype/data_migration_plan/data_migration_plan.py b/frappe/data_migration/doctype/data_migration_plan/data_migration_plan.py index 5cd195f4fe..a8d0e40a4c 100644 --- a/frappe/data_migration/doctype/data_migration_plan/data_migration_plan.py +++ b/frappe/data_migration/doctype/data_migration_plan/data_migration_plan.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.modules import get_module_path, scrub_dt_dn from frappe.modules.export_file import export_to_files, create_init_py diff --git a/frappe/data_migration/doctype/data_migration_plan/test_data_migration_plan.py b/frappe/data_migration/doctype/data_migration_plan/test_data_migration_plan.py index 3a33039c3d..14c585a82d 100644 --- a/frappe/data_migration/doctype/data_migration_plan/test_data_migration_plan.py +++ b/frappe/data_migration/doctype/data_migration_plan/test_data_migration_plan.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals import unittest class TestDataMigrationPlan(unittest.TestCase): diff --git a/frappe/data_migration/doctype/data_migration_plan_mapping/data_migration_plan_mapping.py b/frappe/data_migration/doctype/data_migration_plan_mapping/data_migration_plan_mapping.py index 85f879069c..ba4cf28eb8 100644 --- a/frappe/data_migration/doctype/data_migration_plan_mapping/data_migration_plan_mapping.py +++ b/frappe/data_migration/doctype/data_migration_plan_mapping/data_migration_plan_mapping.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class DataMigrationPlanMapping(Document): diff --git a/frappe/data_migration/doctype/data_migration_run/data_migration_run.py b/frappe/data_migration/doctype/data_migration_run/data_migration_run.py index aed9c6cb1d..c35af5827b 100644 --- a/frappe/data_migration/doctype/data_migration_run/data_migration_run.py +++ b/frappe/data_migration/doctype/data_migration_run/data_migration_run.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, json, math from frappe.model.document import Document from frappe import _ diff --git a/frappe/data_migration/doctype/data_migration_run/test_data_migration_run.py b/frappe/data_migration/doctype/data_migration_run/test_data_migration_run.py index c6c3ea138c..ef7b70dca2 100644 --- a/frappe/data_migration/doctype/data_migration_run/test_data_migration_run.py +++ b/frappe/data_migration/doctype/data_migration_run/test_data_migration_run.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals import frappe, unittest class TestDataMigrationRun(unittest.TestCase): diff --git a/frappe/database/__init__.py b/frappe/database/__init__.py index 1f0d3f9bf5..a899bec3d1 100644 --- a/frappe/database/__init__.py +++ b/frappe/database/__init__.py @@ -4,8 +4,6 @@ # Database Module # -------------------- -from __future__ import unicode_literals - def setup_database(force, source_sql=None, verbose=None, no_mariadb_socket=False): import frappe if frappe.conf.db_type == 'postgres': diff --git a/frappe/database/database.py b/frappe/database/database.py index c9c1ec3909..7e8d2da43b 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -4,8 +4,6 @@ # Database Module # -------------------- -from __future__ import unicode_literals - import re import time import frappe @@ -19,13 +17,6 @@ from frappe.utils import now, getdate, cast_fieldtype, get_datetime from frappe.model.utils.link_count import flush_local_link_count from frappe.utils import cint -# imports - compatibility imports -from six import ( - integer_types, - string_types, - text_type, - iteritems -) class Database(object): """ @@ -277,7 +268,7 @@ class Database(object): for r in result: values = [] for value in r: - if as_utf8 and isinstance(value, text_type): + if as_utf8 and isinstance(value, str): value = value.encode('utf-8') values.append(value) @@ -294,7 +285,7 @@ class Database(object): """Returns true if the first row in the result has a Date, Datetime, Long Int.""" if result and result[0]: for v in result[0]: - if isinstance(v, (datetime.date, datetime.timedelta, datetime.datetime, integer_types)): + if isinstance(v, (datetime.date, datetime.timedelta, datetime.datetime, int)): return True if formatted and isinstance(v, (int, float)): return True @@ -312,7 +303,7 @@ class Database(object): for r in res: nr = [] for val in r: - if as_utf8 and isinstance(val, text_type): + if as_utf8 and isinstance(val, str): val = val.encode('utf-8') nr.append(val) nres.append(nr) @@ -363,7 +354,7 @@ class Database(object): # docname is a number, convert to string filters = str(filters) - if isinstance(filters, string_types): + if isinstance(filters, str): filters = { "name": filters } for f in filters: @@ -428,7 +419,7 @@ class Database(object): user = frappe.db.get_values("User", "test@example.com", "*")[0] """ out = None - if cache and isinstance(filters, string_types) and \ + if cache and isinstance(filters, str) and \ (doctype, filters, fieldname) in self.value_cache: return self.value_cache[(doctype, filters, fieldname)] @@ -440,7 +431,7 @@ class Database(object): else: fields = fieldname if fieldname!="*": - if isinstance(fieldname, string_types): + if isinstance(fieldname, str): fields = [fieldname] else: fields = fieldname @@ -461,7 +452,7 @@ class Database(object): else: out = self.get_values_from_single(fields, filters, doctype, as_dict, debug, update) - if cache and isinstance(filters, string_types): + if cache and isinstance(filters, str): self.value_cache[(doctype, filters, fieldname)] = out return out @@ -673,7 +664,7 @@ class Database(object): where field in ({0}) and doctype=%s'''.format(', '.join(['%s']*len(keys))), list(keys) + [dt], debug=debug) - for key, value in iteritems(to_update): + for key, value in to_update.items(): self.sql('''insert into `tabSingles` (doctype, field, value) values (%s, %s, %s)''', (dt, key, value), debug=debug) @@ -811,7 +802,7 @@ class Database(object): :param dt: DocType name. :param dn: Document name or filter dict.""" - if isinstance(dt, string_types): + if isinstance(dt, str): if dt!="DocType" and dt==dn: return True # single always exists (!) try: diff --git a/frappe/database/mariadb/schema.py b/frappe/database/mariadb/schema.py index 4bbecd2a2e..b40af59286 100644 --- a/frappe/database/mariadb/schema.py +++ b/frappe/database/mariadb/schema.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe from frappe import _ from frappe.database.schema import DBTable diff --git a/frappe/database/mariadb/setup_db.py b/frappe/database/mariadb/setup_db.py index 9b73d77171..6be08c66bb 100644 --- a/frappe/database/mariadb/setup_db.py +++ b/frappe/database/mariadb/setup_db.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe import os from frappe.database.db_manager import DbManager diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py index 6ac2767a71..8235277e30 100644 --- a/frappe/database/postgres/database.py +++ b/frappe/database/postgres/database.py @@ -2,7 +2,6 @@ import re import frappe import psycopg2 import psycopg2.extensions -from six import string_types from frappe.utils import cstr from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT @@ -253,7 +252,7 @@ class PostgresDatabase(Database): self.sql("""CREATE INDEX IF NOT EXISTS "{}" ON `{}`("{}")""".format(index_name, table_name, '", "'.join(fields))) def add_unique(self, doctype, fields, constraint_name=None): - if isinstance(fields, string_types): + if isinstance(fields, str): fields = [fields] if not constraint_name: constraint_name = "unique_" + "_".join(fields) diff --git a/frappe/database/postgres/setup_db.py b/frappe/database/postgres/setup_db.py index 3ee6b6a286..19ba681237 100644 --- a/frappe/database/postgres/setup_db.py +++ b/frappe/database/postgres/setup_db.py @@ -83,7 +83,6 @@ def get_root_connection(root_login=None, root_password=None): root_login = frappe.conf.get("root_login") or None if not root_login: - from six.moves import input root_login = input("Enter postgres super user: ") if not root_password: diff --git a/frappe/database/schema.py b/frappe/database/schema.py index 5f5ba06d8b..31f11dbd5e 100644 --- a/frappe/database/schema.py +++ b/frappe/database/schema.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import re import frappe diff --git a/frappe/defaults.py b/frappe/defaults.py index 4bec6677c7..fde48d71ff 100644 --- a/frappe/defaults.py +++ b/frappe/defaults.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.desk.notifications import clear_notifications from frappe.cache_manager import clear_defaults_cache, common_default_keys diff --git a/frappe/desk/__init__.py b/frappe/desk/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/desk/__init__.py +++ b/frappe/desk/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/desk/calendar.py b/frappe/desk/calendar.py index 064d870092..f00f729415 100644 --- a/frappe/desk/calendar.py +++ b/frappe/desk/calendar.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe from frappe import _ import json diff --git a/frappe/desk/desk_page.py b/frappe/desk/desk_page.py index 6c5fdc6821..d373dbda0e 100644 --- a/frappe/desk/desk_page.py +++ b/frappe/desk/desk_page.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.translate import send_translations diff --git a/frappe/desk/desktop.py b/frappe/desk/desktop.py index 1a3b1ca99b..0a7d436169 100644 --- a/frappe/desk/desktop.py +++ b/frappe/desk/desktop.py @@ -2,12 +2,10 @@ # MIT License. See license.txt # Author - Shivam Mishra -from __future__ import unicode_literals import frappe from json import loads, dumps from frappe import _, DoesNotExistError, ValidationError, _dict from frappe.boot import get_allowed_pages, get_allowed_reports -from six import string_types from functools import wraps from frappe.cache_manager import ( build_domain_restriced_doctype_cache, @@ -61,7 +59,7 @@ class Workspace: shortcuts = self.doc.shortcuts + self.extended_shortcuts for section in cards: - links = loads(section.get('links')) if isinstance(section.get('links'), string_types) else section.get('links') + links = loads(section.get('links')) if isinstance(section.get('links'), str) else section.get('links') for item in links: if self.is_item_allowed(item.get('link_to'), item.get('link_type')): return True diff --git a/frappe/desk/doctype/bulk_update/bulk_update.py b/frappe/desk/doctype/bulk_update/bulk_update.py index 9b9f7d7a73..469ee839f1 100644 --- a/frappe/desk/doctype/bulk_update/bulk_update.py +++ b/frappe/desk/doctype/bulk_update/bulk_update.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ diff --git a/frappe/desk/doctype/calendar_view/calendar_view.py b/frappe/desk/doctype/calendar_view/calendar_view.py index ae8ab1eb46..3a986f3273 100644 --- a/frappe/desk/doctype/calendar_view/calendar_view.py +++ b/frappe/desk/doctype/calendar_view/calendar_view.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class CalendarView(Document): diff --git a/frappe/desk/doctype/console_log/console_log.py b/frappe/desk/doctype/console_log/console_log.py index 635c4c1ba7..5d0f1cfa93 100644 --- a/frappe/desk/doctype/console_log/console_log.py +++ b/frappe/desk/doctype/console_log/console_log.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/desk/doctype/console_log/test_console_log.py b/frappe/desk/doctype/console_log/test_console_log.py index 04dc4f241f..3bb1605204 100644 --- a/frappe/desk/doctype/console_log/test_console_log.py +++ b/frappe/desk/doctype/console_log/test_console_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/dashboard/dashboard.py b/frappe/desk/doctype/dashboard/dashboard.py index 4e66318769..d2e79279d9 100644 --- a/frappe/desk/doctype/dashboard/dashboard.py +++ b/frappe/desk/doctype/dashboard/dashboard.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document from frappe.modules.export_file import export_to_files from frappe.config import get_modules_from_all_apps_for_user diff --git a/frappe/desk/doctype/dashboard/test_dashboard.py b/frappe/desk/doctype/dashboard/test_dashboard.py index d5485d8f70..dd1bc31d86 100644 --- a/frappe/desk/doctype/dashboard/test_dashboard.py +++ b/frappe/desk/doctype/dashboard/test_dashboard.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestDashboard(unittest.TestCase): diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py index 48b34e6cd9..3bea6eded9 100644 --- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ import datetime diff --git a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py index 72ab18385d..78d133b2d5 100644 --- a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest, frappe from frappe.utils import getdate, formatdate, get_last_day from frappe.utils.dateutils import get_period_ending, get_period diff --git a/frappe/desk/doctype/dashboard_chart_field/dashboard_chart_field.py b/frappe/desk/doctype/dashboard_chart_field/dashboard_chart_field.py index 734f27cc28..7d6f66daa2 100644 --- a/frappe/desk/doctype/dashboard_chart_field/dashboard_chart_field.py +++ b/frappe/desk/doctype/dashboard_chart_field/dashboard_chart_field.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/desk/doctype/dashboard_chart_link/dashboard_chart_link.py b/frappe/desk/doctype/dashboard_chart_link/dashboard_chart_link.py index 7cd4f9daa3..359801a303 100644 --- a/frappe/desk/doctype/dashboard_chart_link/dashboard_chart_link.py +++ b/frappe/desk/doctype/dashboard_chart_link/dashboard_chart_link.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/dashboard_chart_source/dashboard_chart_source.py b/frappe/desk/doctype/dashboard_chart_source/dashboard_chart_source.py index 6685009078..791dbc563b 100644 --- a/frappe/desk/doctype/dashboard_chart_source/dashboard_chart_source.py +++ b/frappe/desk/doctype/dashboard_chart_source/dashboard_chart_source.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, os from frappe import _ from frappe.model.document import Document diff --git a/frappe/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py b/frappe/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py index 822526b591..53fe127dfb 100644 --- a/frappe/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py +++ b/frappe/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestDashboardChartSource(unittest.TestCase): diff --git a/frappe/desk/doctype/dashboard_settings/dashboard_settings.py b/frappe/desk/doctype/dashboard_settings/dashboard_settings.py index 4697d897fc..df61c52114 100644 --- a/frappe/desk/doctype/dashboard_settings/dashboard_settings.py +++ b/frappe/desk/doctype/dashboard_settings/dashboard_settings.py @@ -2,7 +2,6 @@ # 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 import frappe diff --git a/frappe/desk/doctype/desktop_icon/desktop_icon.py b/frappe/desk/doctype/desktop_icon/desktop_icon.py index fcf10ef61d..81a79cdb09 100644 --- a/frappe/desk/doctype/desktop_icon/desktop_icon.py +++ b/frappe/desk/doctype/desktop_icon/desktop_icon.py @@ -2,14 +2,11 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import frappe from frappe import _ import json import random from frappe.model.document import Document -from six import iteritems, string_types from frappe.utils.user import UserPermissions class DesktopIcon(Document): @@ -173,7 +170,7 @@ def add_user_icon(_doctype, _report=None, label=None, link=None, type='link', st @frappe.whitelist() def set_order(new_order, user=None): '''set new order by duplicating user icons (if user is set) or set global order''' - if isinstance(new_order, string_types): + if isinstance(new_order, str): new_order = json.loads(new_order) for i, module_name in enumerate(new_order): if module_name not in ('Explore',): @@ -232,7 +229,7 @@ def set_hidden_list(hidden_list, user=None): '''Sets property `hidden`=1 in **Desktop Icon** for given user. If user is None then it will set global values. It will also set the rest of the icons as shown (`hidden` = 0)''' - if isinstance(hidden_list, string_types): + if isinstance(hidden_list, str): hidden_list = json.loads(hidden_list) # set as hidden @@ -329,7 +326,7 @@ def sync_from_app(app): if isinstance(modules, dict): modules_list = [] - for m, desktop_icon in iteritems(modules): + for m, desktop_icon in modules.items(): desktop_icon['module_name'] = m modules_list.append(desktop_icon) else: diff --git a/frappe/desk/doctype/event/__init__.py b/frappe/desk/doctype/event/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/desk/doctype/event/__init__.py +++ b/frappe/desk/doctype/event/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/desk/doctype/event/event.py b/frappe/desk/doctype/event/event.py index 54905bed6a..57c89eaf2e 100644 --- a/frappe/desk/doctype/event/event.py +++ b/frappe/desk/doctype/event/event.py @@ -1,9 +1,7 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals -from six.moves import range -from six import string_types + import frappe import json @@ -106,7 +104,7 @@ class Event(Document): @frappe.whitelist() def delete_communication(event, reference_doctype, reference_docname): deleted_participant = frappe.get_doc(reference_doctype, reference_docname) - if isinstance(event, string_types): + if isinstance(event, str): event = json.loads(event) filters = [ @@ -168,7 +166,7 @@ def get_events(start, end, user=None, for_reminder=False, filters=None): if not user: user = frappe.session.user - if isinstance(filters, string_types): + if isinstance(filters, str): filters = json.loads(filters) filter_condition = get_filters_cond('Event', filters, []) diff --git a/frappe/desk/doctype/event/test_event.py b/frappe/desk/doctype/event/test_event.py index 2926a74a55..77211946a9 100644 --- a/frappe/desk/doctype/event/test_event.py +++ b/frappe/desk/doctype/event/test_event.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - """Use blog post test to test user permissions logic""" import frappe diff --git a/frappe/desk/doctype/event_participants/event_participants.py b/frappe/desk/doctype/event_participants/event_participants.py index 18e4672140..ca4fae9930 100644 --- a/frappe/desk/doctype/event_participants/event_participants.py +++ b/frappe/desk/doctype/event_participants/event_participants.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class EventParticipants(Document): diff --git a/frappe/desk/doctype/global_search_doctype/global_search_doctype.py b/frappe/desk/doctype/global_search_doctype/global_search_doctype.py index 4c9a948278..de8a48af01 100644 --- a/frappe/desk/doctype/global_search_doctype/global_search_doctype.py +++ b/frappe/desk/doctype/global_search_doctype/global_search_doctype.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/global_search_settings/global_search_settings.py b/frappe/desk/doctype/global_search_settings/global_search_settings.py index 85c9687ab3..28a1ed8239 100644 --- a/frappe/desk/doctype/global_search_settings/global_search_settings.py +++ b/frappe/desk/doctype/global_search_settings/global_search_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ diff --git a/frappe/desk/doctype/kanban_board/kanban_board.py b/frappe/desk/doctype/kanban_board/kanban_board.py index a655e9e1da..5100727f43 100644 --- a/frappe/desk/doctype/kanban_board/kanban_board.py +++ b/frappe/desk/doctype/kanban_board/kanban_board.py @@ -2,12 +2,10 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import json from frappe import _ from frappe.model.document import Document -from six import iteritems class KanbanBoard(Document): @@ -107,7 +105,7 @@ def update_order(board_name, order): order_dict = json.loads(order) updated_cards = [] - for col_name, cards in iteritems(order_dict): + for col_name, cards in order_dict.items(): order_list = [] for card in cards: column = frappe.get_value( diff --git a/frappe/desk/doctype/kanban_board/test_kanban_board.py b/frappe/desk/doctype/kanban_board/test_kanban_board.py index 33947f4a54..f9503d736a 100644 --- a/frappe/desk/doctype/kanban_board/test_kanban_board.py +++ b/frappe/desk/doctype/kanban_board/test_kanban_board.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/desk/doctype/kanban_board_column/kanban_board_column.py b/frappe/desk/doctype/kanban_board_column/kanban_board_column.py index 4ea30d21b2..aebba3351c 100644 --- a/frappe/desk/doctype/kanban_board_column/kanban_board_column.py +++ b/frappe/desk/doctype/kanban_board_column/kanban_board_column.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/list_filter/list_filter.py b/frappe/desk/doctype/list_filter/list_filter.py index 035f7e90b9..2467ae40a4 100644 --- a/frappe/desk/doctype/list_filter/list_filter.py +++ b/frappe/desk/doctype/list_filter/list_filter.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, json from frappe.model.document import Document diff --git a/frappe/desk/doctype/list_view_settings/list_view_settings.py b/frappe/desk/doctype/list_view_settings/list_view_settings.py index 74e029f499..f4a288b7ba 100644 --- a/frappe/desk/doctype/list_view_settings/list_view_settings.py +++ b/frappe/desk/doctype/list_view_settings/list_view_settings.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/desk/doctype/list_view_settings/test_list_view_settings.py b/frappe/desk/doctype/list_view_settings/test_list_view_settings.py index c1b2f4a0da..00010d7604 100644 --- a/frappe/desk/doctype/list_view_settings/test_list_view_settings.py +++ b/frappe/desk/doctype/list_view_settings/test_list_view_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/module_onboarding/module_onboarding.py b/frappe/desk/doctype/module_onboarding/module_onboarding.py index 8315c0b304..6f01e0fd8d 100644 --- a/frappe/desk/doctype/module_onboarding/module_onboarding.py +++ b/frappe/desk/doctype/module_onboarding/module_onboarding.py @@ -2,7 +2,6 @@ # 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 from frappe.modules.export_file import export_to_files diff --git a/frappe/desk/doctype/module_onboarding/test_module_onboarding.py b/frappe/desk/doctype/module_onboarding/test_module_onboarding.py index ef305667b1..39184401a1 100644 --- a/frappe/desk/doctype/module_onboarding/test_module_onboarding.py +++ b/frappe/desk/doctype/module_onboarding/test_module_onboarding.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/note/note.py b/frappe/desk/doctype/note/note.py index c54689418e..790f9a514c 100644 --- a/frappe/desk/doctype/note/note.py +++ b/frappe/desk/doctype/note/note.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/note/test_note.py b/frappe/desk/doctype/note/test_note.py index 38894a9c3d..1bb1730357 100644 --- a/frappe/desk/doctype/note/test_note.py +++ b/frappe/desk/doctype/note/test_note.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors # See license.txt -from __future__ import unicode_literals import frappe import unittest diff --git a/frappe/desk/doctype/note_seen_by/note_seen_by.py b/frappe/desk/doctype/note_seen_by/note_seen_by.py index 6123f20929..cec4628b20 100644 --- a/frappe/desk/doctype/note_seen_by/note_seen_by.py +++ b/frappe/desk/doctype/note_seen_by/note_seen_by.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/notification_log/notification_log.py b/frappe/desk/doctype/notification_log/notification_log.py index 25af92f532..414f272f59 100644 --- a/frappe/desk/doctype/notification_log/notification_log.py +++ b/frappe/desk/doctype/notification_log/notification_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document diff --git a/frappe/desk/doctype/notification_log/test_notification_log.py b/frappe/desk/doctype/notification_log/test_notification_log.py index e59aee30c9..af4dee8df3 100644 --- a/frappe/desk/doctype/notification_log/test_notification_log.py +++ b/frappe/desk/doctype/notification_log/test_notification_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe from frappe.desk.form.assign_to import add as assign_task import unittest diff --git a/frappe/desk/doctype/notification_settings/notification_settings.py b/frappe/desk/doctype/notification_settings/notification_settings.py index 4ab40bffe9..eb3a16435f 100644 --- a/frappe/desk/doctype/notification_settings/notification_settings.py +++ b/frappe/desk/doctype/notification_settings/notification_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/notification_subscribed_document/notification_subscribed_document.py b/frappe/desk/doctype/notification_subscribed_document/notification_subscribed_document.py index f005efae76..6931e77754 100644 --- a/frappe/desk/doctype/notification_subscribed_document/notification_subscribed_document.py +++ b/frappe/desk/doctype/notification_subscribed_document/notification_subscribed_document.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py index 7d1a697f6b..d8d5fe0953 100644 --- a/frappe/desk/doctype/number_card/number_card.py +++ b/frappe/desk/doctype/number_card/number_card.py @@ -2,7 +2,6 @@ # 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 from frappe.utils import cint diff --git a/frappe/desk/doctype/number_card/test_number_card.py b/frappe/desk/doctype/number_card/test_number_card.py index 4aa1ecf282..c395f5f915 100644 --- a/frappe/desk/doctype/number_card/test_number_card.py +++ b/frappe/desk/doctype/number_card/test_number_card.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/number_card_link/number_card_link.py b/frappe/desk/doctype/number_card_link/number_card_link.py index 67ad7e70cd..6c16f45f4b 100644 --- a/frappe/desk/doctype/number_card_link/number_card_link.py +++ b/frappe/desk/doctype/number_card_link/number_card_link.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/desk/doctype/onboarding_permission/onboarding_permission.py b/frappe/desk/doctype/onboarding_permission/onboarding_permission.py index f8772480df..40d3dc33b1 100644 --- a/frappe/desk/doctype/onboarding_permission/onboarding_permission.py +++ b/frappe/desk/doctype/onboarding_permission/onboarding_permission.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/desk/doctype/onboarding_permission/test_onboarding_permission.py b/frappe/desk/doctype/onboarding_permission/test_onboarding_permission.py index 9a7e8ae6fd..80b166de0a 100644 --- a/frappe/desk/doctype/onboarding_permission/test_onboarding_permission.py +++ b/frappe/desk/doctype/onboarding_permission/test_onboarding_permission.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/onboarding_step/onboarding_step.py b/frappe/desk/doctype/onboarding_step/onboarding_step.py index e1cc5dfba4..10bd8926ce 100644 --- a/frappe/desk/doctype/onboarding_step/onboarding_step.py +++ b/frappe/desk/doctype/onboarding_step/onboarding_step.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/desk/doctype/onboarding_step/test_onboarding_step.py b/frappe/desk/doctype/onboarding_step/test_onboarding_step.py index 66bd0c6660..2425577478 100644 --- a/frappe/desk/doctype/onboarding_step/test_onboarding_step.py +++ b/frappe/desk/doctype/onboarding_step/test_onboarding_step.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/onboarding_step_map/onboarding_step_map.py b/frappe/desk/doctype/onboarding_step_map/onboarding_step_map.py index ea34de6088..c79244c4ad 100644 --- a/frappe/desk/doctype/onboarding_step_map/onboarding_step_map.py +++ b/frappe/desk/doctype/onboarding_step_map/onboarding_step_map.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/desk/doctype/route_history/route_history.py b/frappe/desk/doctype/route_history/route_history.py index 12d898afa5..b82077f485 100644 --- a/frappe/desk/doctype/route_history/route_history.py +++ b/frappe/desk/doctype/route_history/route_history.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/system_console/system_console.py b/frappe/desk/doctype/system_console/system_console.py index 6c87ca8c36..e2b5656bc0 100644 --- a/frappe/desk/doctype/system_console/system_console.py +++ b/frappe/desk/doctype/system_console/system_console.py @@ -2,8 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import json import frappe diff --git a/frappe/desk/doctype/system_console/test_system_console.py b/frappe/desk/doctype/system_console/test_system_console.py index 55ef199122..743c2d6dde 100644 --- a/frappe/desk/doctype/system_console/test_system_console.py +++ b/frappe/desk/doctype/system_console/test_system_console.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/desk/doctype/tag/tag.py b/frappe/desk/doctype/tag/tag.py index 7e016ee91b..3c67bb4668 100644 --- a/frappe/desk/doctype/tag/tag.py +++ b/frappe/desk/doctype/tag/tag.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils import unique diff --git a/frappe/desk/doctype/tag/test_tag.py b/frappe/desk/doctype/tag/test_tag.py index 8efd692f43..442a891fd8 100644 --- a/frappe/desk/doctype/tag/test_tag.py +++ b/frappe/desk/doctype/tag/test_tag.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/tag_link/tag_link.py b/frappe/desk/doctype/tag_link/tag_link.py index 87c8af7212..4c5149f42c 100644 --- a/frappe/desk/doctype/tag_link/tag_link.py +++ b/frappe/desk/doctype/tag_link/tag_link.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/tag_link/test_tag_link.py b/frappe/desk/doctype/tag_link/test_tag_link.py index 1c22ac18bc..297ee3cc96 100644 --- a/frappe/desk/doctype/tag_link/test_tag_link.py +++ b/frappe/desk/doctype/tag_link/test_tag_link.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/todo/__init__.py b/frappe/desk/doctype/todo/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/desk/doctype/todo/__init__.py +++ b/frappe/desk/doctype/todo/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/desk/doctype/todo/test_todo.py b/frappe/desk/doctype/todo/test_todo.py index de5b6724a6..b38e4a059a 100644 --- a/frappe/desk/doctype/todo/test_todo.py +++ b/frappe/desk/doctype/todo/test_todo.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from frappe.model.db_query import DatabaseQuery diff --git a/frappe/desk/doctype/todo/todo.py b/frappe/desk/doctype/todo/todo.py index a766375fde..4696563445 100644 --- a/frappe/desk/doctype/todo/todo.py +++ b/frappe/desk/doctype/todo/todo.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import json @@ -93,7 +92,7 @@ def get_permission_query_conditions(user): if not user: user = frappe.session.user todo_roles = frappe.permissions.get_doctype_roles('ToDo') - if 'All' in todo_roles: + if 'All' in todo_roles: todo_roles.remove('All') if any(check in todo_roles for check in frappe.get_roles(user)): @@ -105,7 +104,7 @@ def get_permission_query_conditions(user): def has_permission(doc, ptype="read", user=None): user = user or frappe.session.user todo_roles = frappe.permissions.get_doctype_roles('ToDo', ptype) - if 'All' in todo_roles: + if 'All' in todo_roles: todo_roles.remove('All') if any(check in todo_roles for check in frappe.get_roles(user)): diff --git a/frappe/desk/doctype/workspace/test_workspace.py b/frappe/desk/doctype/workspace/test_workspace.py index 7a3f122ee2..619b3608eb 100644 --- a/frappe/desk/doctype/workspace/test_workspace.py +++ b/frappe/desk/doctype/workspace/test_workspace.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/desk/doctype/workspace/workspace.py b/frappe/desk/doctype/workspace/workspace.py index 0934138821..0329e0f7d2 100644 --- a/frappe/desk/doctype/workspace/workspace.py +++ b/frappe/desk/doctype/workspace/workspace.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.modules.export_file import export_to_files diff --git a/frappe/desk/doctype/workspace_chart/workspace_chart.py b/frappe/desk/doctype/workspace_chart/workspace_chart.py index 0bb6194d2e..6ec7abfd3c 100644 --- a/frappe/desk/doctype/workspace_chart/workspace_chart.py +++ b/frappe/desk/doctype/workspace_chart/workspace_chart.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/workspace_link/workspace_link.py b/frappe/desk/doctype/workspace_link/workspace_link.py index 8a139077a6..d6ccc5306a 100644 --- a/frappe/desk/doctype/workspace_link/workspace_link.py +++ b/frappe/desk/doctype/workspace_link/workspace_link.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/doctype/workspace_shortcut/workspace_shortcut.py b/frappe/desk/doctype/workspace_shortcut/workspace_shortcut.py index d676f08b73..83b446e454 100644 --- a/frappe/desk/doctype/workspace_shortcut/workspace_shortcut.py +++ b/frappe/desk/doctype/workspace_shortcut/workspace_shortcut.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/desk/form/__init__.py b/frappe/desk/form/__init__.py index 4dbcd0d163..0e57cb68c3 100644 --- a/frappe/desk/form/__init__.py +++ b/frappe/desk/form/__init__.py @@ -1,4 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals diff --git a/frappe/desk/form/assign_to.py b/frappe/desk/form/assign_to.py index aee7a8e52a..3eda291d1e 100644 --- a/frappe/desk/form/assign_to.py +++ b/frappe/desk/form/assign_to.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals """assign/unassign to ToDo""" import frappe diff --git a/frappe/desk/form/document_follow.py b/frappe/desk/form/document_follow.py index f5e5c0ca9b..7f65f76a58 100644 --- a/frappe/desk/form/document_follow.py +++ b/frappe/desk/form/document_follow.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import frappe.utils from frappe.utils import get_url_to_form diff --git a/frappe/desk/form/linked_with.py b/frappe/desk/form/linked_with.py index a62e2837d5..12880604a2 100644 --- a/frappe/desk/form/linked_with.py +++ b/frappe/desk/form/linked_with.py @@ -1,9 +1,7 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import json from collections import defaultdict -from six import string_types import frappe import frappe.desk.form.load import frappe.desk.form.meta @@ -87,7 +85,7 @@ def cancel_all_linked_docs(docs, ignore_doctypes_on_cancel_all=[]): """ docs = json.loads(docs) - if isinstance(ignore_doctypes_on_cancel_all, string_types): + if isinstance(ignore_doctypes_on_cancel_all, str): ignore_doctypes_on_cancel_all = json.loads(ignore_doctypes_on_cancel_all) for i, doc in enumerate(docs, 1): if validate_linked_doc(doc, ignore_doctypes_on_cancel_all): @@ -139,7 +137,7 @@ def get_exempted_doctypes(): @frappe.whitelist() def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None): - if isinstance(linkinfo, string_types): + if isinstance(linkinfo, str): # additional fields are added in linkinfo linkinfo = json.loads(linkinfo) @@ -202,7 +200,7 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None): else: link_fieldnames = link.get("fieldname") if link_fieldnames: - if isinstance(link_fieldnames, string_types): link_fieldnames = [link_fieldnames] + if isinstance(link_fieldnames, str): link_fieldnames = [link_fieldnames] or_filters = [[dt, fieldname, '=', name] for fieldname in link_fieldnames] # dynamic link if link.get("doctype_fieldname"): diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py index d81bb8c26c..a62bfd01d0 100644 --- a/frappe/desk/form/load.py +++ b/frappe/desk/form/load.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, json import frappe.utils import frappe.share @@ -11,7 +10,7 @@ from frappe.model.utils.user_settings import get_user_settings from frappe.permissions import get_doc_permissions from frappe.desk.form.document_follow import is_document_followed from frappe import _ -from six.moves.urllib.parse import quote +from urllib.parse import quote @frappe.whitelist(allow_guest=True) def getdoc(doctype, name, user=None): diff --git a/frappe/desk/form/meta.py b/frappe/desk/form/meta.py index 087cc54d9d..947a9e5d4b 100644 --- a/frappe/desk/form/meta.py +++ b/frappe/desk/form/meta.py @@ -2,9 +2,7 @@ # MIT License. See license.txt # metadata - -from __future__ import unicode_literals -import frappe, os +import frappe, os, io from frappe.model.meta import Meta from frappe.modules import scrub, get_module_path, load_doctype_module from frappe.utils import get_html_format @@ -12,9 +10,6 @@ from frappe.translate import make_dict_from_messages, extract_messages_from_code from frappe.model.utils import render_include from frappe.build import scrub_html_template -import io - -from six import iteritems def get_meta(doctype, cached=True): # don't cache for developer mode as js files, templates may be edited @@ -199,7 +194,7 @@ class FormMeta(Meta): app = module.__name__.split(".")[0] templates = {} if hasattr(module, "form_grid_templates"): - for key, path in iteritems(module.form_grid_templates): + for key, path in module.form_grid_templates.items(): templates[key] = get_html_format(frappe.get_app_path(app, path)) self.set("__form_grid_templates", templates) diff --git a/frappe/desk/form/save.py b/frappe/desk/form/save.py index da43b14fce..a7a4b829d8 100644 --- a/frappe/desk/form/save.py +++ b/frappe/desk/form/save.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, json from frappe.desk.form.load import run_onload diff --git a/frappe/desk/form/test_form.py b/frappe/desk/form/test_form.py index ff0343b6e0..f3c4132777 100644 --- a/frappe/desk/form/test_form.py +++ b/frappe/desk/form/test_form.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, unittest from frappe.desk.form.linked_with import get_linked_docs, get_linked_doctypes diff --git a/frappe/desk/form/utils.py b/frappe/desk/form/utils.py index 395d2b9571..bfceee6ea2 100644 --- a/frappe/desk/form/utils.py +++ b/frappe/desk/form/utils.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, json import frappe.desk.form.meta import frappe.desk.form.load @@ -9,7 +8,6 @@ from frappe.desk.form.document_follow import follow_document from frappe.utils.file_manager import extract_images_from_html from frappe import _ -from six import string_types @frappe.whitelist() def remove_attach(): @@ -90,7 +88,7 @@ def get_next(doctype, value, prev, filters=None, sort_order='desc', sort_field=' prev = int(prev) if not filters: filters = [] - if isinstance(filters, string_types): + if isinstance(filters, str): filters = json.loads(filters) # # condition based on sort order diff --git a/frappe/desk/gantt.py b/frappe/desk/gantt.py index 521884beaa..7f0889c751 100644 --- a/frappe/desk/gantt.py +++ b/frappe/desk/gantt.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, json @frappe.whitelist() diff --git a/frappe/desk/leaderboard.py b/frappe/desk/leaderboard.py index d651687256..a98ae1a1c6 100644 --- a/frappe/desk/leaderboard.py +++ b/frappe/desk/leaderboard.py @@ -1,5 +1,3 @@ - -from __future__ import unicode_literals, print_function import frappe from frappe.utils import get_fullname diff --git a/frappe/desk/like.py b/frappe/desk/like.py index 6d2e9704af..d44d58a761 100644 --- a/frappe/desk/like.py +++ b/frappe/desk/like.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - """Allow adding of likes to documents""" import frappe, json diff --git a/frappe/desk/listview.py b/frappe/desk/listview.py index 91dc0f3ba9..d2c84d36bf 100644 --- a/frappe/desk/listview.py +++ b/frappe/desk/listview.py @@ -1,7 +1,5 @@ # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe @frappe.whitelist(allow_guest=True) diff --git a/frappe/desk/moduleview.py b/frappe/desk/moduleview.py index df25b77e2d..021698ac92 100644 --- a/frappe/desk/moduleview.py +++ b/frappe/desk/moduleview.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import json from frappe import _ diff --git a/frappe/desk/notifications.py b/frappe/desk/notifications.py index 4b584a2429..c84027928e 100644 --- a/frappe/desk/notifications.py +++ b/frappe/desk/notifications.py @@ -1,11 +1,8 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe from frappe.desk.doctype.notification_settings.notification_settings import get_subscribed_documents -from six import string_types import json @frappe.whitelist() @@ -149,7 +146,7 @@ def clear_doctype_notifications(doc, method=None, *args, **kwargs): config = get_notification_config() if not config: return - if isinstance(doc, string_types): + if isinstance(doc, str): doctype = doc # assuming doctype name was passed directly else: doctype = doc.doctype @@ -213,7 +210,7 @@ def get_filters_for(doctype): '''get open filters for doctype''' config = get_notification_config() doctype_config = config.get("for_doctype").get(doctype, {}) - filters = doctype_config if not isinstance(doctype_config, string_types) else None + filters = doctype_config if not isinstance(doctype_config, str) else None return filters diff --git a/frappe/desk/page/activity/__init__.py b/frappe/desk/page/activity/__init__.py index baffc48825..8b13789179 100644 --- a/frappe/desk/page/activity/__init__.py +++ b/frappe/desk/page/activity/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/desk/page/activity/activity.py b/frappe/desk/page/activity/activity.py index 7de294d2f0..3abc8e0ea5 100644 --- a/frappe/desk/page/activity/activity.py +++ b/frappe/desk/page/activity/activity.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: See license.txt -from __future__ import unicode_literals import frappe from frappe.utils import cint from frappe.core.doctype.activity_log.feed import get_feed_match_conditions diff --git a/frappe/desk/page/backups/backups.py b/frappe/desk/page/backups/backups.py index eaa0c65143..2229a6d89e 100644 --- a/frappe/desk/page/backups/backups.py +++ b/frappe/desk/page/backups/backups.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import os import frappe from frappe import _ diff --git a/frappe/desk/page/leaderboard/leaderboard.py b/frappe/desk/page/leaderboard/leaderboard.py index 819e7fe9d1..9469096f50 100644 --- a/frappe/desk/page/leaderboard/leaderboard.py +++ b/frappe/desk/page/leaderboard/leaderboard.py @@ -1,7 +1,5 @@ # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function import frappe @frappe.whitelist() diff --git a/frappe/desk/page/setup_wizard/install_fixtures.py b/frappe/desk/page/setup_wizard/install_fixtures.py index 6d3aaee22b..06301cdeaf 100644 --- a/frappe/desk/page/setup_wizard/install_fixtures.py +++ b/frappe/desk/page/setup_wizard/install_fixtures.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals - import frappe from frappe import _ from frappe.desk.doctype.global_search_settings.global_search_settings import update_global_search_doctypes diff --git a/frappe/desk/page/setup_wizard/setup_wizard.py b/frappe/desk/page/setup_wizard/setup_wizard.py index 1ac5279508..5edb44e182 100755 --- a/frappe/desk/page/setup_wizard/setup_wizard.py +++ b/frappe/desk/page/setup_wizard/setup_wizard.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: See license.txt -from __future__ import unicode_literals - import frappe, json, os from frappe.utils import strip, cint from frappe.translate import (set_default_language, get_dict, send_translations) @@ -10,7 +8,6 @@ from frappe.geo.country_info import get_country_info from frappe.utils.password import update_password from werkzeug.useragents import UserAgent from . import install_fixtures -from six import string_types def get_setup_stages(args): @@ -208,14 +205,14 @@ def update_user_name(args): def parse_args(args): if not args: args = frappe.local.form_dict - if isinstance(args, string_types): + if isinstance(args, str): args = json.loads(args) args = frappe._dict(args) # strip the whitespace for key, value in args.items(): - if isinstance(value, string_types): + if isinstance(value, str): args[key] = strip(value) return args @@ -294,7 +291,7 @@ def reset_is_first_startup(): def prettify_args(args): # remove attachments for key, val in args.items(): - if isinstance(val, string_types) and "data:image" in val: + if isinstance(val, str) and "data:image" in val: filename = val.split("data:image", 1)[0].strip(", ") size = round((len(val) * 3 / 4) / 1048576.0, 2) args[key] = "Image Attached: '{0}' of size {1} MB".format(filename, size) diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index befaf7b01f..3c0ebf11c1 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe import os import json @@ -22,7 +20,6 @@ from frappe.model.utils import render_include from frappe.translate import send_translations import frappe.desk.reportview from frappe.permissions import get_role_permissions -from six import string_types, iteritems from datetime import timedelta from frappe.core.utils import ljust_list @@ -66,7 +63,7 @@ def generate_report_result(report, filters=None, user=None, custom_columns=None) user = user or frappe.session.user filters = filters or [] - if filters and isinstance(filters, string_types): + if filters and isinstance(filters, str): filters = json.loads(filters) res = [] @@ -222,7 +219,7 @@ def run(report_name, filters=None, user=None, ignore_prepared_report=False, cust and not custom_columns ): if filters: - if isinstance(filters, string_types): + if isinstance(filters, str): filters = json.loads(filters) dn = filters.get("prepared_report_name") @@ -317,7 +314,7 @@ def export_query(): data.pop("cmd", None) data.pop("csrf_token", None) - if isinstance(data.get("filters"), string_types): + if isinstance(data.get("filters"), str): filters = json.loads(data["filters"]) if data.get("report_name"): @@ -332,7 +329,7 @@ def export_query(): include_indentation = data.get("include_indentation") visible_idx = data.get("visible_idx") - if isinstance(visible_idx, string_types): + if isinstance(visible_idx, str): visible_idx = json.loads(visible_idx) if file_format_type == "Excel": @@ -363,7 +360,7 @@ def export_query(): def handle_duration_fieldtype_values(result, columns): for i, col in enumerate(columns): fieldtype = None - if isinstance(col, string_types): + if isinstance(col, str): col = col.split(":") if len(col) > 1: if col[1]: @@ -433,7 +430,7 @@ def add_total_row(result, columns, meta=None): has_percent = [] for i, col in enumerate(columns): fieldtype, options, fieldname = None, None, None - if isinstance(col, string_types): + if isinstance(col, str): if meta: # get fieldtype from the meta field = meta.get_field(col) @@ -483,7 +480,7 @@ def add_total_row(result, columns, meta=None): total_row[i] = flt(total_row[i]) / len(result) first_col_fieldtype = None - if isinstance(columns[0], string_types): + if isinstance(columns[0], str): first_col = columns[0].split(":") if len(first_col) > 1: first_col_fieldtype = first_col[1].split("/")[0] @@ -701,7 +698,7 @@ def get_linked_doctypes(columns, data): if val and col not in columns_with_value: columns_with_value.append(col) - items = list(iteritems(linked_doctypes)) + items = list(linked_doctypes.items()) for doctype, key in items: if key not in columns_with_value: @@ -728,7 +725,7 @@ def get_column_as_dict(col): col_dict = frappe._dict() # string - if isinstance(col, string_types): + if isinstance(col, str): col = col.split(":") if len(col) > 1: if "/" in col[1]: diff --git a/frappe/desk/report/todo/todo.py b/frappe/desk/report/todo/todo.py index f4fe2dc805..6bd22b843e 100644 --- a/frappe/desk/report/todo/todo.py +++ b/frappe/desk/report/todo/todo.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import getdate diff --git a/frappe/desk/report_dump.py b/frappe/desk/report_dump.py index 86b1765814..b2d3ca3443 100644 --- a/frappe/desk/report_dump.py +++ b/frappe/desk/report_dump.py @@ -1,8 +1,7 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals -from six.moves import range + import frappe import json import copy diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 86f8ec0aa7..55515856f1 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -1,16 +1,14 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals """build query for doclistview and return results""" import frappe, json -from six.moves import range import frappe.permissions from frappe.model.db_query import DatabaseQuery from frappe.model import default_fields, optional_fields from frappe import _ -from six import string_types, StringIO +from io import StringIO from frappe.core.doctype.access_log.access_log import make_access_log from frappe.utils import cstr, format_duration from frappe.model.base_document import get_controller @@ -171,7 +169,7 @@ def get_meta_and_docfield(fieldname, data): return meta, df def update_wildcard_field_param(data): - if ((isinstance(data.fields, string_types) and data.fields == "*") + if ((isinstance(data.fields, str) and data.fields == "*") or (isinstance(data.fields, (list, tuple)) and len(data.fields) == 1 and data.fields[0] == "*")): data.fields = frappe.db.get_table_columns(data.doctype) return True @@ -191,15 +189,15 @@ def clean_params(data): def parse_json(data): - if isinstance(data.get("filters"), string_types): + if isinstance(data.get("filters"), str): data["filters"] = json.loads(data["filters"]) - if isinstance(data.get("or_filters"), string_types): + if isinstance(data.get("or_filters"), str): data["or_filters"] = json.loads(data["or_filters"]) - if isinstance(data.get("fields"), string_types): + if isinstance(data.get("fields"), str): data["fields"] = json.loads(data["fields"]) - if isinstance(data.get("docstatus"), string_types): + if isinstance(data.get("docstatus"), str): data["docstatus"] = json.loads(data["docstatus"]) - if isinstance(data.get("save_user_settings"), string_types): + if isinstance(data.get("save_user_settings"), str): data["save_user_settings"] = json.loads(data["save_user_settings"]) else: data["save_user_settings"] = True @@ -311,7 +309,7 @@ def export_query(): for r in data: # encode only unicode type strings and not int, floats etc. writer.writerow([handle_html(frappe.as_unicode(v)) \ - if isinstance(v, string_types) else v for v in r]) + if isinstance(v, str) else v for v in r]) f.seek(0) frappe.response['result'] = cstr(f.read()) @@ -540,7 +538,7 @@ def build_match_conditions(doctype, user=None, as_condition=True): return match_conditions def get_filters_cond(doctype, filters, conditions, ignore_permissions=None, with_match_conditions=False): - if isinstance(filters, string_types): + if isinstance(filters, str): filters = json.loads(filters) if filters: @@ -549,7 +547,7 @@ def get_filters_cond(doctype, filters, conditions, ignore_permissions=None, with filters = filters.items() flt = [] for f in filters: - if isinstance(f[1], string_types) and f[1][0] == '!': + if isinstance(f[1], str) and f[1][0] == '!': flt.append([doctype, f[0], '!=', f[1][1:]]) elif isinstance(f[1], (list, tuple)) and \ f[1][0] in (">", "<", ">=", "<=", "!=", "like", "not like", "in", "not in", "between"): diff --git a/frappe/desk/search.py b/frappe/desk/search.py index 3c9109eca9..040a8c2118 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -2,12 +2,10 @@ # MIT License. See license.txt # Search -from __future__ import unicode_literals import frappe, json from frappe.utils import cstr, unique, cint from frappe.permissions import has_permission from frappe import _, is_whitelisted -from six import string_types import re import wrapt @@ -62,7 +60,7 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0, start = cint(start) - if isinstance(filters, string_types): + if isinstance(filters, str): filters = json.loads(filters) if searchfield: diff --git a/frappe/desk/treeview.py b/frappe/desk/treeview.py index 6f0d7d3d5f..66acde4cb2 100644 --- a/frappe/desk/treeview.py +++ b/frappe/desk/treeview.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ diff --git a/frappe/email/__init__.py b/frappe/email/__init__.py index b05aef7639..3fb539398a 100644 --- a/frappe/email/__init__.py +++ b/frappe/email/__init__.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.desk.reportview import build_match_conditions diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.py b/frappe/email/doctype/auto_email_report/auto_email_report.py index 6f1cd8eebd..911af2d1c7 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/auto_email_report.py @@ -2,8 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import calendar from datetime import timedelta diff --git a/frappe/email/doctype/auto_email_report/test_auto_email_report.py b/frappe/email/doctype/auto_email_report/test_auto_email_report.py index e656ff18f7..211a141ec0 100644 --- a/frappe/email/doctype/auto_email_report/test_auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/test_auto_email_report.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import json import unittest diff --git a/frappe/email/doctype/document_follow/document_follow.py b/frappe/email/doctype/document_follow/document_follow.py index aaabffab6b..a04f8ef4c2 100644 --- a/frappe/email/doctype/document_follow/document_follow.py +++ b/frappe/email/doctype/document_follow/document_follow.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class DocumentFollow(Document): diff --git a/frappe/email/doctype/document_follow/test_document_follow.py b/frappe/email/doctype/document_follow/test_document_follow.py index 38aa870232..456c0931f8 100644 --- a/frappe/email/doctype/document_follow/test_document_follow.py +++ b/frappe/email/doctype/document_follow/test_document_follow.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest import frappe.desk.form.document_follow as document_follow diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 36b662bb39..d797c72859 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt - -from __future__ import unicode_literals, print_function import frappe import imaplib import re diff --git a/frappe/email/doctype/email_account/test_email_account.py b/frappe/email/doctype/email_account/test_email_account.py index f87ee32bb1..1b92811eb7 100644 --- a/frappe/email/doctype/email_account/test_email_account.py +++ b/frappe/email/doctype/email_account/test_email_account.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals import frappe, os import unittest, email diff --git a/frappe/email/doctype/email_domain/email_domain.py b/frappe/email/doctype/email_domain/email_domain.py index ce39523564..0856549eb7 100644 --- a/frappe/email/doctype/email_domain/email_domain.py +++ b/frappe/email/doctype/email_domain/email_domain.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document diff --git a/frappe/email/doctype/email_domain/test_email_domain.py b/frappe/email/doctype/email_domain/test_email_domain.py index 1c5306e9c2..8607151ca8 100644 --- a/frappe/email/doctype/email_domain/test_email_domain.py +++ b/frappe/email/doctype/email_domain/test_email_domain.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from frappe.test_runner import make_test_objects diff --git a/frappe/email/doctype/email_flag_queue/email_flag_queue.py b/frappe/email/doctype/email_flag_queue/email_flag_queue.py index 487ef7db50..9bb30f08b2 100644 --- a/frappe/email/doctype/email_flag_queue/email_flag_queue.py +++ b/frappe/email/doctype/email_flag_queue/email_flag_queue.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/email/doctype/email_flag_queue/test_email_flag_queue.py b/frappe/email/doctype/email_flag_queue/test_email_flag_queue.py index 644a2a8ff7..d09b823ce6 100644 --- a/frappe/email/doctype/email_flag_queue/test_email_flag_queue.py +++ b/frappe/email/doctype/email_flag_queue/test_email_flag_queue.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/email/doctype/email_group/email_group.py b/frappe/email/doctype/email_group/email_group.py index b19a134713..597aa9c18b 100755 --- a/frappe/email/doctype/email_group/email_group.py +++ b/frappe/email/doctype/email_group/email_group.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import validate_email_address diff --git a/frappe/email/doctype/email_group/test_email_group.py b/frappe/email/doctype/email_group/test_email_group.py index 09f4f4c32c..3e894118df 100644 --- a/frappe/email/doctype/email_group/test_email_group.py +++ b/frappe/email/doctype/email_group/test_email_group.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/email/doctype/email_group_member/email_group_member.py b/frappe/email/doctype/email_group_member/email_group_member.py index 23b279e755..1f9303b83e 100644 --- a/frappe/email/doctype/email_group_member/email_group_member.py +++ b/frappe/email/doctype/email_group_member/email_group_member.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/email/doctype/email_group_member/test_email_group_member.py b/frappe/email/doctype/email_group_member/test_email_group_member.py index 35259617c1..829d686400 100644 --- a/frappe/email/doctype/email_group_member/test_email_group_member.py +++ b/frappe/email/doctype/email_group_member/test_email_group_member.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/email/doctype/email_queue/test_email_queue.py b/frappe/email/doctype/email_queue/test_email_queue.py index 7cd79f9259..b76d6347b9 100644 --- a/frappe/email/doctype/email_queue/test_email_queue.py +++ b/frappe/email/doctype/email_queue/test_email_queue.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/email/doctype/email_queue_recipient/email_queue_recipient.py b/frappe/email/doctype/email_queue_recipient/email_queue_recipient.py index 3f07ec58f3..055bdb3fc1 100644 --- a/frappe/email/doctype/email_queue_recipient/email_queue_recipient.py +++ b/frappe/email/doctype/email_queue_recipient/email_queue_recipient.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/email/doctype/email_rule/email_rule.py b/frappe/email/doctype/email_rule/email_rule.py index 220798bbdc..9807724ef1 100644 --- a/frappe/email/doctype/email_rule/email_rule.py +++ b/frappe/email/doctype/email_rule/email_rule.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/email/doctype/email_rule/test_email_rule.py b/frappe/email/doctype/email_rule/test_email_rule.py index 3c7f9c83e6..b2213f7405 100644 --- a/frappe/email/doctype/email_rule/test_email_rule.py +++ b/frappe/email/doctype/email_rule/test_email_rule.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/email/doctype/email_template/email_template.py b/frappe/email/doctype/email_template/email_template.py index 6708e9dd3f..4711451fd2 100644 --- a/frappe/email/doctype/email_template/email_template.py +++ b/frappe/email/doctype/email_template/email_template.py @@ -1,11 +1,9 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, json from frappe.model.document import Document from frappe.utils.jinja import validate_template -from six import string_types class EmailTemplate(Document): def validate(self): @@ -24,7 +22,7 @@ class EmailTemplate(Document): return frappe.render_template(self.response, doc) def get_formatted_email(self, doc): - if isinstance(doc, string_types): + if isinstance(doc, str): doc = json.loads(doc) return { @@ -36,7 +34,7 @@ class EmailTemplate(Document): @frappe.whitelist() def get_email_template(template_name, doc): '''Returns the processed HTML of a email template with the given doc''' - if isinstance(doc, string_types): + if isinstance(doc, str): doc = json.loads(doc) email_template = frappe.get_doc("Email Template", template_name) diff --git a/frappe/email/doctype/email_template/test_email_template.py b/frappe/email/doctype/email_template/test_email_template.py index a48ce94ac5..5a9ee969c6 100644 --- a/frappe/email/doctype/email_template/test_email_template.py +++ b/frappe/email/doctype/email_template/test_email_template.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestEmailTemplate(unittest.TestCase): diff --git a/frappe/email/doctype/email_unsubscribe/email_unsubscribe.py b/frappe/email/doctype/email_unsubscribe/email_unsubscribe.py index e532e2b7eb..6c47d8c538 100644 --- a/frappe/email/doctype/email_unsubscribe/email_unsubscribe.py +++ b/frappe/email/doctype/email_unsubscribe/email_unsubscribe.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ diff --git a/frappe/email/doctype/email_unsubscribe/test_email_unsubscribe.py b/frappe/email/doctype/email_unsubscribe/test_email_unsubscribe.py index ea84253ab6..602840fe3b 100644 --- a/frappe/email/doctype/email_unsubscribe/test_email_unsubscribe.py +++ b/frappe/email/doctype/email_unsubscribe/test_email_unsubscribe.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/email/doctype/newsletter/newsletter.py b/frappe/email/doctype/newsletter/newsletter.py index 6412338e96..3cba8f927c 100755 --- a/frappe/email/doctype/newsletter/newsletter.py +++ b/frappe/email/doctype/newsletter/newsletter.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals - import frappe import frappe.utils from frappe import throw, _ diff --git a/frappe/email/doctype/newsletter/test_newsletter.py b/frappe/email/doctype/newsletter/test_newsletter.py index bd8fadc29c..cfd0df53a9 100644 --- a/frappe/email/doctype/newsletter/test_newsletter.py +++ b/frappe/email/doctype/newsletter/test_newsletter.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals - import unittest from random import choice diff --git a/frappe/email/doctype/newsletter_email_group/newsletter_email_group.py b/frappe/email/doctype/newsletter_email_group/newsletter_email_group.py index a59ac372fd..a453dda9e4 100644 --- a/frappe/email/doctype/newsletter_email_group/newsletter_email_group.py +++ b/frappe/email/doctype/newsletter_email_group/newsletter_email_group.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index 2940a34f63..57418515f5 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import json, os from frappe import _ @@ -12,7 +11,6 @@ from frappe.utils import validate_email_address, nowdate, parse_val, is_html, ad from frappe.utils.jinja import validate_template from frappe.utils.safe_exec import get_safe_globals from frappe.modules.utils import export_module_json, get_doc_module -from six import string_types from frappe.integrations.doctype.slack_webhook_url.slack_webhook_url import send_slack_message from frappe.core.doctype.sms_settings.sms_settings import send_sms from frappe.desk.doctype.notification_log.notification_log import enqueue_create_notification @@ -55,9 +53,7 @@ class Notification(Document): # py if not os.path.exists(path + '.py'): with open(path + '.py', 'w') as f: - f.write("""from __future__ import unicode_literals - -import frappe + f.write("""import frappe def get_context(context): # do your magic here @@ -397,7 +393,7 @@ def trigger_notifications(doc, method=None): def evaluate_alert(doc, alert, event): from jinja2 import TemplateError try: - if isinstance(alert, string_types): + if isinstance(alert, str): alert = frappe.get_doc("Notification", alert) context = get_context(doc) diff --git a/frappe/email/doctype/notification/test_notification.py b/frappe/email/doctype/notification/test_notification.py index 31d5d9d1cc..d6358ccbbe 100644 --- a/frappe/email/doctype/notification/test_notification.py +++ b/frappe/email/doctype/notification/test_notification.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe, frappe.utils, frappe.utils.scheduler from frappe.desk.form import assign_to import unittest diff --git a/frappe/email/doctype/notification_recipient/notification_recipient.py b/frappe/email/doctype/notification_recipient/notification_recipient.py index a85ed62c04..d8480c5455 100644 --- a/frappe/email/doctype/notification_recipient/notification_recipient.py +++ b/frappe/email/doctype/notification_recipient/notification_recipient.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/email/doctype/unhandled_email/test_unhandled_email.py b/frappe/email/doctype/unhandled_email/test_unhandled_email.py index 6cabcf6ec2..5606b8ff30 100644 --- a/frappe/email/doctype/unhandled_email/test_unhandled_email.py +++ b/frappe/email/doctype/unhandled_email/test_unhandled_email.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/email/doctype/unhandled_email/unhandled_email.py b/frappe/email/doctype/unhandled_email/unhandled_email.py index 1276da71a1..6414dbece3 100644 --- a/frappe/email/doctype/unhandled_email/unhandled_email.py +++ b/frappe/email/doctype/unhandled_email/unhandled_email.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/email/email_body.py b/frappe/email/email_body.py index 3b03c42b95..7f4398ef2f 100755 --- a/frappe/email/email_body.py +++ b/frappe/email/email_body.py @@ -1,14 +1,12 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, re, os from frappe.utils.pdf import get_pdf from frappe.email.doctype.email_account.email_account import EmailAccount from frappe.utils import (get_url, scrub_urls, strip, expand_relative_urls, cint, split_emails, to_markdown, markdown, random_string, parse_addr) import email.utils -from six import iteritems, text_type, string_types from email.mime.multipart import MIMEMultipart from email.header import Header from email import policy @@ -55,7 +53,7 @@ class EMail: from email import charset as Charset Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8') - if isinstance(recipients, string_types): + if isinstance(recipients, str): recipients = recipients.replace(';', ',').replace('\n', '') recipients = split_emails(recipients) @@ -225,7 +223,7 @@ class EMail: } # reset headers as values may be changed. - for key, val in iteritems(headers): + for key, val in headers.items(): if val: self.set_header(key, val) @@ -328,7 +326,7 @@ def add_attachment(fname, fcontent, content_type=None, maintype, subtype = content_type.split('/', 1) if maintype == 'text': # Note: we should handle calculating the charset - if isinstance(fcontent, text_type): + if isinstance(fcontent, str): fcontent = fcontent.encode("utf-8") part = MIMEText(fcontent, _subtype=subtype, _charset="utf-8") elif maintype == 'image': @@ -345,7 +343,7 @@ def add_attachment(fname, fcontent, content_type=None, # Set the filename parameter if fname: attachment_type = 'inline' if inline else 'attachment' - part.add_header('Content-Disposition', attachment_type, filename=text_type(fname)) + part.add_header('Content-Disposition', attachment_type, filename=str(fname)) if content_id: part.add_header('Content-ID', '<{0}>'.format(content_id)) @@ -452,7 +450,7 @@ def get_header(header=None): if not header: return None - if isinstance(header, string_types): + if isinstance(header, str): # header = 'My Title' header = [header, None] if len(header) == 1: diff --git a/frappe/email/inbox.py b/frappe/email/inbox.py index 395a2d3e2d..5f8f516772 100644 --- a/frappe/email/inbox.py +++ b/frappe/email/inbox.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe import json diff --git a/frappe/email/queue.py b/frappe/email/queue.py index 52c91baf1c..7aef75b669 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -1,10 +1,9 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import sys -from six.moves import html_parser as HTMLParser +from html.parser import HTMLParser import smtplib, quopri, json from frappe import msgprint, _, safe_decode, safe_encode, enqueue from frappe.email.smtp import SMTPServer @@ -14,7 +13,6 @@ from frappe.utils.verified_command import get_signed_params, verify_request from html2text import html2text from frappe.utils import get_url, nowdate, now_datetime, add_days, split_emails, cstr, cint from rq.timeouts import JobTimeoutException -from six import text_type, string_types, PY3 from email.parser import Parser @@ -62,13 +60,13 @@ def send(recipients=None, sender=None, subject=None, message=None, text_content= if not bcc: bcc = [] - if isinstance(recipients, string_types): + if isinstance(recipients, str): recipients = split_emails(recipients) - if isinstance(cc, string_types): + if isinstance(cc, str): cc = split_emails(cc) - if isinstance(bcc, string_types): + if isinstance(bcc, str): bcc = split_emails(bcc) if isinstance(send_after, int): diff --git a/frappe/email/receive.py b/frappe/email/receive.py index 6d60007cdb..803712e7b0 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -12,7 +12,6 @@ from email.header import decode_header import _socket import chardet -import six from email_reply_parser import EmailReplyParser import frappe @@ -361,13 +360,10 @@ class Email: """Parses headers, content, attachments from given raw message. :param content: Raw message.""" - if six.PY2: - self.mail = email.message_from_string(safe_encode(content)) + if isinstance(content, bytes): + self.mail = email.message_from_bytes(content) else: - if isinstance(content, bytes): - self.mail = email.message_from_bytes(content) - else: - self.mail = email.message_from_string(content) + self.mail = email.message_from_string(content) self.text_content = '' self.html_content = '' diff --git a/frappe/email/smtp.py b/frappe/email/smtp.py index 3acb76af23..9425f4dbd4 100644 --- a/frappe/email/smtp.py +++ b/frappe/email/smtp.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import smtplib import email.utils diff --git a/frappe/email/test_email_body.py b/frappe/email/test_email_body.py index 33668cddba..1961d7801e 100644 --- a/frappe/email/test_email_body.py +++ b/frappe/email/test_email_body.py @@ -1,7 +1,5 @@ # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals - import unittest, os, base64 from frappe import safe_decode from frappe.email.receive import Email @@ -9,7 +7,7 @@ from frappe.email.email_body import (replace_filename_with_cid, get_email, inline_style_in_html, get_header) from frappe.email.queue import get_email_queue from frappe.email.doctype.email_queue.email_queue import SendMailContext -from six import PY3 + class TestEmailBody(unittest.TestCase): def setUp(self): @@ -42,13 +40,8 @@ This is the text version of this email ).as_string().replace("\r\n", "\n") def test_prepare_message_returns_already_encoded_string(self): - - if PY3: - uni_chr1 = chr(40960) - uni_chr2 = chr(1972) - else: - uni_chr1 = unichr(40960) - uni_chr2 = unichr(1972) + uni_chr1 = chr(40960) + uni_chr2 = chr(1972) email = get_email_queue( recipients=['test@example.com'], @@ -73,10 +66,7 @@ This is the text version of this email mail_ctx = SendMailContext(queue_doc = email) result = safe_decode(mail_ctx.build_message(recipient_email='test@test.com')) - if PY3: - self.assertTrue(result.count('\n') == result.count("\r")) - else: - self.assertTrue(True) + self.assertTrue(result.count('\n') == result.count("\r")) def test_image(self): img_signature = ''' diff --git a/frappe/email/utils.py b/frappe/email/utils.py index 8b4bd95ba0..24ce77b922 100644 --- a/frappe/email/utils.py +++ b/frappe/email/utils.py @@ -1,7 +1,5 @@ # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt - -from __future__ import unicode_literals, print_function import imaplib, poplib from frappe.utils import cint diff --git a/frappe/event_streaming/doctype/document_type_field_mapping/document_type_field_mapping.py b/frappe/event_streaming/doctype/document_type_field_mapping/document_type_field_mapping.py index 1ab9534bdc..fc8164d8a4 100644 --- a/frappe/event_streaming/doctype/document_type_field_mapping/document_type_field_mapping.py +++ b/frappe/event_streaming/doctype/document_type_field_mapping/document_type_field_mapping.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/event_streaming/doctype/document_type_mapping/document_type_mapping.py b/frappe/event_streaming/doctype/document_type_mapping/document_type_mapping.py index bf96e4e27b..2cf7282a5a 100644 --- a/frappe/event_streaming/doctype/document_type_mapping/document_type_mapping.py +++ b/frappe/event_streaming/doctype/document_type_mapping/document_type_mapping.py @@ -1,12 +1,9 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt - -from __future__ import unicode_literals import frappe import json from frappe import _ -from six import iteritems from frappe.model.document import Document from frappe.model import default_fields @@ -100,7 +97,7 @@ class DocumentTypeMapping(Document): def get_mapped_dependency(self, mapping, producer_site, doc): inner_mapping = frappe.get_doc('Document Type Mapping', mapping.mapping) filters = json.loads(mapping.remote_value_filters) - for key, value in iteritems(filters): + for key, value in filters.items(): if value.startswith('eval:'): val = frappe.safe_eval(value[5:], None, dict(doc=doc)) filters[key] = val @@ -117,7 +114,7 @@ class DocumentTypeMapping(Document): def map_rows_removed(self, update_diff, mapping): removed = [] mapping['removed'] = update_diff.removed - for key, value in iteritems(update_diff.removed.copy()): + for key, value in update_diff.removed.copy().items(): local_table_name = frappe.db.get_value('Document Type Field Mapping', { 'remote_fieldname': key, 'parent': self.name @@ -133,7 +130,7 @@ class DocumentTypeMapping(Document): def map_rows(self, update_diff, mapping, producer_site, operation): remote_fields = [] - for tablename, entries in iteritems(update_diff.get(operation).copy()): + for tablename, entries in update_diff.get(operation).copy().items(): local_table_name = frappe.db.get_value('Document Type Field Mapping', {'remote_fieldname': tablename}, 'local_fieldname') table_map = frappe.db.get_value('Document Type Field Mapping', {'local_fieldname': local_table_name, 'parent': self.name}, 'mapping') table_map = frappe.get_doc('Document Type Mapping', table_map) diff --git a/frappe/event_streaming/doctype/document_type_mapping/test_document_type_mapping.py b/frappe/event_streaming/doctype/document_type_mapping/test_document_type_mapping.py index 178d7b6b6a..b1bb322855 100644 --- a/frappe/event_streaming/doctype/document_type_mapping/test_document_type_mapping.py +++ b/frappe/event_streaming/doctype/document_type_mapping/test_document_type_mapping.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/event_streaming/doctype/event_consumer/event_consumer.py b/frappe/event_streaming/doctype/event_consumer/event_consumer.py index 5789e09e74..00d304f7f4 100644 --- a/frappe/event_streaming/doctype/event_consumer/event_consumer.py +++ b/frappe/event_streaming/doctype/event_consumer/event_consumer.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import json import requests @@ -31,7 +30,7 @@ class EventConsumer(Document): self.update_consumer_status() else: frappe.db.set_value(self.doctype, self.name, 'incoming_change', 0) - + frappe.cache().delete_value('event_consumer_document_type_map') def on_trash(self): diff --git a/frappe/event_streaming/doctype/event_consumer/test_event_consumer.py b/frappe/event_streaming/doctype/event_consumer/test_event_consumer.py index 9e344842bd..b8072ecabd 100644 --- a/frappe/event_streaming/doctype/event_consumer/test_event_consumer.py +++ b/frappe/event_streaming/doctype/event_consumer/test_event_consumer.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/event_streaming/doctype/event_consumer_document_type/event_consumer_document_type.py b/frappe/event_streaming/doctype/event_consumer_document_type/event_consumer_document_type.py index 197338027f..cf5d18edfd 100644 --- a/frappe/event_streaming/doctype/event_consumer_document_type/event_consumer_document_type.py +++ b/frappe/event_streaming/doctype/event_consumer_document_type/event_consumer_document_type.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/event_streaming/doctype/event_producer/test_event_producer.py b/frappe/event_streaming/doctype/event_producer/test_event_producer.py index 4c259c3729..883f4f2df2 100644 --- a/frappe/event_streaming/doctype/event_producer/test_event_producer.py +++ b/frappe/event_streaming/doctype/event_producer/test_event_producer.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest import json @@ -154,7 +152,7 @@ class TestEventProducer(unittest.TestCase): def test_conditional_events(self): producer = get_remote_site() - + # Add Condition event_producer = frappe.get_doc('Event Producer', producer_url) note_producer_entry = [ @@ -192,7 +190,7 @@ class TestEventProducer(unittest.TestCase): def test_conditional_events_with_cmd(self): producer = get_remote_site() - + # Add Condition event_producer = frappe.get_doc('Event Producer', producer_url) note_producer_entry = [ diff --git a/frappe/event_streaming/doctype/event_producer_document_type/event_producer_document_type.py b/frappe/event_streaming/doctype/event_producer_document_type/event_producer_document_type.py index 2870d5330f..9ae70e0f97 100644 --- a/frappe/event_streaming/doctype/event_producer_document_type/event_producer_document_type.py +++ b/frappe/event_streaming/doctype/event_producer_document_type/event_producer_document_type.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/event_streaming/doctype/event_producer_last_update/event_producer_last_update.py b/frappe/event_streaming/doctype/event_producer_last_update/event_producer_last_update.py index 02e297bdd5..391cf79c27 100644 --- a/frappe/event_streaming/doctype/event_producer_last_update/event_producer_last_update.py +++ b/frappe/event_streaming/doctype/event_producer_last_update/event_producer_last_update.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/event_streaming/doctype/event_producer_last_update/test_event_producer_last_update.py b/frappe/event_streaming/doctype/event_producer_last_update/test_event_producer_last_update.py index 0311cb2df9..62ea71edab 100644 --- a/frappe/event_streaming/doctype/event_producer_last_update/test_event_producer_last_update.py +++ b/frappe/event_streaming/doctype/event_producer_last_update/test_event_producer_last_update.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/event_streaming/doctype/event_sync_log/event_sync_log.py b/frappe/event_streaming/doctype/event_sync_log/event_sync_log.py index 31b1f863aa..1d255a5c30 100644 --- a/frappe/event_streaming/doctype/event_sync_log/event_sync_log.py +++ b/frappe/event_streaming/doctype/event_sync_log/event_sync_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/event_streaming/doctype/event_sync_log/test_event_sync_log.py b/frappe/event_streaming/doctype/event_sync_log/test_event_sync_log.py index 6c621b8b0e..ef55dc0f16 100644 --- a/frappe/event_streaming/doctype/event_sync_log/test_event_sync_log.py +++ b/frappe/event_streaming/doctype/event_sync_log/test_event_sync_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/event_streaming/doctype/event_update_log/event_update_log.py b/frappe/event_streaming/doctype/event_update_log/event_update_log.py index 1c31718c2b..ae851c70d1 100644 --- a/frappe/event_streaming/doctype/event_update_log/event_update_log.py +++ b/frappe/event_streaming/doctype/event_update_log/event_update_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils.background_jobs import get_jobs @@ -235,7 +234,7 @@ def get_update_logs_for_consumer(event_consumer, doctypes, last_update): if isinstance(doctypes, str): doctypes = frappe.parse_json(doctypes) - + from frappe.event_streaming.doctype.event_consumer.event_consumer import has_consumer_access consumer = frappe.get_doc('Event Consumer', event_consumer) diff --git a/frappe/event_streaming/doctype/event_update_log/test_event_update_log.py b/frappe/event_streaming/doctype/event_update_log/test_event_update_log.py index e00fc767d9..99ced3c209 100644 --- a/frappe/event_streaming/doctype/event_update_log/test_event_update_log.py +++ b/frappe/event_streaming/doctype/event_update_log/test_event_update_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/event_streaming/doctype/event_update_log_consumer/event_update_log_consumer.py b/frappe/event_streaming/doctype/event_update_log_consumer/event_update_log_consumer.py index ee6d5d8ca9..80a59e4c31 100644 --- a/frappe/event_streaming/doctype/event_update_log_consumer/event_update_log_consumer.py +++ b/frappe/event_streaming/doctype/event_update_log_consumer/event_update_log_consumer.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/exceptions.py b/frappe/exceptions.py index ab65e6e006..0f111fdcf9 100644 --- a/frappe/exceptions.py +++ b/frappe/exceptions.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import sys # BEWARE don't put anything in this file except exceptions diff --git a/frappe/frappeclient.py b/frappe/frappeclient.py index 054a8c9369..e57f82b60a 100644 --- a/frappe/frappeclient.py +++ b/frappe/frappeclient.py @@ -1,8 +1,6 @@ -from __future__ import print_function, unicode_literals import requests import json import frappe -from six import iteritems, string_types import base64 ''' @@ -88,7 +86,7 @@ class FrappeClient(object): def get_list(self, doctype, fields='["name"]', filters=None, limit_start=0, limit_page_length=0): """Returns list of records of a particular type""" - if not isinstance(fields, string_types): + if not isinstance(fields, str): fields = json.dumps(fields) params = { "fields": fields, @@ -310,7 +308,7 @@ class FrappeClient(object): def preprocess(self, params): """convert dicts, lists to json""" - for key, value in iteritems(params): + for key, value in params.items(): if isinstance(value, (dict, list)): params[key] = json.dumps(value) diff --git a/frappe/geo/country_info.py b/frappe/geo/country_info.py index 4f878325ad..ddebd1fb0e 100644 --- a/frappe/geo/country_info.py +++ b/frappe/geo/country_info.py @@ -2,8 +2,6 @@ # MIT License. See license.txt # all country info -from __future__ import unicode_literals - import os, json, frappe from frappe.utils.momentjs import get_all_timezones diff --git a/frappe/geo/doctype/country/__init__.py b/frappe/geo/doctype/country/__init__.py index baffc48825..8b13789179 100644 --- a/frappe/geo/doctype/country/__init__.py +++ b/frappe/geo/doctype/country/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/geo/doctype/country/country.py b/frappe/geo/doctype/country/country.py index 5f8b6f7bd5..54935e6eaf 100644 --- a/frappe/geo/doctype/country/country.py +++ b/frappe/geo/doctype/country/country.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/geo/doctype/country/test_country.py b/frappe/geo/doctype/country/test_country.py index 81849d6886..e00d6ecf37 100644 --- a/frappe/geo/doctype/country/test_country.py +++ b/frappe/geo/doctype/country/test_country.py @@ -1,6 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: See license.txt -from __future__ import unicode_literals import frappe test_records = frappe.get_test_records('Country') \ No newline at end of file diff --git a/frappe/geo/doctype/currency/__init__.py b/frappe/geo/doctype/currency/__init__.py index baffc48825..8b13789179 100644 --- a/frappe/geo/doctype/currency/__init__.py +++ b/frappe/geo/doctype/currency/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/geo/doctype/currency/currency.py b/frappe/geo/doctype/currency/currency.py index 688303fd50..b3ce67cc67 100644 --- a/frappe/geo/doctype/currency/currency.py +++ b/frappe/geo/doctype/currency/currency.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: See license.txt -from __future__ import unicode_literals import frappe from frappe import throw, _ diff --git a/frappe/geo/doctype/currency/test_currency.py b/frappe/geo/doctype/currency/test_currency.py index 7945e193da..5552e675ec 100644 --- a/frappe/geo/doctype/currency/test_currency.py +++ b/frappe/geo/doctype/currency/test_currency.py @@ -3,6 +3,5 @@ # pre loaded -from __future__ import unicode_literals import frappe test_records = frappe.get_test_records('Currency') \ No newline at end of file diff --git a/frappe/geo/utils.py b/frappe/geo/utils.py index d94a13ea41..89de176f0b 100644 --- a/frappe/geo/utils.py +++ b/frappe/geo/utils.py @@ -2,8 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import frappe from pymysql import InternalError diff --git a/frappe/handler.py b/frappe/handler.py index b622667e18..de86c15c8f 100755 --- a/frappe/handler.py +++ b/frappe/handler.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - from werkzeug.wrappers import Response import frappe diff --git a/frappe/hooks.py b/frappe/hooks.py index d0968ce051..ac42a03461 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + from . import __version__ as app_version diff --git a/frappe/integrations/doctype/braintree_settings/braintree_settings.py b/frappe/integrations/doctype/braintree_settings/braintree_settings.py index 768f58c0a0..9dc9778bee 100644 --- a/frappe/integrations/doctype/braintree_settings/braintree_settings.py +++ b/frappe/integrations/doctype/braintree_settings/braintree_settings.py @@ -2,12 +2,11 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document import braintree from frappe import _ -from six.moves.urllib.parse import urlencode +from urllib.parse import urlencode from frappe.utils import get_url, call_hook_method from frappe.integrations.utils import create_request_log, create_payment_gateway diff --git a/frappe/integrations/doctype/braintree_settings/test_braintree_settings.py b/frappe/integrations/doctype/braintree_settings/test_braintree_settings.py index 80fa3c54b8..72a678a92c 100644 --- a/frappe/integrations/doctype/braintree_settings/test_braintree_settings.py +++ b/frappe/integrations/doctype/braintree_settings/test_braintree_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestBraintreeSettings(unittest.TestCase): diff --git a/frappe/integrations/doctype/connected_app/test_connected_app.py b/frappe/integrations/doctype/connected_app/test_connected_app.py index b4304f6ee8..d1ff19ecb2 100644 --- a/frappe/integrations/doctype/connected_app/test_connected_app.py +++ b/frappe/integrations/doctype/connected_app/test_connected_app.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and contributors # See license.txt -from __future__ import unicode_literals - import unittest import requests from urllib.parse import urljoin diff --git a/frappe/integrations/doctype/dropbox_settings/test_dropbox_settings.py b/frappe/integrations/doctype/dropbox_settings/test_dropbox_settings.py index 539fc417f2..d34e65de50 100644 --- a/frappe/integrations/doctype/dropbox_settings/test_dropbox_settings.py +++ b/frappe/integrations/doctype/dropbox_settings/test_dropbox_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/integrations/doctype/google_drive/test_google_drive.py b/frappe/integrations/doctype/google_drive/test_google_drive.py index f06e13572c..96e8577c7c 100644 --- a/frappe/integrations/doctype/google_drive/test_google_drive.py +++ b/frappe/integrations/doctype/google_drive/test_google_drive.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/integrations/doctype/google_settings/google_settings.py b/frappe/integrations/doctype/google_settings/google_settings.py index ecc975235a..9a3f3c8ae2 100644 --- a/frappe/integrations/doctype/google_settings/google_settings.py +++ b/frappe/integrations/doctype/google_settings/google_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/integrations/doctype/integration_request/integration_request.py b/frappe/integrations/doctype/integration_request/integration_request.py index f1d59beb5a..4c4961d96d 100644 --- a/frappe/integrations/doctype/integration_request/integration_request.py +++ b/frappe/integrations/doctype/integration_request/integration_request.py @@ -2,11 +2,9 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document import json -from six import string_types from frappe.integrations.utils import json_handler class IntegrationRequest(Document): @@ -25,14 +23,14 @@ class IntegrationRequest(Document): def handle_success(self, response): """update the output field with the response along with the relevant status""" - if isinstance(response, string_types): + if isinstance(response, str): response = json.loads(response) self.db_set("status", "Completed") self.db_set("output", json.dumps(response, default=json_handler)) def handle_failure(self, response): """update the error field with the response along with the relevant status""" - if isinstance(response, string_types): + if isinstance(response, str): response = json.loads(response) self.db_set("status", "Failed") self.db_set("error", json.dumps(response, default=json_handler)) \ No newline at end of file diff --git a/frappe/integrations/doctype/integration_request/test_integration_request.py b/frappe/integrations/doctype/integration_request/test_integration_request.py index 6b77b57de4..a26eb4ba93 100644 --- a/frappe/integrations/doctype/integration_request/test_integration_request.py +++ b/frappe/integrations/doctype/integration_request/test_integration_request.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/integrations/doctype/ldap_group_mapping/ldap_group_mapping.py b/frappe/integrations/doctype/ldap_group_mapping/ldap_group_mapping.py index f9f2adeed0..b6bb77d964 100644 --- a/frappe/integrations/doctype/ldap_group_mapping/ldap_group_mapping.py +++ b/frappe/integrations/doctype/ldap_group_mapping/ldap_group_mapping.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.py b/frappe/integrations/doctype/ldap_settings/ldap_settings.py index 80dfef2693..122096cf6f 100644 --- a/frappe/integrations/doctype/ldap_settings/ldap_settings.py +++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _, safe_encode from frappe.model.document import Document diff --git a/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py b/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py index e6cf4eef3a..113692b6c4 100644 --- a/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py +++ b/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/integrations/doctype/oauth_authorization_code/oauth_authorization_code.py b/frappe/integrations/doctype/oauth_authorization_code/oauth_authorization_code.py index f08e7eb5bb..0c7f02844c 100644 --- a/frappe/integrations/doctype/oauth_authorization_code/oauth_authorization_code.py +++ b/frappe/integrations/doctype/oauth_authorization_code/oauth_authorization_code.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py b/frappe/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py index cecf187e61..6084dd64b4 100644 --- a/frappe/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py +++ b/frappe/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/integrations/doctype/oauth_bearer_token/oauth_bearer_token.py b/frappe/integrations/doctype/oauth_bearer_token/oauth_bearer_token.py index 09fd29075b..916d0205d2 100644 --- a/frappe/integrations/doctype/oauth_bearer_token/oauth_bearer_token.py +++ b/frappe/integrations/doctype/oauth_bearer_token/oauth_bearer_token.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py b/frappe/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py index af7de360ab..6028cebcf9 100644 --- a/frappe/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py +++ b/frappe/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/integrations/doctype/oauth_client/oauth_client.py b/frappe/integrations/doctype/oauth_client/oauth_client.py index 02f5041dfb..0b449ff968 100644 --- a/frappe/integrations/doctype/oauth_client/oauth_client.py +++ b/frappe/integrations/doctype/oauth_client/oauth_client.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document diff --git a/frappe/integrations/doctype/oauth_client/test_oauth_client.py b/frappe/integrations/doctype/oauth_client/test_oauth_client.py index ee119455e5..a4e50e15d8 100644 --- a/frappe/integrations/doctype/oauth_client/test_oauth_client.py +++ b/frappe/integrations/doctype/oauth_client/test_oauth_client.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/integrations/doctype/oauth_provider_settings/oauth_provider_settings.py b/frappe/integrations/doctype/oauth_provider_settings/oauth_provider_settings.py index 2bf086e0fe..3ab5df92ac 100644 --- a/frappe/integrations/doctype/oauth_provider_settings/oauth_provider_settings.py +++ b/frappe/integrations/doctype/oauth_provider_settings/oauth_provider_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ diff --git a/frappe/integrations/doctype/oauth_scope/oauth_scope.py b/frappe/integrations/doctype/oauth_scope/oauth_scope.py index a5dfe7e1ce..ae579e6b51 100644 --- a/frappe/integrations/doctype/oauth_scope/oauth_scope.py +++ b/frappe/integrations/doctype/oauth_scope/oauth_scope.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/integrations/doctype/paypal_settings/paypal_settings.py b/frappe/integrations/doctype/paypal_settings/paypal_settings.py index efd1b03355..da045d2c6a 100644 --- a/frappe/integrations/doctype/paypal_settings/paypal_settings.py +++ b/frappe/integrations/doctype/paypal_settings/paypal_settings.py @@ -63,12 +63,11 @@ More Details: """ -from __future__ import unicode_literals import frappe import json import pytz from frappe import _ -from six.moves.urllib.parse import urlencode +from urllib.parse import urlencode from frappe.model.document import Document from frappe.integrations.utils import create_request_log, make_post_request, create_payment_gateway from frappe.utils import get_url, call_hook_method, cint, get_datetime diff --git a/frappe/integrations/doctype/paytm_settings/paytm_settings.py b/frappe/integrations/doctype/paytm_settings/paytm_settings.py index 616c3837d4..9f15d73f09 100644 --- a/frappe/integrations/doctype/paytm_settings/paytm_settings.py +++ b/frappe/integrations/doctype/paytm_settings/paytm_settings.py @@ -2,10 +2,9 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import json import requests -from six.moves.urllib.parse import urlencode +from urllib.parse import urlencode import frappe from frappe.model.document import Document @@ -59,7 +58,7 @@ def get_paytm_params(payment_details, order_id, paytm_config): # initialize a dictionary paytm_params = dict() - + redirect_uri = get_request_site_address(True) + "/api/method/frappe.integrations.doctype.paytm_settings.paytm_settings.verify_transaction" diff --git a/frappe/integrations/doctype/paytm_settings/test_paytm_settings.py b/frappe/integrations/doctype/paytm_settings/test_paytm_settings.py index 77a16c82ae..a00ce86327 100644 --- a/frappe/integrations/doctype/paytm_settings/test_paytm_settings.py +++ b/frappe/integrations/doctype/paytm_settings/test_paytm_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/integrations/doctype/query_parameters/query_parameters.py b/frappe/integrations/doctype/query_parameters/query_parameters.py index bfb8eae0b6..13fb94dbe3 100644 --- a/frappe/integrations/doctype/query_parameters/query_parameters.py +++ b/frappe/integrations/doctype/query_parameters/query_parameters.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py b/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py index af7686c9b0..d24e15f480 100644 --- a/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py +++ b/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py @@ -60,14 +60,13 @@ For razorpay payment status is Authorized """ -from __future__ import unicode_literals import frappe from frappe import _ import json import hmac import razorpay import hashlib -from six.moves.urllib.parse import urlencode +from urllib.parse import urlencode from frappe.model.document import Document from frappe.utils import get_url, call_hook_method, cint, get_timestamp from frappe.integrations.utils import (make_get_request, make_post_request, create_request_log, diff --git a/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py b/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py index 308d34c5c2..1346811652 100755 --- a/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py +++ b/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt - -from __future__ import print_function, unicode_literals import os import os.path import frappe diff --git a/frappe/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py b/frappe/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py index 04d90f9b44..3aecdf3489 100755 --- a/frappe/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py +++ b/frappe/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestS3BackupSettings(unittest.TestCase): diff --git a/frappe/integrations/doctype/slack_webhook_url/slack_webhook_url.py b/frappe/integrations/doctype/slack_webhook_url/slack_webhook_url.py index 8756d19c88..a970fc1f11 100644 --- a/frappe/integrations/doctype/slack_webhook_url/slack_webhook_url.py +++ b/frappe/integrations/doctype/slack_webhook_url/slack_webhook_url.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils import get_url_to_form diff --git a/frappe/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py b/frappe/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py index a7f9316ddd..4285c2c4bc 100644 --- a/frappe/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py +++ b/frappe/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestSlackWebhookURL(unittest.TestCase): diff --git a/frappe/integrations/doctype/social_login_key/social_login_key.py b/frappe/integrations/doctype/social_login_key/social_login_key.py index dffb730513..4a4fcd44f4 100644 --- a/frappe/integrations/doctype/social_login_key/social_login_key.py +++ b/frappe/integrations/doctype/social_login_key/social_login_key.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe, json from frappe import _ from frappe.model.document import Document diff --git a/frappe/integrations/doctype/social_login_key/test_social_login_key.py b/frappe/integrations/doctype/social_login_key/test_social_login_key.py index e0b99ad391..23effd6a44 100644 --- a/frappe/integrations/doctype/social_login_key/test_social_login_key.py +++ b/frappe/integrations/doctype/social_login_key/test_social_login_key.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe from frappe.integrations.doctype.social_login_key.social_login_key import BaseUrlNotSetError import unittest diff --git a/frappe/integrations/doctype/social_login_keys/social_login_keys.py b/frappe/integrations/doctype/social_login_keys/social_login_keys.py index bd4cea01af..da9e21cd8e 100644 --- a/frappe/integrations/doctype/social_login_keys/social_login_keys.py +++ b/frappe/integrations/doctype/social_login_keys/social_login_keys.py @@ -1,5 +1,4 @@ # see license -from __future__ import unicode_literals from frappe.model.document import Document class SocialLoginKeys(Document): diff --git a/frappe/integrations/doctype/stripe_settings/stripe_settings.py b/frappe/integrations/doctype/stripe_settings/stripe_settings.py index 70ca6002e4..9bb9c60775 100644 --- a/frappe/integrations/doctype/stripe_settings/stripe_settings.py +++ b/frappe/integrations/doctype/stripe_settings/stripe_settings.py @@ -2,11 +2,10 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ -from six.moves.urllib.parse import urlencode +from urllib.parse import urlencode from frappe.utils import get_url, call_hook_method, cint, flt from frappe.integrations.utils import make_get_request, make_post_request, create_request_log, create_payment_gateway diff --git a/frappe/integrations/doctype/stripe_settings/test_stripe_settings.py b/frappe/integrations/doctype/stripe_settings/test_stripe_settings.py index 39e128192f..ba11c3c38b 100644 --- a/frappe/integrations/doctype/stripe_settings/test_stripe_settings.py +++ b/frappe/integrations/doctype/stripe_settings/test_stripe_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestStripeSettings(unittest.TestCase): diff --git a/frappe/integrations/doctype/token_cache/test_token_cache.py b/frappe/integrations/doctype/token_cache/test_token_cache.py index 7aa069647d..2ffd57403b 100644 --- a/frappe/integrations/doctype/token_cache/test_token_cache.py +++ b/frappe/integrations/doctype/token_cache/test_token_cache.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and contributors # See license.txt -from __future__ import unicode_literals - import unittest import frappe diff --git a/frappe/integrations/doctype/token_cache/token_cache.py b/frappe/integrations/doctype/token_cache/token_cache.py index 7cac58fae0..3001d12b2b 100644 --- a/frappe/integrations/doctype/token_cache/token_cache.py +++ b/frappe/integrations/doctype/token_cache/token_cache.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from datetime import datetime, timedelta import frappe diff --git a/frappe/integrations/doctype/webhook/__init__.py b/frappe/integrations/doctype/webhook/__init__.py index 19233bd175..b92497f16c 100644 --- a/frappe/integrations/doctype/webhook/__init__.py +++ b/frappe/integrations/doctype/webhook/__init__.py @@ -2,8 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import frappe @@ -21,7 +19,7 @@ def run_webhooks(doc, method): if webhooks is None: # query webhooks webhooks_list = frappe.get_all('Webhook', - fields=["name", "`condition`", "webhook_docevent", "webhook_doctype"], + fields=["name", "`condition`", "webhook_docevent", "webhook_doctype"], filters={"enabled": True} ) diff --git a/frappe/integrations/doctype/webhook/test_webhook.py b/frappe/integrations/doctype/webhook/test_webhook.py index acf2f609e7..09ad56a190 100644 --- a/frappe/integrations/doctype/webhook/test_webhook.py +++ b/frappe/integrations/doctype/webhook/test_webhook.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest import frappe @@ -86,7 +84,7 @@ class TestWebhook(unittest.TestCase): # Insert the user to db self.test_user.insert() - + self.assertTrue("User" in frappe.flags.webhooks) # only 1 hook (enabled) must be queued self.assertEqual( @@ -95,7 +93,7 @@ class TestWebhook(unittest.TestCase): ) self.assertTrue(self.test_user.email in frappe.flags.webhooks_executed) self.assertEqual( - frappe.flags.webhooks_executed.get(self.test_user.email)[0], + frappe.flags.webhooks_executed.get(self.test_user.email)[0], self.sample_webhooks[0].name ) diff --git a/frappe/integrations/doctype/webhook/webhook.py b/frappe/integrations/doctype/webhook/webhook.py index ad64d9f714..1fb2bc6743 100644 --- a/frappe/integrations/doctype/webhook/webhook.py +++ b/frappe/integrations/doctype/webhook/webhook.py @@ -2,8 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import base64 import datetime import hashlib @@ -12,7 +10,7 @@ import json from time import sleep import requests -from six.moves.urllib.parse import urlparse +from urllib.parse import urlparse import frappe from frappe import _ diff --git a/frappe/integrations/doctype/webhook_data/webhook_data.py b/frappe/integrations/doctype/webhook_data/webhook_data.py index b7d989410f..dbd9328482 100644 --- a/frappe/integrations/doctype/webhook_data/webhook_data.py +++ b/frappe/integrations/doctype/webhook_data/webhook_data.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/integrations/doctype/webhook_header/webhook_header.py b/frappe/integrations/doctype/webhook_header/webhook_header.py index 11d3ee4085..428b287db2 100644 --- a/frappe/integrations/doctype/webhook_header/webhook_header.py +++ b/frappe/integrations/doctype/webhook_header/webhook_header.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/integrations/oauth2_logins.py b/frappe/integrations/oauth2_logins.py index 14a6bcc417..c38b43beb7 100644 --- a/frappe/integrations/oauth2_logins.py +++ b/frappe/integrations/oauth2_logins.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import frappe.utils from frappe.utils.oauth import login_via_oauth2, login_via_oauth2_id_token @@ -33,7 +32,7 @@ def login_via_salesforce(code, state): @frappe.whitelist(allow_guest=True) def login_via_fairlogin(code, state): - login_via_oauth2("fairlogin", code, state, decoder=decoder_compat) + login_via_oauth2("fairlogin", code, state, decoder=decoder_compat) @frappe.whitelist(allow_guest=True) def custom(code, state): diff --git a/frappe/integrations/offsite_backup_utils.py b/frappe/integrations/offsite_backup_utils.py index 48a2c89107..7a263e9d04 100644 --- a/frappe/integrations/offsite_backup_utils.py +++ b/frappe/integrations/offsite_backup_utils.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import glob import os diff --git a/frappe/integrations/utils.py b/frappe/integrations/utils.py index 1af9682073..09c20568b5 100644 --- a/frappe/integrations/utils.py +++ b/frappe/integrations/utils.py @@ -2,11 +2,9 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import json,datetime -from six.moves.urllib.parse import parse_qs -from six import string_types, text_type +from urllib.parse import parse_qs from frappe.utils import get_request_session from frappe import _ @@ -50,10 +48,10 @@ def make_post_request(url, auth=None, headers=None, data=None): raise exc def create_request_log(data, integration_type, service_name, name=None, error=None): - if isinstance(data, string_types): + if isinstance(data, str): data = json.loads(data) - if isinstance(error, string_types): + if isinstance(error, str): error = json.loads(error) integration_request = frappe.get_doc({ @@ -116,4 +114,4 @@ def create_payment_gateway(gateway, settings=None, controller=None): def json_handler(obj): if isinstance(obj, (datetime.date, datetime.timedelta, datetime.datetime)): - return text_type(obj) + return str(obj) diff --git a/frappe/middlewares.py b/frappe/middlewares.py index 252be56c47..05944ec37a 100644 --- a/frappe/middlewares.py +++ b/frappe/middlewares.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe import os from werkzeug.exceptions import NotFound diff --git a/frappe/migrate.py b/frappe/migrate.py index 619510fe5e..d19e255639 100644 --- a/frappe/migrate.py +++ b/frappe/migrate.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import json import os import sys diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 205b451336..dd93fbcc18 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -2,7 +2,6 @@ # MIT License. See license.txt # model __init__.py -from __future__ import unicode_literals import frappe data_fieldtypes = ( diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 54d77ba988..2f5154cfd9 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -1,9 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals -from six import iteritems, string_types - import frappe import datetime from frappe import _ @@ -109,7 +105,7 @@ class BaseDocument(object): if key in d: self.set(key, d.get(key)) - for key, value in iteritems(d): + for key, value in d.items(): self.set(key, value) return self @@ -120,7 +116,7 @@ class BaseDocument(object): if "doctype" in d: self.set("doctype", d.get("doctype")) - for key, value in iteritems(d): + for key, value in d.items(): # dont_update_if_missing is a list of fieldnames, for which, you don't want to set default value if (self.get(key) is None) and (value is not None) and (key not in self.dont_update_if_missing): self.set(key, value) @@ -670,7 +666,7 @@ class BaseDocument(object): if data_field_options == "URL": if not data: continue - + frappe.utils.validate_url(data, throw=True) def _validate_constants(self): @@ -705,7 +701,7 @@ class BaseDocument(object): type_map = frappe.db.type_map - for fieldname, value in iteritems(self.get_valid_dict()): + for fieldname, value in self.get_valid_dict().items(): df = self.meta.get_field(fieldname) if not df or df.fieldtype == 'Check': @@ -770,7 +766,7 @@ class BaseDocument(object): return for fieldname, value in self.get_valid_dict().items(): - if not value or not isinstance(value, string_types): + if not value or not isinstance(value, str): continue value = frappe.as_unicode(value) @@ -839,7 +835,7 @@ class BaseDocument(object): :param parentfield: If fieldname is in child table.""" from frappe.model.meta import get_field_precision - if parentfield and not isinstance(parentfield, string_types): + if parentfield and not isinstance(parentfield, str): parentfield = parentfield.parentfield cache_key = parentfield or "main" @@ -986,7 +982,7 @@ def _filter(data, filters, limit=None): fval = ("not None", fval) elif fval is False: fval = ("None", fval) - elif isinstance(fval, string_types) and fval.startswith("^"): + elif isinstance(fval, str) and fval.startswith("^"): fval = ("^", fval[1:]) else: fval = ("=", fval) @@ -995,7 +991,7 @@ def _filter(data, filters, limit=None): for d in data: add = True - for f, fval in iteritems(_filters): + for f, fval in _filters.items(): if not frappe.compare(getattr(d, f, None), fval[0], fval[1]): add = False break diff --git a/frappe/model/create_new.py b/frappe/model/create_new.py index dc4fd97e4c..fba6765479 100644 --- a/frappe/model/create_new.py +++ b/frappe/model/create_new.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals """ Create a new document with defaults set """ diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index e0c3406c46..7f22282acf 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -1,10 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals - -from six import iteritems, string_types - """build query for doclistview and return results""" import frappe.defaults @@ -54,7 +49,7 @@ class DatabaseQuery(object): filters, fields = fields, filters elif fields and isinstance(filters, list) \ - and len(filters) > 1 and isinstance(filters[0], string_types): + and len(filters) > 1 and isinstance(filters[0], str): # if `filters` is a list of strings, its probably fields filters, fields = fields, filters @@ -209,7 +204,7 @@ class DatabaseQuery(object): def parse_args(self): """Convert fields and filters from strings to list, dicts""" - if isinstance(self.fields, string_types): + if isinstance(self.fields, str): if self.fields == "*": self.fields = ["*"] else: @@ -223,13 +218,13 @@ class DatabaseQuery(object): for filter_name in ["filters", "or_filters"]: filters = getattr(self, filter_name) - if isinstance(filters, string_types): + if isinstance(filters, str): filters = json.loads(filters) if isinstance(filters, dict): fdict = filters filters = [] - for key, value in iteritems(fdict): + for key, value in fdict.items(): filters.append(make_filter_tuple(self.doctype, key, value)) setattr(self, filter_name, filters) @@ -357,7 +352,7 @@ class DatabaseQuery(object): # remove from filters to_remove = [] for each in self.filters: - if isinstance(each, string_types): + if isinstance(each, str): each = [each] for element in each: @@ -391,7 +386,7 @@ class DatabaseQuery(object): filters = [filters] for f in filters: - if isinstance(f, string_types): + if isinstance(f, str): conditions.append(f) else: conditions.append(self.prepare_filter_condition(f)) @@ -522,12 +517,12 @@ class DatabaseQuery(object): value = get_time(f.value).strftime("%H:%M:%S.%f") fallback = "'00:00:00'" - elif f.operator.lower() in ("like", "not like") or (isinstance(f.value, string_types) and + elif f.operator.lower() in ("like", "not like") or (isinstance(f.value, str) and (not df or df.fieldtype not in ["Float", "Int", "Currency", "Percent", "Check"])): value = "" if f.value==None else f.value fallback = "''" - if f.operator.lower() in ("like", "not like") and isinstance(value, string_types): + if f.operator.lower() in ("like", "not like") and isinstance(value, str): # because "like" uses backslash (\) for escaping value = value.replace("\\", "\\\\").replace("%", "%%") @@ -544,7 +539,7 @@ class DatabaseQuery(object): fallback = 0 # escape value - if isinstance(value, string_types) and not f.operator.lower() == 'between': + if isinstance(value, str) and not f.operator.lower() == 'between': value = "{0}".format(frappe.db.escape(value, percent=False)) if (self.ignore_ifnull diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 5fcc74a734..c01439d330 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -1,9 +1,7 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import os -from six import string_types, integer_types import shutil import frappe @@ -35,7 +33,7 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa name = frappe.form_dict.get('dn') names = name - if isinstance(name, string_types) or isinstance(name, integer_types): + if isinstance(name, str) or isinstance(name, int): names = [name] for name in names or []: diff --git a/frappe/model/docfield.py b/frappe/model/docfield.py index 19b78e329d..6360c3866d 100644 --- a/frappe/model/docfield.py +++ b/frappe/model/docfield.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals """docfield utililtes""" import frappe diff --git a/frappe/model/document.py b/frappe/model/document.py index a3f8ad0cfa..8f57aae475 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -1,14 +1,11 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function import frappe import time from frappe import _, msgprint, is_whitelisted from frappe.utils import flt, cstr, now, get_datetime_str, file_lock, date_diff from frappe.model.base_document import BaseDocument, get_controller from frappe.model.naming import set_new_name -from six import iteritems, string_types from werkzeug.exceptions import NotFound, Forbidden import hashlib, json from frappe.model import optional_fields, table_fields @@ -54,7 +51,7 @@ def get_doc(*args, **kwargs): if isinstance(args[0], BaseDocument): # already a document return args[0] - elif isinstance(args[0], string_types): + elif isinstance(args[0], str): doctype = args[0] elif isinstance(args[0], dict): @@ -91,7 +88,7 @@ class Document(BaseDocument): self._default_new_docs = {} self.flags = frappe._dict() - if args and args[0] and isinstance(args[0], string_types): + if args and args[0] and isinstance(args[0], str): # first arugment is doctype if len(args)==1: # single @@ -438,7 +435,7 @@ class Document(BaseDocument): def get_values(): values = self.as_dict() # format values - for key, value in iteritems(values): + for key, value in values.items(): if value==None: values[key] = "" return values @@ -455,7 +452,7 @@ class Document(BaseDocument): def update_single(self, d): """Updates values for Single type Document in `tabSingles`.""" frappe.db.sql("""delete from `tabSingles` where doctype=%s""", self.doctype) - for field, value in iteritems(d): + for field, value in d.items(): if field != "doctype": frappe.db.sql("""insert into `tabSingles` (doctype, field, value) values (%s, %s, %s)""", (self.doctype, field, value)) diff --git a/frappe/model/dynamic_links.py b/frappe/model/dynamic_links.py index 7404ba407e..676c86d7da 100644 --- a/frappe/model/dynamic_links.py +++ b/frappe/model/dynamic_links.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe # select doctypes that are accessed by the user (not read_only) first, so that the diff --git a/frappe/model/mapper.py b/frappe/model/mapper.py index d3014435e0..eb685fb840 100644 --- a/frappe/model/mapper.py +++ b/frappe/model/mapper.py @@ -1,12 +1,10 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, json from frappe import _ from frappe.utils import cstr from frappe.model import default_fields, table_fields -from six import string_types @frappe.whitelist() def make_mapped_doc(method, source_name, selected_children=None, args=None): @@ -60,7 +58,7 @@ def get_mapped_doc(from_doctype, from_docname, table_maps, target_doc=None, # main if not target_doc: target_doc = frappe.new_doc(table_maps[from_doctype]["doctype"]) - elif isinstance(target_doc, string_types): + elif isinstance(target_doc, str): target_doc = frappe.get_doc(json.loads(target_doc)) if (not apply_strict_user_permissions diff --git a/frappe/model/meta.py b/frappe/model/meta.py index 66e8b08d92..b67c41c990 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -14,10 +14,7 @@ Example: ''' - -from __future__ import unicode_literals, print_function from datetime import datetime -from six.moves import range import frappe, json, os from frappe.utils import cstr, cint, cast_fieldtype from frappe.model import default_fields, no_value_fields, optional_fields, data_fieldtypes, table_fields diff --git a/frappe/model/naming.py b/frappe/model/naming.py index b8d6a6f8d7..fe136adce8 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -1,12 +1,10 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import now_datetime, cint, cstr import re -from six import string_types from frappe.model import log_types @@ -146,7 +144,7 @@ def make_autoname(key="", doctype="", doc=""): def parse_naming_series(parts, doctype='', doc=''): n = '' - if isinstance(parts, string_types): + if isinstance(parts, str): parts = parts.split('.') series_set = False today = now_datetime() @@ -177,7 +175,7 @@ def parse_naming_series(parts, doctype='', doc=''): else: part = e - if isinstance(part, string_types): + if isinstance(part, str): n += part return n @@ -203,7 +201,7 @@ def revert_series_if_last(key, name, doc=None): Reverts the series for particular naming series: * key is naming series - SINV-.YYYY-.#### * name is actual name - SINV-2021-0001 - + 1. This function split the key into two parts prefix (SINV-YYYY) & hashes (####). 2. Use prefix to get the current index of that naming series from Series table 3. Then revert the current index. @@ -213,7 +211,7 @@ def revert_series_if_last(key, name, doc=None): 2. If hash doesn't exit in hashes, we get the hash from prefix, then update name and prefix accordingly. *Example:* - 1. key = SINV-.YYYY.- + 1. key = SINV-.YYYY.- * If key doesn't have hash it will add hash at the end * prefix will be SINV-YYYY based on this will get current index from Series table. 2. key = SINV-.####.-2021 @@ -221,9 +219,9 @@ def revert_series_if_last(key, name, doc=None): * will search hash in key then accordingly get prefix = SINV- 3. key = ####.-2021 * prefix = #### and hashes = 2021 (hash doesn't exist) - * will search hash in key then accordingly get prefix = "" + * will search hash in key then accordingly get prefix = "" """ - if ".#" in key: + if ".#" in key: prefix, hashes = key.rsplit(".", 1) if "#" not in hashes: # get the hash part from the key diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 2c9dc5d823..fc5b3ca9fe 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -1,8 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import print_function, unicode_literals - import frappe from frappe import _, bold from frappe.model.dynamic_links import get_dynamic_link_map diff --git a/frappe/model/sync.py b/frappe/model/sync.py index 61983d322c..28f9deb25d 100644 --- a/frappe/model/sync.py +++ b/frappe/model/sync.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function """ Sync's doctype and docfields from txt files to database perms will get synced only if none exist diff --git a/frappe/model/utils/__init__.py b/frappe/model/utils/__init__.py index efbe46a4ab..47615182e4 100644 --- a/frappe/model/utils/__init__.py +++ b/frappe/model/utils/__init__.py @@ -1,15 +1,10 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function -from six.moves import range import frappe from frappe import _ from frappe.utils import cstr from frappe.build import html_to_js_template import re -from six import text_type - import io STANDARD_FIELD_CONVERSION_MAP = { diff --git a/frappe/model/utils/link_count.py b/frappe/model/utils/link_count.py index 5faa5ba44b..7562aaae45 100644 --- a/frappe/model/utils/link_count.py +++ b/frappe/model/utils/link_count.py @@ -1,10 +1,7 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe -from six import iteritems ignore_doctypes = ("DocType", "Print Format", "Role", "Module Def", "Communication", "ToDo") @@ -39,7 +36,7 @@ def update_link_count(): link_count = frappe.cache().get_value('_link_count') if link_count: - for key, count in iteritems(link_count): + for key, count in link_count.items(): if key[0] not in ignore_doctypes: try: frappe.db.sql('update `tab{0}` set idx = idx + {1} where name=%s'.format(key[0], count), diff --git a/frappe/model/utils/rename_field.py b/frappe/model/utils/rename_field.py index 778f623092..9fe9d64041 100644 --- a/frappe/model/utils/rename_field.py +++ b/frappe/model/utils/rename_field.py @@ -1,8 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function - import frappe import json from frappe.model import no_value_fields, table_fields diff --git a/frappe/model/utils/user_settings.py b/frappe/model/utils/user_settings.py index d59bda71a5..ad378ab93f 100644 --- a/frappe/model/utils/user_settings.py +++ b/frappe/model/utils/user_settings.py @@ -1,9 +1,8 @@ -from __future__ import unicode_literals + # Settings saved per user basis # such as page_limit, filters, last_view import frappe, json -from six import iteritems, string_types from frappe import safe_decode # dict for mapping the index and index type for the filters of different views @@ -36,7 +35,7 @@ def update_user_settings(doctype, user_settings, for_update=False): else: current = json.loads(get_user_settings(doctype, for_update = True)) - if isinstance(current, string_types): + if isinstance(current, str): # corrupt due to old code, remove this in a future release current = {} @@ -47,7 +46,7 @@ def update_user_settings(doctype, user_settings, for_update=False): def sync_user_settings(): '''Sync from cache to database (called asynchronously via the browser)''' - for key, data in iteritems(frappe.cache().hgetall('_user_settings')): + for key, data in frappe.cache().hgetall('_user_settings').items(): key = safe_decode(key) doctype, user = key.split('::') # WTF? frappe.db.multisql({ diff --git a/frappe/model/workflow.py b/frappe/model/workflow.py index 3e8125f9b1..fa2f557370 100644 --- a/frappe/model/workflow.py +++ b/frappe/model/workflow.py @@ -1,11 +1,9 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.utils import cint from frappe import _ -from six import string_types import json class WorkflowStateError(frappe.ValidationError): pass @@ -268,7 +266,7 @@ def print_workflow_log(messages, title, doctype, indicator): @frappe.whitelist() def get_common_transition_actions(docs, doctype): common_actions = [] - if isinstance(docs, string_types): + if isinstance(docs, str): docs = json.loads(docs) try: for (i, doc) in enumerate(docs, 1): diff --git a/frappe/modules/__init__.py b/frappe/modules/__init__.py index fef4829bb6..33411f8d74 100644 --- a/frappe/modules/__init__.py +++ b/frappe/modules/__init__.py @@ -1,2 +1,2 @@ -from __future__ import unicode_literals + from .utils import * \ No newline at end of file diff --git a/frappe/modules/export_file.py b/frappe/modules/export_file.py index 4b22c82105..ae9f11d53b 100644 --- a/frappe/modules/export_file.py +++ b/frappe/modules/export_file.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, os import frappe.model from frappe.modules import scrub, get_module_path, scrub_dt_dn diff --git a/frappe/modules/import_file.py b/frappe/modules/import_file.py index fdfd00404c..e743f0c3da 100644 --- a/frappe/modules/import_file.py +++ b/frappe/modules/import_file.py @@ -1,8 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function - import frappe, os, json from frappe.modules import get_module_path, scrub_dt_dn from frappe.utils import get_datetime_str diff --git a/frappe/modules/patch_handler.py b/frappe/modules/patch_handler.py index 0ed10d1e0d..029234d5d9 100644 --- a/frappe/modules/patch_handler.py +++ b/frappe/modules/patch_handler.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function """ Execute Patch Files @@ -14,9 +12,6 @@ from __future__ import unicode_literals, print_function """ import frappe, frappe.permissions, time -# for patches -import os - class PatchError(Exception): pass def run_all(skip_failing=False): diff --git a/frappe/modules/utils.py b/frappe/modules/utils.py index 132aa1e2a5..0f3e57a5a0 100644 --- a/frappe/modules/utils.py +++ b/frappe/modules/utils.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function """ Utilities for using modules """ diff --git a/frappe/monitor.py b/frappe/monitor.py index 6802a59584..34ca7d67f7 100644 --- a/frappe/monitor.py +++ b/frappe/monitor.py @@ -2,8 +2,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - from datetime import datetime import json import traceback diff --git a/frappe/patches/v10_0/enable_chat_by_default_within_system_settings.py b/frappe/patches/v10_0/enable_chat_by_default_within_system_settings.py index eddca78051..24f915c512 100644 --- a/frappe/patches/v10_0/enable_chat_by_default_within_system_settings.py +++ b/frappe/patches/v10_0/enable_chat_by_default_within_system_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v10_0/enhance_security.py b/frappe/patches/v10_0/enhance_security.py index 865d18dcff..4f6ca4faa1 100644 --- a/frappe/patches/v10_0/enhance_security.py +++ b/frappe/patches/v10_0/enhance_security.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe from frappe.utils import cint diff --git a/frappe/patches/v10_0/increase_single_table_column_length.py b/frappe/patches/v10_0/increase_single_table_column_length.py index 18de0cff9e..e578d192fc 100644 --- a/frappe/patches/v10_0/increase_single_table_column_length.py +++ b/frappe/patches/v10_0/increase_single_table_column_length.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + """ Run this after updating country_info.json and or """ diff --git a/frappe/patches/v10_0/migrate_passwords_passlib.py b/frappe/patches/v10_0/migrate_passwords_passlib.py index 22b7a86f85..d0b36efbaa 100644 --- a/frappe/patches/v10_0/migrate_passwords_passlib.py +++ b/frappe/patches/v10_0/migrate_passwords_passlib.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils.password import LegacyPassword diff --git a/frappe/patches/v10_0/modify_naming_series_table.py b/frappe/patches/v10_0/modify_naming_series_table.py index 659e247a38..ca6114eb55 100644 --- a/frappe/patches/v10_0/modify_naming_series_table.py +++ b/frappe/patches/v10_0/modify_naming_series_table.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - ''' Modify the Integer 10 Digits Value to BigInt 20 Digit value to generate long Naming Series diff --git a/frappe/patches/v10_0/modify_smallest_currency_fraction.py b/frappe/patches/v10_0/modify_smallest_currency_fraction.py index f875d6b87d..c9ae477359 100644 --- a/frappe/patches/v10_0/modify_smallest_currency_fraction.py +++ b/frappe/patches/v10_0/modify_smallest_currency_fraction.py @@ -1,7 +1,6 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v10_0/refactor_social_login_keys.py b/frappe/patches/v10_0/refactor_social_login_keys.py index 07737912df..a3f08939ec 100644 --- a/frappe/patches/v10_0/refactor_social_login_keys.py +++ b/frappe/patches/v10_0/refactor_social_login_keys.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe from frappe.utils import cstr diff --git a/frappe/patches/v10_0/reload_countries_and_currencies.py b/frappe/patches/v10_0/reload_countries_and_currencies.py index f83ed9c3aa..8d019a4855 100644 --- a/frappe/patches/v10_0/reload_countries_and_currencies.py +++ b/frappe/patches/v10_0/reload_countries_and_currencies.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + """ Run this after updating country_info.json and or """ diff --git a/frappe/patches/v10_0/remove_custom_field_for_disabled_domain.py b/frappe/patches/v10_0/remove_custom_field_for_disabled_domain.py index f27639388e..54839cfe02 100644 --- a/frappe/patches/v10_0/remove_custom_field_for_disabled_domain.py +++ b/frappe/patches/v10_0/remove_custom_field_for_disabled_domain.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v10_0/set_default_locking_time.py b/frappe/patches/v10_0/set_default_locking_time.py index 1c9797a6cc..045fa0e3fa 100644 --- a/frappe/patches/v10_0/set_default_locking_time.py +++ b/frappe/patches/v10_0/set_default_locking_time.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v10_0/set_no_copy_to_workflow_state.py b/frappe/patches/v10_0/set_no_copy_to_workflow_state.py index 800d4a4d1b..eb469b8452 100644 --- a/frappe/patches/v10_0/set_no_copy_to_workflow_state.py +++ b/frappe/patches/v10_0/set_no_copy_to_workflow_state.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/change_email_signature_fieldtype.py b/frappe/patches/v11_0/change_email_signature_fieldtype.py index f6d4bd5dcb..ccfa8541c3 100644 --- a/frappe/patches/v11_0/change_email_signature_fieldtype.py +++ b/frappe/patches/v11_0/change_email_signature_fieldtype.py @@ -1,7 +1,6 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v11_0/copy_fetch_data_from_options.py b/frappe/patches/v11_0/copy_fetch_data_from_options.py index ae7788450a..e256c7085f 100644 --- a/frappe/patches/v11_0/copy_fetch_data_from_options.py +++ b/frappe/patches/v11_0/copy_fetch_data_from_options.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/create_contact_for_user.py b/frappe/patches/v11_0/create_contact_for_user.py index b4722ab3ae..21e681a83e 100644 --- a/frappe/patches/v11_0/create_contact_for_user.py +++ b/frappe/patches/v11_0/create_contact_for_user.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.core.doctype.user.user import create_contact import re diff --git a/frappe/patches/v11_0/delete_all_prepared_reports.py b/frappe/patches/v11_0/delete_all_prepared_reports.py index 1d722da7e6..77f041e3ee 100644 --- a/frappe/patches/v11_0/delete_all_prepared_reports.py +++ b/frappe/patches/v11_0/delete_all_prepared_reports.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/delete_duplicate_user_permissions.py b/frappe/patches/v11_0/delete_duplicate_user_permissions.py index 9d9d516ac5..518c1f7714 100644 --- a/frappe/patches/v11_0/delete_duplicate_user_permissions.py +++ b/frappe/patches/v11_0/delete_duplicate_user_permissions.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/drop_column_apply_user_permissions.py b/frappe/patches/v11_0/drop_column_apply_user_permissions.py index 4f46bc0907..629d5a5da4 100644 --- a/frappe/patches/v11_0/drop_column_apply_user_permissions.py +++ b/frappe/patches/v11_0/drop_column_apply_user_permissions.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/fix_order_by_in_reports_json.py b/frappe/patches/v11_0/fix_order_by_in_reports_json.py index 2cd82d442d..096e0e7654 100644 --- a/frappe/patches/v11_0/fix_order_by_in_reports_json.py +++ b/frappe/patches/v11_0/fix_order_by_in_reports_json.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, json def execute(): diff --git a/frappe/patches/v11_0/make_all_prepared_report_attachments_private.py b/frappe/patches/v11_0/make_all_prepared_report_attachments_private.py index f7b9e476a9..a099b89b40 100644 --- a/frappe/patches/v11_0/make_all_prepared_report_attachments_private.py +++ b/frappe/patches/v11_0/make_all_prepared_report_attachments_private.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe diff --git a/frappe/patches/v11_0/migrate_report_settings_for_new_listview.py b/frappe/patches/v11_0/migrate_report_settings_for_new_listview.py index 5bef52c295..e5b18368db 100644 --- a/frappe/patches/v11_0/migrate_report_settings_for_new_listview.py +++ b/frappe/patches/v11_0/migrate_report_settings_for_new_listview.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, json def execute(): diff --git a/frappe/patches/v11_0/multiple_references_in_events.py b/frappe/patches/v11_0/multiple_references_in_events.py index 57d4787eca..9fa5968d8e 100644 --- a/frappe/patches/v11_0/multiple_references_in_events.py +++ b/frappe/patches/v11_0/multiple_references_in_events.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/reload_and_rename_view_log.py b/frappe/patches/v11_0/reload_and_rename_view_log.py index 12c71b746f..fa0432c4e2 100644 --- a/frappe/patches/v11_0/reload_and_rename_view_log.py +++ b/frappe/patches/v11_0/reload_and_rename_view_log.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py b/frappe/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py index e2c2ef5f0e..5c54b1e5c1 100644 --- a/frappe/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py +++ b/frappe/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v11_0/remove_skip_for_doctype.py b/frappe/patches/v11_0/remove_skip_for_doctype.py index edd385e317..638a5a0fd7 100644 --- a/frappe/patches/v11_0/remove_skip_for_doctype.py +++ b/frappe/patches/v11_0/remove_skip_for_doctype.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.desk.form.linked_with import get_linked_doctypes from frappe.patches.v11_0.replicate_old_user_permissions import get_doctypes_to_skip diff --git a/frappe/patches/v11_0/rename_email_alert_to_notification.py b/frappe/patches/v11_0/rename_email_alert_to_notification.py index 727055fcc4..365b76ea48 100644 --- a/frappe/patches/v11_0/rename_email_alert_to_notification.py +++ b/frappe/patches/v11_0/rename_email_alert_to_notification.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.rename_doc import rename_doc diff --git a/frappe/patches/v11_0/rename_google_maps_doctype.py b/frappe/patches/v11_0/rename_google_maps_doctype.py index 5420dcfc20..4e8aee6280 100644 --- a/frappe/patches/v11_0/rename_google_maps_doctype.py +++ b/frappe/patches/v11_0/rename_google_maps_doctype.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.rename_doc import rename_doc diff --git a/frappe/patches/v11_0/rename_standard_reply_to_email_template.py b/frappe/patches/v11_0/rename_standard_reply_to_email_template.py index 06869530e2..2906085738 100644 --- a/frappe/patches/v11_0/rename_standard_reply_to_email_template.py +++ b/frappe/patches/v11_0/rename_standard_reply_to_email_template.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.rename_doc import rename_doc diff --git a/frappe/patches/v11_0/rename_workflow_action_to_workflow_action_master.py b/frappe/patches/v11_0/rename_workflow_action_to_workflow_action_master.py index 32f17ac2d8..9a48104611 100644 --- a/frappe/patches/v11_0/rename_workflow_action_to_workflow_action_master.py +++ b/frappe/patches/v11_0/rename_workflow_action_to_workflow_action_master.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.rename_doc import rename_doc diff --git a/frappe/patches/v11_0/replicate_old_user_permissions.py b/frappe/patches/v11_0/replicate_old_user_permissions.py index d1ceae8a7f..50a81b5ce7 100644 --- a/frappe/patches/v11_0/replicate_old_user_permissions.py +++ b/frappe/patches/v11_0/replicate_old_user_permissions.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe import json from frappe.utils import cint diff --git a/frappe/patches/v11_0/set_allow_self_approval_in_workflow.py b/frappe/patches/v11_0/set_allow_self_approval_in_workflow.py index 24c01e1a58..63ae5f949f 100644 --- a/frappe/patches/v11_0/set_allow_self_approval_in_workflow.py +++ b/frappe/patches/v11_0/set_allow_self_approval_in_workflow.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/set_default_letter_head_source.py b/frappe/patches/v11_0/set_default_letter_head_source.py index a43ea397e4..3639524e7d 100644 --- a/frappe/patches/v11_0/set_default_letter_head_source.py +++ b/frappe/patches/v11_0/set_default_letter_head_source.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe def execute(): diff --git a/frappe/patches/v11_0/set_dropbox_file_backup.py b/frappe/patches/v11_0/set_dropbox_file_backup.py index 884fef320e..27492b3ab2 100644 --- a/frappe/patches/v11_0/set_dropbox_file_backup.py +++ b/frappe/patches/v11_0/set_dropbox_file_backup.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + from frappe.utils import cint import frappe diff --git a/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py b/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py index 331b0eba32..a8e9bd4de1 100644 --- a/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py +++ b/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils.password import get_decrypted_password diff --git a/frappe/patches/v11_0/sync_user_permission_doctype_before_migrate.py b/frappe/patches/v11_0/sync_user_permission_doctype_before_migrate.py index 738fea1a48..55a7b74f7e 100644 --- a/frappe/patches/v11_0/sync_user_permission_doctype_before_migrate.py +++ b/frappe/patches/v11_0/sync_user_permission_doctype_before_migrate.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v11_0/update_list_user_settings.py b/frappe/patches/v11_0/update_list_user_settings.py index d492ff1704..1b179d8cdf 100644 --- a/frappe/patches/v11_0/update_list_user_settings.py +++ b/frappe/patches/v11_0/update_list_user_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, json from frappe.model.utils.user_settings import update_user_settings, sync_user_settings diff --git a/frappe/patches/v12_0/create_notification_settings_for_user.py b/frappe/patches/v12_0/create_notification_settings_for_user.py index 63eeccc07a..6edfd88872 100644 --- a/frappe/patches/v12_0/create_notification_settings_for_user.py +++ b/frappe/patches/v12_0/create_notification_settings_for_user.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.desk.doctype.notification_settings.notification_settings import create_notification_settings diff --git a/frappe/patches/v12_0/init_desk_settings.py b/frappe/patches/v12_0/init_desk_settings.py index ecd9c94d5b..fceb44b924 100644 --- a/frappe/patches/v12_0/init_desk_settings.py +++ b/frappe/patches/v12_0/init_desk_settings.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import json import frappe from frappe.config import get_modules_from_all_apps_for_user diff --git a/frappe/patches/v12_0/move_timeline_links_to_dynamic_links.py b/frappe/patches/v12_0/move_timeline_links_to_dynamic_links.py index 040fde1bee..85be3f7feb 100644 --- a/frappe/patches/v12_0/move_timeline_links_to_dynamic_links.py +++ b/frappe/patches/v12_0/move_timeline_links_to_dynamic_links.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe def execute(): diff --git a/frappe/patches/v12_0/setup_comments_from_communications.py b/frappe/patches/v12_0/setup_comments_from_communications.py index 28c7aa93c0..039ceeff35 100644 --- a/frappe/patches/v12_0/setup_comments_from_communications.py +++ b/frappe/patches/v12_0/setup_comments_from_communications.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe def execute(): diff --git a/frappe/patches/v12_0/setup_email_linking.py b/frappe/patches/v12_0/setup_email_linking.py index 08f57ca5e4..9e939e1245 100644 --- a/frappe/patches/v12_0/setup_email_linking.py +++ b/frappe/patches/v12_0/setup_email_linking.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - from frappe.desk.page.setup_wizard.install_fixtures import setup_email_linking def execute(): diff --git a/frappe/patches/v12_0/update_auto_repeat_status_and_not_submittable.py b/frappe/patches/v12_0/update_auto_repeat_status_and_not_submittable.py index d696b6c53a..3a3dcec315 100644 --- a/frappe/patches/v12_0/update_auto_repeat_status_and_not_submittable.py +++ b/frappe/patches/v12_0/update_auto_repeat_status_and_not_submittable.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.custom.doctype.custom_field.custom_field import create_custom_field diff --git a/frappe/patches/v13_0/add_standard_navbar_items.py b/frappe/patches/v13_0/add_standard_navbar_items.py index 9982e6e3f5..4473cb8c07 100644 --- a/frappe/patches/v13_0/add_standard_navbar_items.py +++ b/frappe/patches/v13_0/add_standard_navbar_items.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils.install import add_standard_navbar_items diff --git a/frappe/patches/v13_0/add_switch_theme_to_navbar_settings.py b/frappe/patches/v13_0/add_switch_theme_to_navbar_settings.py index 29b99464b5..b5542c9c8a 100644 --- a/frappe/patches/v13_0/add_switch_theme_to_navbar_settings.py +++ b/frappe/patches/v13_0/add_switch_theme_to_navbar_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v13_0/add_toggle_width_in_navbar_settings.py b/frappe/patches/v13_0/add_toggle_width_in_navbar_settings.py index 59acb77480..bd3367377c 100644 --- a/frappe/patches/v13_0/add_toggle_width_in_navbar_settings.py +++ b/frappe/patches/v13_0/add_toggle_width_in_navbar_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v13_0/cleanup_desk_cards.py b/frappe/patches/v13_0/cleanup_desk_cards.py index 6ac8604041..b6fab66475 100644 --- a/frappe/patches/v13_0/cleanup_desk_cards.py +++ b/frappe/patches/v13_0/cleanup_desk_cards.py @@ -1,11 +1,10 @@ import frappe -from six import string_types from json import loads from frappe.desk.doctype.workspace.workspace import get_link_type, get_report_type def execute(): frappe.reload_doc('desk', 'doctype', 'workspace') - + pages = frappe.db.sql("Select `name` from `tabDesk Page`") # pages = frappe.get_all("Workspace", filters={"is_standard": 0}, pluck="name") @@ -21,14 +20,14 @@ def rebuild_links(page): doc = frappe.get_doc("Workspace", page) except frappe.DoesNotExistError: db_doc = get_doc_from_db(page) - + doc = frappe.get_doc(db_doc) doc.insert(ignore_permissions=True) - + doc.links = [] for card in get_all_cards(page): - if isinstance(card.links, string_types): + if isinstance(card.links, str): links = loads(card.links) else: links = card.links @@ -43,7 +42,7 @@ def rebuild_links(page): for link in links: if not frappe.db.exists(get_link_type(link.get('type')), link.get('name')): continue - + doc.append('links', { "label": link.get('label') or link.get('name'), "type": "Link", @@ -53,7 +52,7 @@ def rebuild_links(page): "dependencies": ', '.join(link.get('dependencies', [])), "is_query_report": get_report_type(link.get('name')) if link.get('type').lower() == "report" else 0 }) - + try: doc.save(ignore_permissions=True) except frappe.LinkValidationError: diff --git a/frappe/patches/v13_0/delete_event_producer_and_consumer_keys.py b/frappe/patches/v13_0/delete_event_producer_and_consumer_keys.py index 1eba5871c2..776e9c796e 100644 --- a/frappe/patches/v13_0/delete_event_producer_and_consumer_keys.py +++ b/frappe/patches/v13_0/delete_event_producer_and_consumer_keys.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v13_0/delete_package_publish_tool.py b/frappe/patches/v13_0/delete_package_publish_tool.py index 25024f58dd..bf9aaf5a76 100644 --- a/frappe/patches/v13_0/delete_package_publish_tool.py +++ b/frappe/patches/v13_0/delete_package_publish_tool.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe diff --git a/frappe/patches/v13_0/enable_custom_script.py b/frappe/patches/v13_0/enable_custom_script.py index edc242e700..0684074fe7 100644 --- a/frappe/patches/v13_0/enable_custom_script.py +++ b/frappe/patches/v13_0/enable_custom_script.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v13_0/generate_theme_files_in_public_folder.py b/frappe/patches/v13_0/generate_theme_files_in_public_folder.py index bcb47bec24..dd9fb1961a 100644 --- a/frappe/patches/v13_0/generate_theme_files_in_public_folder.py +++ b/frappe/patches/v13_0/generate_theme_files_in_public_folder.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe diff --git a/frappe/patches/v13_0/jinja_hook.py b/frappe/patches/v13_0/jinja_hook.py index 84ed6e6cff..990ae50f35 100644 --- a/frappe/patches/v13_0/jinja_hook.py +++ b/frappe/patches/v13_0/jinja_hook.py @@ -1,7 +1,6 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from click import secho diff --git a/frappe/patches/v13_0/queryreport_columns.py b/frappe/patches/v13_0/queryreport_columns.py index 6c2a1b1219..5c381f4f3e 100644 --- a/frappe/patches/v13_0/queryreport_columns.py +++ b/frappe/patches/v13_0/queryreport_columns.py @@ -1,7 +1,6 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import json diff --git a/frappe/patches/v13_0/remove_duplicate_navbar_items.py b/frappe/patches/v13_0/remove_duplicate_navbar_items.py index cb4de4ca07..b6c6033f64 100644 --- a/frappe/patches/v13_0/remove_duplicate_navbar_items.py +++ b/frappe/patches/v13_0/remove_duplicate_navbar_items.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v13_0/remove_tailwind_from_page_builder.py b/frappe/patches/v13_0/remove_tailwind_from_page_builder.py index 6e7bf67bac..2bf2c7bf87 100644 --- a/frappe/patches/v13_0/remove_tailwind_from_page_builder.py +++ b/frappe/patches/v13_0/remove_tailwind_from_page_builder.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe diff --git a/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py b/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py index 7c3aec9510..3122de8bea 100644 --- a/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py +++ b/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe diff --git a/frappe/patches/v13_0/rename_notification_fields.py b/frappe/patches/v13_0/rename_notification_fields.py index 2984e6503c..1413d80358 100644 --- a/frappe/patches/v13_0/rename_notification_fields.py +++ b/frappe/patches/v13_0/rename_notification_fields.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.utils.rename_field import rename_field diff --git a/frappe/patches/v13_0/rename_onboarding.py b/frappe/patches/v13_0/rename_onboarding.py index c506c6076e..852065dfd2 100644 --- a/frappe/patches/v13_0/rename_onboarding.py +++ b/frappe/patches/v13_0/rename_onboarding.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v13_0/replace_old_data_import.py b/frappe/patches/v13_0/replace_old_data_import.py index 920ee7b553..838881b48e 100644 --- a/frappe/patches/v13_0/replace_old_data_import.py +++ b/frappe/patches/v13_0/replace_old_data_import.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe diff --git a/frappe/patches/v13_0/update_date_filters_in_user_settings.py b/frappe/patches/v13_0/update_date_filters_in_user_settings.py index d4c6aa1d03..3b1b07fe0a 100644 --- a/frappe/patches/v13_0/update_date_filters_in_user_settings.py +++ b/frappe/patches/v13_0/update_date_filters_in_user_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, json from frappe.model.utils.user_settings import update_user_settings, sync_user_settings diff --git a/frappe/patches/v13_0/update_duration_options.py b/frappe/patches/v13_0/update_duration_options.py index 60eef8fc93..e0d8dea4ea 100644 --- a/frappe/patches/v13_0/update_duration_options.py +++ b/frappe/patches/v13_0/update_duration_options.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v13_0/update_icons_in_customized_desk_pages.py b/frappe/patches/v13_0/update_icons_in_customized_desk_pages.py index 93bf5c766e..ff58f99c2f 100644 --- a/frappe/patches/v13_0/update_icons_in_customized_desk_pages.py +++ b/frappe/patches/v13_0/update_icons_in_customized_desk_pages.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v13_0/update_newsletter_content_type.py b/frappe/patches/v13_0/update_newsletter_content_type.py index 6f8dcc1935..5f047680ee 100644 --- a/frappe/patches/v13_0/update_newsletter_content_type.py +++ b/frappe/patches/v13_0/update_newsletter_content_type.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v13_0/update_notification_channel_if_empty.py b/frappe/patches/v13_0/update_notification_channel_if_empty.py index 2c2a40e81b..bcf9a7b28c 100644 --- a/frappe/patches/v13_0/update_notification_channel_if_empty.py +++ b/frappe/patches/v13_0/update_notification_channel_if_empty.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v13_0/web_template_set_module.py b/frappe/patches/v13_0/web_template_set_module.py index df008557d8..2ee9e3ba2d 100644 --- a/frappe/patches/v13_0/web_template_set_module.py +++ b/frappe/patches/v13_0/web_template_set_module.py @@ -1,7 +1,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/add_delete_permission.py b/frappe/patches/v4_0/add_delete_permission.py index 091bdab3ff..9e375a431d 100644 --- a/frappe/patches/v4_0/add_delete_permission.py +++ b/frappe/patches/v4_0/add_delete_permission.py @@ -1,13 +1,13 @@ -from __future__ import unicode_literals + import frappe def execute(): frappe.reload_doc("core", "doctype", "docperm") - + # delete same as cancel (map old permissions) frappe.db.sql("""update tabDocPerm set `delete`=ifnull(`cancel`,0)""") - + # can't cancel if can't submit frappe.db.sql("""update tabDocPerm set `cancel`=0 where ifnull(`submit`,0)=0""") - + frappe.clear_cache() \ No newline at end of file diff --git a/frappe/patches/v4_0/change_varchar_length.py b/frappe/patches/v4_0/change_varchar_length.py index 29fe8f310d..914034ccba 100644 --- a/frappe/patches/v4_0/change_varchar_length.py +++ b/frappe/patches/v4_0/change_varchar_length.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/create_custom_field_for_owner_match.py b/frappe/patches/v4_0/create_custom_field_for_owner_match.py index 60dafc27da..438e280669 100644 --- a/frappe/patches/v4_0/create_custom_field_for_owner_match.py +++ b/frappe/patches/v4_0/create_custom_field_for_owner_match.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function import frappe from frappe.custom.doctype.custom_field.custom_field import create_custom_field diff --git a/frappe/patches/v4_0/deprecate_control_panel.py b/frappe/patches/v4_0/deprecate_control_panel.py index 892d3043c4..29ec8d35f6 100644 --- a/frappe/patches/v4_0/deprecate_control_panel.py +++ b/frappe/patches/v4_0/deprecate_control_panel.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/deprecate_link_selects.py b/frappe/patches/v4_0/deprecate_link_selects.py index a3243cffb8..837144a6ba 100644 --- a/frappe/patches/v4_0/deprecate_link_selects.py +++ b/frappe/patches/v4_0/deprecate_link_selects.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/enable_scheduler_in_system_settings.py b/frappe/patches/v4_0/enable_scheduler_in_system_settings.py index 5d1b836270..68c74edb4f 100644 --- a/frappe/patches/v4_0/enable_scheduler_in_system_settings.py +++ b/frappe/patches/v4_0/enable_scheduler_in_system_settings.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.utils.scheduler import disable_scheduler, enable_scheduler from frappe.utils import cint diff --git a/frappe/patches/v4_0/file_manager_hooks.py b/frappe/patches/v4_0/file_manager_hooks.py index 6be3b25124..ccf95d1d31 100644 --- a/frappe/patches/v4_0/file_manager_hooks.py +++ b/frappe/patches/v4_0/file_manager_hooks.py @@ -1,11 +1,7 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function - import frappe import os -from frappe.utils import get_files_path from frappe.core.doctype.file.file import get_content_hash diff --git a/frappe/patches/v4_0/fix_attach_field_file_url.py b/frappe/patches/v4_0/fix_attach_field_file_url.py index c29e5763f1..61c35b120d 100644 --- a/frappe/patches/v4_0/fix_attach_field_file_url.py +++ b/frappe/patches/v4_0/fix_attach_field_file_url.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/private_backups.py b/frappe/patches/v4_0/private_backups.py index 016af0615d..7920564677 100644 --- a/frappe/patches/v4_0/private_backups.py +++ b/frappe/patches/v4_0/private_backups.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.installer import make_site_dirs diff --git a/frappe/patches/v4_0/remove_index_sitemap.py b/frappe/patches/v4_0/remove_index_sitemap.py index 5dcd0d79c7..8f48729276 100644 --- a/frappe/patches/v4_0/remove_index_sitemap.py +++ b/frappe/patches/v4_0/remove_index_sitemap.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v4_0/remove_old_parent.py b/frappe/patches/v4_0/remove_old_parent.py index 7717f7b7e3..f2d125953a 100644 --- a/frappe/patches/v4_0/remove_old_parent.py +++ b/frappe/patches/v4_0/remove_old_parent.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/remove_user_owner_custom_field.py b/frappe/patches/v4_0/remove_user_owner_custom_field.py index be6a45e090..4620f055d9 100644 --- a/frappe/patches/v4_0/remove_user_owner_custom_field.py +++ b/frappe/patches/v4_0/remove_user_owner_custom_field.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/rename_profile_to_user.py b/frappe/patches/v4_0/rename_profile_to_user.py index 48555ead9e..3e6f269329 100644 --- a/frappe/patches/v4_0/rename_profile_to_user.py +++ b/frappe/patches/v4_0/rename_profile_to_user.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.utils.rename_field import rename_field diff --git a/frappe/patches/v4_0/rename_sitemap_to_route.py b/frappe/patches/v4_0/rename_sitemap_to_route.py index 8ae5170b44..b4606672bc 100644 --- a/frappe/patches/v4_0/rename_sitemap_to_route.py +++ b/frappe/patches/v4_0/rename_sitemap_to_route.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.utils.rename_field import rename_field diff --git a/frappe/patches/v4_0/replace_deprecated_timezones.py b/frappe/patches/v4_0/replace_deprecated_timezones.py index a491325ebc..a3d8ecbf2b 100644 --- a/frappe/patches/v4_0/replace_deprecated_timezones.py +++ b/frappe/patches/v4_0/replace_deprecated_timezones.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.utils.momentjs import data as momentjs_data diff --git a/frappe/patches/v4_0/set_module_in_report.py b/frappe/patches/v4_0/set_module_in_report.py index 9760f7efb3..6c670f4c8e 100644 --- a/frappe/patches/v4_0/set_module_in_report.py +++ b/frappe/patches/v4_0/set_module_in_report.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/set_todo_checked_as_closed.py b/frappe/patches/v4_0/set_todo_checked_as_closed.py index 59e8df3793..5f02e1447b 100644 --- a/frappe/patches/v4_0/set_todo_checked_as_closed.py +++ b/frappe/patches/v4_0/set_todo_checked_as_closed.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v4_0/set_user_gravatar.py b/frappe/patches/v4_0/set_user_gravatar.py index 733b9bfe11..348991c320 100644 --- a/frappe/patches/v4_0/set_user_gravatar.py +++ b/frappe/patches/v4_0/set_user_gravatar.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/set_user_permissions.py b/frappe/patches/v4_0/set_user_permissions.py index 726b9ee715..ef6f3a27e5 100644 --- a/frappe/patches/v4_0/set_user_permissions.py +++ b/frappe/patches/v4_0/set_user_permissions.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import frappe.permissions diff --git a/frappe/patches/v4_0/set_website_route_idx.py b/frappe/patches/v4_0/set_website_route_idx.py index 663a324008..c3dba712d8 100644 --- a/frappe/patches/v4_0/set_website_route_idx.py +++ b/frappe/patches/v4_0/set_website_route_idx.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v4_0/update_custom_field_insert_after.py b/frappe/patches/v4_0/update_custom_field_insert_after.py index ddb888c493..4cb50956d6 100644 --- a/frappe/patches/v4_0/update_custom_field_insert_after.py +++ b/frappe/patches/v4_0/update_custom_field_insert_after.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_0/update_datetime.py b/frappe/patches/v4_0/update_datetime.py index 0e91174780..4034d8f665 100644 --- a/frappe/patches/v4_0/update_datetime.py +++ b/frappe/patches/v4_0/update_datetime.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v4_0/webnotes_to_frappe.py b/frappe/patches/v4_0/webnotes_to_frappe.py index 22b3848d5a..c29f6f603e 100644 --- a/frappe/patches/v4_0/webnotes_to_frappe.py +++ b/frappe/patches/v4_0/webnotes_to_frappe.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, json def execute(): diff --git a/frappe/patches/v4_0/website_sitemap_hierarchy.py b/frappe/patches/v4_0/website_sitemap_hierarchy.py index bb22144cd7..6404986245 100644 --- a/frappe/patches/v4_0/website_sitemap_hierarchy.py +++ b/frappe/patches/v4_0/website_sitemap_hierarchy.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe def execute(): diff --git a/frappe/patches/v4_1/enable_outgoing_email_settings.py b/frappe/patches/v4_1/enable_outgoing_email_settings.py index 7ffa84a278..ffa891ae7c 100644 --- a/frappe/patches/v4_1/enable_outgoing_email_settings.py +++ b/frappe/patches/v4_1/enable_outgoing_email_settings.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_1/enable_print_as_pdf.py b/frappe/patches/v4_1/enable_print_as_pdf.py index 74db9f72ca..e5a8f830f6 100644 --- a/frappe/patches/v4_1/enable_print_as_pdf.py +++ b/frappe/patches/v4_1/enable_print_as_pdf.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_1/file_manager_fix.py b/frappe/patches/v4_1/file_manager_fix.py index cd30c94177..18f44203f2 100644 --- a/frappe/patches/v4_1/file_manager_fix.py +++ b/frappe/patches/v4_1/file_manager_fix.py @@ -1,8 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function - import frappe import os from frappe.core.doctype.file.file import get_content_hash, get_file_name @@ -19,7 +16,6 @@ from frappe.utils import get_files_path, get_site_path # a backup from a time before version 3 migration # # * Patch remaining unpatched File records. -from six import iteritems def execute(): @@ -52,7 +48,7 @@ def get_replaced_files(): old_files = dict(frappe.db.sql("select name, file_name from `tabFile` where ifnull(content_hash, '')=''")) invfiles = invert_dict(new_files) - for nname, nfilename in iteritems(new_files): + for nname, nfilename in new_files.items(): if 'files/' + nfilename in old_files.values(): ret.append((nfilename, invfiles[nfilename])) return ret @@ -85,7 +81,7 @@ def rename_replacing_files(): def invert_dict(ddict): ret = {} - for k,v in iteritems(ddict): + for k,v in ddict.items(): if not ret.get(v): ret[v] = [k] else: diff --git a/frappe/patches/v4_2/print_with_letterhead.py b/frappe/patches/v4_2/print_with_letterhead.py index 3e611ce073..111f6c762e 100644 --- a/frappe/patches/v4_2/print_with_letterhead.py +++ b/frappe/patches/v4_2/print_with_letterhead.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v4_2/refactor_website_routing.py b/frappe/patches/v4_2/refactor_website_routing.py index a5856db1c9..77eea3d429 100644 --- a/frappe/patches/v4_2/refactor_website_routing.py +++ b/frappe/patches/v4_2/refactor_website_routing.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v4_2/set_assign_in_doc.py b/frappe/patches/v4_2/set_assign_in_doc.py index a6a06492a0..8fbd37c5c5 100644 --- a/frappe/patches/v4_2/set_assign_in_doc.py +++ b/frappe/patches/v4_2/set_assign_in_doc.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v4_3/remove_allow_on_submit_customization.py b/frappe/patches/v4_3/remove_allow_on_submit_customization.py index af6ade68e6..a762fd10ab 100644 --- a/frappe/patches/v4_3/remove_allow_on_submit_customization.py +++ b/frappe/patches/v4_3/remove_allow_on_submit_customization.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v5_0/bookmarks_to_stars.py b/frappe/patches/v5_0/bookmarks_to_stars.py index 048d059701..0d2c13525e 100644 --- a/frappe/patches/v5_0/bookmarks_to_stars.py +++ b/frappe/patches/v5_0/bookmarks_to_stars.py @@ -1,9 +1,8 @@ -from __future__ import unicode_literals + import json import frappe import frappe.defaults from frappe.desk.like import _toggle_like -from six import string_types def execute(): for user in frappe.get_all("User"): @@ -13,7 +12,7 @@ def execute(): if not bookmarks: continue - if isinstance(bookmarks, string_types): + if isinstance(bookmarks, str): bookmarks = json.loads(bookmarks) for opts in bookmarks: diff --git a/frappe/patches/v5_0/clear_website_group_and_notifications.py b/frappe/patches/v5_0/clear_website_group_and_notifications.py index bad50222a3..3d3d0c0d16 100644 --- a/frappe/patches/v5_0/clear_website_group_and_notifications.py +++ b/frappe/patches/v5_0/clear_website_group_and_notifications.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v5_0/communication_parent.py b/frappe/patches/v5_0/communication_parent.py index 2ea3b401c6..3c73d91972 100644 --- a/frappe/patches/v5_0/communication_parent.py +++ b/frappe/patches/v5_0/communication_parent.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v5_0/convert_to_barracuda_and_utf8mb4.py b/frappe/patches/v5_0/convert_to_barracuda_and_utf8mb4.py index 0ea2ee2387..6fa6434f98 100644 --- a/frappe/patches/v5_0/convert_to_barracuda_and_utf8mb4.py +++ b/frappe/patches/v5_0/convert_to_barracuda_and_utf8mb4.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.database.mariadb.setup_db import check_database_settings from frappe.model.meta import trim_tables diff --git a/frappe/patches/v5_0/expire_old_scheduler_logs.py b/frappe/patches/v5_0/expire_old_scheduler_logs.py index 8b65ed5fb1..0262acd346 100644 --- a/frappe/patches/v5_0/expire_old_scheduler_logs.py +++ b/frappe/patches/v5_0/expire_old_scheduler_logs.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v5_0/fix_email_alert.py b/frappe/patches/v5_0/fix_email_alert.py index 0676f50a9c..e7366e8b66 100644 --- a/frappe/patches/v5_0/fix_email_alert.py +++ b/frappe/patches/v5_0/fix_email_alert.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe def execute(): diff --git a/frappe/patches/v5_0/fix_null_date_datetime.py b/frappe/patches/v5_0/fix_null_date_datetime.py index e4f4e9e8b9..078cba079a 100644 --- a/frappe/patches/v5_0/fix_null_date_datetime.py +++ b/frappe/patches/v5_0/fix_null_date_datetime.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v5_0/fix_text_editor_file_urls.py b/frappe/patches/v5_0/fix_text_editor_file_urls.py index a6d7d2fb9a..43f0c9d8a5 100644 --- a/frappe/patches/v5_0/fix_text_editor_file_urls.py +++ b/frappe/patches/v5_0/fix_text_editor_file_urls.py @@ -1,4 +1,3 @@ -from __future__ import unicode_literals, print_function import frappe import re diff --git a/frappe/patches/v5_0/force_sync_website.py b/frappe/patches/v5_0/force_sync_website.py index 5dcd0d79c7..8f48729276 100644 --- a/frappe/patches/v5_0/force_sync_website.py +++ b/frappe/patches/v5_0/force_sync_website.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v5_0/modify_session.py b/frappe/patches/v5_0/modify_session.py index f0e247a633..1c2ff0d6e6 100644 --- a/frappe/patches/v5_0/modify_session.py +++ b/frappe/patches/v5_0/modify_session.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v5_0/move_scheduler_last_event_to_system_settings.py b/frappe/patches/v5_0/move_scheduler_last_event_to_system_settings.py index 0fa1dad1e5..bdc52e6152 100644 --- a/frappe/patches/v5_0/move_scheduler_last_event_to_system_settings.py +++ b/frappe/patches/v5_0/move_scheduler_last_event_to_system_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v5_0/remove_shopping_cart_app.py b/frappe/patches/v5_0/remove_shopping_cart_app.py index babde585a1..ed9414159e 100644 --- a/frappe/patches/v5_0/remove_shopping_cart_app.py +++ b/frappe/patches/v5_0/remove_shopping_cart_app.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals - def execute(): from frappe.installer import remove_from_installed_apps remove_from_installed_apps("shopping_cart") diff --git a/frappe/patches/v5_0/rename_ref_type_fieldnames.py b/frappe/patches/v5_0/rename_ref_type_fieldnames.py index dd24f6e5b5..01e36af8a9 100644 --- a/frappe/patches/v5_0/rename_ref_type_fieldnames.py +++ b/frappe/patches/v5_0/rename_ref_type_fieldnames.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v5_0/rename_table_fieldnames.py b/frappe/patches/v5_0/rename_table_fieldnames.py index b716599f28..79703bbba2 100644 --- a/frappe/patches/v5_0/rename_table_fieldnames.py +++ b/frappe/patches/v5_0/rename_table_fieldnames.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.utils.rename_field import rename_field from frappe.modules import scrub, get_doctype_module diff --git a/frappe/patches/v5_0/style_settings_to_website_theme.py b/frappe/patches/v5_0/style_settings_to_website_theme.py index 40414d4e20..73ee28c1fc 100644 --- a/frappe/patches/v5_0/style_settings_to_website_theme.py +++ b/frappe/patches/v5_0/style_settings_to_website_theme.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe import _ from frappe.utils import cint diff --git a/frappe/patches/v5_0/update_shared.py b/frappe/patches/v5_0/update_shared.py index f2b77895d8..e549d7271d 100644 --- a/frappe/patches/v5_0/update_shared.py +++ b/frappe/patches/v5_0/update_shared.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe import frappe.share diff --git a/frappe/patches/v5_0/v4_to_v5.py b/frappe/patches/v5_0/v4_to_v5.py index cd34f04c97..479acc6d63 100644 --- a/frappe/patches/v5_0/v4_to_v5.py +++ b/frappe/patches/v5_0/v4_to_v5.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v5_2/change_checks_to_not_null.py b/frappe/patches/v5_2/change_checks_to_not_null.py index 23f5d659b5..32be3aa752 100644 --- a/frappe/patches/v5_2/change_checks_to_not_null.py +++ b/frappe/patches/v5_2/change_checks_to_not_null.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils import cint from frappe.model import default_fields diff --git a/frappe/patches/v5_3/rename_chinese_languages.py b/frappe/patches/v5_3/rename_chinese_languages.py index 8bc954c04c..f720fb7538 100644 --- a/frappe/patches/v5_3/rename_chinese_languages.py +++ b/frappe/patches/v5_3/rename_chinese_languages.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals import frappe from frappe.translate import rename_language diff --git a/frappe/patches/v6_0/communication_status_and_permission.py b/frappe/patches/v6_0/communication_status_and_permission.py index c68ed9b4d6..435dcc21a5 100644 --- a/frappe/patches/v6_0/communication_status_and_permission.py +++ b/frappe/patches/v6_0/communication_status_and_permission.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.permissions import reset_perms diff --git a/frappe/patches/v6_0/document_type_rename.py b/frappe/patches/v6_0/document_type_rename.py index 16c7d34286..53eec5d85c 100644 --- a/frappe/patches/v6_0/document_type_rename.py +++ b/frappe/patches/v6_0/document_type_rename.py @@ -1,8 +1,8 @@ -from __future__ import unicode_literals + import frappe def execute(): frappe.db.sql("""update tabDocType set document_type='Document' where document_type='Transaction'""") frappe.db.sql("""update tabDocType set document_type='Setup' - where document_type='Master'""") + where document_type='Master'""") diff --git a/frappe/patches/v6_0/fix_ghana_currency.py b/frappe/patches/v6_0/fix_ghana_currency.py index 67f740d240..50feb3ca3f 100644 --- a/frappe/patches/v6_0/fix_ghana_currency.py +++ b/frappe/patches/v6_0/fix_ghana_currency.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - def execute(): from frappe.geo.country_info import get_all import frappe.utils.install diff --git a/frappe/patches/v6_0/make_task_log_folder.py b/frappe/patches/v6_0/make_task_log_folder.py index 87d6e4126f..b5ed547d71 100644 --- a/frappe/patches/v6_0/make_task_log_folder.py +++ b/frappe/patches/v6_0/make_task_log_folder.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe.utils, os def execute(): diff --git a/frappe/patches/v6_1/rename_file_data.py b/frappe/patches/v6_1/rename_file_data.py index 83152271eb..3c62217e8d 100644 --- a/frappe/patches/v6_1/rename_file_data.py +++ b/frappe/patches/v6_1/rename_file_data.py @@ -1,4 +1,3 @@ -from __future__ import print_function, unicode_literals import frappe def execute(): diff --git a/frappe/patches/v6_11/rename_field_in_email_account.py b/frappe/patches/v6_11/rename_field_in_email_account.py index 319b569802..8e600cc2b9 100644 --- a/frappe/patches/v6_11/rename_field_in_email_account.py +++ b/frappe/patches/v6_11/rename_field_in_email_account.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_15/remove_property_setter_for_previous_field.py b/frappe/patches/v6_15/remove_property_setter_for_previous_field.py index b24bf38442..9f0cd69489 100644 --- a/frappe/patches/v6_15/remove_property_setter_for_previous_field.py +++ b/frappe/patches/v6_15/remove_property_setter_for_previous_field.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, json from frappe.utils import cstr diff --git a/frappe/patches/v6_15/set_username.py b/frappe/patches/v6_15/set_username.py index 513ff3301d..ebf01763d0 100644 --- a/frappe/patches/v6_15/set_username.py +++ b/frappe/patches/v6_15/set_username.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_16/feed_doc_owner.py b/frappe/patches/v6_16/feed_doc_owner.py index 2dac9a143d..b7e738b6d9 100644 --- a/frappe/patches/v6_16/feed_doc_owner.py +++ b/frappe/patches/v6_16/feed_doc_owner.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_16/star_to_like.py b/frappe/patches/v6_16/star_to_like.py index e859223d54..f3fc6310d9 100644 --- a/frappe/patches/v6_16/star_to_like.py +++ b/frappe/patches/v6_16/star_to_like.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.database.schema import add_column diff --git a/frappe/patches/v6_19/comment_feed_communication.py b/frappe/patches/v6_19/comment_feed_communication.py index a7503c08ab..64c5ad9c4c 100644 --- a/frappe/patches/v6_19/comment_feed_communication.py +++ b/frappe/patches/v6_19/comment_feed_communication.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe import _ from frappe.model.rename_doc import get_link_fields diff --git a/frappe/patches/v6_2/ignore_user_permissions_if_missing.py b/frappe/patches/v6_2/ignore_user_permissions_if_missing.py index 356d28989a..e216dc36b6 100644 --- a/frappe/patches/v6_2/ignore_user_permissions_if_missing.py +++ b/frappe/patches/v6_2/ignore_user_permissions_if_missing.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_2/rename_backup_manager.py b/frappe/patches/v6_2/rename_backup_manager.py index af02e55878..df2fa72c05 100644 --- a/frappe/patches/v6_2/rename_backup_manager.py +++ b/frappe/patches/v6_2/rename_backup_manager.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_20x/remove_roles_from_website_user.py b/frappe/patches/v6_20x/remove_roles_from_website_user.py index a4d579a1f0..19009ff455 100644 --- a/frappe/patches/v6_20x/remove_roles_from_website_user.py +++ b/frappe/patches/v6_20x/remove_roles_from_website_user.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_20x/set_allow_draft_for_print.py b/frappe/patches/v6_20x/set_allow_draft_for_print.py index 90c15e22b2..0b604567ec 100644 --- a/frappe/patches/v6_20x/set_allow_draft_for_print.py +++ b/frappe/patches/v6_20x/set_allow_draft_for_print.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_20x/update_insert_after.py b/frappe/patches/v6_20x/update_insert_after.py index 5ebec52fc9..37820b2437 100644 --- a/frappe/patches/v6_20x/update_insert_after.py +++ b/frappe/patches/v6_20x/update_insert_after.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, json def execute(): diff --git a/frappe/patches/v6_21/print_settings_repeat_header_footer.py b/frappe/patches/v6_21/print_settings_repeat_header_footer.py index 941a145a54..0919c35903 100644 --- a/frappe/patches/v6_21/print_settings_repeat_header_footer.py +++ b/frappe/patches/v6_21/print_settings_repeat_header_footer.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_24/set_language_as_code.py b/frappe/patches/v6_24/set_language_as_code.py index d685fd7d0e..6f862ede2e 100644 --- a/frappe/patches/v6_24/set_language_as_code.py +++ b/frappe/patches/v6_24/set_language_as_code.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.translate import get_lang_dict diff --git a/frappe/patches/v6_4/reduce_varchar_length.py b/frappe/patches/v6_4/reduce_varchar_length.py index 93a8be8c92..7edde55778 100644 --- a/frappe/patches/v6_4/reduce_varchar_length.py +++ b/frappe/patches/v6_4/reduce_varchar_length.py @@ -1,4 +1,3 @@ -from __future__ import unicode_literals, print_function import frappe def execute(): diff --git a/frappe/patches/v6_4/rename_bengali_language.py b/frappe/patches/v6_4/rename_bengali_language.py index dbbcb62f8d..f872dea1b9 100644 --- a/frappe/patches/v6_4/rename_bengali_language.py +++ b/frappe/patches/v6_4/rename_bengali_language.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals import frappe from frappe.translate import rename_language diff --git a/frappe/patches/v6_6/fix_file_url.py b/frappe/patches/v6_6/fix_file_url.py index 4f8956d343..48e292f4d4 100644 --- a/frappe/patches/v6_6/fix_file_url.py +++ b/frappe/patches/v6_6/fix_file_url.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.meta import is_single diff --git a/frappe/patches/v6_6/rename_slovak_language.py b/frappe/patches/v6_6/rename_slovak_language.py index a942543372..198949e79c 100644 --- a/frappe/patches/v6_6/rename_slovak_language.py +++ b/frappe/patches/v6_6/rename_slovak_language.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals import frappe from frappe.translate import rename_language diff --git a/frappe/patches/v6_6/user_last_active.py b/frappe/patches/v6_6/user_last_active.py index fd55935174..b9f63fa45e 100644 --- a/frappe/patches/v6_6/user_last_active.py +++ b/frappe/patches/v6_6/user_last_active.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v6_9/int_float_not_null.py b/frappe/patches/v6_9/int_float_not_null.py index 97495f9077..c414d6b583 100644 --- a/frappe/patches/v6_9/int_float_not_null.py +++ b/frappe/patches/v6_9/int_float_not_null.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils import cint, flt diff --git a/frappe/patches/v6_9/rename_burmese_language.py b/frappe/patches/v6_9/rename_burmese_language.py index 66477f7efe..5e1333077e 100644 --- a/frappe/patches/v6_9/rename_burmese_language.py +++ b/frappe/patches/v6_9/rename_burmese_language.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals import frappe from frappe.translate import rename_language diff --git a/frappe/patches/v7_0/add_communication_in_doc.py b/frappe/patches/v7_0/add_communication_in_doc.py index 4db02c5bab..8be229fe3a 100644 --- a/frappe/patches/v7_0/add_communication_in_doc.py +++ b/frappe/patches/v7_0/add_communication_in_doc.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.core.doctype.comment.comment import update_comment_in_doc diff --git a/frappe/patches/v7_0/cleanup_list_settings.py b/frappe/patches/v7_0/cleanup_list_settings.py index e03ff57406..9fe2e71ed1 100644 --- a/frappe/patches/v7_0/cleanup_list_settings.py +++ b/frappe/patches/v7_0/cleanup_list_settings.py @@ -1,8 +1,8 @@ -from __future__ import unicode_literals + import frappe, json def execute(): - if frappe.db.table_exists("__ListSettings"): + if frappe.db.table_exists("__ListSettings"): list_settings = frappe.db.sql("select user, doctype, data from __ListSettings", as_dict=1) for ls in list_settings: if ls and ls.data: @@ -14,7 +14,7 @@ def execute(): if "name as" in field: fields.remove(field) data["fields"] = fields - - frappe.db.sql("update __ListSettings set data = %s where user=%s and doctype=%s", - (json.dumps(data), ls.user, ls.doctype)) - + + frappe.db.sql("update __ListSettings set data = %s where user=%s and doctype=%s", + (json.dumps(data), ls.user, ls.doctype)) + diff --git a/frappe/patches/v7_0/create_private_file_folder.py b/frappe/patches/v7_0/create_private_file_folder.py index bd26917a78..e89beb5d0f 100644 --- a/frappe/patches/v7_0/create_private_file_folder.py +++ b/frappe/patches/v7_0/create_private_file_folder.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, os def execute(): diff --git a/frappe/patches/v7_0/re_route.py b/frappe/patches/v7_0/re_route.py index cc36594ae8..8a4daaea86 100644 --- a/frappe/patches/v7_0/re_route.py +++ b/frappe/patches/v7_0/re_route.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.model.base_document import get_controller diff --git a/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py b/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py index 9a7a756144..42f2dfe4c2 100644 --- a/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py +++ b/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_0/rename_newsletter_list_to_email_group.py b/frappe/patches/v7_0/rename_newsletter_list_to_email_group.py index 79061d383c..5e40d9df35 100644 --- a/frappe/patches/v7_0/rename_newsletter_list_to_email_group.py +++ b/frappe/patches/v7_0/rename_newsletter_list_to_email_group.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_0/set_email_group.py b/frappe/patches/v7_0/set_email_group.py index e3dd66ebf3..251e9a27b6 100644 --- a/frappe/patches/v7_0/set_email_group.py +++ b/frappe/patches/v7_0/set_email_group.py @@ -1,11 +1,10 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): frappe.reload_doc("email", "doctype", "email_group_member") if "newsletter_list" in frappe.db.get_table_columns("Email Group Member"): - frappe.db.sql("""update `tabEmail Group Member` set email_group = newsletter_list + frappe.db.sql("""update `tabEmail Group Member` set email_group = newsletter_list where email_group is null or email_group = ''""") \ No newline at end of file diff --git a/frappe/patches/v7_0/set_user_fullname.py b/frappe/patches/v7_0/set_user_fullname.py index a7c6670f45..e69c180c27 100644 --- a/frappe/patches/v7_0/set_user_fullname.py +++ b/frappe/patches/v7_0/set_user_fullname.py @@ -1,9 +1,9 @@ -from __future__ import unicode_literals + import frappe def execute(): frappe.reload_doc("Core", "DocType", "User") - + for user in frappe.db.get_all('User'): user = frappe.get_doc('User', user.name) user.set_full_name() diff --git a/frappe/patches/v7_0/update_auth.py b/frappe/patches/v7_0/update_auth.py index 3d47edf4b5..098081563f 100644 --- a/frappe/patches/v7_0/update_auth.py +++ b/frappe/patches/v7_0/update_auth.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils.password import create_auth_table, set_encrypted_password diff --git a/frappe/patches/v7_0/update_report_builder_json.py b/frappe/patches/v7_0/update_report_builder_json.py index a344ca5412..01a6126de7 100644 --- a/frappe/patches/v7_0/update_report_builder_json.py +++ b/frappe/patches/v7_0/update_report_builder_json.py @@ -1,7 +1,6 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v7_0/update_send_after_in_bulk_email.py b/frappe/patches/v7_0/update_send_after_in_bulk_email.py index 1b08309b6a..b9da83eaab 100644 --- a/frappe/patches/v7_0/update_send_after_in_bulk_email.py +++ b/frappe/patches/v7_0/update_send_after_in_bulk_email.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils import now_datetime diff --git a/frappe/patches/v7_1/disabled_print_settings_for_custom_print_format.py b/frappe/patches/v7_1/disabled_print_settings_for_custom_print_format.py index c74d2d98f9..6ab9340845 100644 --- a/frappe/patches/v7_1/disabled_print_settings_for_custom_print_format.py +++ b/frappe/patches/v7_1/disabled_print_settings_for_custom_print_format.py @@ -2,16 +2,15 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): frappe.reload_doctype('Print Format') - frappe.db.sql(""" - update - `tabPrint Format` - set - align_labels_right = 0, line_breaks = 0, show_section_headings = 0 - where + frappe.db.sql(""" + update + `tabPrint Format` + set + align_labels_right = 0, line_breaks = 0, show_section_headings = 0 + where custom_format = 1 """) diff --git a/frappe/patches/v7_1/refactor_integration_broker.py b/frappe/patches/v7_1/refactor_integration_broker.py index 8c9aaa6795..05ccae5d46 100644 --- a/frappe/patches/v7_1/refactor_integration_broker.py +++ b/frappe/patches/v7_1/refactor_integration_broker.py @@ -2,14 +2,13 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe import json def execute(): for doctype_name in ["Razorpay Log", "Razorpay Payment", "Razorpay Settings"]: delete_doc("DocType", doctype_name) - + reload_doctypes() setup_services() @@ -32,19 +31,19 @@ def setup_services(): service_settings = frappe.new_doc("{0} Settings".format(service["new_name"])) service_settings.update(settings) - + service_settings.flags.ignore_mandatory = True service_settings.save(ignore_permissions=True) if service["old_name"] in ["Dropbox Integration", "LDAP Auth"]: delete_doc("Integration Service", service["old_name"]) - + new_service_doc = frappe.get_doc({ "doctype": "Integration Service", "service": service["new_name"], "enabled": 1 }) - + new_service_doc.flags.ignore_mandatory = True new_service_doc.save(ignore_permissions=True) diff --git a/frappe/patches/v7_1/rename_chinese_language_codes.py b/frappe/patches/v7_1/rename_chinese_language_codes.py index 1ed25a4959..91ed73ccae 100644 --- a/frappe/patches/v7_1/rename_chinese_language_codes.py +++ b/frappe/patches/v7_1/rename_chinese_language_codes.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_1/rename_scheduler_log_to_error_log.py b/frappe/patches/v7_1/rename_scheduler_log_to_error_log.py index 4d1a39538f..c0c9e03565 100644 --- a/frappe/patches/v7_1/rename_scheduler_log_to_error_log.py +++ b/frappe/patches/v7_1/rename_scheduler_log_to_error_log.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_1/set_backup_limit.py b/frappe/patches/v7_1/set_backup_limit.py index 7b0a344305..ce502393b2 100644 --- a/frappe/patches/v7_1/set_backup_limit.py +++ b/frappe/patches/v7_1/set_backup_limit.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + from frappe.utils import cint import frappe diff --git a/frappe/patches/v7_1/setup_integration_services.py b/frappe/patches/v7_1/setup_integration_services.py index 1c70b8e835..9f4c8a3915 100644 --- a/frappe/patches/v7_1/setup_integration_services.py +++ b/frappe/patches/v7_1/setup_integration_services.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.exceptions import DataError from frappe.utils.password import get_decrypted_password diff --git a/frappe/patches/v7_1/sync_language_doctype.py b/frappe/patches/v7_1/sync_language_doctype.py index 83d1a4f5a6..a5e9ad1cb1 100644 --- a/frappe/patches/v7_1/sync_language_doctype.py +++ b/frappe/patches/v7_1/sync_language_doctype.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.translate import get_lang_dict diff --git a/frappe/patches/v7_2/fix_email_queue_recipient.py b/frappe/patches/v7_2/fix_email_queue_recipient.py index 645b17b5c9..021397031b 100644 --- a/frappe/patches/v7_2/fix_email_queue_recipient.py +++ b/frappe/patches/v7_2/fix_email_queue_recipient.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_2/merge_knowledge_base.py b/frappe/patches/v7_2/merge_knowledge_base.py index 301d15e1dd..04e6c16213 100644 --- a/frappe/patches/v7_2/merge_knowledge_base.py +++ b/frappe/patches/v7_2/merge_knowledge_base.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.patches.v7_0.re_route import update_routes diff --git a/frappe/patches/v7_2/remove_in_filter.py b/frappe/patches/v7_2/remove_in_filter.py index 36556d7c13..306879f996 100644 --- a/frappe/patches/v7_2/remove_in_filter.py +++ b/frappe/patches/v7_2/remove_in_filter.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_2/set_doctype_engine.py b/frappe/patches/v7_2/set_doctype_engine.py index 3a5cc384a2..e0df9cff87 100644 --- a/frappe/patches/v7_2/set_doctype_engine.py +++ b/frappe/patches/v7_2/set_doctype_engine.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_2/set_in_standard_filter_property.py b/frappe/patches/v7_2/set_in_standard_filter_property.py index 12f97f7f8e..568f43d2aa 100644 --- a/frappe/patches/v7_2/set_in_standard_filter_property.py +++ b/frappe/patches/v7_2/set_in_standard_filter_property.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_2/setup_custom_perms.py b/frappe/patches/v7_2/setup_custom_perms.py index 1b3b86236c..1f46072782 100644 --- a/frappe/patches/v7_2/setup_custom_perms.py +++ b/frappe/patches/v7_2/setup_custom_perms.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.permissions import setup_custom_perms from frappe.core.page.permission_manager.permission_manager import get_standard_permissions diff --git a/frappe/patches/v7_2/setup_ldap_config.py b/frappe/patches/v7_2/setup_ldap_config.py index 31dd8ca6fe..c9ad3e6714 100644 --- a/frappe/patches/v7_2/setup_ldap_config.py +++ b/frappe/patches/v7_2/setup_ldap_config.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils import cint diff --git a/frappe/patches/v7_2/update_communications.py b/frappe/patches/v7_2/update_communications.py index f3d859b95a..114e531324 100644 --- a/frappe/patches/v7_2/update_communications.py +++ b/frappe/patches/v7_2/update_communications.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v7_2/update_feedback_request.py b/frappe/patches/v7_2/update_feedback_request.py index 11e9eb8e92..9bc656bf67 100644 --- a/frappe/patches/v7_2/update_feedback_request.py +++ b/frappe/patches/v7_2/update_feedback_request.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_0/deprecate_integration_broker.py b/frappe/patches/v8_0/deprecate_integration_broker.py index ad1a7d9571..9aeee17837 100644 --- a/frappe/patches/v8_0/deprecate_integration_broker.py +++ b/frappe/patches/v8_0/deprecate_integration_broker.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.integrations.utils import create_payment_gateway @@ -7,14 +7,14 @@ def execute(): for doctype in ["integration_request", "oauth_authorization_code", "oauth_bearer_token", "oauth_client"]: frappe.reload_doc('integrations', 'doctype', doctype) - + frappe.reload_doc("core", "doctype", "payment_gateway") update_doctype_module() create_payment_gateway_master_records() for doctype in ["Integration Service", "Integration Service Parameter"]: frappe.delete_doc("DocType", doctype) - + if not frappe.db.get_value("DocType", {"module": "Integration Broker"}, "name"): frappe.delete_doc("Module Def", "Integration Broker") diff --git a/frappe/patches/v8_0/drop_in_dialog.py b/frappe/patches/v8_0/drop_in_dialog.py index 231d757f26..5022333d22 100644 --- a/frappe/patches/v8_0/drop_in_dialog.py +++ b/frappe/patches/v8_0/drop_in_dialog.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_0/drop_is_custom_from_docperm.py b/frappe/patches/v8_0/drop_is_custom_from_docperm.py index 4530dcd2e0..0f17bbef5c 100644 --- a/frappe/patches/v8_0/drop_is_custom_from_docperm.py +++ b/frappe/patches/v8_0/drop_is_custom_from_docperm.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_0/drop_unwanted_indexes.py b/frappe/patches/v8_0/drop_unwanted_indexes.py index fc66ed43fd..655bce1a4b 100644 --- a/frappe/patches/v8_0/drop_unwanted_indexes.py +++ b/frappe/patches/v8_0/drop_unwanted_indexes.py @@ -2,14 +2,13 @@ # License: GNU General Public License v3. See license.txt # -*- coding: utf-8 -*- -from __future__ import unicode_literals import frappe def execute(): # communication - unwanted_indexes = ["communication_date_index", "message_id_index", "modified_index", + unwanted_indexes = ["communication_date_index", "message_id_index", "modified_index", "creation_index", "reference_owner", "communication_date"] - + for k in unwanted_indexes: try: frappe.db.sql("drop index {0} on `tabCommunication`".format(k)) diff --git a/frappe/patches/v8_0/install_new_build_system_requirements.py b/frappe/patches/v8_0/install_new_build_system_requirements.py index 536c2fcfb3..75ccfa87cd 100644 --- a/frappe/patches/v8_0/install_new_build_system_requirements.py +++ b/frappe/patches/v8_0/install_new_build_system_requirements.py @@ -1,4 +1,3 @@ -from __future__ import print_function, unicode_literals from subprocess import Popen, call, PIPE def execute(): diff --git a/frappe/patches/v8_0/newsletter_childtable_migrate.py b/frappe/patches/v8_0/newsletter_childtable_migrate.py index f652b37f56..67ff5e586f 100644 --- a/frappe/patches/v8_0/newsletter_childtable_migrate.py +++ b/frappe/patches/v8_0/newsletter_childtable_migrate.py @@ -1,7 +1,6 @@ # Copyright (c) 2017, Frappe and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): @@ -10,7 +9,7 @@ def execute(): if "email_group" not in frappe.db.get_table_columns("Newsletter"): return - + newsletters = frappe.get_all("Newsletter", fields=["name", "email_group"]) for newsletter in newsletters: if newsletter.email_group: diff --git a/frappe/patches/v8_0/rename_listsettings_to_usersettings.py b/frappe/patches/v8_0/rename_listsettings_to_usersettings.py index 584e4a1111..9545953e34 100644 --- a/frappe/patches/v8_0/rename_listsettings_to_usersettings.py +++ b/frappe/patches/v8_0/rename_listsettings_to_usersettings.py @@ -1,7 +1,6 @@ -from __future__ import unicode_literals + from frappe.model.utils.user_settings import update_user_settings import frappe, json -from six import iteritems def execute(): @@ -35,7 +34,7 @@ def execute(): for user in frappe.db.get_all('User', {'user_type': 'System User'}): defaults = frappe.defaults.get_defaults_for(user.name) - for key, value in iteritems(defaults): + for key, value in defaults.items(): if key.startswith('_list_settings:'): doctype = key.replace('_list_settings:', '') columns = ['`tab{1}`.`{0}`'.format(*c) for c in json.loads(value)] diff --git a/frappe/patches/v8_0/rename_page_role_to_has_role.py b/frappe/patches/v8_0/rename_page_role_to_has_role.py index 9c610d857d..49006ea419 100644 --- a/frappe/patches/v8_0/rename_page_role_to_has_role.py +++ b/frappe/patches/v8_0/rename_page_role_to_has_role.py @@ -1,7 +1,6 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): @@ -17,7 +16,7 @@ def reload_doc(): frappe.reload_doc("core", 'doctype', "report") frappe.reload_doc("core", 'doctype', "user") frappe.reload_doc("core", 'doctype', "has_role") - + def set_ref_doctype_roles_to_report(): for data in frappe.get_all('Report', fields=["name"]): doc = frappe.get_doc('Report', data.name) diff --git a/frappe/patches/v8_0/rename_print_to_printing.py b/frappe/patches/v8_0/rename_print_to_printing.py index ecdbc3f7be..56889d630e 100644 --- a/frappe/patches/v8_0/rename_print_to_printing.py +++ b/frappe/patches/v8_0/rename_print_to_printing.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): @@ -9,5 +9,5 @@ def execute(): frappe.reload_doc('printing', 'doctype', 'letter_head') frappe.reload_doc('printing', 'page', 'print_format_builder') frappe.db.sql("""update `tabPrint Format` set module='Printing' where module='Print'""") - + frappe.delete_doc('Module Def', 'Print') \ No newline at end of file diff --git a/frappe/patches/v8_0/set_allow_traceback.py b/frappe/patches/v8_0/set_allow_traceback.py index 3eceb3e29c..bb72e7dde6 100644 --- a/frappe/patches/v8_0/set_allow_traceback.py +++ b/frappe/patches/v8_0/set_allow_traceback.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_0/set_currency_field_precision.py b/frappe/patches/v8_0/set_currency_field_precision.py index 89835c8c1e..57b12ffdee 100644 --- a/frappe/patches/v8_0/set_currency_field_precision.py +++ b/frappe/patches/v8_0/set_currency_field_precision.py @@ -1,7 +1,6 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe.utils import get_number_format_info diff --git a/frappe/patches/v8_0/set_doctype_values_in_custom_role.py b/frappe/patches/v8_0/set_doctype_values_in_custom_role.py index 58cdc4497d..50e7eb83e1 100644 --- a/frappe/patches/v8_0/set_doctype_values_in_custom_role.py +++ b/frappe/patches/v8_0/set_doctype_values_in_custom_role.py @@ -1,13 +1,12 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): frappe.reload_doctype('Custom Role') - + # set ref doctype in custom role for reports - frappe.db.sql(""" update `tabCustom Role` set + frappe.db.sql(""" update `tabCustom Role` set `tabCustom Role`.ref_doctype = (select ref_doctype from `tabReport` where name = `tabCustom Role`.report) where `tabCustom Role`.report is not null""") diff --git a/frappe/patches/v8_0/set_user_permission_for_page_and_report.py b/frappe/patches/v8_0/set_user_permission_for_page_and_report.py index 560ea46db2..55789a8301 100644 --- a/frappe/patches/v8_0/set_user_permission_for_page_and_report.py +++ b/frappe/patches/v8_0/set_user_permission_for_page_and_report.py @@ -1,7 +1,6 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): @@ -9,13 +8,13 @@ def execute(): frappe.reload_doc("core", 'doctype', "custom_role") set_user_permission_for_page_and_report() - update_ref_doctype_in_custom_role() + update_ref_doctype_in_custom_role() def update_ref_doctype_in_custom_role(): frappe.reload_doc("core", 'doctype', "custom_role") - frappe.db.sql("""update `tabCustom Role` - set - ref_doctype = (select ref_doctype from tabReport where name = `tabCustom Role`.report) + frappe.db.sql("""update `tabCustom Role` + set + ref_doctype = (select ref_doctype from tabReport where name = `tabCustom Role`.report) where report is not null""") def set_user_permission_for_page_and_report(): diff --git a/frappe/patches/v8_0/setup_email_inbox.py b/frappe/patches/v8_0/setup_email_inbox.py index 1bfe3b0b74..ad99068eb9 100644 --- a/frappe/patches/v8_0/setup_email_inbox.py +++ b/frappe/patches/v8_0/setup_email_inbox.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe, json from frappe.core.doctype.user.user import ask_pass_update, setup_user_email_inbox diff --git a/frappe/patches/v8_0/update_gender_and_salutation.py b/frappe/patches/v8_0/update_gender_and_salutation.py index bcd9d4cbd7..913e0f714b 100644 --- a/frappe/patches/v8_0/update_gender_and_salutation.py +++ b/frappe/patches/v8_0/update_gender_and_salutation.py @@ -1,6 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -from __future__ import unicode_literals import frappe from frappe.desk.page.setup_wizard.install_fixtures import update_genders, update_salutations diff --git a/frappe/patches/v8_0/update_global_search_table.py b/frappe/patches/v8_0/update_global_search_table.py index 3c0a70155b..4d5c8be9cf 100644 --- a/frappe/patches/v8_0/update_global_search_table.py +++ b/frappe/patches/v8_0/update_global_search_table.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_0/update_published_in_global_search.py b/frappe/patches/v8_0/update_published_in_global_search.py index a378f24732..ae86cb8b24 100644 --- a/frappe/patches/v8_0/update_published_in_global_search.py +++ b/frappe/patches/v8_0/update_published_in_global_search.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_0/update_records_in_global_search.py b/frappe/patches/v8_0/update_records_in_global_search.py index dafa1e76d3..316f84b2f0 100644 --- a/frappe/patches/v8_0/update_records_in_global_search.py +++ b/frappe/patches/v8_0/update_records_in_global_search.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils.global_search import get_doctypes_with_global_search, rebuild_for_doctype from frappe.utils import update_progress_bar @@ -6,7 +6,7 @@ from frappe.utils import update_progress_bar def execute(): frappe.cache().delete_value('doctypes_with_global_search') doctypes_with_global_search = get_doctypes_with_global_search(with_child_tables=False) - + for i, doctype in enumerate(doctypes_with_global_search): update_progress_bar("Updating Global Search", i, len(doctypes_with_global_search)) rebuild_for_doctype(doctype) diff --git a/frappe/patches/v8_1/delete_custom_docperm_if_doctype_not_exists.py b/frappe/patches/v8_1/delete_custom_docperm_if_doctype_not_exists.py index 92b54edfd4..510018eb47 100644 --- a/frappe/patches/v8_1/delete_custom_docperm_if_doctype_not_exists.py +++ b/frappe/patches/v8_1/delete_custom_docperm_if_doctype_not_exists.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_1/enable_allow_error_traceback_in_system_settings.py b/frappe/patches/v8_1/enable_allow_error_traceback_in_system_settings.py index 9bd9757a86..513bb274bc 100644 --- a/frappe/patches/v8_1/enable_allow_error_traceback_in_system_settings.py +++ b/frappe/patches/v8_1/enable_allow_error_traceback_in_system_settings.py @@ -1,7 +1,6 @@ # Copyright (c) 2017, Frappe and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v8_1/update_format_options_in_auto_email_report.py b/frappe/patches/v8_1/update_format_options_in_auto_email_report.py index 56609780cb..8bea2b7bf5 100644 --- a/frappe/patches/v8_1/update_format_options_in_auto_email_report.py +++ b/frappe/patches/v8_1/update_format_options_in_auto_email_report.py @@ -1,7 +1,6 @@ # Copyright (c) 2017, Frappe and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v8_10/delete_static_web_page_from_global_search.py b/frappe/patches/v8_10/delete_static_web_page_from_global_search.py index 336562c157..aa6a053412 100644 --- a/frappe/patches/v8_10/delete_static_web_page_from_global_search.py +++ b/frappe/patches/v8_10/delete_static_web_page_from_global_search.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_5/delete_email_group_member_with_invalid_emails.py b/frappe/patches/v8_5/delete_email_group_member_with_invalid_emails.py index 89a9a7a1b9..5851e2855b 100644 --- a/frappe/patches/v8_5/delete_email_group_member_with_invalid_emails.py +++ b/frappe/patches/v8_5/delete_email_group_member_with_invalid_emails.py @@ -1,7 +1,6 @@ # Copyright (c) 2017, Frappe and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe.utils import validate_email_address diff --git a/frappe/patches/v8_5/patch_event_colors.py b/frappe/patches/v8_5/patch_event_colors.py index 8ac7aec238..3c34f7946b 100644 --- a/frappe/patches/v8_5/patch_event_colors.py +++ b/frappe/patches/v8_5/patch_event_colors.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v8_x/update_user_permission.py b/frappe/patches/v8_x/update_user_permission.py index 693b87c974..387751500f 100644 --- a/frappe/patches/v8_x/update_user_permission.py +++ b/frappe/patches/v8_x/update_user_permission.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v9_1/__init__.py b/frappe/patches/v9_1/__init__.py index baffc48825..8b13789179 100644 --- a/frappe/patches/v9_1/__init__.py +++ b/frappe/patches/v9_1/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/patches/v9_1/add_sms_sender_name_as_parameters.py b/frappe/patches/v9_1/add_sms_sender_name_as_parameters.py index 9d7c0f003f..f63e86a340 100644 --- a/frappe/patches/v9_1/add_sms_sender_name_as_parameters.py +++ b/frappe/patches/v9_1/add_sms_sender_name_as_parameters.py @@ -1,7 +1,6 @@ # Copyright (c) 2017, Frappe and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe def execute(): diff --git a/frappe/patches/v9_1/move_feed_to_activity_log.py b/frappe/patches/v9_1/move_feed_to_activity_log.py index db46b4e419..a549296357 100644 --- a/frappe/patches/v9_1/move_feed_to_activity_log.py +++ b/frappe/patches/v9_1/move_feed_to_activity_log.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils.background_jobs import enqueue diff --git a/frappe/patches/v9_1/resave_domain_settings.py b/frappe/patches/v9_1/resave_domain_settings.py index 1e54ad3aa5..5814871c2e 100644 --- a/frappe/patches/v9_1/resave_domain_settings.py +++ b/frappe/patches/v9_1/resave_domain_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/patches/v9_1/revert_domain_settings.py b/frappe/patches/v9_1/revert_domain_settings.py index a14b48dae6..99c5561d78 100644 --- a/frappe/patches/v9_1/revert_domain_settings.py +++ b/frappe/patches/v9_1/revert_domain_settings.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe def execute(): diff --git a/frappe/permissions.py b/frappe/permissions.py index 19f101aab5..e139b04a4e 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -1,12 +1,10 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function -from six import string_types -import frappe, copy, json +import frappe, copy from frappe import _, msgprint from frappe.utils import cint import frappe.share + rights = ("select", "read", "write", "create", "delete", "submit", "cancel", "amend", "print", "email", "report", "import", "export", "set_user_permissions", "share") @@ -58,7 +56,7 @@ def has_permission(doctype, ptype="read", doc=None, verbose=False, user=None, ra meta = frappe.get_meta(doctype) if doc: - if isinstance(doc, string_types): + if isinstance(doc, str): doc = frappe.get_doc(meta.name, doc) perm = get_doc_permissions(doc, user=user, ptype=ptype).get(ptype) if not perm: push_perm_check_log(_('User {0} does not have access to this document').format(frappe.bold(user))) @@ -159,7 +157,7 @@ def get_role_permissions(doctype_meta, user=None, is_owner=None): } } """ - if isinstance(doctype_meta, string_types): + if isinstance(doctype_meta, str): doctype_meta = frappe.get_meta(doctype_meta) # assuming doctype name was passed if not user: user = frappe.session.user @@ -534,7 +532,7 @@ def get_linked_doctypes(dt): def get_doc_name(doc): if not doc: return None - return doc if isinstance(doc, string_types) else doc.name + return doc if isinstance(doc, str) else doc.name def allow_everything(): ''' diff --git a/frappe/printing/doctype/letter_head/letter_head.py b/frappe/printing/doctype/letter_head/letter_head.py index 3a3b14faad..948be60b88 100644 --- a/frappe/printing/doctype/letter_head/letter_head.py +++ b/frappe/printing/doctype/letter_head/letter_head.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.utils import is_image from frappe.model.document import Document @@ -19,7 +18,7 @@ class LetterHead(Document): def validate_disabled_and_default(self): if self.disabled and self.is_default: frappe.throw(_("Letter Head cannot be both disabled and default")) - + if not self.is_default and not self.disabled: if not frappe.db.exists('Letter Head', dict(is_default=1)): self.is_default = 1 diff --git a/frappe/printing/doctype/letter_head/test_letter_head.py b/frappe/printing/doctype/letter_head/test_letter_head.py index b69e9924ea..96dfc68705 100644 --- a/frappe/printing/doctype/letter_head/test_letter_head.py +++ b/frappe/printing/doctype/letter_head/test_letter_head.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/printing/doctype/print_format/print_format.py b/frappe/printing/doctype/print_format/print_format.py index 1c11f2d519..5d4ff92fe2 100644 --- a/frappe/printing/doctype/print_format/print_format.py +++ b/frappe/printing/doctype/print_format/print_format.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import frappe.utils import json diff --git a/frappe/printing/doctype/print_format/test_print_format.py b/frappe/printing/doctype/print_format/test_print_format.py index 121916ae5f..e65eb0183f 100644 --- a/frappe/printing/doctype/print_format/test_print_format.py +++ b/frappe/printing/doctype/print_format/test_print_format.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals, print_function - import frappe import unittest import re diff --git a/frappe/printing/doctype/print_heading/print_heading.py b/frappe/printing/doctype/print_heading/print_heading.py index 1bb3e52dd5..f9955c019d 100644 --- a/frappe/printing/doctype/print_heading/print_heading.py +++ b/frappe/printing/doctype/print_heading/print_heading.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/printing/doctype/print_heading/test_print_heading.py b/frappe/printing/doctype/print_heading/test_print_heading.py index 1a6435e783..ce99cde607 100644 --- a/frappe/printing/doctype/print_heading/test_print_heading.py +++ b/frappe/printing/doctype/print_heading/test_print_heading.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/printing/doctype/print_settings/print_settings.py b/frappe/printing/doctype/print_settings/print_settings.py index cf6a71a8ac..610c083097 100644 --- a/frappe/printing/doctype/print_settings/print_settings.py +++ b/frappe/printing/doctype/print_settings/print_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import cint diff --git a/frappe/printing/doctype/print_settings/test_print_settings.py b/frappe/printing/doctype/print_settings/test_print_settings.py index b8ad70a681..d1dec861b2 100644 --- a/frappe/printing/doctype/print_settings/test_print_settings.py +++ b/frappe/printing/doctype/print_settings/test_print_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import unittest class TestPrintSettings(unittest.TestCase): diff --git a/frappe/printing/doctype/print_style/print_style.py b/frappe/printing/doctype/print_style/print_style.py index 310babd5df..a91786795c 100644 --- a/frappe/printing/doctype/print_style/print_style.py +++ b/frappe/printing/doctype/print_style/print_style.py @@ -2,7 +2,6 @@ # Copyright (c) 2017, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/printing/doctype/print_style/test_print_style.py b/frappe/printing/doctype/print_style/test_print_style.py index cee57f8826..b717b23df8 100644 --- a/frappe/printing/doctype/print_style/test_print_style.py +++ b/frappe/printing/doctype/print_style/test_print_style.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2017, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/pythonrc.py b/frappe/pythonrc.py index 308bad7382..6761ead05b 100755 --- a/frappe/pythonrc.py +++ b/frappe/pythonrc.py @@ -3,8 +3,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import os import frappe frappe.connect(site=os.environ.get("site")) \ No newline at end of file diff --git a/frappe/rate_limiter.py b/frappe/rate_limiter.py index 40db8fe892..023cdb9cb0 100644 --- a/frappe/rate_limiter.py +++ b/frappe/rate_limiter.py @@ -2,8 +2,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - from datetime import datetime from functools import wraps from typing import Union, Callable diff --git a/frappe/recorder.py b/frappe/recorder.py index e3eef94809..02036b7374 100644 --- a/frappe/recorder.py +++ b/frappe/recorder.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - from collections import Counter import datetime import inspect diff --git a/frappe/sessions.py b/frappe/sessions.py index 9e503bbe25..1bc78448e7 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals """ Boot session from cache or build @@ -16,8 +14,7 @@ import frappe.model.meta import frappe.defaults import frappe.translate import redis -from six.moves.urllib.parse import unquote -from six import text_type +from urllib.parse import unquote from frappe.cache_manager import clear_user_cache @frappe.whitelist(allow_guest=True) @@ -337,7 +334,7 @@ class Session: now = frappe.utils.now() self.data['data']['last_updated'] = now - self.data['data']['lang'] = text_type(frappe.lang) + self.data['data']['lang'] = str(frappe.lang) # update session in db last_updated = frappe.cache().hget("last_db_session_update", self.sid) diff --git a/frappe/share.py b/frappe/share.py index 63c6ce2f35..10c0f26c22 100644 --- a/frappe/share.py +++ b/frappe/share.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.desk.form.document_follow import follow_document diff --git a/frappe/social/doctype/energy_point_log/energy_point_log.py b/frappe/social/doctype/energy_point_log/energy_point_log.py index 2e2289aed4..aff3c7cd59 100644 --- a/frappe/social/doctype/energy_point_log/energy_point_log.py +++ b/frappe/social/doctype/energy_point_log/energy_point_log.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ import json diff --git a/frappe/social/doctype/energy_point_log/test_energy_point_log.py b/frappe/social/doctype/energy_point_log/test_energy_point_log.py index 222c64389b..8c4dba5d6b 100644 --- a/frappe/social/doctype/energy_point_log/test_energy_point_log.py +++ b/frappe/social/doctype/energy_point_log/test_energy_point_log.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from .energy_point_log import get_energy_points as _get_energy_points, create_review_points_log, review diff --git a/frappe/social/doctype/energy_point_rule/energy_point_rule.py b/frappe/social/doctype/energy_point_rule/energy_point_rule.py index 9acb63d087..1c736528de 100644 --- a/frappe/social/doctype/energy_point_rule/energy_point_rule.py +++ b/frappe/social/doctype/energy_point_rule/energy_point_rule.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ import frappe.cache_manager diff --git a/frappe/social/doctype/energy_point_settings/energy_point_settings.py b/frappe/social/doctype/energy_point_settings/energy_point_settings.py index 7299eef916..64944d64e8 100644 --- a/frappe/social/doctype/energy_point_settings/energy_point_settings.py +++ b/frappe/social/doctype/energy_point_settings/energy_point_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.social.doctype.energy_point_log.energy_point_log import create_review_points_log diff --git a/frappe/social/doctype/post/post.py b/frappe/social/doctype/post/post.py index 9c7c1db1d4..62a0155d3c 100644 --- a/frappe/social/doctype/post/post.py +++ b/frappe/social/doctype/post/post.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import requests from bs4 import BeautifulSoup diff --git a/frappe/social/doctype/post_comment/post_comment.py b/frappe/social/doctype/post_comment/post_comment.py index df24c4609a..14001ba537 100644 --- a/frappe/social/doctype/post_comment/post_comment.py +++ b/frappe/social/doctype/post_comment/post_comment.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.core.doctype.user.user import extract_mentions diff --git a/frappe/social/doctype/review_level/review_level.py b/frappe/social/doctype/review_level/review_level.py index 87720b63fc..6622a89ab9 100644 --- a/frappe/social/doctype/review_level/review_level.py +++ b/frappe/social/doctype/review_level/review_level.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/templates/includes/comments/comments.py b/frappe/templates/includes/comments/comments.py index d08eb12ba8..2dda492593 100644 --- a/frappe/templates/includes/comments/comments.py +++ b/frappe/templates/includes/comments/comments.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe import re from frappe.website.render import clear_cache diff --git a/frappe/templates/pages/integrations/braintree_checkout.py b/frappe/templates/pages/integrations/braintree_checkout.py index ad262c34b2..c4aa1a7b9f 100644 --- a/frappe/templates/pages/integrations/braintree_checkout.py +++ b/frappe/templates/pages/integrations/braintree_checkout.py @@ -1,6 +1,5 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import flt diff --git a/frappe/templates/pages/integrations/payment_cancel.py b/frappe/templates/pages/integrations/payment_cancel.py index db335b8549..0387c9e50a 100644 --- a/frappe/templates/pages/integrations/payment_cancel.py +++ b/frappe/templates/pages/integrations/payment_cancel.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals import frappe def get_context(context): diff --git a/frappe/templates/pages/integrations/payment_success.py b/frappe/templates/pages/integrations/payment_success.py index bdc14db5b9..1eb9215121 100644 --- a/frappe/templates/pages/integrations/payment_success.py +++ b/frappe/templates/pages/integrations/payment_success.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe no_cache = True diff --git a/frappe/templates/pages/integrations/paytm_checkout.py b/frappe/templates/pages/integrations/paytm_checkout.py index bc385b5784..1bd9457ef6 100644 --- a/frappe/templates/pages/integrations/paytm_checkout.py +++ b/frappe/templates/pages/integrations/paytm_checkout.py @@ -1,6 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ import json diff --git a/frappe/templates/pages/integrations/razorpay_checkout.py b/frappe/templates/pages/integrations/razorpay_checkout.py index 039ea1776e..1901577d4b 100644 --- a/frappe/templates/pages/integrations/razorpay_checkout.py +++ b/frappe/templates/pages/integrations/razorpay_checkout.py @@ -1,11 +1,9 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import flt, cint import json -from six import string_types no_cache = 1 @@ -47,7 +45,7 @@ def get_api_key(): def make_payment(razorpay_payment_id, options, reference_doctype, reference_docname, token): data = {} - if isinstance(options, string_types): + if isinstance(options, str): data = json.loads(options) data.update({ diff --git a/frappe/templates/pages/integrations/stripe_checkout.py b/frappe/templates/pages/integrations/stripe_checkout.py index 142d5b35cd..186c3e0942 100644 --- a/frappe/templates/pages/integrations/stripe_checkout.py +++ b/frappe/templates/pages/integrations/stripe_checkout.py @@ -1,6 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import cint, fmt_money diff --git a/frappe/test_runner.py b/frappe/test_runner.py index cd71dd46c5..1f99e55fb8 100644 --- a/frappe/test_runner.py +++ b/frappe/test_runner.py @@ -1,8 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function - import frappe import unittest, json, sys, os import time @@ -11,8 +8,8 @@ import importlib from frappe.modules import load_doctype_module, get_module_name import frappe.utils.scheduler import cProfile, pstats -from six import StringIO -from six.moves import reload_module +from io import StringIO +from importlib import reload from frappe.model.naming import revert_series_if_last unittest_runner = unittest.TextTestRunner @@ -281,7 +278,7 @@ def get_modules(doctype): try: test_module = load_doctype_module(doctype, module, "test_") if test_module: - reload_module(test_module) + reload(test_module) except ImportError: test_module = None diff --git a/frappe/tests/test_assign.py b/frappe/tests/test_assign.py index 439e1546c0..e9c1ccec6d 100644 --- a/frappe/tests/test_assign.py +++ b/frappe/tests/test_assign.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, unittest import frappe.desk.form.assign_to from frappe.desk.listview import get_group_by_count diff --git a/frappe/tests/test_auth.py b/frappe/tests/test_auth.py index bbe9c36aea..bc23cb591c 100644 --- a/frappe/tests/test_auth.py +++ b/frappe/tests/test_auth.py @@ -1,7 +1,5 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import time import unittest diff --git a/frappe/tests/test_bot.py b/frappe/tests/test_bot.py index b098584a8f..4500ab95ac 100644 --- a/frappe/tests/test_bot.py +++ b/frappe/tests/test_bot.py @@ -3,7 +3,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import unittest class TestBot(unittest.TestCase): diff --git a/frappe/tests/test_client.py b/frappe/tests/test_client.py index 6be437601b..aed8dc8581 100644 --- a/frappe/tests/test_client.py +++ b/frappe/tests/test_client.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -from __future__ import unicode_literals - import unittest import frappe diff --git a/frappe/tests/test_cors.py b/frappe/tests/test_cors.py index d4ed260f61..38c3fd8132 100644 --- a/frappe/tests/test_cors.py +++ b/frappe/tests/test_cors.py @@ -1,7 +1,5 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, unittest from werkzeug.wrappers import Response from frappe.app import process_response diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index 79ab8bf421..a31a898d73 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -3,8 +3,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import unittest from random import choice diff --git a/frappe/tests/test_db_query.py b/frappe/tests/test_db_query.py index 085c257550..42ebd05b67 100644 --- a/frappe/tests/test_db_query.py +++ b/frappe/tests/test_db_query.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, unittest from frappe.model.db_query import DatabaseQuery diff --git a/frappe/tests/test_defaults.py b/frappe/tests/test_defaults.py index 210ea0d36c..d051c0a184 100644 --- a/frappe/tests/test_defaults.py +++ b/frappe/tests/test_defaults.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, unittest from frappe.defaults import * diff --git a/frappe/tests/test_document.py b/frappe/tests/test_document.py index 1a5a8721fd..7c0c95671a 100644 --- a/frappe/tests/test_document.py +++ b/frappe/tests/test_document.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import os import unittest diff --git a/frappe/tests/test_document_locks.py b/frappe/tests/test_document_locks.py index c270b1bf28..650a913bee 100644 --- a/frappe/tests/test_document_locks.py +++ b/frappe/tests/test_document_locks.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, unittest class TestDocumentLocks(unittest.TestCase): diff --git a/frappe/tests/test_domainification.py b/frappe/tests/test_domainification.py index 2fd2e7f3ff..c9acd9ec45 100644 --- a/frappe/tests/test_domainification.py +++ b/frappe/tests/test_domainification.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import unittest, frappe from frappe.core.page.permission_manager.permission_manager import get_roles_and_doctypes from frappe.desk.doctype.desktop_icon.desktop_icon import (get_desktop_icons, add_user_icon, diff --git a/frappe/tests/test_dynamic_links.py b/frappe/tests/test_dynamic_links.py index 98da2cbc35..04ccc91ff2 100644 --- a/frappe/tests/test_dynamic_links.py +++ b/frappe/tests/test_dynamic_links.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, unittest class TestDynamicLinks(unittest.TestCase): diff --git a/frappe/tests/test_email.py b/frappe/tests/test_email.py index 8340db4852..445ee21b36 100644 --- a/frappe/tests/test_email.py +++ b/frappe/tests/test_email.py @@ -1,10 +1,7 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import unittest, frappe, re, email -from six import PY3 test_dependencies = ['Email Account'] @@ -118,10 +115,7 @@ class TestEmail(unittest.TestCase): content = part.get_payload(decode=True) if content: - if PY3: - eol = "\r\n" - else: - eol = "\n" + eol = "\r\n" frappe.local.flags.signed_query_string = \ re.search(r'(?<=/api/method/frappe.email.queue.unsubscribe\?).*(?=' + eol + ')', diff --git a/frappe/tests/test_exporter_fixtures.py b/frappe/tests/test_exporter_fixtures.py index b8bd94e3e9..0893500daf 100644 --- a/frappe/tests/test_exporter_fixtures.py +++ b/frappe/tests/test_exporter_fixtures.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe import frappe.defaults from frappe.core.doctype.data_import.data_import import export_csv diff --git a/frappe/tests/test_fmt_datetime.py b/frappe/tests/test_fmt_datetime.py index e19eb25fe6..8f5408948d 100644 --- a/frappe/tests/test_fmt_datetime.py +++ b/frappe/tests/test_fmt_datetime.py @@ -1,7 +1,5 @@ # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import datetime import frappe diff --git a/frappe/tests/test_fmt_money.py b/frappe/tests/test_fmt_money.py index a1321658b7..160ea33378 100644 --- a/frappe/tests/test_fmt_money.py +++ b/frappe/tests/test_fmt_money.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe from frappe.utils import fmt_money import unittest diff --git a/frappe/tests/test_form_load.py b/frappe/tests/test_form_load.py index b382fa7381..dd6ccd9bcd 100644 --- a/frappe/tests/test_form_load.py +++ b/frappe/tests/test_form_load.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, unittest from frappe.desk.form.load import getdoctype, getdoc from frappe.core.page.permission_manager.permission_manager import update, reset, add diff --git a/frappe/tests/test_geo_ip.py b/frappe/tests/test_geo_ip.py index b292e43ba5..f085838f37 100644 --- a/frappe/tests/test_geo_ip.py +++ b/frappe/tests/test_geo_ip.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import unittest class TestGeoIP(unittest.TestCase): diff --git a/frappe/tests/test_global_search.py b/frappe/tests/test_global_search.py index 5cffbaaf64..3921af6738 100644 --- a/frappe/tests/test_global_search.py +++ b/frappe/tests/test_global_search.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import unittest import frappe diff --git a/frappe/tests/test_goal.py b/frappe/tests/test_goal.py index b4c0736482..5a83baa1af 100644 --- a/frappe/tests/test_goal.py +++ b/frappe/tests/test_goal.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import unittest import frappe diff --git a/frappe/tests/test_hooks.py b/frappe/tests/test_hooks.py index ff71e2414c..3f66afe34a 100644 --- a/frappe/tests/test_hooks.py +++ b/frappe/tests/test_hooks.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import unittest import frappe from frappe.desk.doctype.todo.todo import ToDo @@ -19,12 +18,12 @@ class TestHooks(unittest.TestCase): def test_override_doctype_class(self): from frappe import hooks - + # Set hook hooks.override_doctype_class = { 'ToDo': ['frappe.tests.test_hooks.CustomToDo'] } - + # Clear cache frappe.cache().delete_value('app_hooks') clear_controller_cache('ToDo') diff --git a/frappe/tests/test_listview.py b/frappe/tests/test_listview.py index 1ef72fdd32..4efb570e7e 100644 --- a/frappe/tests/test_listview.py +++ b/frappe/tests/test_listview.py @@ -1,7 +1,5 @@ # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import unittest import frappe import json diff --git a/frappe/tests/test_monitor.py b/frappe/tests/test_monitor.py index b447e89b06..dbc5fe35e1 100644 --- a/frappe/tests/test_monitor.py +++ b/frappe/tests/test_monitor.py @@ -2,7 +2,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import unittest import frappe import frappe.monitor diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py index 66d48e3612..557993882f 100644 --- a/frappe/tests/test_naming.py +++ b/frappe/tests/test_naming.py @@ -1,7 +1,6 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import unittest import frappe from frappe.utils import now_datetime diff --git a/frappe/tests/test_password.py b/frappe/tests/test_password.py index 98141072e2..6a5b948d93 100644 --- a/frappe/tests/test_password.py +++ b/frappe/tests/test_password.py @@ -1,6 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import unittest from frappe.utils.password import update_password, check_password, passlibctx diff --git a/frappe/tests/test_patches.py b/frappe/tests/test_patches.py index ed22afc0af..7f4efc700c 100644 --- a/frappe/tests/test_patches.py +++ b/frappe/tests/test_patches.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import unittest, frappe from frappe.modules import patch_handler diff --git a/frappe/tests/test_pdf.py b/frappe/tests/test_pdf.py index be287e0d92..b67aee7a68 100644 --- a/frappe/tests/test_pdf.py +++ b/frappe/tests/test_pdf.py @@ -1,11 +1,9 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import unittest import frappe.utils.pdf as pdfgen -import frappe, io, six +import frappe, io from PyPDF2 import PdfFileReader #class TestPdfBorders(unittest.TestCase): @@ -44,8 +42,6 @@ class TestPdf(unittest.TestCase): pdf = pdfgen.get_pdf(self.html, options={"password": password}) reader = PdfFileReader(io.BytesIO(pdf)) self.assertTrue(reader.isEncrypted) - if six.PY2: - password = frappe.safe_encode(password) self.assertTrue(reader.decrypt(password)) def test_pdf_generation_as_a_user(self): diff --git a/frappe/tests/test_permissions.py b/frappe/tests/test_permissions.py index 82f0ee920a..ada64156de 100644 --- a/frappe/tests/test_permissions.py +++ b/frappe/tests/test_permissions.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - """Use blog post test to test user permissions logic""" import frappe diff --git a/frappe/tests/test_query_report.py b/frappe/tests/test_query_report.py index eaf4dc7070..3d9791d399 100644 --- a/frappe/tests/test_query_report.py +++ b/frappe/tests/test_query_report.py @@ -1,8 +1,6 @@ # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import unittest import frappe diff --git a/frappe/tests/test_rate_limiter.py b/frappe/tests/test_rate_limiter.py index ae1857bb31..71977935a9 100644 --- a/frappe/tests/test_rate_limiter.py +++ b/frappe/tests/test_rate_limiter.py @@ -3,8 +3,6 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import unittest from werkzeug.wrappers import Response import time diff --git a/frappe/tests/test_recorder.py b/frappe/tests/test_recorder.py index 64d3c52f63..08dbde0144 100644 --- a/frappe/tests/test_recorder.py +++ b/frappe/tests/test_recorder.py @@ -3,7 +3,6 @@ # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import unittest import frappe import frappe.recorder diff --git a/frappe/tests/test_safe_exec.py b/frappe/tests/test_safe_exec.py index d7b25b8194..79f2c504a4 100644 --- a/frappe/tests/test_safe_exec.py +++ b/frappe/tests/test_safe_exec.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import unittest, frappe from frappe.utils.safe_exec import safe_exec, get_safe_globals diff --git a/frappe/tests/test_scheduler.py b/frappe/tests/test_scheduler.py index d5344c60b5..ec43c08ab7 100644 --- a/frappe/tests/test_scheduler.py +++ b/frappe/tests/test_scheduler.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - from unittest import TestCase from dateutil.relativedelta import relativedelta from frappe.core.doctype.scheduled_job_type.scheduled_job_type import sync_jobs diff --git a/frappe/tests/test_search.py b/frappe/tests/test_search.py index e39d6c4691..9ad02f49a6 100644 --- a/frappe/tests/test_search.py +++ b/frappe/tests/test_search.py @@ -1,7 +1,6 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import unittest import frappe from frappe.desk.search import search_link diff --git a/frappe/tests/test_seen.py b/frappe/tests/test_seen.py index 8eea30d773..7f4e8719a3 100644 --- a/frappe/tests/test_seen.py +++ b/frappe/tests/test_seen.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, unittest, json class TestSeen(unittest.TestCase): diff --git a/frappe/tests/test_sitemap.py b/frappe/tests/test_sitemap.py index 22669000c1..e29a453a14 100644 --- a/frappe/tests/test_sitemap.py +++ b/frappe/tests/test_sitemap.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe, unittest from frappe.utils import get_html_for_route diff --git a/frappe/tests/test_translate.py b/frappe/tests/test_translate.py index c29390c429..f51f31d509 100644 --- a/frappe/tests/test_translate.py +++ b/frappe/tests/test_translate.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, unittest, os import frappe.translate from frappe import _ diff --git a/frappe/tests/test_translation.py b/frappe/tests/test_translation.py index 0e1e6e452c..94af1bb870 100644 --- a/frappe/tests/test_translation.py +++ b/frappe/tests/test_translation.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, unittest, os import frappe.translate diff --git a/frappe/tests/test_twofactor.py b/frappe/tests/test_twofactor.py index 709b88b8f3..7de155bc96 100644 --- a/frappe/tests/test_twofactor.py +++ b/frappe/tests/test_twofactor.py @@ -1,7 +1,5 @@ # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import unittest, frappe, pyotp from frappe.auth import HTTPRequest from frappe.utils import cint diff --git a/frappe/tests/test_utils.py b/frappe/tests/test_utils.py index 74ceec8287..c2e5d99731 100644 --- a/frappe/tests/test_utils.py +++ b/frappe/tests/test_utils.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import unittest import frappe @@ -56,14 +54,14 @@ class TestMoney(unittest.TestCase): for num in nums_bhd: self.assertEqual( - money_in_words(num[0], "BHD"), - num[1], + money_in_words(num[0], "BHD"), + num[1], "{0} is not the same as {1}".format(money_in_words(num[0], "BHD"), num[1]) ) for num in nums_ngn: self.assertEqual( - money_in_words(num[0], "NGN"), num[1], + money_in_words(num[0], "NGN"), num[1], "{0} is not the same as {1}".format(money_in_words(num[0], "NGN"), num[1]) ) @@ -139,7 +137,7 @@ class TestValidationUtils(unittest.TestCase): # Valid URLs self.assertTrue(validate_url('https://google.com')) self.assertTrue(validate_url('http://frappe.io', throw=True)) - + # Invalid URLs without throw self.assertFalse(validate_url('google.io')) self.assertFalse(validate_url('google.io')) @@ -152,9 +150,9 @@ class TestValidationUtils(unittest.TestCase): self.assertTrue(validate_url('ftp://frappe.cloud', valid_schemes=['https', 'ftp'])) self.assertFalse(validate_url('bolo://frappe.io', valid_schemes=("http", "https", "ftp", "ftps"))) self.assertRaises( - frappe.ValidationError, - validate_url, - 'gopher://frappe.io', + frappe.ValidationError, + validate_url, + 'gopher://frappe.io', valid_schemes='https', throw=True ) @@ -167,16 +165,16 @@ class TestValidationUtils(unittest.TestCase): # Valid addresses self.assertTrue(validate_email_address('someone@frappe.com')) self.assertTrue(validate_email_address('someone@frappe.com, anyone@frappe.io')) - + # Invalid address self.assertFalse(validate_email_address('someone')) self.assertFalse(validate_email_address('someone@----.com')) # Invalid with throw self.assertRaises( - frappe.InvalidEmailAddressError, - validate_email_address, - 'someone.com', + frappe.InvalidEmailAddressError, + validate_email_address, + 'someone.com', throw=True ) diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index dc3862174d..52ddc5ef71 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import unittest import frappe diff --git a/frappe/tests/tests_geo_utils.py b/frappe/tests/tests_geo_utils.py index 2067a6aa97..e928bc0fef 100644 --- a/frappe/tests/tests_geo_utils.py +++ b/frappe/tests/tests_geo_utils.py @@ -2,8 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import unittest import frappe diff --git a/frappe/translate.py b/frappe/translate.py index b9dd5e263a..f95fdad015 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -1,12 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function - -from six import iteritems, text_type, string_types, PY2 - -from frappe.utils import cstr - """ frappe.translate ~~~~~~~~~~~~~~~~ @@ -14,10 +7,11 @@ from frappe.utils import cstr Translation tools for frappe """ -import frappe, os, re, io, codecs, json +import frappe, os, re, io, json from frappe.model.utils import render_include, InvalidIncludePath from frappe.utils import strip, strip_html_tags, is_html import itertools, operator +from csv import reader def guess_language(lang_list=None): """Set `frappe.local.lang` from HTTP headers at beginning of request""" @@ -36,8 +30,8 @@ def guess_language(lang_list=None): for l in lang_codes: code = l.strip() - if not isinstance(code, text_type): - code = text_type(code, 'utf-8') + if not isinstance(code, str): + code = str(code, 'utf-8') if code in lang_list or code == "en": guess = code break @@ -127,7 +121,7 @@ def get_dict(fortype, name=None): message_dict = make_dict_from_messages(messages, load_user_translation=False) message_dict.update(get_dict_from_hooks(fortype, name)) # remove untranslated - message_dict = {k:v for k, v in iteritems(message_dict) if k!=v} + message_dict = {k: v for k, v in message_dict.items() if k!=v} translation_assets[asset_key] = message_dict cache.hset("translation_assets", frappe.local.lang, translation_assets, shared=True) @@ -389,7 +383,7 @@ def get_messages_from_workflow(doctype=None, app_name=None): else: fixtures = frappe.get_hooks('fixtures', app_name=app_name) or [] for fixture in fixtures: - if isinstance(fixture, string_types) and fixture == 'Worflow': + if isinstance(fixture, str) and fixture == 'Worflow': workflows = frappe.get_all('Workflow') break elif isinstance(fixture, dict) and fixture.get('dt', fixture.get('doctype')) == 'Workflow': @@ -425,7 +419,7 @@ def get_messages_from_custom_fields(app_name): custom_fields = [] for fixture in fixtures: - if isinstance(fixture, string_types) and fixture == 'Custom Field': + if isinstance(fixture, str) and fixture == 'Custom Field': custom_fields = frappe.get_all('Custom Field', fields=['name','label', 'description', 'fieldtype', 'options']) break elif isinstance(fixture, dict) and fixture.get('dt', fixture.get('doctype')) == 'Custom Field': @@ -609,20 +603,11 @@ def read_csv_file(path): """Read CSV file and return as list of list :param path: File path""" - from csv import reader - if PY2: - with codecs.open(path, 'r', 'utf-8') as msgfile: - data = msgfile.read() + with io.open(path, mode='r', encoding='utf-8', newline='') as msgfile: + data = reader(msgfile) + newdata = [[ val for val in row ] for row in data] - # for japanese! #wtf - data = data.replace(chr(28), "").replace(chr(29), "") - data = reader([r.encode('utf-8') for r in data.splitlines()]) - newdata = [[text_type(val, 'utf-8') for val in row] for row in data] - else: - with io.open(path, mode='r', encoding='utf-8', newline='') as msgfile: - data = reader(msgfile) - newdata = [[ val for val in row ] for row in data] return newdata def write_csv_file(path, app_messages, lang_dict): @@ -812,7 +797,7 @@ def update_translations_for_source(source=None, translation_dict=None): frappe.delete_doc('Translation', d.name) # remaining values are to be inserted - for lang, translated_text in iteritems(translation_dict): + for lang, translated_text in translation_dict.items(): doc = frappe.new_doc('Translation') doc.language = lang doc.source_text = source diff --git a/frappe/twofactor.py b/frappe/twofactor.py index 4e098c3075..c2fb6d5de9 100644 --- a/frappe/twofactor.py +++ b/frappe/twofactor.py @@ -1,17 +1,13 @@ # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals - import frappe from frappe import _ import pyotp, os from frappe.utils.background_jobs import enqueue from pyqrcode import create as qrcreate -from six import BytesIO +from io import BytesIO from base64 import b64encode, b32encode from frappe.utils import get_url, get_datetime, time_diff_in_seconds, cint -from six import iteritems, string_types class ExpiredLoginException(Exception): pass @@ -78,7 +74,7 @@ def cache_2fa_data(user, token, otp_secret, tmp_id): frappe.cache().expire(tmp_id + '_token', expiry_time) else: expiry_time = frappe.flags.otp_expiry or 180 - for k, v in iteritems({'_usr': user, '_pwd': pwd, '_otp_secret': otp_secret}): + for k, v in {'_usr': user, '_pwd': pwd, '_otp_secret': otp_secret}.items(): frappe.cache().set("{0}{1}".format(tmp_id, k), v) frappe.cache().expire("{0}{1}".format(tmp_id, k), expiry_time) @@ -87,7 +83,7 @@ def two_factor_is_enabled_for_(user): if user == "Administrator": return False - if isinstance(user, string_types): + if isinstance(user, str): user = frappe.get_doc('User', user) roles = [frappe.db.escape(d.role) for d in user.roles or []] @@ -393,7 +389,7 @@ def delete_all_barcodes_for_users(): def should_remove_barcode_image(barcode): '''Check if it's time to delete barcode image from server. ''' - if isinstance(barcode, string_types): + if isinstance(barcode, str): barcode = frappe.get_doc('File', barcode) lifespan = frappe.db.get_value('System Settings', 'System Settings', 'lifespan_qrcode_image') or 240 if time_diff_in_seconds(get_datetime(), barcode.creation) > int(lifespan): diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 436dcba028..cb02035299 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -739,7 +739,7 @@ def get_safe_filters(filters): try: filters = json.loads(filters) - if isinstance(filters, (integer_types, float)): + if isinstance(filters, (int, float)): filters = frappe.as_unicode(filters) except (TypeError, ValueError): diff --git a/frappe/utils/background_jobs.py b/frappe/utils/background_jobs.py index bd1f1154a9..8456835ca7 100755 --- a/frappe/utils/background_jobs.py +++ b/frappe/utils/background_jobs.py @@ -1,4 +1,3 @@ -from __future__ import unicode_literals, print_function import redis from rq import Connection, Queue, Worker from rq.logutils import setup_loghandlers @@ -7,11 +6,9 @@ from collections import defaultdict import frappe import os, socket, time from frappe import _ -from six import string_types from uuid import uuid4 import frappe.monitor -# imports - third-party imports default_timeout = 300 queue_timeout = { @@ -89,7 +86,7 @@ def execute_job(site, method, event, job_name, kwargs, user=None, is_async=True, if user: frappe.set_user(user) - if isinstance(method, string_types): + if isinstance(method, str): method_name = method method = frappe.get_attr(method) else: @@ -193,7 +190,7 @@ def get_queue_list(queue_list=None): '''Defines possible queues. Also wraps a given queue in a list after validating.''' default_queue_list = list(queue_timeout) if queue_list: - if isinstance(queue_list, string_types): + if isinstance(queue_list, str): queue_list = [queue_list] for queue in queue_list: diff --git a/frappe/utils/bench_helper.py b/frappe/utils/bench_helper.py index 2fb0bda058..ec08d2a15e 100644 --- a/frappe/utils/bench_helper.py +++ b/frappe/utils/bench_helper.py @@ -1,4 +1,3 @@ -from __future__ import unicode_literals, print_function import click import frappe import os diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index d950d9f082..92150b79cf 100755 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -1,8 +1,5 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function - import frappe, os, re, git from frappe.utils import touch_file, cstr diff --git a/frappe/utils/bot.py b/frappe/utils/bot.py index 45e1bd5a4e..572723c056 100644 --- a/frappe/utils/bot.py +++ b/frappe/utils/bot.py @@ -1,8 +1,6 @@ # Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe, re, frappe.utils from frappe.desk.notifications import get_notifications from frappe import _ diff --git a/frappe/utils/change_log.py b/frappe/utils/change_log.py index 33801af722..6cb71c6ac5 100644 --- a/frappe/utils/change_log.py +++ b/frappe/utils/change_log.py @@ -7,7 +7,6 @@ import subprocess # nosec import requests from semantic_version import Version -from six.moves import range import frappe from frappe import _, safe_decode diff --git a/frappe/utils/connections.py b/frappe/utils/connections.py index 1f57d8fbae..5640da666c 100644 --- a/frappe/utils/connections.py +++ b/frappe/utils/connections.py @@ -1,6 +1,6 @@ import socket -from six.moves.urllib.parse import urlparse +from urllib.parse import urlparse from frappe import get_conf REDIS_KEYS = ('redis_cache', 'redis_queue', 'redis_socketio') diff --git a/frappe/utils/csvutils.py b/frappe/utils/csvutils.py index 00163ade5f..734d68fe8a 100644 --- a/frappe/utils/csvutils.py +++ b/frappe/utils/csvutils.py @@ -1,14 +1,11 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals import frappe from frappe import msgprint, _ import json import csv -import six import requests -from six import StringIO, text_type, string_types +from io import StringIO from frappe.utils import encode, cstr, cint, flt, comma_or def read_csv_content_from_uploaded_file(ignore_encoding=False): @@ -40,11 +37,11 @@ def read_csv_content_from_attached_file(doc): def read_csv_content(fcontent, ignore_encoding=False): rows = [] - if not isinstance(fcontent, text_type): + if not isinstance(fcontent, str): decoded = False for encoding in ["utf-8", "windows-1250", "windows-1252"]: try: - fcontent = text_type(fcontent, encoding) + fcontent = str(fcontent, encoding) decoded = True break except UnicodeDecodeError: @@ -56,10 +53,7 @@ def read_csv_content(fcontent, ignore_encoding=False): fcontent = fcontent.encode("utf-8") content = [ ] for line in fcontent.splitlines(True): - if six.PY2: - content.append(line) - else: - content.append(frappe.safe_decode(line)) + content.append(frappe.safe_decode(line)) try: rows = [] @@ -85,7 +79,7 @@ def read_csv_content(fcontent, ignore_encoding=False): @frappe.whitelist() def send_csv_to_client(args): - if isinstance(args, string_types): + if isinstance(args, str): args = json.loads(args) args = frappe._dict(args) @@ -113,8 +107,6 @@ class UnicodeWriter: self.writer = csv.writer(self.queue, quoting=quoting) def writerow(self, row): - if six.PY2: - row = encode(row, self.encoding) self.writer.writerow(row) def getvalue(self): diff --git a/frappe/utils/dashboard.py b/frappe/utils/dashboard.py index e386dcd881..ad61486113 100644 --- a/frappe/utils/dashboard.py +++ b/frappe/utils/dashboard.py @@ -1,6 +1,5 @@ # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ from functools import wraps diff --git a/frappe/utils/data.py b/frappe/utils/data.py index a9af30ab2c..09b02a918a 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -1,14 +1,11 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe import operator import json import re, datetime, math, time -from six.moves.urllib.parse import quote, urljoin -from six import iteritems, text_type, string_types, integer_types +from urllib.parse import quote, urljoin from frappe.desk.utils import slug DATE_FORMAT = "%Y-%m-%d" @@ -72,7 +69,7 @@ def get_datetime(datetime_str=None): def to_timedelta(time_str): from dateutil import parser - if isinstance(time_str, string_types): + if isinstance(time_str, str): t = parser.parse(time_str) return datetime.timedelta(hours=t.hour, minutes=t.minute, seconds=t.second, microseconds=t.microsecond) @@ -91,7 +88,7 @@ def add_to_date(date, years=0, months=0, weeks=0, days=0, hours=0, minutes=0, se if hours: as_datetime = True - if isinstance(date, string_types): + if isinstance(date, str): as_string = True if " " in date: as_datetime = True @@ -274,17 +271,17 @@ def get_time(time_str): return parser.parse(time_str).time() def get_datetime_str(datetime_obj): - if isinstance(datetime_obj, string_types): + if isinstance(datetime_obj, str): datetime_obj = get_datetime(datetime_obj) return datetime_obj.strftime(DATETIME_FORMAT) def get_date_str(date_obj): - if isinstance(date_obj, string_types): + if isinstance(date_obj, str): date_obj = get_datetime(date_obj) return date_obj.strftime(DATE_FORMAT) def get_time_str(timedelta_obj): - if isinstance(timedelta_obj, string_types): + if isinstance(timedelta_obj, str): timedelta_obj = to_timedelta(timedelta_obj) hours, remainder = divmod(timedelta_obj.seconds, 3600) @@ -549,7 +546,7 @@ def flt(s, precision=None): >>> flt("a") 0.0 """ - if isinstance(s, string_types): + if isinstance(s, str): s = s.replace(',','') try: @@ -706,12 +703,12 @@ def encode(obj, encoding="utf-8"): if isinstance(obj, list): out = [] for o in obj: - if isinstance(o, text_type): + if isinstance(o, str): out.append(o.encode(encoding)) else: out.append(o) return out - elif isinstance(obj, text_type): + elif isinstance(obj, str): return obj.encode(encoding) else: return obj @@ -719,10 +716,10 @@ def encode(obj, encoding="utf-8"): def parse_val(v): """Converts to simple datatypes from SQL query results""" if isinstance(v, (datetime.date, datetime.datetime)): - v = text_type(v) + v = str(v) elif isinstance(v, datetime.timedelta): - v = ":".join(text_type(v).split(":")[:2]) - elif isinstance(v, integer_types): + v = ":".join(str(v).split(":")[:2]) + elif isinstance(v, int): v = int(v) return v @@ -743,7 +740,7 @@ def fmt_money(amount, precision=None, currency=None, format=None): # 40,000.00000 -> 40,000.00 # 40,000.23000 -> 40,000.23 - if isinstance(amount, string_types): + if isinstance(amount, str): amount = flt(amount, precision) if decimal_str: @@ -959,7 +956,7 @@ def strip_html(text): return _striptags_re.sub("", text) def escape_html(text): - if not isinstance(text, string_types): + if not isinstance(text, str): return text html_escape_table = { @@ -982,7 +979,7 @@ def pretty_date(iso_datetime): if not iso_datetime: return '' import math - if isinstance(iso_datetime, string_types): + if isinstance(iso_datetime, str): iso_datetime = datetime.datetime.strptime(iso_datetime, DATETIME_FORMAT) now_dt = datetime.datetime.strptime(now(), DATETIME_FORMAT) dt_diff = now_dt - iso_datetime @@ -1031,7 +1028,7 @@ def comma_and(some_list ,add_quotes=True): def comma_sep(some_list, pattern, add_quotes=True): if isinstance(some_list, (list, tuple)): # list(some_list) is done to preserve the existing list - some_list = [text_type(s) for s in list(some_list)] + some_list = [str(s) for s in list(some_list)] if not some_list: return "" elif len(some_list) == 1: @@ -1045,7 +1042,7 @@ def comma_sep(some_list, pattern, add_quotes=True): def new_line_sep(some_list): if isinstance(some_list, (list, tuple)): # list(some_list) is done to preserve the existing list - some_list = [text_type(s) for s in list(some_list)] + some_list = [str(s) for s in list(some_list)] if not some_list: return "" elif len(some_list) == 1: @@ -1131,7 +1128,7 @@ def get_link_to_report(name, label=None, report_type=None, doctype=None, filters if filters: conditions = [] - for k,v in iteritems(filters): + for k,v in filters.items(): if isinstance(v, list): for value in v: conditions.append(str(k)+'='+'["'+str(value[0]+'"'+','+'"'+str(value[1])+'"]')) @@ -1187,7 +1184,7 @@ operator_map = { def evaluate_filters(doc, filters): '''Returns true if doc matches filters''' if isinstance(filters, dict): - for key, value in iteritems(filters): + for key, value in filters.items(): f = get_filter(None, {key:value}) if not compare(doc.get(f.fieldname), f.operator, f.value, f.fieldtype): return False @@ -1375,7 +1372,7 @@ def strip(val, chars=None): def to_markdown(html): from html2text import html2text - from six.moves import html_parser as HTMLParser + from html.parser import HTMLParser text = None try: @@ -1514,7 +1511,7 @@ def get_user_info_for_avatar(user_id): return user_info -class UnicodeWithAttrs(text_type): +class UnicodeWithAttrs(str): def __init__(self, text): self.toc_html = text.toc_html self.metadata = text.metadata diff --git a/frappe/utils/dateutils.py b/frappe/utils/dateutils.py index 06b434a512..2c2537da5f 100644 --- a/frappe/utils/dateutils.py +++ b/frappe/utils/dateutils.py @@ -1,14 +1,12 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import frappe.defaults import datetime from frappe.utils import get_datetime, add_to_date, getdate from frappe.utils.data import get_first_day, get_first_day_of_week, get_quarter_start, get_year_start,\ get_last_day, get_last_day_of_week, get_quarter_ending, get_year_ending -from six import string_types # global values -- used for caching dateformats = { @@ -71,7 +69,7 @@ def get_user_date_format(): def datetime_in_user_format(date_time): if not date_time: return "" - if isinstance(date_time, string_types): + if isinstance(date_time, str): date_time = get_datetime(date_time) from frappe.utils import formatdate return formatdate(date_time.date()) + " " + date_time.strftime("%H:%M") diff --git a/frappe/utils/doctor.py b/frappe/utils/doctor.py index e97f792b88..9dafc4dd21 100644 --- a/frappe/utils/doctor.py +++ b/frappe/utils/doctor.py @@ -1,10 +1,8 @@ -from __future__ import unicode_literals, print_function import frappe.utils from collections import defaultdict from rq import Worker, Connection from frappe.utils.background_jobs import get_redis_conn, get_queue, get_queue_list from frappe.utils.scheduler import is_scheduler_disabled, is_scheduler_inactive -from six import iteritems def get_workers(): @@ -130,7 +128,7 @@ def doctor(site=None): print("Queue:", queue) print("Number of Jobs: ", job_count[queue]) print("Methods:") - for method, count in iteritems(jobs_per_queue[queue]): + for method, count in jobs_per_queue[queue].items(): print("{0} : {1}".format(method, count)) print("------------") diff --git a/frappe/utils/error.py b/frappe/utils/error.py index 2d8d6491a5..61905b4985 100644 --- a/frappe/utils/error.py +++ b/frappe/utils/error.py @@ -2,8 +2,6 @@ # Copyright (c) 2015, Maxwell Morais and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import os import sys import traceback @@ -17,7 +15,7 @@ import pydoc import cgitb import datetime import json -import six + def make_error_snapshot(exception): if frappe.conf.disable_error_snapshot: @@ -51,7 +49,7 @@ def get_snapshot(exception, context=10): """ etype, evalue, etb = sys.exc_info() - if isinstance(etype, six.class_types): + if isinstance(etype, type): etype = etype.__name__ # creates a snapshot dict with some basic information @@ -131,7 +129,7 @@ def get_snapshot(exception, context=10): # add all local values (of last frame) to the snapshot for name, value in locals.items(): - s['locals'][name] = value if isinstance(value, six.text_type) else pydoc.text.repr(value) + s['locals'][name] = value if isinstance(value, str) else pydoc.text.repr(value) return s diff --git a/frappe/utils/file_lock.py b/frappe/utils/file_lock.py index b85ace7db9..8c65dd32ce 100644 --- a/frappe/utils/file_lock.py +++ b/frappe/utils/file_lock.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - ''' File based locking utility ''' diff --git a/frappe/utils/file_manager.py b/frappe/utils/file_manager.py index 2177e67274..30b0d816bb 100644 --- a/frappe/utils/file_manager.py +++ b/frappe/utils/file_manager.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import os, base64, re, json import hashlib @@ -11,8 +10,7 @@ from frappe.utils import get_hook_method, get_files_path, random_string, encode, from frappe import _ from frappe import conf from copy import copy -from six.moves.urllib.parse import unquote -from six import text_type, PY2, string_types +from urllib.parse import unquote class MaxFileSizeReachedError(frappe.ValidationError): @@ -123,7 +121,7 @@ def get_uploaded_content(): def save_file(fname, content, dt, dn, folder=None, decode=False, is_private=0, df=None): if decode: - if isinstance(content, text_type): + if isinstance(content, str): content = content.encode("utf-8") if b"," in content: @@ -207,7 +205,7 @@ def write_file(content, fname, is_private=0): # create directory (if not exists) frappe.create_folder(file_path) # write the file - if isinstance(content, text_type): + if isinstance(content, str): content = content.encode() with open(os.path.join(file_path.encode('utf-8'), fname.encode('utf-8')), 'wb+') as f: f.write(content) @@ -297,18 +295,14 @@ def get_file(fname): file_path = get_file_path(fname) # read the file - if PY2: - with open(encode(file_path)) as f: - content = f.read() - else: - with io.open(encode(file_path), mode='rb') as f: - content = f.read() - try: - # for plain text files - content = content.decode() - except UnicodeDecodeError: - # for .png, .jpg, etc - pass + with io.open(encode(file_path), mode='rb') as f: + content = f.read() + try: + # for plain text files + content = content.decode() + except UnicodeDecodeError: + # for .png, .jpg, etc + pass return [file_path.rsplit("/", 1)[-1], content] @@ -338,7 +332,7 @@ def get_file_path(file_name): def get_content_hash(content): - if isinstance(content, text_type): + if isinstance(content, str): content = content.encode() return hashlib.md5(content).hexdigest() @@ -397,8 +391,8 @@ def extract_images_from_html(doc, content): filename = headers.split("filename=")[-1] # decode filename - if not isinstance(filename, text_type): - filename = text_type(filename, 'utf-8') + if not isinstance(filename, str): + filename = str(filename, 'utf-8') else: mtype = headers.split(";")[0] filename = get_random_filename(content_type=mtype) @@ -443,12 +437,12 @@ def validate_filename(filename): @frappe.whitelist() def add_attachments(doctype, name, attachments): '''Add attachments to the given DocType''' - if isinstance(attachments, string_types): + if isinstance(attachments, str): attachments = json.loads(attachments) # loop through attachments files =[] for a in attachments: - if isinstance(a, string_types): + if isinstance(a, str): attach = frappe.db.get_value("File", {"name":a}, ["file_name", "file_url", "is_private"], as_dict=1) # save attachments to new doc f = save_url(attach.file_url, attach.file_name, doctype, name, "Home/Attachments", attach.is_private) diff --git a/frappe/utils/formatters.py b/frappe/utils/formatters.py index 7913413878..9efccc15f0 100644 --- a/frappe/utils/formatters.py +++ b/frappe/utils/formatters.py @@ -1,18 +1,16 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import datetime from frappe.utils import formatdate, fmt_money, flt, cstr, cint, format_datetime, format_time, format_duration from frappe.model.meta import get_field_currency, get_field_precision import re -from six import string_types def format_value(value, df=None, doc=None, currency=None, translated=False): '''Format value based on given fieldtype, document reference, currency reference. If docfield info (df) is not given, it will try and guess based on the datatype of the value''' - if isinstance(df, string_types): + if isinstance(df, str): df = frappe._dict(fieldtype=df) if not df: diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index c20f3b29d4..3cf10cd9e0 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals - import frappe import re import redis @@ -11,7 +9,6 @@ import os from frappe.utils import cint, strip_html_tags from frappe.utils.html_utils import unescape_html from frappe.model.base_document import get_controller -from six import text_type def setup_global_search_table(): """ @@ -348,9 +345,9 @@ def get_formatted_value(value, field): if getattr(field, 'fieldtype', None) in ["Text", "Text Editor"]: value = unescape_html(frappe.safe_decode(value)) - value = (re.subn(r'(?s)<[\s]*(script|style).*?', '', text_type(value))[0]) + value = (re.subn(r'(?s)<[\s]*(script|style).*?', '', str(value))[0]) value = ' '.join(value.split()) - return field.label + " : " + strip_html_tags(text_type(value)) + return field.label + " : " + strip_html_tags(str(value)) def sync_global_search(): diff --git a/frappe/utils/goal.py b/frappe/utils/goal.py index 4c63eb9fc4..195c962aab 100644 --- a/frappe/utils/goal.py +++ b/frappe/utils/goal.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ diff --git a/frappe/utils/html_utils.py b/frappe/utils/html_utils.py index f3f86dcad2..24def07c63 100644 --- a/frappe/utils/html_utils.py +++ b/frappe/utils/html_utils.py @@ -1,14 +1,13 @@ -from __future__ import unicode_literals + import frappe import json import re import bleach_whitelist.bleach_whitelist as bleach_whitelist -from six import string_types def clean_html(html): import bleach - if not isinstance(html, string_types): + if not isinstance(html, str): return html return bleach.clean(clean_script_and_style(html), @@ -21,7 +20,7 @@ def clean_html(html): def clean_email_html(html): import bleach - if not isinstance(html, string_types): + if not isinstance(html, str): return html return bleach.clean(clean_script_and_style(html), @@ -60,7 +59,7 @@ def sanitize_html(html, linkify=False): import bleach from bs4 import BeautifulSoup - if not isinstance(html, string_types): + if not isinstance(html, str): return html elif is_json(html): diff --git a/frappe/utils/identicon.py b/frappe/utils/identicon.py index 328fb03836..e570875b4a 100644 --- a/frappe/utils/identicon.py +++ b/frappe/utils/identicon.py @@ -1,9 +1,9 @@ -from __future__ import unicode_literals + from PIL import Image, ImageDraw from hashlib import md5 import base64 import random -from six import StringIO +from io import StringIO GRID_SIZE = 5 BORDER_SIZE = 20 diff --git a/frappe/utils/image.py b/frappe/utils/image.py index 60595464a1..b6f4c67c44 100644 --- a/frappe/utils/image.py +++ b/frappe/utils/image.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function import os def resize_images(path, maxdim=700): @@ -24,7 +22,7 @@ def strip_exif_data(content, content_type): Works by creating a new Image object which ignores exif by default and then extracts the binary data back into content. - Returns: + Returns: Bytes: Stripped image content """ @@ -33,11 +31,11 @@ def strip_exif_data(content, content_type): original_image = Image.open(io.BytesIO(content)) output = io.BytesIO() - + new_image = Image.new(original_image.mode, original_image.size) new_image.putdata(list(original_image.getdata())) new_image.save(output, format=content_type.split('/')[1]) - + content = output.getvalue() return content \ No newline at end of file diff --git a/frappe/utils/install.py b/frappe/utils/install.py index 93f46a2a16..91d8f04eb4 100644 --- a/frappe/utils/install.py +++ b/frappe/utils/install.py @@ -1,8 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function - import frappe import getpass from frappe.utils.password import update_password diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py index a77eca4977..685208e71f 100644 --- a/frappe/utils/jinja.py +++ b/frappe/utils/jinja.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - def get_jenv(): import frappe from frappe.utils.safe_exec import get_safe_globals diff --git a/frappe/utils/jinja_globals.py b/frappe/utils/jinja_globals.py index 347d52dc57..e24dff05f1 100644 --- a/frappe/utils/jinja_globals.py +++ b/frappe/utils/jinja_globals.py @@ -1,7 +1,6 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals from frappe.utils.jinja import get_jenv diff --git a/frappe/utils/logger.py b/frappe/utils/logger.py index 364ffa776d..617572deb7 100755 --- a/frappe/utils/logger.py +++ b/frappe/utils/logger.py @@ -1,13 +1,9 @@ -# imports - compatibility imports -from __future__ import unicode_literals - # imports - standard imports import logging import os from logging.handlers import RotatingFileHandler # imports - third party imports -from six import text_type # imports - module imports import frappe @@ -83,10 +79,10 @@ class SiteContextFilter(logging.Filter): """This is a filter which injects request information (if available) into the log.""" def filter(self, record): - if "Form Dict" not in text_type(record.msg): + if "Form Dict" not in str(record.msg): site = getattr(frappe.local, "site", None) form_dict = getattr(frappe.local, "form_dict", None) - record.msg = text_type(record.msg) + "\nSite: {0}\nForm Dict: {1}".format(site, form_dict) + record.msg = str(record.msg) + "\nSite: {0}\nForm Dict: {1}".format(site, form_dict) return True diff --git a/frappe/utils/make_random.py b/frappe/utils/make_random.py index ad353dea84..2ebabb78f9 100644 --- a/frappe/utils/make_random.py +++ b/frappe/utils/make_random.py @@ -1,7 +1,5 @@ -from __future__ import unicode_literals + import frappe, random -from six.moves import range -from six import string_types settings = frappe._dict( prob = { @@ -17,7 +15,7 @@ def add_random_children(doc, fieldname, rows, randomize, unique=None): for i in range(nrows): d = {} for key, val in randomize.items(): - if isinstance(val[0], string_types): + if isinstance(val[0], str): d[key] = get_random(*val) else: d[key] = random.randrange(*val) diff --git a/frappe/utils/minify.py b/frappe/utils/minify.py index 08d05c7225..b2cc93e554 100644 --- a/frappe/utils/minify.py +++ b/frappe/utils/minify.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + # This code is original from jsmin by Douglas Crockford, it was translated to # Python by Baruch Even. The original code had the following copyright and # license. @@ -29,7 +29,7 @@ from __future__ import unicode_literals # SOFTWARE. # */ -from six import StringIO +from io import StringIO def jsmin(js): ins = StringIO(js) diff --git a/frappe/utils/momentjs.py b/frappe/utils/momentjs.py index 3c5ec91f63..18df9903a7 100644 --- a/frappe/utils/momentjs.py +++ b/frappe/utils/momentjs.py @@ -1,5 +1,4 @@ # get data for moment.js -from __future__ import unicode_literals def update(tz, out): ltz = data["links"].get(tz, tz) zone = data["zones"].get(ltz) diff --git a/frappe/utils/nestedset.py b/frappe/utils/nestedset.py index 531699db0c..3c024c40e4 100644 --- a/frappe/utils/nestedset.py +++ b/frappe/utils/nestedset.py @@ -10,8 +10,6 @@ # 3. call update_nsm(doc_obj) in the on_upate method # ------------------------------------------ -from __future__ import unicode_literals - import frappe from frappe import _ from frappe.model.document import Document diff --git a/frappe/utils/oauth.py b/frappe/utils/oauth.py index 6a92737a0d..45cce737e1 100644 --- a/frappe/utils/oauth.py +++ b/frappe/utils/oauth.py @@ -1,14 +1,12 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import frappe.utils import json, jwt import base64 from frappe import _ from frappe.utils.password import get_decrypted_password -from six import string_types class SignupDisabledError(frappe.PermissionError): pass @@ -163,10 +161,10 @@ def login_oauth_user(data=None, provider=None, state=None, email_id=None, key=No # return # json.loads data and state - if isinstance(data, string_types): + if isinstance(data, str): data = json.loads(data) - if isinstance(state, string_types): + if isinstance(state, str): state = base64.b64decode(state) state = json.loads(state.decode("utf-8")) diff --git a/frappe/utils/password.py b/frappe/utils/password.py index fbed3cd8e7..005a3cede6 100644 --- a/frappe/utils/password.py +++ b/frappe/utils/password.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import string import frappe from frappe import _ diff --git a/frappe/utils/password_strength.py b/frappe/utils/password_strength.py index a4182d1cab..3959d8c3dd 100644 --- a/frappe/utils/password_strength.py +++ b/frappe/utils/password_strength.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - try: from zxcvbn import zxcvbn except Exception: diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index 70464aafc5..92459abc46 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import io import os import re @@ -9,7 +7,6 @@ from distutils.version import LooseVersion import subprocess import pdfkit -import six from bs4 import BeautifulSoup from PyPDF2 import PdfFileReader, PdfFileWriter @@ -57,8 +54,6 @@ def get_pdf(html, options=None, output=None): if "password" in options: password = options["password"] - if six.PY2: - password = frappe.safe_encode(password) if output: output.appendPagesFromReader(reader) diff --git a/frappe/utils/print_format.py b/frappe/utils/print_format.py index e83a5f6c71..6cf2ecc304 100644 --- a/frappe/utils/print_format.py +++ b/frappe/utils/print_format.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import frappe, os from frappe import _ diff --git a/frappe/utils/redis_wrapper.py b/frappe/utils/redis_wrapper.py index 678a61ca6e..41aa4e7ad1 100644 --- a/frappe/utils/redis_wrapper.py +++ b/frappe/utils/redis_wrapper.py @@ -1,11 +1,7 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - -import redis, frappe, re -from six.moves import cPickle as pickle +import redis, frappe, re, pickle from frappe.utils import cstr -from six import iteritems class RedisWrapper(redis.Redis): @@ -165,8 +161,10 @@ class RedisWrapper(redis.Redis): pass def hgetall(self, name): - return {key: pickle.loads(value) for key, value in - iteritems(super(RedisWrapper, self).hgetall(self.make_key(name)))} + value = super(RedisWrapper, self).hgetall(self.make_key(name)) + return { + key: pickle.loads(value) for key, value in value.items() + } def hget(self, name, key, generator=None, shared=False): _name = self.make_key(name, shared=shared) diff --git a/frappe/utils/reset_doc.py b/frappe/utils/reset_doc.py index 2119df5897..15aff4dc6c 100755 --- a/frappe/utils/reset_doc.py +++ b/frappe/utils/reset_doc.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe import json, os from frappe.modules import scrub, get_module_path, utils @@ -7,7 +7,7 @@ from frappe.custom.doctype.property_setter.property_setter import make_property_ from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.core.page.permission_manager.permission_manager import get_standard_permissions from frappe.permissions import setup_custom_perms -from six.moves.urllib.request import urlopen +from urllib.request import urlopen branch = 'develop' diff --git a/frappe/utils/response.py b/frappe/utils/response.py index b152d69d8d..84f30f22fd 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import json import datetime import decimal @@ -18,8 +17,7 @@ from werkzeug.wsgi import wrap_file from werkzeug.wrappers import Response from werkzeug.exceptions import NotFound, Forbidden from frappe.utils import cint -from six import text_type -from six.moves.urllib.parse import quote +from urllib.parse import quote from frappe.core.doctype.access_log.access_log import make_access_log @@ -125,13 +123,13 @@ def json_handler(obj): import collections.abc if isinstance(obj, (datetime.date, datetime.timedelta, datetime.datetime)): - return text_type(obj) + return str(obj) elif isinstance(obj, decimal.Decimal): return float(obj) elif isinstance(obj, LocalProxy): - return text_type(obj) + return str(obj) elif isinstance(obj, frappe.model.document.BaseDocument): doc = obj.as_dict(no_nulls=True) diff --git a/frappe/utils/scheduler.py b/frappe/utils/scheduler.py index 749a41682f..0bfc50eab5 100755 --- a/frappe/utils/scheduler.py +++ b/frappe/utils/scheduler.py @@ -7,8 +7,6 @@ Events: monthly weekly """ -# imports - compatibility imports -from __future__ import print_function, unicode_literals # imports - standard imports import os diff --git a/frappe/utils/testutils.py b/frappe/utils/testutils.py index 4af3ad647c..c451d090f1 100644 --- a/frappe/utils/testutils.py +++ b/frappe/utils/testutils.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe def add_custom_field(doctype, fieldname, fieldtype='Data', options=None): diff --git a/frappe/utils/user.py b/frappe/utils/user.py index 61b698db9f..fe768c28c5 100755 --- a/frappe/utils/user.py +++ b/frappe/utils/user.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, json from frappe import _dict import frappe.share diff --git a/frappe/utils/verified_command.py b/frappe/utils/verified_command.py index 971c0a243c..4b038b88c3 100644 --- a/frappe/utils/verified_command.py +++ b/frappe/utils/verified_command.py @@ -1,20 +1,17 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals import hmac, hashlib -from six.moves.urllib.parse import urlencode +from urllib.parse import urlencode from frappe import _ import frappe import frappe.utils -from six import string_types def get_signed_params(params): """Sign a url by appending `&_signature=xxxxx` to given params (string or dict). :param params: String or dict of parameters.""" - if not isinstance(params, string_types): + if not isinstance(params, str): params = urlencode(params) signature = hmac.new(params.encode(), digestmod=hashlib.md5) diff --git a/frappe/website/context.py b/frappe/website/context.py index c898d39869..8278edb958 100644 --- a/frappe/website/context.py +++ b/frappe/website/context.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, os, json from frappe.website.doctype.website_settings.website_settings import get_website_settings diff --git a/frappe/website/doctype/__init__.py b/frappe/website/doctype/__init__.py index baffc48825..8b13789179 100644 --- a/frappe/website/doctype/__init__.py +++ b/frappe/website/doctype/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/website/doctype/about_us_settings/about_us_settings.py b/frappe/website/doctype/about_us_settings/about_us_settings.py index 5b93cdcede..938a8de1b9 100644 --- a/frappe/website/doctype/about_us_settings/about_us_settings.py +++ b/frappe/website/doctype/about_us_settings/about_us_settings.py @@ -3,17 +3,16 @@ # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document class AboutUsSettings(Document): - + def on_update(self): from frappe.website.render import clear_cache clear_cache("about") - + def get_args(): obj = frappe.get_doc("About Us Settings") return { diff --git a/frappe/website/doctype/about_us_settings/test_about_us_settings.py b/frappe/website/doctype/about_us_settings/test_about_us_settings.py index 85173a20e5..ab47505fb1 100644 --- a/frappe/website/doctype/about_us_settings/test_about_us_settings.py +++ b/frappe/website/doctype/about_us_settings/test_about_us_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/website/doctype/about_us_team_member/about_us_team_member.py b/frappe/website/doctype/about_us_team_member/about_us_team_member.py index a872a6e5e2..864b6a3e81 100644 --- a/frappe/website/doctype/about_us_team_member/about_us_team_member.py +++ b/frappe/website/doctype/about_us_team_member/about_us_team_member.py @@ -3,7 +3,6 @@ # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/website/doctype/blog_category/blog_category.py b/frappe/website/doctype/blog_category/blog_category.py index 375ba5b6a3..91b213744f 100644 --- a/frappe/website/doctype/blog_category/blog_category.py +++ b/frappe/website/doctype/blog_category/blog_category.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals from frappe.website.website_generator import WebsiteGenerator from frappe.website.render import clear_cache diff --git a/frappe/website/doctype/blog_category/test_blog_category.py b/frappe/website/doctype/blog_category/test_blog_category.py index fe8f4544cd..d9093e76f0 100644 --- a/frappe/website/doctype/blog_category/test_blog_category.py +++ b/frappe/website/doctype/blog_category/test_blog_category.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/website/doctype/blog_post/blog_post.py b/frappe/website/doctype/blog_post/blog_post.py index bfccc0bbc7..b39b7c7d04 100644 --- a/frappe/website/doctype/blog_post/blog_post.py +++ b/frappe/website/doctype/blog_post/blog_post.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe from frappe import _ from frappe.website.website_generator import WebsiteGenerator diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py index 9ecac07ee5..99acf2b710 100644 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ b/frappe/website/doctype/blog_post/test_blog_post.py @@ -1,6 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import unittest from bs4 import BeautifulSoup diff --git a/frappe/website/doctype/blog_settings/blog_settings.py b/frappe/website/doctype/blog_settings/blog_settings.py index 0ed98b9b87..01e18931a3 100644 --- a/frappe/website/doctype/blog_settings/blog_settings.py +++ b/frappe/website/doctype/blog_settings/blog_settings.py @@ -3,13 +3,12 @@ # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document class BlogSettings(Document): - + def on_update(self): from frappe.website.render import clear_cache clear_cache("blog") diff --git a/frappe/website/doctype/blog_settings/test_blog_settings.py b/frappe/website/doctype/blog_settings/test_blog_settings.py index e4ddb85c4b..6b03ab88d9 100644 --- a/frappe/website/doctype/blog_settings/test_blog_settings.py +++ b/frappe/website/doctype/blog_settings/test_blog_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/website/doctype/blogger/blogger.py b/frappe/website/doctype/blogger/blogger.py index cbba6a98a5..77e7a6e7a1 100644 --- a/frappe/website/doctype/blogger/blogger.py +++ b/frappe/website/doctype/blogger/blogger.py @@ -3,7 +3,6 @@ # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ diff --git a/frappe/website/doctype/blogger/test_blogger.py b/frappe/website/doctype/blogger/test_blogger.py index 77ade561fb..f121859cbb 100644 --- a/frappe/website/doctype/blogger/test_blogger.py +++ b/frappe/website/doctype/blogger/test_blogger.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe test_records = frappe.get_test_records('Blogger') \ No newline at end of file diff --git a/frappe/website/doctype/color/color.py b/frappe/website/doctype/color/color.py index 245b9e9165..080e51e406 100644 --- a/frappe/website/doctype/color/color.py +++ b/frappe/website/doctype/color/color.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/website/doctype/color/test_color.py b/frappe/website/doctype/color/test_color.py index 2f2be331ad..99e4310a1c 100644 --- a/frappe/website/doctype/color/test_color.py +++ b/frappe/website/doctype/color/test_color.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/website/doctype/company_history/company_history.py b/frappe/website/doctype/company_history/company_history.py index 2c3c81a898..128aefe636 100644 --- a/frappe/website/doctype/company_history/company_history.py +++ b/frappe/website/doctype/company_history/company_history.py @@ -3,7 +3,6 @@ # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/website/doctype/contact_us_settings/contact_us_settings.py b/frappe/website/doctype/contact_us_settings/contact_us_settings.py index 24e9811a47..12e62f8403 100644 --- a/frappe/website/doctype/contact_us_settings/contact_us_settings.py +++ b/frappe/website/doctype/contact_us_settings/contact_us_settings.py @@ -3,7 +3,6 @@ # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/website/doctype/help_article/help_article.py b/frappe/website/doctype/help_article/help_article.py index fa26cfef99..139fced70a 100644 --- a/frappe/website/doctype/help_article/help_article.py +++ b/frappe/website/doctype/help_article/help_article.py @@ -1,7 +1,6 @@ # Copyright (c) 2013, Frappe and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.website.website_generator import WebsiteGenerator from frappe.utils import is_markdown, markdown, cint diff --git a/frappe/website/doctype/help_article/test_help_article.py b/frappe/website/doctype/help_article/test_help_article.py index e20a3a20e3..a1a0a59a24 100644 --- a/frappe/website/doctype/help_article/test_help_article.py +++ b/frappe/website/doctype/help_article/test_help_article.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/website/doctype/help_category/help_category.py b/frappe/website/doctype/help_category/help_category.py index 59df82b90c..912c1f568e 100644 --- a/frappe/website/doctype/help_category/help_category.py +++ b/frappe/website/doctype/help_category/help_category.py @@ -1,7 +1,6 @@ # Copyright (c) 2013, Frappe and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.website.website_generator import WebsiteGenerator from frappe.website.doctype.help_article.help_article import clear_cache diff --git a/frappe/website/doctype/help_category/test_help_category.py b/frappe/website/doctype/help_category/test_help_category.py index 9954e7424e..68fcbd2872 100644 --- a/frappe/website/doctype/help_category/test_help_category.py +++ b/frappe/website/doctype/help_category/test_help_category.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/website/doctype/personal_data_deletion_request/test_personal_data_deletion_request.py b/frappe/website/doctype/personal_data_deletion_request/test_personal_data_deletion_request.py index 1bc90daf1c..606e5aad34 100644 --- a/frappe/website/doctype/personal_data_deletion_request/test_personal_data_deletion_request.py +++ b/frappe/website/doctype/personal_data_deletion_request/test_personal_data_deletion_request.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from frappe.website.doctype.personal_data_deletion_request.personal_data_deletion_request import ( diff --git a/frappe/website/doctype/personal_data_deletion_step/personal_data_deletion_step.py b/frappe/website/doctype/personal_data_deletion_step/personal_data_deletion_step.py index 2a7451473d..7cd1277f6c 100644 --- a/frappe/website/doctype/personal_data_deletion_step/personal_data_deletion_step.py +++ b/frappe/website/doctype/personal_data_deletion_step/personal_data_deletion_step.py @@ -2,7 +2,6 @@ # Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/website/doctype/personal_data_download_request/personal_data_download_request.py b/frappe/website/doctype/personal_data_download_request/personal_data_download_request.py index 46faeb8539..9255adb8c2 100644 --- a/frappe/website/doctype/personal_data_download_request/personal_data_download_request.py +++ b/frappe/website/doctype/personal_data_download_request/personal_data_download_request.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe import json from frappe import _ diff --git a/frappe/website/doctype/personal_data_download_request/test_personal_data_download_request.py b/frappe/website/doctype/personal_data_download_request/test_personal_data_download_request.py index 6eb381e7ce..563b6b3843 100644 --- a/frappe/website/doctype/personal_data_download_request/test_personal_data_download_request.py +++ b/frappe/website/doctype/personal_data_download_request/test_personal_data_download_request.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest import json diff --git a/frappe/website/doctype/portal_menu_item/portal_menu_item.py b/frappe/website/doctype/portal_menu_item/portal_menu_item.py index 5fe8e9fcd7..5effb3a195 100644 --- a/frappe/website/doctype/portal_menu_item/portal_menu_item.py +++ b/frappe/website/doctype/portal_menu_item/portal_menu_item.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/website/doctype/portal_settings/portal_settings.py b/frappe/website/doctype/portal_settings/portal_settings.py index 1bfbc70d60..589656bbf0 100644 --- a/frappe/website/doctype/portal_settings/portal_settings.py +++ b/frappe/website/doctype/portal_settings/portal_settings.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/website/doctype/portal_settings/test_portal_settings.py b/frappe/website/doctype/portal_settings/test_portal_settings.py index 3dd4aa0c35..40f6eaac2c 100644 --- a/frappe/website/doctype/portal_settings/test_portal_settings.py +++ b/frappe/website/doctype/portal_settings/test_portal_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/website/doctype/social_link_settings/social_link_settings.py b/frappe/website/doctype/social_link_settings/social_link_settings.py index b382afac99..093a21ea10 100644 --- a/frappe/website/doctype/social_link_settings/social_link_settings.py +++ b/frappe/website/doctype/social_link_settings/social_link_settings.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/website/doctype/top_bar_item/__init__.py b/frappe/website/doctype/top_bar_item/__init__.py index baffc48825..8b13789179 100644 --- a/frappe/website/doctype/top_bar_item/__init__.py +++ b/frappe/website/doctype/top_bar_item/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/website/doctype/top_bar_item/top_bar_item.py b/frappe/website/doctype/top_bar_item/top_bar_item.py index 6cda8e2beb..14e579dae2 100644 --- a/frappe/website/doctype/top_bar_item/top_bar_item.py +++ b/frappe/website/doctype/top_bar_item/top_bar_item.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/website/doctype/web_form/test_web_form.py b/frappe/website/doctype/web_form/test_web_form.py index 78f7fd6337..5ea18ed7cc 100644 --- a/frappe/website/doctype/web_form/test_web_form.py +++ b/frappe/website/doctype/web_form/test_web_form.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest, json diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index f78aaac934..213c2a84dd 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -1,14 +1,8 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import json import os - -from six import iteritems -from six.moves.urllib.parse import urlencode - import frappe from frappe import _, scrub from frappe.core.doctype.file.file import get_max_file_size, remove_file_by_url @@ -109,9 +103,7 @@ class WebForm(WebsiteGenerator): # py if not os.path.exists(path + '.py'): with open(path + '.py', 'w') as f: - f.write("""from __future__ import unicode_literals - -import frappe + f.write("""import frappe def get_context(context): # do your magic here @@ -219,7 +211,7 @@ def get_context(context): from decimal import Decimal if amount is None or Decimal(amount) <= 0: return frappe.utils.get_url(self.success_url or self.route) - + payment_details = { "amount": amount, "title": title, diff --git a/frappe/website/doctype/web_form_field/web_form_field.py b/frappe/website/doctype/web_form_field/web_form_field.py index ec3c3fbea4..44005d27e3 100644 --- a/frappe/website/doctype/web_form_field/web_form_field.py +++ b/frappe/website/doctype/web_form_field/web_form_field.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/website/doctype/web_page/__init__.py b/frappe/website/doctype/web_page/__init__.py index baffc48825..8b13789179 100644 --- a/frappe/website/doctype/web_page/__init__.py +++ b/frappe/website/doctype/web_page/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/website/doctype/web_page/test_web_page.py b/frappe/website/doctype/web_page/test_web_page.py index 7a2ddc6961..5dc2c9ff45 100644 --- a/frappe/website/doctype/web_page/test_web_page.py +++ b/frappe/website/doctype/web_page/test_web_page.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import unittest import frappe from frappe.website.router import resolve_route diff --git a/frappe/website/doctype/web_page/web_page.py b/frappe/website/doctype/web_page/web_page.py index cce00564ff..cea14d3bbe 100644 --- a/frappe/website/doctype/web_page/web_page.py +++ b/frappe/website/doctype/web_page/web_page.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import print_function, unicode_literals - import re import requests diff --git a/frappe/website/doctype/web_page_block/web_page_block.py b/frappe/website/doctype/web_page_block/web_page_block.py index 7561e55aa9..77e7dec155 100644 --- a/frappe/website/doctype/web_page_block/web_page_block.py +++ b/frappe/website/doctype/web_page_block/web_page_block.py @@ -2,8 +2,6 @@ # 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 diff --git a/frappe/website/doctype/web_page_view/test_web_page_view.py b/frappe/website/doctype/web_page_view/test_web_page_view.py index d51727ec68..c3d05fcaab 100644 --- a/frappe/website/doctype/web_page_view/test_web_page_view.py +++ b/frappe/website/doctype/web_page_view/test_web_page_view.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest 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 d9794c6d99..e679b58043 100644 --- a/frappe/website/doctype/web_page_view/web_page_view.py +++ b/frappe/website/doctype/web_page_view/web_page_view.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/website/doctype/web_template/test_web_template.py b/frappe/website/doctype/web_template/test_web_template.py index b4ea9e2f97..45e35c4626 100644 --- a/frappe/website/doctype/web_template/test_web_template.py +++ b/frappe/website/doctype/web_template/test_web_template.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from bs4 import BeautifulSoup diff --git a/frappe/website/doctype/web_template/web_template.py b/frappe/website/doctype/web_template/web_template.py index 2fd5bfa179..891a0c3679 100644 --- a/frappe/website/doctype/web_template/web_template.py +++ b/frappe/website/doctype/web_template/web_template.py @@ -2,8 +2,6 @@ # Copyright (c) 2020, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import os from shutil import rmtree diff --git a/frappe/website/doctype/web_template_field/test_web_template_field.py b/frappe/website/doctype/web_template_field/test_web_template_field.py index 40f5d7a1cc..5628da37db 100644 --- a/frappe/website/doctype/web_template_field/test_web_template_field.py +++ b/frappe/website/doctype/web_template_field/test_web_template_field.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/website/doctype/web_template_field/web_template_field.py b/frappe/website/doctype/web_template_field/web_template_field.py index 8c25b619b6..8270ac7845 100644 --- a/frappe/website/doctype/web_template_field/web_template_field.py +++ b/frappe/website/doctype/web_template_field/web_template_field.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/website/doctype/website_meta_tag/website_meta_tag.py b/frappe/website/doctype/website_meta_tag/website_meta_tag.py index 2972e6d317..0856440663 100644 --- a/frappe/website/doctype/website_meta_tag/website_meta_tag.py +++ b/frappe/website/doctype/website_meta_tag/website_meta_tag.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/website/doctype/website_route_meta/test_website_route_meta.py b/frappe/website/doctype/website_route_meta/test_website_route_meta.py index 0ccedb0ca4..1f927abafc 100644 --- a/frappe/website/doctype/website_route_meta/test_website_route_meta.py +++ b/frappe/website/doctype/website_route_meta/test_website_route_meta.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from frappe.utils import set_request diff --git a/frappe/website/doctype/website_route_meta/website_route_meta.py b/frappe/website/doctype/website_route_meta/website_route_meta.py index f31784e9bc..78d440a1a1 100644 --- a/frappe/website/doctype/website_route_meta/website_route_meta.py +++ b/frappe/website/doctype/website_route_meta/website_route_meta.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class WebsiteRouteMeta(Document): diff --git a/frappe/website/doctype/website_route_redirect/website_route_redirect.py b/frappe/website/doctype/website_route_redirect/website_route_redirect.py index 7255008dd0..f721ab81cb 100644 --- a/frappe/website/doctype/website_route_redirect/website_route_redirect.py +++ b/frappe/website/doctype/website_route_redirect/website_route_redirect.py @@ -2,7 +2,6 @@ # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/frappe/website/doctype/website_script/website_script.py b/frappe/website/doctype/website_script/website_script.py index 5648c27fd6..111beeaf2a 100644 --- a/frappe/website/doctype/website_script/website_script.py +++ b/frappe/website/doctype/website_script/website_script.py @@ -3,7 +3,6 @@ # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/website/doctype/website_settings/__init__.py b/frappe/website/doctype/website_settings/__init__.py index baffc48825..8b13789179 100644 --- a/frappe/website/doctype/website_settings/__init__.py +++ b/frappe/website/doctype/website_settings/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/website/doctype/website_settings/test_website_settings.py b/frappe/website/doctype/website_settings/test_website_settings.py index 9eca957713..f7bf9d3bf1 100644 --- a/frappe/website/doctype/website_settings/test_website_settings.py +++ b/frappe/website/doctype/website_settings/test_website_settings.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - # import frappe import unittest diff --git a/frappe/website/doctype/website_settings/website_settings.py b/frappe/website/doctype/website_settings/website_settings.py index f7f22aa2df..03a1aecc5f 100644 --- a/frappe/website/doctype/website_settings/website_settings.py +++ b/frappe/website/doctype/website_settings/website_settings.py @@ -1,12 +1,11 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import get_request_site_address, encode from frappe.model.document import Document -from six.moves.urllib.parse import quote +from urllib.parse import quote from frappe.website.router import resolve_route from frappe.website.doctype.website_theme.website_theme import add_website_theme from frappe.integrations.doctype.google_settings.google_settings import get_auth_url diff --git a/frappe/website/doctype/website_sidebar/test_website_sidebar.py b/frappe/website/doctype/website_sidebar/test_website_sidebar.py index d49ea84a35..bfaee51169 100644 --- a/frappe/website/doctype/website_sidebar/test_website_sidebar.py +++ b/frappe/website/doctype/website_sidebar/test_website_sidebar.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/website/doctype/website_sidebar/website_sidebar.py b/frappe/website/doctype/website_sidebar/website_sidebar.py index 87daf47680..f8a431ac15 100644 --- a/frappe/website/doctype/website_sidebar/website_sidebar.py +++ b/frappe/website/doctype/website_sidebar/website_sidebar.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/website/doctype/website_sidebar_item/website_sidebar_item.py b/frappe/website/doctype/website_sidebar_item/website_sidebar_item.py index 83e987ed09..f4d88e3259 100644 --- a/frappe/website/doctype/website_sidebar_item/website_sidebar_item.py +++ b/frappe/website/doctype/website_sidebar_item/website_sidebar_item.py @@ -2,7 +2,6 @@ # Copyright (c) 2015, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/website/doctype/website_slideshow/test_website_slideshow.py b/frappe/website/doctype/website_slideshow/test_website_slideshow.py index 9eccc71ad3..5e3ca3c617 100644 --- a/frappe/website/doctype/website_slideshow/test_website_slideshow.py +++ b/frappe/website/doctype/website_slideshow/test_website_slideshow.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest diff --git a/frappe/website/doctype/website_slideshow/website_slideshow.py b/frappe/website/doctype/website_slideshow/website_slideshow.py index 90f62d1bb1..d31adbf986 100644 --- a/frappe/website/doctype/website_slideshow/website_slideshow.py +++ b/frappe/website/doctype/website_slideshow/website_slideshow.py @@ -3,7 +3,6 @@ # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ diff --git a/frappe/website/doctype/website_slideshow_item/website_slideshow_item.py b/frappe/website/doctype/website_slideshow_item/website_slideshow_item.py index 3b809a7f65..beaa9ecf9e 100644 --- a/frappe/website/doctype/website_slideshow_item/website_slideshow_item.py +++ b/frappe/website/doctype/website_slideshow_item/website_slideshow_item.py @@ -3,7 +3,6 @@ # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/website/doctype/website_theme/test_website_theme.py b/frappe/website/doctype/website_theme/test_website_theme.py index 11956ba3d9..4ff4e269da 100644 --- a/frappe/website/doctype/website_theme/test_website_theme.py +++ b/frappe/website/doctype/website_theme/test_website_theme.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import os import frappe import unittest diff --git a/frappe/website/doctype/website_theme/website_theme.py b/frappe/website/doctype/website_theme/website_theme.py index dbc54782e1..248c4bc176 100644 --- a/frappe/website/doctype/website_theme/website_theme.py +++ b/frappe/website/doctype/website_theme/website_theme.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document diff --git a/frappe/website/doctype/website_theme_ignore_app/website_theme_ignore_app.py b/frappe/website/doctype/website_theme_ignore_app/website_theme_ignore_app.py index e91e91a68f..ec232c0c79 100644 --- a/frappe/website/doctype/website_theme_ignore_app/website_theme_ignore_app.py +++ b/frappe/website/doctype/website_theme_ignore_app/website_theme_ignore_app.py @@ -2,7 +2,6 @@ # 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 diff --git a/frappe/website/page/__init__.py b/frappe/website/page/__init__.py index baffc48825..8b13789179 100644 --- a/frappe/website/page/__init__.py +++ b/frappe/website/page/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/website/purifycss.py b/frappe/website/purifycss.py index 39e989db6e..bac68b881b 100644 --- a/frappe/website/purifycss.py +++ b/frappe/website/purifycss.py @@ -1,4 +1,3 @@ -from __future__ import print_function, unicode_literals ''' Check for unused CSS Classes diff --git a/frappe/website/redirect.py b/frappe/website/redirect.py index 73e3c21727..3194895d95 100644 --- a/frappe/website/redirect.py +++ b/frappe/website/redirect.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import re, frappe def resolve_redirect(path): diff --git a/frappe/website/render.py b/frappe/website/render.py index 71382c18ff..2b4a5e2dab 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ import frappe.sessions @@ -9,8 +8,6 @@ from frappe.utils import cstr import os, mimetypes, json import re -import six -from six import iteritems from werkzeug.wrappers import Response from werkzeug.routing import Rule from werkzeug.wsgi import wrap_file @@ -131,7 +128,7 @@ def build_response(path, data, http_status_code, headers=None): add_preload_headers(response) if headers: - for key, val in iteritems(headers): + for key, val in headers.items(): response.headers[key] = val.encode("ascii", errors="xmlcharrefreplace") return response @@ -321,7 +318,7 @@ def clear_cache(path=None): frappe.get_attr(method)(path) def render_403(e, pathname): - frappe.local.message = cstr(e.message if six.PY2 else e) + frappe.local.message = cstr(e) frappe.local.message_title = _("Not Permitted") frappe.local.response['context'] = dict( indicator_color = 'red', diff --git a/frappe/website/report/website_analytics/website_analytics.py b/frappe/website/report/website_analytics/website_analytics.py index b7e0aab27b..d141972679 100644 --- a/frappe/website/report/website_analytics/website_analytics.py +++ b/frappe/website/report/website_analytics/website_analytics.py @@ -1,7 +1,6 @@ # Copyright (c) 2013, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals import frappe from datetime import datetime from frappe.utils import getdate diff --git a/frappe/website/router.py b/frappe/website/router.py index f3518e179e..899f2722ab 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import io import os import re diff --git a/frappe/website/utils.py b/frappe/website/utils.py index 0738c836f3..aa98595e2d 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -1,6 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - import re import os import frappe diff --git a/frappe/website/web_form/request_data/request_data.py b/frappe/website/web_form/request_data/request_data.py index 07616644d3..1224e3f095 100644 --- a/frappe/website/web_form/request_data/request_data.py +++ b/frappe/website/web_form/request_data/request_data.py @@ -1,4 +1,2 @@ -from __future__ import unicode_literals - def get_context(context): pass diff --git a/frappe/website/web_form/request_to_delete_data/request_to_delete_data.py b/frappe/website/web_form/request_to_delete_data/request_to_delete_data.py index 74908f4119..75b748913a 100644 --- a/frappe/website/web_form/request_to_delete_data/request_to_delete_data.py +++ b/frappe/website/web_form/request_to_delete_data/request_to_delete_data.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - def get_context(context): # do your magic here pass \ No newline at end of file diff --git a/frappe/website/website_generator.py b/frappe/website/website_generator.py index fc08abeed9..351f2f1832 100644 --- a/frappe/website/website_generator.py +++ b/frappe/website/website_generator.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.website.utils import cleanup_page_name diff --git a/frappe/workflow/doctype/workflow/test_workflow.py b/frappe/workflow/doctype/workflow/test_workflow.py index 9ad0562a86..9bafd377fc 100644 --- a/frappe/workflow/doctype/workflow/test_workflow.py +++ b/frappe/workflow/doctype/workflow/test_workflow.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals - import frappe import unittest from frappe.utils import random_string diff --git a/frappe/workflow/doctype/workflow/workflow.py b/frappe/workflow/doctype/workflow/workflow.py index b4d7293fed..4959ee5639 100644 --- a/frappe/workflow/doctype/workflow/workflow.py +++ b/frappe/workflow/doctype/workflow/workflow.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ diff --git a/frappe/workflow/doctype/workflow_action/workflow_action.py b/frappe/workflow/doctype/workflow_action/workflow_action.py index 8c10af8251..b70ffb2406 100644 --- a/frappe/workflow/doctype/workflow_action/workflow_action.py +++ b/frappe/workflow/doctype/workflow_action/workflow_action.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils.background_jobs import enqueue diff --git a/frappe/workflow/doctype/workflow_action_master/workflow_action_master.py b/frappe/workflow/doctype/workflow_action_master/workflow_action_master.py index df1123f786..b438a708dc 100644 --- a/frappe/workflow/doctype/workflow_action_master/workflow_action_master.py +++ b/frappe/workflow/doctype/workflow_action_master/workflow_action_master.py @@ -2,7 +2,6 @@ # Copyright (c) 2018, Frappe Technologies and contributors # For license information, please see license.txt -from __future__ import unicode_literals from frappe.model.document import Document class WorkflowActionMaster(Document): diff --git a/frappe/workflow/doctype/workflow_document_state/workflow_document_state.py b/frappe/workflow/doctype/workflow_document_state/workflow_document_state.py index 6e2ac212ae..ae7372f5c2 100644 --- a/frappe/workflow/doctype/workflow_document_state/workflow_document_state.py +++ b/frappe/workflow/doctype/workflow_document_state/workflow_document_state.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/workflow/doctype/workflow_state/test_workflow_state.py b/frappe/workflow/doctype/workflow_state/test_workflow_state.py index 63a2f9b151..eeb0807d52 100644 --- a/frappe/workflow/doctype/workflow_state/test_workflow_state.py +++ b/frappe/workflow/doctype/workflow_state/test_workflow_state.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe test_records = frappe.get_test_records('Workflow State') \ No newline at end of file diff --git a/frappe/workflow/doctype/workflow_state/workflow_state.py b/frappe/workflow/doctype/workflow_state/workflow_state.py index c446a4445e..40f7d370dd 100644 --- a/frappe/workflow/doctype/workflow_state/workflow_state.py +++ b/frappe/workflow/doctype/workflow_state/workflow_state.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/workflow/doctype/workflow_transition/workflow_transition.py b/frappe/workflow/doctype/workflow_transition/workflow_transition.py index 4c07d700c6..e49a90f9d6 100644 --- a/frappe/workflow/doctype/workflow_transition/workflow_transition.py +++ b/frappe/workflow/doctype/workflow_transition/workflow_transition.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.model.document import Document diff --git a/frappe/www/404.py b/frappe/www/404.py index c9de234743..f064a66c17 100644 --- a/frappe/www/404.py +++ b/frappe/www/404.py @@ -1,4 +1,2 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - diff --git a/frappe/www/__init__.py b/frappe/www/__init__.py index baffc48825..8b13789179 100644 --- a/frappe/www/__init__.py +++ b/frappe/www/__init__.py @@ -1 +1 @@ -from __future__ import unicode_literals + diff --git a/frappe/www/about.py b/frappe/www/about.py index e7a7be23e8..05fe34b162 100644 --- a/frappe/www/about.py +++ b/frappe/www/about.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe sitemap = 1 diff --git a/frappe/www/app.py b/frappe/www/app.py index 5f19712cd3..b0fa19df9b 100644 --- a/frappe/www/app.py +++ b/frappe/www/app.py @@ -1,8 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function - no_cache = 1 base_template_path = "templates/www/app.html" diff --git a/frappe/www/complete_signup.py b/frappe/www/complete_signup.py index 00c499e786..0e57cb68c3 100644 --- a/frappe/www/complete_signup.py +++ b/frappe/www/complete_signup.py @@ -1,5 +1,3 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - diff --git a/frappe/www/contact.py b/frappe/www/contact.py index d976350075..53b0666be8 100644 --- a/frappe/www/contact.py +++ b/frappe/www/contact.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe from frappe.utils import now from frappe import _ diff --git a/frappe/www/error.py b/frappe/www/error.py index 161038373d..dcbfb38e1f 100644 --- a/frappe/www/error.py +++ b/frappe/www/error.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt - -from __future__ import unicode_literals, print_function import frappe no_cache = 1 diff --git a/frappe/www/list.py b/frappe/www/list.py index fc4dc602c3..881aaf085b 100644 --- a/frappe/www/list.py +++ b/frappe/www/list.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe, json from frappe.utils import cint, quoted from frappe.website.render import resolve_path diff --git a/frappe/www/login.py b/frappe/www/login.py index 1ce25a81d9..6542b29d42 100644 --- a/frappe/www/login.py +++ b/frappe/www/login.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe import frappe.utils from frappe.utils.oauth import get_oauth2_authorize_url, get_oauth_keys, login_via_oauth2, login_via_oauth2_id_token, login_oauth_user as _login_oauth_user, redirect_post_login diff --git a/frappe/www/me.py b/frappe/www/me.py index 2191f28bca..f8356b6d07 100644 --- a/frappe/www/me.py +++ b/frappe/www/me.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe import _ import frappe.www.list diff --git a/frappe/www/message.py b/frappe/www/message.py index ea45b9c4b5..3658261503 100644 --- a/frappe/www/message.py +++ b/frappe/www/message.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.utils import strip_html_tags diff --git a/frappe/www/printview.py b/frappe/www/printview.py index 132d863375..6b879e2452 100644 --- a/frappe/www/printview.py +++ b/frappe/www/printview.py @@ -1,15 +1,12 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe, os, copy, json, re from frappe import _ from frappe.modules import get_doc_path from frappe.core.doctype.access_log.access_log import make_access_log from frappe.utils import cint, sanitize_html, strip_html -from six import string_types no_cache = 1 @@ -73,7 +70,7 @@ def get_rendered_template(doc, name=None, print_format=None, meta=None, print_settings = frappe.get_single("Print Settings").as_dict() print_settings.update(settings or {}) - if isinstance(no_letterhead, string_types): + if isinstance(no_letterhead, str): no_letterhead = cint(no_letterhead) elif no_letterhead is None: @@ -186,10 +183,10 @@ def get_html_and_style(doc, name=None, print_format=None, meta=None, settings=None, templates=None): """Returns `html` and `style` of print format, used in PDF etc""" - if isinstance(doc, string_types) and isinstance(name, string_types): + if isinstance(doc, str) and isinstance(name, str): doc = frappe.get_doc(doc, name) - if isinstance(doc, string_types): + if isinstance(doc, str): doc = frappe.get_doc(json.loads(doc)) print_format = get_print_format_doc(print_format, meta=meta or frappe.get_meta(doc.doctype)) @@ -211,10 +208,10 @@ def get_html_and_style(doc, name=None, print_format=None, meta=None, def get_rendered_raw_commands(doc, name=None, print_format=None, meta=None, lang=None): """Returns Rendered Raw Commands of print format, used to send directly to printer""" - if isinstance(doc, string_types) and isinstance(name, string_types): + if isinstance(doc, str) and isinstance(name, str): doc = frappe.get_doc(doc, name) - if isinstance(doc, string_types): + if isinstance(doc, str): doc = frappe.get_doc(json.loads(doc)) print_format = get_print_format_doc(print_format, meta=meta or frappe.get_meta(doc.doctype)) @@ -380,7 +377,7 @@ def has_value(df, doc): if value in (None, ""): return False - elif isinstance(value, string_types) and not strip_html(value).strip(): + elif isinstance(value, str) and not strip_html(value).strip(): if df.fieldtype in ["Text", "Text Editor"]: return True @@ -480,7 +477,7 @@ def column_has_value(data, fieldname, col_df): for row in data: value = row.get(fieldname) if value: - if isinstance(value, string_types): + if isinstance(value, str): if strip_html(value).strip(): has_value = True break diff --git a/frappe/www/profile.py b/frappe/www/profile.py index e1badf10e5..7db7beb7d8 100644 --- a/frappe/www/profile.py +++ b/frappe/www/profile.py @@ -1,8 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - no_cache = 1 diff --git a/frappe/www/qrcode.py b/frappe/www/qrcode.py index ddee635ded..37ee204925 100644 --- a/frappe/www/qrcode.py +++ b/frappe/www/qrcode.py @@ -1,11 +1,9 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe from frappe import _ -from six.moves.urllib.parse import parse_qsl +from urllib.parse import parse_qsl from frappe.twofactor import get_qr_svg_code def get_context(context): diff --git a/frappe/www/robots.py b/frappe/www/robots.py index 6a7c74cf41..487f378415 100644 --- a/frappe/www/robots.py +++ b/frappe/www/robots.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe base_template_path = "templates/www/robots.txt" diff --git a/frappe/www/rss.py b/frappe/www/rss.py index a389304e5e..033c341137 100644 --- a/frappe/www/rss.py +++ b/frappe/www/rss.py @@ -1,10 +1,9 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.utils import escape_html, get_request_site_address, now, cstr -from six.moves.urllib.parse import quote, urljoin +from urllib.parse import quote, urljoin no_cache = 1 base_template_path = "templates/www/rss.xml" diff --git a/frappe/www/search.py b/frappe/www/search.py index a8bb1a5294..292c9e532f 100644 --- a/frappe/www/search.py +++ b/frappe/www/search.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils.global_search import web_search from html2text import html2text diff --git a/frappe/www/sitemap.py b/frappe/www/sitemap.py index f8f03c45f8..9eb36e5335 100644 --- a/frappe/www/sitemap.py +++ b/frappe/www/sitemap.py @@ -1,14 +1,11 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - import frappe from frappe.model.document import get_controller from frappe.utils import get_datetime, nowdate, get_url from frappe.website.router import get_pages, get_all_page_context_from_doctypes -from six import iteritems -from six.moves.urllib.parse import quote, urljoin +from urllib.parse import quote no_cache = 1 base_template_path = "templates/www/sitemap.xml" @@ -22,14 +19,14 @@ def get_context(context): host = frappe.utils.get_host_name_from_request() links = [] - for route, page in iteritems(get_pages()): + for route, page in get_pages().items(): if page.sitemap: links.append({ "loc": get_url(quote(page.name.encode("utf-8"))), "lastmod": nowdate() }) - for route, data in iteritems(get_public_pages_from_doctypes()): + for route, data in get_public_pages_from_doctypes().items: links.append({ "loc": get_url(quote((route or "").encode("utf-8"))), "lastmod": get_datetime(data.get("modified")).strftime("%Y-%m-%d") diff --git a/frappe/www/third_party_apps.py b/frappe/www/third_party_apps.py index 77928af511..2668923449 100644 --- a/frappe/www/third_party_apps.py +++ b/frappe/www/third_party_apps.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe import _ import frappe.www.list diff --git a/frappe/www/unsubscribe.py b/frappe/www/unsubscribe.py index 8bf0741fef..4ebaa35991 100644 --- a/frappe/www/unsubscribe.py +++ b/frappe/www/unsubscribe.py @@ -1,4 +1,4 @@ -from __future__ import unicode_literals + import frappe from frappe.utils.verified_command import verify_request from frappe.email.doctype.newsletter.newsletter import confirmed_unsubscribe diff --git a/frappe/www/update_password.py b/frappe/www/update_password.py index f81098b916..4c7d3d5401 100644 --- a/frappe/www/update_password.py +++ b/frappe/www/update_password.py @@ -1,7 +1,5 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals - from frappe import _ no_cache = 1 diff --git a/frappe/www/website_script.py b/frappe/www/website_script.py index 0bb5a8a80b..915ef23367 100644 --- a/frappe/www/website_script.py +++ b/frappe/www/website_script.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -from __future__ import unicode_literals import frappe from frappe.utils import strip from frappe.website.doctype.website_theme.website_theme import get_active_theme diff --git a/setup.py b/setup.py index 515e9448c2..f4f826d6b1 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - # imports - standard imports import os, shutil from distutils.command.clean import clean as Clean From d236a931693fccc0a59852cf18833afe24ce09d9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 26 May 2021 15:45:34 +0530 Subject: [PATCH 124/495] chore: Replacing and updating bleach source list * The library bleach-whitelist was deprecated and renamed to bleach-allowlist. * Updated the usages and requirements for the same. --- frappe/utils/html_utils.py | 10 ++++++---- requirements.txt | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/frappe/utils/html_utils.py b/frappe/utils/html_utils.py index 24def07c63..ccb374fbcc 100644 --- a/frappe/utils/html_utils.py +++ b/frappe/utils/html_utils.py @@ -1,8 +1,10 @@ - -import frappe import json import re -import bleach_whitelist.bleach_whitelist as bleach_whitelist + +from bleach_allowlist import bleach_allowlist + +import frappe + def clean_html(html): import bleach @@ -71,7 +73,7 @@ def sanitize_html(html, linkify=False): tags = (acceptable_elements + svg_elements + mathml_elements + ["html", "head", "meta", "link", "body", "style", "o:p"]) attributes = {"*": acceptable_attributes, 'svg': svg_attributes} - styles = bleach_whitelist.all_styles + styles = bleach_allowlist.all_styles strip_comments = False # returns html with escaped tags, escaped orphan >, <, etc. diff --git a/requirements.txt b/requirements.txt index 769d8c3e7b..b3bfc12567 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ Babel~=2.9.0 beautifulsoup4~=4.9.3 -bleach-whitelist~=0.0.11 +bleach-allowlist~=1.0.3 bleach~=3.3.0 boto3~=1.17.53 braintree~=4.8.0 From 1acb2ddd2f7b671816f83790f5b2a5c7231c38d2 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 26 May 2021 15:54:36 +0530 Subject: [PATCH 125/495] fix: Use context managers to stream data While executing git commands in the shell via Frappe processes, use context managers to ensure files get closed after usage. This fixes the ResourceWarning errors due to unclosed files. --- frappe/utils/change_log.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frappe/utils/change_log.py b/frappe/utils/change_log.py index 6cb71c6ac5..ddd11265eb 100644 --- a/frappe/utils/change_log.py +++ b/frappe/utils/change_log.py @@ -118,9 +118,9 @@ def get_versions(): def get_app_branch(app): '''Returns branch of an app''' try: - null_stream = open(os.devnull, 'wb') - result = subprocess.check_output('cd ../apps/{0} && git rev-parse --abbrev-ref HEAD'.format(app), - shell=True, stdin=null_stream, stderr=null_stream) + with open(os.devnull, 'wb') as null_stream: + result = subprocess.check_output(f'cd ../apps/{app} && git rev-parse --abbrev-ref HEAD', + shell=True, stdin=null_stream, stderr=null_stream) result = safe_decode(result) result = result.strip() return result @@ -129,9 +129,9 @@ def get_app_branch(app): def get_app_last_commit_ref(app): try: - null_stream = open(os.devnull, 'wb') - result = subprocess.check_output('cd ../apps/{0} && git rev-parse HEAD --short 7'.format(app), - shell=True, stdin=null_stream, stderr=null_stream) + with open(os.devnull, 'wb') as null_stream: + result = subprocess.check_output(f'cd ../apps/{app} && git rev-parse HEAD --short 7', + shell=True, stdin=null_stream, stderr=null_stream) result = safe_decode(result) result = result.strip() return result From 1e9b0cf38611f7358d1e920352992ce6c2dbb7f7 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 26 May 2021 16:02:27 +0530 Subject: [PATCH 126/495] fix: Use dict.items instead of six.iteritems --- frappe/utils/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index cb02035299..e776d9945a 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -618,7 +618,7 @@ def get_installed_apps_info(): out = [] from frappe.utils.change_log import get_versions - for app, version_details in iteritems(get_versions()): + for app, version_details in get_versions().items(): out.append({ 'app_name': app, 'version': version_details.get('branch_version') or version_details.get('version'), From feea2f3c44dd653efd106476777add1975fbe3ae Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 26 May 2021 16:32:41 +0530 Subject: [PATCH 127/495] fix: Use raw string to avoid invalid escape sequence errors --- .github/helper/roulette.py | 2 +- frappe/utils/__init__.py | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/helper/roulette.py b/.github/helper/roulette.py index ba775d6794..ea4f07b9f7 100644 --- a/.github/helper/roulette.py +++ b/.github/helper/roulette.py @@ -18,7 +18,7 @@ def is_js(file): return file.endswith("js") def is_docs(file): - regex = re.compile('\.(md|png|jpg|jpeg)$|^.github|LICENSE') + regex = re.compile(r'\.(md|png|jpg|jpeg)$|^.github|LICENSE') return bool(regex.search(file)) diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index e776d9945a..94eed0b2bb 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -117,7 +117,10 @@ def validate_email_address(email_str, throw=False): else: email_id = extract_email_id(e) - match = re.match("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", email_id.lower()) if email_id else None + match = re.match( + r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", + email_id.lower() + ) if email_id else None if not match: _valid = False @@ -287,7 +290,7 @@ def remove_blanks(d): def strip_html_tags(text): """Remove html tags from text""" - return re.sub("\<[^>]*\>", "", text) + return re.sub(r"\<[^>]*\>", "", text) def get_file_timestamp(fn): """ @@ -499,7 +502,7 @@ def is_markdown(text): elif "" in text: return False else: - return not re.search("|", text) + return not re.search(r"|", text) def get_sites(sites_path=None): if not sites_path: @@ -609,7 +612,7 @@ def check_format(email_id): def get_name_from_email_string(email_string, email_id, name): name = email_string.replace(email_id, '') - name = re.sub('[^A-Za-z0-9\u00C0-\u024F\/\_\' ]+', '', name).strip() + name = re.sub(r'[^A-Za-z0-9\u00C0-\u024F\/\_\' ]+', '', name).strip() if not name: name = email_id return name From bd6fcddd0446e6da56b5ae71e48aab0bc9a66bee Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 26 May 2021 16:41:32 +0530 Subject: [PATCH 128/495] chore: Drop unreachable code --- frappe/contacts/doctype/address/address.py | 5 +---- frappe/utils/__init__.py | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py index 9aced0dacb..b3150aa75a 100644 --- a/frappe/contacts/doctype/address/address.py +++ b/frappe/contacts/doctype/address/address.py @@ -171,14 +171,11 @@ def get_address_list(doctype, txt, filters, limit_start, limit_page_length = 20, def has_website_permission(doc, ptype, user, verbose=False): """Returns true if there is a related lead or contact related to this document""" contact_name = frappe.db.get_value("Contact", {"email_id": frappe.session.user}) + if contact_name: contact = frappe.get_doc('Contact', contact_name) return contact.has_common_link(doc) - lead_name = frappe.db.get_value("Lead", {"email_id": frappe.session.user}) - if lead_name: - return doc.has_link('Lead', lead_name) - return False def get_address_templates(address): diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 94eed0b2bb..8d4905edd6 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -371,7 +371,7 @@ def get_path(*path, **kwargs): base = frappe.local.site_path return os.path.join(base, *path) -def get_site_base_path(sites_dir=None, hostname=None): +def get_site_base_path(): return frappe.local.site_path def get_site_path(*path): From 3adb84eb8d550f46fb7738dcd5f9db0d6d6d047d Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 26 May 2021 17:04:28 +0530 Subject: [PATCH 129/495] chore: Drop watchdog dependency Watchdog isn't used by Frappe, and there wasn't any mechanism to access it directly either. By default, bench serve (or start) uses Werkzeug's watchdogreloader --- frappe/utils/__init__.py | 38 +++++++------------------------------- requirements.txt | 1 - 2 files changed, 7 insertions(+), 32 deletions(-) diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 8d4905edd6..9985d2dcf3 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -520,42 +520,18 @@ def get_sites(sites_path=None): return sorted(sites) -def get_request_session(max_retries=3): +def get_request_session(max_retries=5): import requests from urllib3.util import Retry + session = requests.Session() - session.mount("http://", requests.adapters.HTTPAdapter(max_retries=Retry(total=5, status_forcelist=[500]))) - session.mount("https://", requests.adapters.HTTPAdapter(max_retries=Retry(total=5, status_forcelist=[500]))) + http_adapter = requests.adapters.HTTPAdapter(max_retries=Retry(total=max_retries, status_forcelist=[500])) + + session.mount("http://", http_adapter) + session.mount("https://", http_adapter) + return session -def watch(path, handler=None, debug=True): - import time - - from watchdog.events import FileSystemEventHandler - from watchdog.observers import Observer - - class Handler(FileSystemEventHandler): - def on_any_event(self, event): - if debug: - print("File {0}: {1}".format(event.event_type, event.src_path)) - - if not handler: - print("No handler specified") - return - - handler(event.src_path, event.event_type) - - event_handler = Handler() - observer = Observer() - observer.schedule(event_handler, path, recursive=True) - observer.start() - try: - while True: - time.sleep(1) - except KeyboardInterrupt: - observer.stop() - observer.join() - def markdown(text, sanitize=True, linkify=True): html = text if is_html(text) else frappe.utils.md_to_html(text) diff --git a/requirements.txt b/requirements.txt index b3bfc12567..9c61e500ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -72,7 +72,6 @@ stripe~=2.56.0 terminaltables~=3.1.0 unittest-xml-reporting~=3.0.4 urllib3~=1.26.4 -watchdog~=2.0.2 Werkzeug~=0.16.1 Whoosh~=2.7.4 wrapt~=1.12.1 From 131cfaee8bd8a51a18af7f780086dd617c040622 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 27 May 2021 07:51:12 +0530 Subject: [PATCH 130/495] fix: Close file after read This fixes the ResourceWarning errors due to unclosed files while utilizing the website router --- frappe/website/router.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/website/router.py b/frappe/website/router.py index 899f2722ab..aa74d140c1 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -372,7 +372,8 @@ def setup_index(page_info): # load index.txt if loading all pages index_txt_path = os.path.join(page_info.basepath, 'index.txt') if os.path.exists(index_txt_path): - page_info.index = open(index_txt_path, 'r').read().splitlines() + with open(index_txt_path, 'r') as f: + page_info.index = f.read().splitlines() def load_properties_from_source(page_info): '''Load properties like no_cache, title from source html''' From f6b215938a9758f7c1e54ddc19161280a37dbe68 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 26 May 2021 19:30:08 +0530 Subject: [PATCH 131/495] fix: Use raw string to avoid invalid sequence errors Escaped when proven easier --- frappe/translate.py | 2 +- frappe/utils/__init__.py | 2 +- frappe/utils/data.py | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frappe/translate.py b/frappe/translate.py index f95fdad015..b7b780e594 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -589,7 +589,7 @@ def is_translatable(m): def add_line_number(messages, code): ret = [] messages = sorted(messages, key=lambda x: x[0]) - newlines = [m.start() for m in re.compile('\\n').finditer(code)] + newlines = [m.start() for m in re.compile(r'\n').finditer(code)] line = 1 newline_i = 0 for pos, message, context in messages: diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 9985d2dcf3..2d3637d527 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -151,7 +151,7 @@ def split_emails(txt): # emails can be separated by comma or newline s = re.sub(r'[\t\n\r]', ' ', cstr(txt)) - for email in re.split('''[,\\n](?=(?:[^"]|"[^"]*")*$)''', s): + for email in re.split(r'[,\n](?=(?:[^"]|"[^"]*")*$)', s): email = strip(cstr(email)) if email: email_list.append(email) diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 09b02a918a..df36524c16 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -454,7 +454,7 @@ def duration_to_seconds(duration): def validate_duration_format(duration): import re - is_valid_duration = re.match("^(?:(\d+d)?((^|\s)\d+h)?((^|\s)\d+m)?((^|\s)\d+s)?)$", duration) + is_valid_duration = re.match(r"^(?:(\d+d)?((^|\s)\d+h)?((^|\s)\d+m)?((^|\s)\d+s)?)$", duration) if not is_valid_duration: frappe.throw(frappe._("Value {0} must be in the valid duration format: d h m s").format(frappe.bold(duration))) @@ -1341,10 +1341,10 @@ def expand_relative_urls(html): return "".join(to_expand) - html = re.sub('(href|src){1}([\s]*=[\s]*[\'"]?)((?!http)[^\'" >]+)([\'"]?)', _expand_relative_urls, html) + html = re.sub(r'(href|src){1}([\s]*=[\s]*[\'"]?)((?!http)[^\'" >]+)([\'"]?)', _expand_relative_urls, html) # background-image: url('/assets/...') - html = re.sub('(:[\s]?url)(\([\'"]?)((?!http)[^\'" >]+)([\'"]?\))', _expand_relative_urls, html) + html = re.sub(r'(:[\s]?url)(\([\'"]?)((?!http)[^\'" >]+)([\'"]?\))', _expand_relative_urls, html) return html def quoted(url): @@ -1355,7 +1355,7 @@ def quote_urls(html): groups = list(match.groups()) groups[2] = quoted(groups[2]) return "".join(groups) - return re.sub('(href|src){1}([\s]*=[\s]*[\'"]?)((?:http)[^\'">]+)([\'"]?)', + return re.sub(r'(href|src){1}([\s]*=[\s]*[\'"]?)((?:http)[^\'">]+)([\'"]?)', _quote_url, html) def unique(seq): From 295d44cee500d24f9556099e87120493983ae23c Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 26 May 2021 19:31:17 +0530 Subject: [PATCH 132/495] chore: Drop future from requirements.txt --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9c61e500ef..4f4614955b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,6 @@ cryptography~=3.4.7 dropbox~=11.7.0 email-reply-parser~=0.5.12 Faker~=8.1.0 -future==0.18.2 git-url-parse~=1.2.2 gitdb~=4.0.7 GitPython~=3.1.14 From 88ef41bc58a2e9df5efd3441aacbf3f6beb6d952 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 26 May 2021 19:49:22 +0530 Subject: [PATCH 133/495] fix(typo): Let's try executing bound method instead --- frappe/utils/bench_helper.py | 1 + frappe/www/sitemap.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/utils/bench_helper.py b/frappe/utils/bench_helper.py index ec08d2a15e..2ee1e6691f 100644 --- a/frappe/utils/bench_helper.py +++ b/frappe/utils/bench_helper.py @@ -100,4 +100,5 @@ def get_apps(): if __name__ == "__main__": if not frappe._dev_server: warnings.simplefilter('ignore') + warnings.simplefilter('always') main() diff --git a/frappe/www/sitemap.py b/frappe/www/sitemap.py index 9eb36e5335..1606e91200 100644 --- a/frappe/www/sitemap.py +++ b/frappe/www/sitemap.py @@ -26,7 +26,7 @@ def get_context(context): "lastmod": nowdate() }) - for route, data in get_public_pages_from_doctypes().items: + for route, data in get_public_pages_from_doctypes().items(): links.append({ "loc": get_url(quote((route or "").encode("utf-8"))), "lastmod": get_datetime(data.get("modified")).strftime("%Y-%m-%d") From eca30667d2f10d793e93383cbc1ebd6317b218ff Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 26 May 2021 22:21:23 +0530 Subject: [PATCH 134/495] chore: Drop FileNotFoundError handling for PY2 --- frappe/exceptions.py | 8 -------- frappe/model/delete_doc.py | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/frappe/exceptions.py b/frappe/exceptions.py index 0f111fdcf9..13abd8f4f8 100644 --- a/frappe/exceptions.py +++ b/frappe/exceptions.py @@ -1,17 +1,9 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -import sys - # BEWARE don't put anything in this file except exceptions from werkzeug.exceptions import NotFound - -if sys.version_info.major == 2: - class FileNotFoundError(Exception): pass -else: - from builtins import FileNotFoundError - class SiteNotSpecifiedError(Exception): def __init__(self, *args, **kwargs): self.message = "Please specify --site sitename" diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index c01439d330..cc88cfa106 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -15,7 +15,7 @@ from frappe.utils.password import delete_all_passwords_for from frappe.model.naming import revert_series_if_last from frappe.utils.global_search import delete_for_document from frappe.desk.doctype.tag.tag import delete_tags_for_document -from frappe.exceptions import FileNotFoundError + doctypes_to_skip = ("Communication", "ToDo", "DocShare", "Email Unsubscribe", "Activity Log", "File", "Version", "Document Follow", "Comment" , "View Log", "Tag Link", "Notification Log", "Email Queue") From 793b5638d3c4baa34673017ff757e2c9cea85031 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 27 May 2021 12:48:01 +0530 Subject: [PATCH 135/495] style: Fix E201, E202, E401, E701 reported by sider: https://sider.review/gh/repos/1864194/pulls/13344?statuses#issues --- frappe/desk/form/linked_with.py | 5 ++++- frappe/desk/form/meta.py | 15 ++++++++------- frappe/permissions.py | 10 ++++------ frappe/tests/test_pdf.py | 8 +++++--- frappe/translate.py | 17 ++++++++++++----- frappe/utils/redis_wrapper.py | 7 ++++++- 6 files changed, 39 insertions(+), 23 deletions(-) diff --git a/frappe/desk/form/linked_with.py b/frappe/desk/form/linked_with.py index 12880604a2..ae48b7fc6b 100644 --- a/frappe/desk/form/linked_with.py +++ b/frappe/desk/form/linked_with.py @@ -2,6 +2,7 @@ # MIT License. See license.txt import json from collections import defaultdict + import frappe import frappe.desk.form.load import frappe.desk.form.meta @@ -9,6 +10,7 @@ from frappe import _ from frappe.model.meta import is_single from frappe.modules import load_doctype_module + @frappe.whitelist() def get_submitted_linked_docs(doctype, name, docs=None, visited=None): """ @@ -200,7 +202,8 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None): else: link_fieldnames = link.get("fieldname") if link_fieldnames: - if isinstance(link_fieldnames, str): link_fieldnames = [link_fieldnames] + if isinstance(link_fieldnames, str): + link_fieldnames = [link_fieldnames] or_filters = [[dt, fieldname, '=', name] for fieldname in link_fieldnames] # dynamic link if link.get("doctype_fieldname"): diff --git a/frappe/desk/form/meta.py b/frappe/desk/form/meta.py index 947a9e5d4b..cf3606e785 100644 --- a/frappe/desk/form/meta.py +++ b/frappe/desk/form/meta.py @@ -1,14 +1,15 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt +import io +import os -# metadata -import frappe, os, io -from frappe.model.meta import Meta -from frappe.modules import scrub, get_module_path, load_doctype_module -from frappe.utils import get_html_format -from frappe.translate import make_dict_from_messages, extract_messages_from_code -from frappe.model.utils import render_include +import frappe from frappe.build import scrub_html_template +from frappe.model.meta import Meta +from frappe.model.utils import render_include +from frappe.modules import get_module_path, load_doctype_module, scrub +from frappe.translate import extract_messages_from_code, make_dict_from_messages +from frappe.utils import get_html_format def get_meta(doctype, cached=True): diff --git a/frappe/permissions.py b/frappe/permissions.py index e139b04a4e..c25a7c3947 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -1,17 +1,15 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -import frappe, copy +import copy + +import frappe +import frappe.share from frappe import _, msgprint from frappe.utils import cint -import frappe.share rights = ("select", "read", "write", "create", "delete", "submit", "cancel", "amend", "print", "email", "report", "import", "export", "set_user_permissions", "share") -# TODO: - -# optimize: meta.get_link_map (check if the doctype link exists for the given permission type) - def check_admin_or_system_manager(user=None): if not user: user = frappe.session.user diff --git a/frappe/tests/test_pdf.py b/frappe/tests/test_pdf.py index b67aee7a68..9e22b9ddbf 100644 --- a/frappe/tests/test_pdf.py +++ b/frappe/tests/test_pdf.py @@ -1,12 +1,14 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt +import io import unittest -import frappe.utils.pdf as pdfgen -import frappe, io from PyPDF2 import PdfFileReader -#class TestPdfBorders(unittest.TestCase): +import frappe +import frappe.utils.pdf as pdfgen + + class TestPdf(unittest.TestCase): @property def html(self): diff --git a/frappe/translate.py b/frappe/translate.py index b7b780e594..a407ad9579 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -7,12 +7,19 @@ Translation tools for frappe """ -import frappe, os, re, io, json -from frappe.model.utils import render_include, InvalidIncludePath -from frappe.utils import strip, strip_html_tags, is_html -import itertools, operator +import io +import itertools +import json +import operator +import os +import re from csv import reader +import frappe +from frappe.model.utils import InvalidIncludePath, render_include +from frappe.utils import is_html, strip, strip_html_tags + + def guess_language(lang_list=None): """Set `frappe.local.lang` from HTTP headers at beginning of request""" user_preferred_language = frappe.request.cookies.get('preferred_language') @@ -606,7 +613,7 @@ def read_csv_file(path): with io.open(path, mode='r', encoding='utf-8', newline='') as msgfile: data = reader(msgfile) - newdata = [[ val for val in row ] for row in data] + newdata = [[val for val in row] for row in data] return newdata diff --git a/frappe/utils/redis_wrapper.py b/frappe/utils/redis_wrapper.py index 41aa4e7ad1..8a7ff8334b 100644 --- a/frappe/utils/redis_wrapper.py +++ b/frappe/utils/redis_wrapper.py @@ -1,6 +1,11 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -import redis, frappe, re, pickle +import pickle +import re + +import redis + +import frappe from frappe.utils import cstr From d13eac6f50ad727c1e77a66b828c7f60f5b4bbcc Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 27 May 2021 12:56:38 +0530 Subject: [PATCH 136/495] fix: Breadcrumbs for a page --- .../website/page_renderers/template_page.py | 25 +++++++++++++------ frappe/website/page_renderers/web_page.py | 9 ++++--- frappe/website/router.py | 2 +- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index 2b326d7383..8f613ae9bd 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -3,6 +3,7 @@ import os import click import frappe +from frappe.website.router import get_page_info from frappe.website.page_renderers.base_template_page import BaseTemplatePage from frappe.website.router import get_base_template from frappe.website.utils import (extract_comment_tag, extract_title, get_next_link, @@ -37,11 +38,11 @@ class TemplatePage(BaseTemplatePage): for dirname in folders: search_path = os.path.join(app_path, dirname, self.path) - for p in self.get_index_path_options(search_path): - file_path = frappe.as_unicode(p) - if os.path.exists(file_path) and not os.path.isdir(file_path): + for file_path in self.get_index_path_options(search_path): + if os.path.isfile(file_path): self.app = app self.app_path = app_path + self.file_dir = dirname self.basename = os.path.splitext(file_path)[0] self.template_path = os.path.relpath(file_path, self.app_path) self.basepath = os.path.dirname(file_path) @@ -52,8 +53,9 @@ class TemplatePage(BaseTemplatePage): def can_render(self): return hasattr(self, 'template_path') and bool(self.template_path) - def get_index_path_options(self, search_path): - return (f'{search_path}{d}' for d in ('', '.html', '.md', '/index.html', '/index.md')) + @staticmethod + def get_index_path_options(search_path): + return (frappe.as_unicode(f'{search_path}{d}') for d in ('.html', '.md', '/index.html', '/index.md')) def render(self): return build_response(self.path, self.get_html(), self.http_status_code, self.headers) @@ -85,9 +87,18 @@ class TemplatePage(BaseTemplatePage): self.context.sidebar_items = get_sidebar_items(self.context.website_sidebar, self.basepath) if self.context.add_breadcrumbs and not self.context.parents: - # TODO: set correct title and route for breadcrumbs parent_path = os.path.dirname(self.path) - self.context.parents = [dict(route=parent_path, title=extract_title(source='', path=parent_path))] + if self.path.endswith('index'): + # in case of index page move one directory up for parent path + parent_path = os.path.dirname(parent_path) + + for parent_file_path in self.get_index_path_options(parent_path): + parent_file_path = os.path.join(self.app_path, self.file_dir, parent_file_path) + if os.path.isfile(parent_file_path): + parent_page_context = get_page_info(parent_file_path, self.app, self.file_dir) + if parent_page_context: + self.context.parents = [dict(route=os.path.dirname(self.path), title=parent_page_context.title)] + break def set_pymodule(self): ''' diff --git a/frappe/website/page_renderers/web_page.py b/frappe/website/page_renderers/web_page.py index 6e738e8df9..1814a9615a 100644 --- a/frappe/website/page_renderers/web_page.py +++ b/frappe/website/page_renderers/web_page.py @@ -7,10 +7,11 @@ class WebPage(object): if not path: path = frappe.local.request.path self.path = path.strip('/ ') - self.basepath = '' - self.basename = '' - self.name = '' - self.route = '' + self.basepath = None + self.basename = None + self.name = None + self.route = None + self.file_dir = None def can_render(self): pass diff --git a/frappe/website/router.py b/frappe/website/router.py index 23fd5cb21f..6d803886ef 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -107,7 +107,7 @@ def get_page_info(path, app, start, basepath=None, app_path=None, fname=None): if basepath is None: basepath = os.path.dirname(path) - page_name, extn = fname.rsplit(".", 1) + page_name, extn = os.path.splitext(fname) # add website route page_info = frappe._dict() From 9ec451b28e2e92589bb7d29a3aaf861776f75a98 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 27 May 2021 12:57:55 +0530 Subject: [PATCH 137/495] test: Add test case to check breadcrumbs --- frappe/templates/includes/full_index.html | 5 ----- frappe/templates/test/_test_base.html | 1 + frappe/website/doctype/web_page/test_web_page.py | 8 +++++++- frappe/www/_test/_test_folder/_test_page.py | 1 + frappe/www/_test/_test_folder/index.md | 6 ++++++ frappe/www/_test/index.html | 1 + 6 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 frappe/www/_test/index.html diff --git a/frappe/templates/includes/full_index.html b/frappe/templates/includes/full_index.html index a7443c482a..eb8fb322f6 100644 --- a/frappe/templates/includes/full_index.html +++ b/frappe/templates/includes/full_index.html @@ -3,11 +3,6 @@ {% for item in children_map[route] %}
  • {{ item.title }} - {# - {% if children_map[item.route] %} - {{ make_item_list(item.route, children_map) }} - {% endif %} - #}
  • {% endfor %} diff --git a/frappe/templates/test/_test_base.html b/frappe/templates/test/_test_base.html index 5a88584a5d..17caf8df1b 100644 --- a/frappe/templates/test/_test_base.html +++ b/frappe/templates/test/_test_base.html @@ -8,6 +8,7 @@ {%- endblock -%} + {% include "templates/includes/breadcrumbs.html" %}

    This is for testing

    {% block content %}{% endblock %} {%- block script %} diff --git a/frappe/website/doctype/web_page/test_web_page.py b/frappe/website/doctype/web_page/test_web_page.py index d213fd57a8..d097ceb083 100644 --- a/frappe/website/doctype/web_page/test_web_page.py +++ b/frappe/website/doctype/web_page/test_web_page.py @@ -113,7 +113,13 @@ class TestWebPage(unittest.TestCase): frappe.as_unicode(content)) def test_breadcrumbs(self): - pass + content = get_response_content('/_test/_test_folder/_test_page') + self.assertIn('Test TOC', content) + self.assertIn(' Test Page', content) + + content = get_response_content('/_test/_test_folder/index') + self.assertIn(' Test', content) + self.assertIn('Test TOC', content) def test_downloadable_file(self): pass diff --git a/frappe/www/_test/_test_folder/_test_page.py b/frappe/www/_test/_test_folder/_test_page.py index fa7b5f5727..1813a06bac 100644 --- a/frappe/www/_test/_test_folder/_test_page.py +++ b/frappe/www/_test/_test_folder/_test_page.py @@ -1,2 +1,3 @@ def get_context(context): context.base_template_path = 'frappe/templates/test/_test_base.html' + context.add_breadcrumbs = 1 diff --git a/frappe/www/_test/_test_folder/index.md b/frappe/www/_test/_test_folder/index.md index d0b531a642..1a5a9e7f81 100644 --- a/frappe/www/_test/_test_folder/index.md +++ b/frappe/www/_test/_test_folder/index.md @@ -1,3 +1,9 @@ +--- +title: Test TOC +add_breadcrumbs: 1 +show_sidebar: 1 +--- + # Index {index} \ No newline at end of file diff --git a/frappe/www/_test/index.html b/frappe/www/_test/index.html new file mode 100644 index 0000000000..0dff60b400 --- /dev/null +++ b/frappe/www/_test/index.html @@ -0,0 +1 @@ +{index} \ No newline at end of file From 6e9207cd626a373d7e6488c56ec4d027458ede9e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 27 May 2021 12:58:48 +0530 Subject: [PATCH 138/495] test: Refactor webpage test to remove unnecessary operations --- .../website/doctype/web_page/test_web_page.py | 47 +++++++------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/frappe/website/doctype/web_page/test_web_page.py b/frappe/website/doctype/web_page/test_web_page.py index d097ceb083..67b3cdbe89 100644 --- a/frappe/website/doctype/web_page/test_web_page.py +++ b/frappe/website/doctype/web_page/test_web_page.py @@ -2,16 +2,10 @@ from __future__ import unicode_literals import unittest import frappe from frappe.website.path_resolver import PathResolver -from frappe.website.serve import get_response, get_response_content -from frappe.utils import set_request +from frappe.website.serve import get_response_content test_records = frappe.get_test_records('Web Page') -def get_page_content(route): - set_request(method='GET', path = route) - response = get_response() - return frappe.as_unicode(response.data) - class TestWebPage(unittest.TestCase): def setUp(self): frappe.db.sql("delete from `tabWeb Page`") @@ -25,13 +19,13 @@ class TestWebPage(unittest.TestCase): self.assertFalse(PathResolver("test-web-page-1/test-web-page-Random").is_valid_path()) def test_base_template(self): - content = get_page_content('/_test/_test_custom_base.html') + content = get_response_content('/_test/_test_custom_base.html') # assert the text in base template is rendered - self.assertIn('

    This is for testing

    ', frappe.as_unicode(content)) + self.assertIn('

    This is for testing

    ', content) # assert template block rendered - self.assertIn('

    Test content

    ', frappe.as_unicode(content)) + self.assertIn('

    Test content

    ', content) def test_content_type(self): web_page = frappe.get_doc(dict( @@ -44,15 +38,15 @@ class TestWebPage(unittest.TestCase): main_section_html = '
    html content
    ' )).insert() - self.assertIn('rich text', get_page_content('/test-content-type')) + self.assertIn('rich text', get_response_content('/test-content-type')) web_page.content_type = 'Markdown' web_page.save() - self.assertIn('markdown content', get_page_content('/test-content-type')) + self.assertIn('markdown content', get_response_content('/test-content-type')) web_page.content_type = 'HTML' web_page.save() - self.assertIn('html content', get_page_content('/test-content-type')) + self.assertIn('html content', get_response_content('/test-content-type')) web_page.delete() @@ -67,9 +61,9 @@ class TestWebPage(unittest.TestCase): dynamic_template = 1, main_section_html = '
    {{ frappe.form_dict.doctype }}
    ' )).insert() - try: - content = get_page_content('/doctype-view/DocField') + from frappe.utils import get_html_for_route + content = get_html_for_route('/doctype-view/DocField') self.assertIn('
    DocField
    ', content) finally: web_page.delete() @@ -77,40 +71,35 @@ class TestWebPage(unittest.TestCase): def test_custom_base_template_path(self): content = get_response_content('/_test/_test_folder/_test_page') # assert the text in base template is rendered - self.assertIn('

    This is for testing

    ', frappe.as_unicode(content)) + self.assertIn('

    This is for testing

    ', content) # assert template block rendered - self.assertIn('

    Test content

    ', frappe.as_unicode(content)) + self.assertIn('

    Test content

    ', content) def test_json_sidebar_data(self): frappe.flags.look_for_sidebar = False content = get_response_content('/_test/_test_folder/_test_page') - self.assertNotIn('Test Sidebar', frappe.as_unicode(content)) + self.assertNotIn('Test Sidebar', content) frappe.flags.look_for_sidebar = True content = get_response_content('/_test/_test_folder/_test_page') - self.assertIn('Test Sidebar', frappe.as_unicode(content)) + self.assertIn('Test Sidebar', content) frappe.flags.look_for_sidebar = False def test_index_and_next_comment(self): content = get_response_content('/_test/_test_folder') # test if {index} was rendered - self.assertIn(' Test Page', - frappe.as_unicode(content)) + self.assertIn(' Test Page', content) - self.assertIn('Test TOC', - frappe.as_unicode(content)) + self.assertIn('Test TOC', content) content = get_response_content('/_test/_test_folder/_test_page') # test if {next} was rendered - self.assertIn('Next: Test TOC', - frappe.as_unicode(content)) + self.assertIn('Next: Test TOC', content) def test_colocated_assets(self): content = get_response_content('/_test/_test_folder/_test_page') - self.assertIn("", - frappe.as_unicode(content)) - self.assertIn("background-color: var(--bg-color);", - frappe.as_unicode(content)) + self.assertIn("", content) + self.assertIn("background-color: var(--bg-color);", content) def test_breadcrumbs(self): content = get_response_content('/_test/_test_folder/_test_page') From e2ee75e3cbc9a72140ba33416638569c332b983a Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 27 May 2021 13:44:53 +0530 Subject: [PATCH 139/495] fix: Translate strings passed in frappe.throw * Remove trailing whitespaces in translatable strings * Sort imports appropriately --- frappe/desk/doctype/dashboard/dashboard.py | 2 +- frappe/desk/doctype/dashboard_chart/dashboard_chart.py | 5 ++--- frappe/model/mapper.py | 6 ++++-- frappe/utils/jinja.py | 4 ++-- frappe/utils/password.py | 2 +- frappe/www/search.py | 9 +++++---- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/frappe/desk/doctype/dashboard/dashboard.py b/frappe/desk/doctype/dashboard/dashboard.py index d2e79279d9..1d333609db 100644 --- a/frappe/desk/doctype/dashboard/dashboard.py +++ b/frappe/desk/doctype/dashboard/dashboard.py @@ -21,7 +21,7 @@ class Dashboard(Document): def validate(self): if not frappe.conf.developer_mode and self.is_standard: - frappe.throw('Cannot edit Standard Dashboards') + frappe.throw(_("Cannot edit Standard Dashboards")) if self.is_standard: non_standard_docs_map = { diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py index 3bea6eded9..db5964e7b2 100644 --- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py @@ -8,8 +8,7 @@ import datetime import json from frappe.utils.dashboard import cache_source from frappe.utils import nowdate, getdate, get_datetime, cint, now_datetime -from frappe.utils.dateutils import\ - get_period, get_period_beginning, get_from_date_from_timespan, get_dates_from_timegrain +from frappe.utils.dateutils import get_period, get_period_beginning, get_from_date_from_timespan, get_dates_from_timegrain from frappe.model.naming import append_number_if_name_exists from frappe.boot import get_allowed_reports from frappe.config import get_modules_from_all_apps_for_user @@ -325,7 +324,7 @@ class DashboardChart(Document): def validate(self): if not frappe.conf.developer_mode and self.is_standard: - frappe.throw('Cannot edit Standard charts') + frappe.throw(_("Cannot edit Standard charts")) if self.chart_type != 'Custom' and self.chart_type != 'Report': self.check_required_field() self.check_document_type() diff --git a/frappe/model/mapper.py b/frappe/model/mapper.py index eb685fb840..8b1543c99e 100644 --- a/frappe/model/mapper.py +++ b/frappe/model/mapper.py @@ -1,10 +1,12 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt +import json -import frappe, json +import frappe from frappe import _ -from frappe.utils import cstr from frappe.model import default_fields, table_fields +from frappe.utils import cstr + @frappe.whitelist() def make_mapped_doc(method, source_name, selected_children=None, args=None): diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py index 685208e71f..65ea2c20b8 100644 --- a/frappe/utils/jinja.py +++ b/frappe/utils/jinja.py @@ -65,7 +65,7 @@ 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 frappe import _, get_traceback, throw from jinja2 import TemplateError if not template: @@ -75,7 +75,7 @@ def render_template(template, context, is_path=None, safe_render=True): return get_jenv().get_template(template).render(context) else: if safe_render and ".__" in template: - throw("Illegal template") + throw(_("Illegal template")) try: return get_jenv().from_string(template).render(context) except TemplateError: diff --git a/frappe/utils/password.py b/frappe/utils/password.py index 005a3cede6..d86275c39c 100644 --- a/frappe/utils/password.py +++ b/frappe/utils/password.py @@ -60,7 +60,7 @@ def set_encrypted_password(doctype, name, pwd, fieldname='password'): except frappe.db.DataError as e: if ((frappe.db.db_type == 'mariadb' and e.args[0] == DATA_TOO_LONG) or (frappe.db.db_type == 'postgres' and e.pgcode == STRING_DATA_RIGHT_TRUNCATION)): - frappe.throw("Most probably your password is too long.", exc=e) + frappe.throw(_("Most probably your password is too long."), exc=e) raise e diff --git a/frappe/www/search.py b/frappe/www/search.py index 292c9e532f..db3a8eb118 100644 --- a/frappe/www/search.py +++ b/frappe/www/search.py @@ -1,16 +1,17 @@ +from html2text import html2text +from jinja2 import utils import frappe -from frappe.utils.global_search import web_search -from html2text import html2text from frappe import _ -from jinja2 import utils from frappe.utils import sanitize_html +from frappe.utils.global_search import web_search + def get_context(context): context.no_cache = 1 if frappe.form_dict.q: query = str(utils.escape(sanitize_html(frappe.form_dict.q))) - context.title = _('Search Results for ') + context.title = _('Search Results for') context.query = query context.route = '/search' context.update(get_search_results(query, frappe.utils.sanitize_html(frappe.form_dict.scope))) From 6fb14120d9dcce31a080aaa68e21616c44b3ca35 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 27 May 2021 13:47:55 +0530 Subject: [PATCH 140/495] fix: Remove trailing whitespace in translatable str Simplify conditional block and str appending --- frappe/model/mapper.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/frappe/model/mapper.py b/frappe/model/mapper.py index 8b1543c99e..fa8858d950 100644 --- a/frappe/model/mapper.py +++ b/frappe/model/mapper.py @@ -137,10 +137,8 @@ def get_mapped_doc(from_doctype, from_docname, table_maps, target_doc=None, def map_doc(source_doc, target_doc, table_map, source_parent=None): if table_map.get("validation"): for key, condition in table_map["validation"].items(): - if condition[0]=="=": - if source_doc.get(key) != condition[1]: - frappe.throw(_("Cannot map because following condition fails: ") - + key + "=" + cstr(condition[1])) + if condition[0] == "=" and source_doc.get(key) != condition[1]: + frappe.throw(_("Cannot map because following condition fails:") + f" {key}={cstr(condition[1])}") map_fields(source_doc, target_doc, table_map, source_parent) From 96c03fd26dfb46738572de8a0bc5db4b80014b10 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 27 May 2021 13:48:58 +0530 Subject: [PATCH 141/495] chore: Drop commented out test_translation file --- frappe/tests/test_translation.py | 63 -------------------------------- 1 file changed, 63 deletions(-) delete mode 100644 frappe/tests/test_translation.py diff --git a/frappe/tests/test_translation.py b/frappe/tests/test_translation.py deleted file mode 100644 index 94af1bb870..0000000000 --- a/frappe/tests/test_translation.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt -import frappe, unittest, os -import frappe.translate - -# class TestTranslations(unittest.TestCase): -# def test_doctype(self, messages=None): -# if not messages: -# messages = frappe.translate.get_messages_from_doctype("Role") -# self.assertTrue("Role Name" in messages) -# -# def test_page(self, messages=None): -# if not messages: -# messages = frappe.translate.get_messages_from_page("finder") -# self.assertTrue("Finder" in messages) -# -# def test_report(self, messages=None): -# if not messages: -# messages = frappe.translate.get_messages_from_report("ToDo") -# self.assertTrue("Test" in messages) -# -# def test_include_js(self, messages=None): -# if not messages: -# messages = frappe.translate.get_messages_from_include_files("frappe") -# self.assertTrue("History" in messages) -# -# def test_server(self, messages=None): -# if not messages: -# messages = frappe.translate.get_server_messages("frappe") -# self.assertTrue("Login" in messages) -# self.assertTrue("Did not save" in messages) -# -# def test_all_app(self): -# messages = frappe.translate.get_messages_for_app("frappe") -# self.test_doctype(messages) -# self.test_page(messages) -# self.test_report(messages) -# self.test_include_js(messages) -# self.test_server(messages) -# -# def test_load_translations(self): -# frappe.translate.clear_cache() -# self.assertFalse(frappe.cache().hget("lang_full_dict", "de")) -# -# langdict = frappe.translate.get_full_dict("de") -# self.assertEqual(langdict['Row'], 'Reihe') -# -# def test_write_csv(self): -# tpath = frappe.get_pymodule_path("frappe", "translations", "de.csv") -# if os.path.exists(tpath): -# os.remove(tpath) -# frappe.translate.write_translations_file("frappe", "de") -# self.assertTrue(os.path.exists(tpath)) -# self.assertEqual(dict(frappe.translate.read_csv_file(tpath)).get("Row"), "Reihe") -# -# def test_get_dict(self): -# frappe.local.lang = "de" -# self.assertEqual(frappe.get_lang_dict("doctype", "Role").get("Role"), "Rolle") -# frappe.local.lang = "en" -# -# if __name__=="__main__": -# frappe.connect("site1") -# unittest.main() From c8749bcc72a48abd7815dea3c4fc2aa59d223e2a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 27 May 2021 14:27:30 +0530 Subject: [PATCH 142/495] fix: get_index_path_options method --- frappe/website/page_renderers/template_page.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index 8f613ae9bd..d1f0b47032 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -55,7 +55,7 @@ class TemplatePage(BaseTemplatePage): @staticmethod def get_index_path_options(search_path): - return (frappe.as_unicode(f'{search_path}{d}') for d in ('.html', '.md', '/index.html', '/index.md')) + return (frappe.as_unicode(f'{search_path}{d}') for d in ('', '.html', '.md', '/index.html', '/index.md')) def render(self): return build_response(self.path, self.get_html(), self.http_status_code, self.headers) From 369adf5a3a303612edf9f0169c7b37b7c711a852 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 27 May 2021 14:44:47 +0530 Subject: [PATCH 143/495] fix: Set default value as empty string --- frappe/website/page_renderers/web_page.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/website/page_renderers/web_page.py b/frappe/website/page_renderers/web_page.py index 1814a9615a..20e5bab062 100644 --- a/frappe/website/page_renderers/web_page.py +++ b/frappe/website/page_renderers/web_page.py @@ -7,10 +7,10 @@ class WebPage(object): if not path: path = frappe.local.request.path self.path = path.strip('/ ') - self.basepath = None - self.basename = None - self.name = None - self.route = None + self.basepath = '' + self.basename = '' + self.name = '' + self.route = '' self.file_dir = None def can_render(self): From 1a41e16e2a5d724b04b5ef0acaced5887f2db658 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 27 May 2021 15:14:27 +0530 Subject: [PATCH 144/495] fix: Blog post style --- frappe/public/scss/website/blog.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/public/scss/website/blog.scss b/frappe/public/scss/website/blog.scss index 9918b490c5..ea82efed21 100644 --- a/frappe/public/scss/website/blog.scss +++ b/frappe/public/scss/website/blog.scss @@ -14,6 +14,10 @@ position: relative; width: 100%; + .card { + border: 1px solid var(--border-color) + } + .card-body { display: flex; flex-direction: column; From 72c239342ed31e7bb3e1a7e0e07672872ea81000 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 27 May 2021 16:40:02 +0530 Subject: [PATCH 145/495] chore: Remove temporary warnings filter --- frappe/utils/bench_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/bench_helper.py b/frappe/utils/bench_helper.py index 2ee1e6691f..b406c7e427 100644 --- a/frappe/utils/bench_helper.py +++ b/frappe/utils/bench_helper.py @@ -100,5 +100,5 @@ def get_apps(): if __name__ == "__main__": if not frappe._dev_server: warnings.simplefilter('ignore') - warnings.simplefilter('always') + main() From 651a9ff42edf0ddf17aa66b9e851109ca83ac1c3 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 28 May 2021 21:43:07 +0530 Subject: [PATCH 146/495] fix: Return address 'name' instead of 'parent' field --- frappe/contacts/doctype/address/address.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py index b3150aa75a..bfcf91427d 100644 --- a/frappe/contacts/doctype/address/address.py +++ b/frappe/contacts/doctype/address/address.py @@ -111,8 +111,8 @@ def get_default_address(doctype, name, sort_key='is_primary_address'): if out: for contact in out: if contact.get(sort_key): - return contact.parent - return out[0].parent + return contact.name + return out[0].name else: return None From 9b4c19192805499dcc870709351c78fedb3352d5 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 28 May 2021 23:10:59 +0530 Subject: [PATCH 147/495] chore: Update Jinja2 dependency Update from 2.11.3 to 3.0.1 Ref: https://jinja.palletsprojects.com/en/3.0.x/changes/ --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4f4614955b..8b1d9e649d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -26,7 +26,7 @@ html2text==2020.1.16 html5lib~=1.1 ipython~=7.16.1 jedi==0.17.2 # not directly required. Pinned to fix upstream IPython issue (https://github.com/ipython/ipython/issues/12740) -Jinja2~=2.11.3 +Jinja2~=3.0.1 ldap3~=2.9 markdown2~=2.4.0 maxminddb-geolite2==2018.703 From 165ff8e1bfe1a8e94b9995626ea5dd188b2e23f2 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Sat, 29 May 2021 17:22:30 +0530 Subject: [PATCH 148/495] chore: Update PyJWT dependency * Update pinned dep fromm 1.7.1 to 2.0.1 * Updated usages as per changelog ref: https://python.libhunt.com/pyjwt-changelog --- frappe/oauth.py | 3 ++- frappe/tests/test_oauth20.py | 2 +- frappe/utils/oauth.py | 2 +- requirements.txt | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/frappe/oauth.py b/frappe/oauth.py index a4c66bf3f2..67d346ad8a 100644 --- a/frappe/oauth.py +++ b/frappe/oauth.py @@ -486,6 +486,7 @@ class OAuthWebRequestValidator(RequestValidator): user = None payload = jwt.decode( id_token_hint, + algorithms=["HS256"], options={ "verify_signature": False, "verify_aud": False, @@ -508,7 +509,7 @@ class OAuthWebRequestValidator(RequestValidator): id_token_hint, key=client_secret, audience=client_id, - algorithm="HS256", + algorithms=["HS256"], options={ "verify_exp": False, }, diff --git a/frappe/tests/test_oauth20.py b/frappe/tests/test_oauth20.py index cf3b2ea90d..2a5bed34b3 100644 --- a/frappe/tests/test_oauth20.py +++ b/frappe/tests/test_oauth20.py @@ -302,7 +302,7 @@ class TestOAuth20(unittest.TestCase): id_token, audience=client.client_id, key=client.client_secret, - algorithm="HS256", + algorithms=["HS256"], ) self.assertTrue(payload.get("nonce") == nonce) diff --git a/frappe/utils/oauth.py b/frappe/utils/oauth.py index 45cce737e1..9f5ff9d360 100644 --- a/frappe/utils/oauth.py +++ b/frappe/utils/oauth.py @@ -134,7 +134,7 @@ def get_info_via_oauth(provider, code, decoder=None, id_token=False): token = parsed_access['id_token'] - info = jwt.decode(token, flow.client_secret, verify=False) + info = jwt.decode(token, flow.client_secret, options={"verify_signature": False}) else: api_endpoint = oauth2_providers[provider].get("api_endpoint") api_endpoint_args = oauth2_providers[provider].get("api_endpoint_args") diff --git a/requirements.txt b/requirements.txt index 8b1d9e649d..0791f01b27 100644 --- a/requirements.txt +++ b/requirements.txt @@ -43,7 +43,7 @@ psutil~=5.8.0 psycopg2-binary~=2.8.6 pyasn1~=0.4.8 pycryptodome~=3.10.1 -PyJWT~=1.7.1 +PyJWT~=2.0.1 PyMySQL~=1.0.2 pyngrok~=5.0.5 pyOpenSSL~=20.0.1 From 63a42f54b292d8592b7eb48ddf33686d17778547 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 31 May 2021 14:55:23 +0530 Subject: [PATCH 149/495] fix: Deprecation warning --- frappe/website/page_renderers/template_page.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index d1f0b47032..ea92c7103c 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -70,8 +70,8 @@ class TemplatePage(BaseTemplatePage): self.setup_template() self.update_context() self.post_process_context() - html = self.render_template() + html = self.render_template() html = self.update_toc(html) html = self.add_csrf_token(html) @@ -182,7 +182,8 @@ class TemplatePage(BaseTemplatePage): comment_tag = f"" if comment_tag in self.source: self.context[context_key] = value - click.echo(f'⚠️ DEPRECATION WARNING: {comment_tag} will be deprecated on 2021-12-31.') + click.echo(f'\n⚠️ DEPRECATION WARNING: {comment_tag} will be deprecated on 2021-12-31.') + click.echo(f'Please remove it from {self.template_path} in {self.app}') def run_pymodule_method(self, method): if hasattr(self.pymodule, method): From a42fc3c5dfe674a48c91cb29acb5e61cf03dd61c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 31 May 2021 14:56:09 +0530 Subject: [PATCH 150/495] feat: Option to add custom renderers --- frappe/website/path_resolver.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/website/path_resolver.py b/frappe/website/path_resolver.py index 9816900677..0519d82735 100644 --- a/frappe/website/path_resolver.py +++ b/frappe/website/path_resolver.py @@ -35,7 +35,8 @@ class PathResolver(): return frappe.flags.redirect_location, RedirectPage(self.path) endpoint = resolve_path(self.path) - renderers = (StaticPage, WebFormPage, TemplatePage, ListPage, DocumentPage, PrintPage, NotFoundPage) + custom_renderers = frappe.get_hooks('page_renderer') + renderers = custom_renderers + [StaticPage, WebFormPage, TemplatePage, ListPage, DocumentPage, PrintPage, NotFoundPage] for renderer in renderers: renderer_instance = renderer(endpoint, 200) From 384d875b6235573cef9ead8e9f0b2185167f78ff Mon Sep 17 00:00:00 2001 From: leela Date: Mon, 31 May 2021 15:42:45 +0530 Subject: [PATCH 151/495] refactor: smtp session creation cleanup --- frappe/email/smtp.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frappe/email/smtp.py b/frappe/email/smtp.py index 3acb76af23..2a1d6d297f 100644 --- a/frappe/email/smtp.py +++ b/frappe/email/smtp.py @@ -85,18 +85,19 @@ class SMTPServer: SMTP = smtplib.SMTP_SSL if self.use_ssl else smtplib.SMTP try: - self._session = SMTP(self.server, self.port) - if not self._session: + _session = SMTP(self.server, self.port) + if not _session: frappe.msgprint(CONNECTION_FAILED, raise_exception=frappe.OutgoingEmailError) - self.secure_session(self._session) + self.secure_session(_session) if self.login and self.password: - res = self._session.login(str(self.login or ""), str(self.password or "")) + res = _session.login(str(self.login or ""), str(self.password or "")) # check if logged correctly if res[0]!=235: frappe.msgprint(res[1], raise_exception=frappe.OutgoingEmailError) + self._session = _session return self._session except smtplib.SMTPAuthenticationError as e: From 55a49ce445aac635c09bfba14fbdadc621d59811 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 1 Jun 2021 17:39:25 +0530 Subject: [PATCH 152/495] fix: cannot set shortcut for actions menu item --- frappe/public/js/frappe/ui/page.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/ui/page.js b/frappe/public/js/frappe/ui/page.js index e740718ef9..a302c26892 100644 --- a/frappe/public/js/frappe/ui/page.js +++ b/frappe/public/js/frappe/ui/page.js @@ -377,11 +377,12 @@ frappe.ui.Page = class Page { }); } - add_actions_menu_item(label, click, standard) { + add_actions_menu_item(label, click, standard, shortcut) { return this.add_dropdown_item({ label, click, standard, + shortcut, parent: this.actions, show_parent: false }); From 39eee3e5c8fbd514ebedbd0526eb13508250336d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 1 Jun 2021 19:02:01 +0530 Subject: [PATCH 153/495] fix: Add min-width for awesomplete class --- frappe/public/scss/common/awesomeplete.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/scss/common/awesomeplete.scss b/frappe/public/scss/common/awesomeplete.scss index 096da1e2fd..619d6a9e3e 100644 --- a/frappe/public/scss/common/awesomeplete.scss +++ b/frappe/public/scss/common/awesomeplete.scss @@ -31,6 +31,7 @@ margin: 0; padding: var(--padding-xs); z-index: 1; + min-width: 250px; &> li { cursor: pointer; From f15edd21d1ca19d0353ca138a63d3d80d0848bd2 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Tue, 1 Jun 2021 19:41:18 +0530 Subject: [PATCH 154/495] feat: enable use of custom encryption_key in encrypt()/decrypt() --- frappe/tests/test_password.py | 12 +++++++++++- frappe/utils/password.py | 14 +++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/frappe/tests/test_password.py b/frappe/tests/test_password.py index 98141072e2..9b68f3aace 100644 --- a/frappe/tests/test_password.py +++ b/frappe/tests/test_password.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe import unittest -from frappe.utils.password import update_password, check_password, passlibctx +from frappe.utils.password import update_password, check_password, passlibctx, encrypt, decrypt class TestPassword(unittest.TestCase): def setUp(self): @@ -105,6 +105,16 @@ class TestPassword(unittest.TestCase): doc.save() self.assertEqual(doc.get_password(raise_exception=False), None) + def test_custom_encryption_key(self): + text = 'Frappe Framework' + custom_encryption_key = 'DFTBA' + + encrypted_text = encrypt(text, encryption_key=custom_encryption_key) + decrypted_text = decrypt(encrypted_text, encryption_key=custom_encryption_key) + + self.assertEqual(text, decrypted_text) + + pass def get_password_list(doc): return frappe.db.sql("""SELECT `password` diff --git a/frappe/utils/password.py b/frappe/utils/password.py index fbed3cd8e7..d97c788568 100644 --- a/frappe/utils/password.py +++ b/frappe/utils/password.py @@ -157,20 +157,20 @@ def create_auth_table(): frappe.db.create_auth_table() -def encrypt(pwd): - cipher_suite = Fernet(encode(get_encryption_key())) - cipher_text = cstr(cipher_suite.encrypt(encode(pwd))) +def encrypt(txt, encryption_key=None): + cipher_suite = Fernet(encode(encryption_key or get_encryption_key())) + cipher_text = cstr(cipher_suite.encrypt(encode(txt))) return cipher_text -def decrypt(pwd): +def decrypt(txt, encryption_key=None): try: - cipher_suite = Fernet(encode(get_encryption_key())) - plain_text = cstr(cipher_suite.decrypt(encode(pwd))) + cipher_suite = Fernet(encode(encryption_key or get_encryption_key())) + plain_text = cstr(cipher_suite.decrypt(encode(txt))) return plain_text except InvalidToken: # encryption_key in site_config is changed and not valid - frappe.throw(_('Encryption key is invalid, Please check site_config.json')) + frappe.throw(_('Encryption key is invalid' + '!' if encryption_key else ', please check site_config.json.')) def get_encryption_key(): From 464e93a405d831d6496d56956673068f3282436e Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Tue, 1 Jun 2021 21:32:32 +0530 Subject: [PATCH 155/495] fix: Allow only use of Fernet generated key for using custom encryption_key (#13399) * fix: only allow keys generated by fernet in encrypt()/decrypt() * fix: sider and semgrep fixes --- frappe/tests/test_password.py | 4 ++-- frappe/utils/password.py | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/frappe/tests/test_password.py b/frappe/tests/test_password.py index 9b68f3aace..6bc4bc7729 100644 --- a/frappe/tests/test_password.py +++ b/frappe/tests/test_password.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe import unittest from frappe.utils.password import update_password, check_password, passlibctx, encrypt, decrypt - +from cryptography.fernet import Fernet class TestPassword(unittest.TestCase): def setUp(self): frappe.delete_doc('Email Account', 'Test Email Account Password') @@ -107,7 +107,7 @@ class TestPassword(unittest.TestCase): def test_custom_encryption_key(self): text = 'Frappe Framework' - custom_encryption_key = 'DFTBA' + custom_encryption_key = Fernet.generate_key().decode() encrypted_text = encrypt(text, encryption_key=custom_encryption_key) decrypted_text = decrypt(encrypted_text, encryption_key=custom_encryption_key) diff --git a/frappe/utils/password.py b/frappe/utils/password.py index d97c788568..34719eb384 100644 --- a/frappe/utils/password.py +++ b/frappe/utils/password.py @@ -61,7 +61,7 @@ def set_encrypted_password(doctype, name, pwd, fieldname='password'): except frappe.db.DataError as e: if ((frappe.db.db_type == 'mariadb' and e.args[0] == DATA_TOO_LONG) or (frappe.db.db_type == 'postgres' and e.pgcode == STRING_DATA_RIGHT_TRUNCATION)): - frappe.throw("Most probably your password is too long.", exc=e) + frappe.throw(_("Most probably your password is too long.", exc=e)) raise e @@ -158,12 +158,21 @@ def create_auth_table(): def encrypt(txt, encryption_key=None): - cipher_suite = Fernet(encode(encryption_key or get_encryption_key())) + # Only use Fernet.generate_key().decode() to enter encyption_key value + + try: + cipher_suite = Fernet(encode(encryption_key or get_encryption_key())) + except Exception: + # encryption_key is not in 32 url-safe base64-encoded format + frappe.throw(_('Encryption key is in invalid format!')) + cipher_text = cstr(cipher_suite.encrypt(encode(txt))) return cipher_text def decrypt(txt, encryption_key=None): + # Only use encryption_key value generated with Fernet.generate_key().decode() + try: cipher_suite = Fernet(encode(encryption_key or get_encryption_key())) plain_text = cstr(cipher_suite.decrypt(encode(txt))) From 8e3b358b9fbeb18cdbf04aeff1359e3bbaf694cb Mon Sep 17 00:00:00 2001 From: leela Date: Mon, 31 May 2021 22:15:57 +0530 Subject: [PATCH 156/495] refactor: email queue builder is added replaced queue.send function with Queue builder. Queue builder builds a queue from sendmail request and sends the mail if requested to send mail immediately. --- frappe/__init__.py | 10 +- .../email/doctype/email_queue/email_queue.py | 307 +++++++++++++++++- frappe/email/doctype/newsletter/newsletter.py | 1 - frappe/email/queue.py | 252 +------------- frappe/email/test_email_body.py | 21 +- frappe/tests/test_email.py | 13 +- frappe/utils/error.py | 2 +- 7 files changed, 327 insertions(+), 279 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 9b208f7c2d..880e5d48a4 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -528,16 +528,20 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message if not delayed: now = True - from frappe.email import queue - queue.send(recipients=recipients, sender=sender, + from frappe.email.doctype.email_queue.email_queue import QueueBuilder + builder = QueueBuilder(recipients=recipients, sender=sender, subject=subject, message=message, text_content=text_content, reference_doctype = doctype or reference_doctype, reference_name = name or reference_name, add_unsubscribe_link=add_unsubscribe_link, unsubscribe_method=unsubscribe_method, unsubscribe_params=unsubscribe_params, unsubscribe_message=unsubscribe_message, attachments=attachments, reply_to=reply_to, cc=cc, bcc=bcc, message_id=message_id, in_reply_to=in_reply_to, send_after=send_after, expose_recipients=expose_recipients, send_priority=send_priority, queue_separately=queue_separately, - communication=communication, now=now, read_receipt=read_receipt, is_notification=is_notification, + communication=communication, read_receipt=read_receipt, is_notification=is_notification, inline_images=inline_images, header=header, print_letterhead=print_letterhead, with_container=with_container) + # build email queue and send the email if send_now is True. + builder.process(send_now=now) + + whitelisted = [] guest_methods = [] xss_safe_methods = [] diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py index 394fe50468..dad473b8aa 100644 --- a/frappe/email/doctype/email_queue/email_queue.py +++ b/frappe/email/doctype/email_queue/email_queue.py @@ -9,14 +9,18 @@ from rq.timeouts import JobTimeoutException import smtplib import quopri from email.parser import Parser +from email.policy import SMTPUTF8 +from html2text import html2text +from six.moves import html_parser as HTMLParser import frappe from frappe import _, safe_encode, task from frappe.model.document import Document -from frappe.email.queue import get_unsubcribed_url -from frappe.email.email_body import add_attachment -from frappe.utils import cint -from email.policy import SMTPUTF8 +from frappe.email.queue import get_unsubcribed_url, get_unsubscribe_message +from frappe.email.email_body import add_attachment, get_formatted_html, get_email +from frappe.utils import cint, split_emails, add_days, nowdate, cstr +from frappe.email.doctype.email_account.email_account import EmailAccount + MAX_RETRY_COUNT = 3 class EmailQueue(Document): @@ -41,6 +45,19 @@ class EmailQueue(Document): duplicate.set_recipients(recipients) return duplicate + @classmethod + def new(cls, doc_data, ignore_permissions=False): + data = doc_data.copy() + if not data.get('recipients'): + return + + recipients = data.pop('recipients') + doc = frappe.new_doc(cls.DOCTYPE) + doc.update(data) + doc.set_recipients(recipients) + doc.insert(ignore_permissions=ignore_permissions) + return doc + @classmethod def find(cls, name): return frappe.get_doc(cls.DOCTYPE, name) @@ -74,8 +91,6 @@ class EmailQueue(Document): return json.loads(self.attachments) if self.attachments else [] def get_email_account(self): - from frappe.email.doctype.email_account.email_account import EmailAccount - if self.email_account: return frappe.get_doc('Email Account', self.email_account) @@ -300,3 +315,283 @@ def send_now(name): def on_doctype_update(): """Add index in `tabCommunication` for `(reference_doctype, reference_name)`""" frappe.db.add_index('Email Queue', ('status', 'send_after', 'priority', 'creation'), 'index_bulk_flush') + +class QueueBuilder: + """Builds Email Queue from the given data + """ + def __init__(self, recipients=None, sender=None, subject=None, message=None, + text_content=None, reference_doctype=None, reference_name=None, + unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None, + attachments=None, reply_to=None, cc=None, bcc=None, message_id=None, in_reply_to=None, + send_after=None, expose_recipients=None, send_priority=1, communication=None, + read_receipt=None, queue_separately=False, is_notification=False, + add_unsubscribe_link=1, inline_images=None, header=None, + print_letterhead=False, with_container=False): + """Add email to sending queue (Email Queue) + + :param recipients: List of recipients. + :param sender: Email sender. + :param subject: Email subject. + :param message: Email message. + :param text_content: Text version of email message. + :param reference_doctype: Reference DocType of caller document. + :param reference_name: Reference name of caller document. + :param send_priority: Priority for Email Queue, default 1. + :param unsubscribe_method: URL method for unsubscribe. Default is `/api/method/frappe.email.queue.unsubscribe`. + :param unsubscribe_params: additional params for unsubscribed links. default are name, doctype, email + :param attachments: Attachments to be sent. + :param reply_to: Reply to be captured here (default inbox) + :param in_reply_to: Used to send the Message-Id of a received email back as In-Reply-To. + :param send_after: Send this email after the given datetime. If value is in integer, then `send_after` will be the automatically set to no of days from current date. + :param communication: Communication link to be set in Email Queue record + :param queue_separately: Queue each email separately + :param is_notification: Marks email as notification so will not trigger notifications from system + :param add_unsubscribe_link: Send unsubscribe link in the footer of the Email, default 1. + :param inline_images: List of inline images as {"filename", "filecontent"}. All src properties will be replaced with random Content-Id + :param header: Append header in email (boolean) + :param with_container: Wraps email inside styled container + """ + + self._unsubscribe_method = unsubscribe_method + self._recipients = recipients + self._cc = cc + self._bcc = bcc + self._send_after = send_after + self._sender = sender + self._text_content = text_content + self._message = message + self._add_unsubscribe_link = add_unsubscribe_link + self._unsubscribe_message = unsubscribe_message + self._attachments = attachments + + self._unsubscribed_user_emails = None + self._email_account = None + + self.unsubscribe_params = unsubscribe_params + self.subject = subject + self.reference_doctype = reference_doctype + self.reference_name = reference_name + self.expose_recipients = expose_recipients + self.with_container = with_container + self.header = header + self.reply_to = reply_to + self.message_id = message_id + self.in_reply_to = in_reply_to + self.send_priority = send_priority + self.communication = communication + self.read_receipt = read_receipt + self.queue_separately = queue_separately + self.is_notification = is_notification + self.inline_images = inline_images + self.print_letterhead = print_letterhead + + @property + def unsubscribe_method(self): + return self._unsubscribe_method or '/api/method/frappe.email.queue.unsubscribe' + + def _get_emails_list(self, emails=None): + emails = split_emails(emails) if isinstance(emails, str) else (emails or []) + return [each for each in set(emails) if each] + + @property + def recipients(self): + return self._get_emails_list(self._recipients) + + @property + def cc(self): + return self._get_emails_list(self._cc) + + @property + def bcc(self): + return self._get_emails_list(self._bcc) + + @property + def send_after(self): + if isinstance(self._send_after, int): + return add_days(nowdate(), self._send_after) + return self._send_after + + @property + def sender(self): + if not self._sender or self._sender == "Administrator": + email_account = self.get_outgoing_email_account() + return email_account.default_sender + return self._sender + + def email_text_content(self): + unsubscribe_msg = self.unsubscribe_message() + unsubscribe_text_message = (unsubscribe_msg and unsubscribe_msg.text) or '' + + if self._text_content: + return self._text_content + unsubscribe_text_message + + try: + text_content = html2text(self._message) + except HTMLParser.HTMLParseError: + text_content = "See html attachment" + return text_content + unsubscribe_text_message + + def email_html_content(self): + email_account = self.get_outgoing_email_account() + return get_formatted_html(self.subject, self._message, header=self.header, + email_account=email_account, unsubscribe_link=self.unsubscribe_message(), + with_container=self.with_container) + + def should_include_unsubscribe_link(self): + return (self._add_unsubscribe_link == 1 + and self.reference_doctype + and (self._unsubscribe_message or self.reference_doctype=="Newsletter")) + + def unsubscribe_message(self): + if self.should_include_unsubscribe_link(): + return get_unsubscribe_message(self._unsubscribe_message, self.expose_recipients) + + def get_outgoing_email_account(self): + if self._email_account: + return self._email_account + + self._email_account = EmailAccount.find_outgoing( + match_by_doctype=self.reference_doctype, match_by_email=self._sender, _raise_error=True) + return self._email_account + + def get_unsubscribed_user_emails(self): + if self._unsubscribed_user_emails is not None: + return self._unsubscribed_user_emails + + all_ids = tuple(set(self.recipients + self.cc)) + + unsubscribed = frappe.db.sql_list(''' + SELECT + distinct email + from + `tabEmail Unsubscribe` + where + email in %(all_ids)s + and ( + ( + reference_doctype = %(reference_doctype)s + and reference_name = %(reference_name)s + ) + or global_unsubscribe = 1 + ) + ''', { + 'all_ids': all_ids, + 'reference_doctype': self.reference_doctype, + 'reference_name': self.reference_name, + }) + + self._unsubscribed_user_emails = unsubscribed or [] + return self._unsubscribed_user_emails + + def final_recipients(self): + unsubscribed_emails = self.get_unsubscribed_user_emails() + return [mail_id for mail_id in self.recipients if mail_id not in unsubscribed_emails] + + def final_cc(self): + unsubscribed_emails = self.get_unsubscribed_user_emails() + return [mail_id for mail_id in self.cc if mail_id not in unsubscribed_emails] + + def get_attachments(self): + attachments = [] + if self._attachments: + # store attachments with fid or print format details, to be attached on-demand later + for att in self._attachments: + if att.get('fid'): + attachments.append(att) + elif att.get("print_format_attachment") == 1: + if not att.get('lang', None): + att['lang'] = frappe.local.lang + att['print_letterhead'] = self.print_letterhead + attachments.append(att) + return attachments + + def prepare_email_content(self): + mail = get_email(recipients=self.final_recipients(), + sender=self.sender, + subject=self.subject, + formatted=self.email_html_content(), + text_content=self.email_text_content(), + attachments=self._attachments, + reply_to=self.reply_to, + cc=self.final_cc(), + bcc=self.bcc, + email_account=self.get_outgoing_email_account(), + expose_recipients=self.expose_recipients, + inline_images=self.inline_images, + header=self.header) + + mail.set_message_id(self.message_id, self.is_notification) + if self.read_receipt: + mail.msg_root["Disposition-Notification-To"] = self.sender + if self.in_reply_to: + mail.set_in_reply_to(self.in_reply_to) + return mail + + def process(self, send_now=False): + """Build and return the email queues those are created. + + Sends email incase if it is requested to send now. + """ + final_recipients = self.final_recipients() + queue_separately = (final_recipients and self.queue_separately) or len(final_recipients) > 20 + if not (final_recipients + self.final_cc()): + return [] + + email_queues = [] + queue_data = self.as_dict(include_recipients=False) + if not queue_data: + return [] + + if not queue_separately: + recipients = list(set(final_recipients + self.final_cc() + self.bcc)) + q = EmailQueue.new({**queue_data, **{'recipients': recipients}}, ignore_permissions=True) + email_queues.append(q) + else: + for r in final_recipients: + recipients = [r] if email_queues else list(set([r] + self.final_cc() + self.bcc)) + q = EmailQueue.new({**queue_data, **{'recipients': recipients}}, ignore_permissions=True) + email_queues.append(q) + + if send_now: + for doc in email_queues: + doc.send() + return email_queues + + def as_dict(self, include_recipients=True): + email_account = self.get_outgoing_email_account() + email_account_name = email_account and email_account.is_exists_in_db() and email_account.name + + mail = self.prepare_email_content() + try: + mail_to_string = cstr(mail.as_string()) + except frappe.InvalidEmailAddressError: + # bad Email Address - don't add to queue + frappe.log_error('Invalid Email ID Sender: {0}, Recipients: {1}, \nTraceback: {2} ' + .format(self.sender, ', '.join(self.final_recipients()), traceback.format_exc()), + 'Email Not Sent' + ) + return + + d = { + 'priority': self.send_priority, + 'attachments': json.dumps(self.get_attachments()), + 'message_id': mail.msg_root["Message-Id"].strip(" <>"), + 'message': mail_to_string, + 'sender': self.sender, + 'reference_doctype': self.reference_doctype, + 'reference_name': self.reference_name, + 'add_unsubscribe_link': self._add_unsubscribe_link, + 'unsubscribe_method': self.unsubscribe_method, + 'unsubscribe_params': self.unsubscribe_params, + 'expose_recipients': self.expose_recipients, + 'communication': self.communication, + 'send_after': self.send_after, + 'show_as_cc': ",".join(self.final_cc()), + 'show_as_bcc': ','.join(self.bcc), + 'email_account': email_account_name or None + } + + if include_recipients: + d['recipients'] = self.final_recipients() + + return d diff --git a/frappe/email/doctype/newsletter/newsletter.py b/frappe/email/doctype/newsletter/newsletter.py index 6412338e96..469390322c 100755 --- a/frappe/email/doctype/newsletter/newsletter.py +++ b/frappe/email/doctype/newsletter/newsletter.py @@ -8,7 +8,6 @@ import frappe.utils from frappe import throw, _ from frappe.website.website_generator import WebsiteGenerator from frappe.utils.verified_command import get_signed_params, verify_request -from frappe.email.queue import send from frappe.email.doctype.email_group.email_group import add_subscribers from frappe.utils import parse_addr, now_datetime, markdown, validate_email_address diff --git a/frappe/email/queue.py b/frappe/email/queue.py index 52c91baf1c..451d1c970e 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -3,257 +3,9 @@ from __future__ import unicode_literals import frappe -import sys -from six.moves import html_parser as HTMLParser -import smtplib, quopri, json -from frappe import msgprint, _, safe_decode, safe_encode, enqueue -from frappe.email.smtp import SMTPServer -from frappe.email.doctype.email_account.email_account import EmailAccount -from frappe.email.email_body import get_email, get_formatted_html, add_attachment +from frappe import msgprint, _ from frappe.utils.verified_command import get_signed_params, verify_request -from html2text import html2text -from frappe.utils import get_url, nowdate, now_datetime, add_days, split_emails, cstr, cint -from rq.timeouts import JobTimeoutException -from six import text_type, string_types, PY3 -from email.parser import Parser - - -class EmailLimitCrossedError(frappe.ValidationError): pass - -def send(recipients=None, sender=None, subject=None, message=None, text_content=None, reference_doctype=None, - reference_name=None, unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None, - attachments=None, reply_to=None, cc=None, bcc=None, message_id=None, in_reply_to=None, send_after=None, - expose_recipients=None, send_priority=1, communication=None, now=False, read_receipt=None, - queue_separately=False, is_notification=False, add_unsubscribe_link=1, inline_images=None, - header=None, print_letterhead=False, with_container=False): - """Add email to sending queue (Email Queue) - - :param recipients: List of recipients. - :param sender: Email sender. - :param subject: Email subject. - :param message: Email message. - :param text_content: Text version of email message. - :param reference_doctype: Reference DocType of caller document. - :param reference_name: Reference name of caller document. - :param send_priority: Priority for Email Queue, default 1. - :param unsubscribe_method: URL method for unsubscribe. Default is `/api/method/frappe.email.queue.unsubscribe`. - :param unsubscribe_params: additional params for unsubscribed links. default are name, doctype, email - :param attachments: Attachments to be sent. - :param reply_to: Reply to be captured here (default inbox) - :param in_reply_to: Used to send the Message-Id of a received email back as In-Reply-To. - :param send_after: Send this email after the given datetime. If value is in integer, then `send_after` will be the automatically set to no of days from current date. - :param communication: Communication link to be set in Email Queue record - :param now: Send immediately (don't send in the background) - :param queue_separately: Queue each email separately - :param is_notification: Marks email as notification so will not trigger notifications from system - :param add_unsubscribe_link: Send unsubscribe link in the footer of the Email, default 1. - :param inline_images: List of inline images as {"filename", "filecontent"}. All src properties will be replaced with random Content-Id - :param header: Append header in email (boolean) - :param with_container: Wraps email inside styled container - """ - if not unsubscribe_method: - unsubscribe_method = "/api/method/frappe.email.queue.unsubscribe" - - if not recipients and not cc: - return - - if not cc: - cc = [] - if not bcc: - bcc = [] - - if isinstance(recipients, string_types): - recipients = split_emails(recipients) - - if isinstance(cc, string_types): - cc = split_emails(cc) - - if isinstance(bcc, string_types): - bcc = split_emails(bcc) - - if isinstance(send_after, int): - send_after = add_days(nowdate(), send_after) - - email_account = EmailAccount.find_outgoing( - match_by_doctype=reference_doctype, match_by_email=sender, _raise_error=True) - - if not sender or sender == "Administrator": - sender = email_account.default_sender - - if not text_content: - try: - text_content = html2text(message) - except HTMLParser.HTMLParseError: - text_content = "See html attachment" - - recipients = list(set(recipients)) - cc = list(set(cc)) - - all_ids = tuple(recipients + cc) - - unsubscribed = frappe.db.sql_list(''' - SELECT - distinct email - from - `tabEmail Unsubscribe` - where - email in %(all_ids)s - and ( - ( - reference_doctype = %(reference_doctype)s - and reference_name = %(reference_name)s - ) - or global_unsubscribe = 1 - ) - ''', { - 'all_ids': all_ids, - 'reference_doctype': reference_doctype, - 'reference_name': reference_name, - }) - - recipients = [r for r in recipients if r and r not in unsubscribed] - - if cc: - cc = [r for r in cc if r and r not in unsubscribed] - - if not recipients and not cc: - # Recipients may have been unsubscribed, exit quietly - return - - email_text_context = text_content - - should_append_unsubscribe = (add_unsubscribe_link - and reference_doctype - and (unsubscribe_message or reference_doctype=="Newsletter") - and add_unsubscribe_link==1) - - unsubscribe_link = None - if should_append_unsubscribe: - unsubscribe_link = get_unsubscribe_message(unsubscribe_message, expose_recipients) - email_text_context += unsubscribe_link.text - - email_content = get_formatted_html(subject, message, - email_account=email_account, header=header, - unsubscribe_link=unsubscribe_link, with_container=with_container) - - # add to queue - add(recipients, sender, subject, - formatted=email_content, - text_content=email_text_context, - reference_doctype=reference_doctype, - reference_name=reference_name, - attachments=attachments, - reply_to=reply_to, - cc=cc, - bcc=bcc, - message_id=message_id, - in_reply_to=in_reply_to, - send_after=send_after, - send_priority=send_priority, - email_account=email_account, - communication=communication, - add_unsubscribe_link=add_unsubscribe_link, - unsubscribe_method=unsubscribe_method, - unsubscribe_params=unsubscribe_params, - expose_recipients=expose_recipients, - read_receipt=read_receipt, - queue_separately=queue_separately, - is_notification = is_notification, - inline_images = inline_images, - header=header, - now=now, - print_letterhead=print_letterhead) - - -def add(recipients, sender, subject, **kwargs): - """Add to Email Queue""" - if kwargs.get('queue_separately') or len(recipients) > 20: - email_queue = None - for r in recipients: - if not email_queue: - email_queue = get_email_queue([r], sender, subject, **kwargs) - if kwargs.get('now'): - email_queue.send() - else: - duplicate = email_queue.get_duplicate([r]) - duplicate.insert(ignore_permissions=True) - - if kwargs.get('now'): - duplicate.send() - - frappe.db.commit() - else: - email_queue = get_email_queue(recipients, sender, subject, **kwargs) - if kwargs.get('now'): - email_queue.send() - -def get_email_queue(recipients, sender, subject, **kwargs): - '''Make Email Queue object''' - e = frappe.new_doc('Email Queue') - e.priority = kwargs.get('send_priority') - attachments = kwargs.get('attachments') - if attachments: - # store attachments with fid or print format details, to be attached on-demand later - _attachments = [] - for att in attachments: - if att.get('fid'): - _attachments.append(att) - elif att.get("print_format_attachment") == 1: - if not att.get('lang', None): - att['lang'] = frappe.local.lang - att['print_letterhead'] = kwargs.get('print_letterhead') - _attachments.append(att) - e.attachments = json.dumps(_attachments) - - try: - mail = get_email(recipients, - sender=sender, - subject=subject, - formatted=kwargs.get('formatted'), - text_content=kwargs.get('text_content'), - attachments=kwargs.get('attachments'), - reply_to=kwargs.get('reply_to'), - cc=kwargs.get('cc'), - bcc=kwargs.get('bcc'), - email_account=kwargs.get('email_account'), - expose_recipients=kwargs.get('expose_recipients'), - inline_images=kwargs.get('inline_images'), - header=kwargs.get('header')) - - mail.set_message_id(kwargs.get('message_id'),kwargs.get('is_notification')) - if kwargs.get('read_receipt'): - mail.msg_root["Disposition-Notification-To"] = sender - if kwargs.get('in_reply_to'): - mail.set_in_reply_to(kwargs.get('in_reply_to')) - - e.message_id = mail.msg_root["Message-Id"].strip(" <>") - e.message = cstr(mail.as_string()) - e.sender = mail.sender - - except frappe.InvalidEmailAddressError: - # bad Email Address - don't add to queue - import traceback - frappe.log_error('Invalid Email ID Sender: {0}, Recipients: {1}, \nTraceback: {2} '.format(mail.sender, - ', '.join(mail.recipients), traceback.format_exc()), 'Email Not Sent') - - recipients = list(set(recipients + kwargs.get('cc', []) + kwargs.get('bcc', []))) - email_account = kwargs.get('email_account') - email_account_name = email_account and email_account.is_exists_in_db() and email_account.name - - e.set_recipients(recipients) - e.reference_doctype = kwargs.get('reference_doctype') - e.reference_name = kwargs.get('reference_name') - e.add_unsubscribe_link = kwargs.get("add_unsubscribe_link") - e.unsubscribe_method = kwargs.get('unsubscribe_method') - e.unsubscribe_params = kwargs.get('unsubscribe_params') - e.expose_recipients = kwargs.get('expose_recipients') - e.communication = kwargs.get('communication') - e.send_after = kwargs.get('send_after') - e.show_as_cc = ",".join(kwargs.get('cc', [])) - e.show_as_bcc = ",".join(kwargs.get('bcc', [])) - e.email_account = email_account_name or None - e.insert(ignore_permissions=True) - return e +from frappe.utils import get_url, now_datetime, cint def get_emails_sent_this_month(): return frappe.db.sql(""" diff --git a/frappe/email/test_email_body.py b/frappe/email/test_email_body.py index 33668cddba..5b37102a17 100644 --- a/frappe/email/test_email_body.py +++ b/frappe/email/test_email_body.py @@ -7,8 +7,7 @@ from frappe import safe_decode from frappe.email.receive import Email from frappe.email.email_body import (replace_filename_with_cid, get_email, inline_style_in_html, get_header) -from frappe.email.queue import get_email_queue -from frappe.email.doctype.email_queue.email_queue import SendMailContext +from frappe.email.doctype.email_queue.email_queue import SendMailContext, QueueBuilder from six import PY3 class TestEmailBody(unittest.TestCase): @@ -50,27 +49,25 @@ This is the text version of this email uni_chr1 = unichr(40960) uni_chr2 = unichr(1972) - email = get_email_queue( + queue_doc = QueueBuilder( recipients=['test@example.com'], sender='me@example.com', subject='Test Subject', - content='

    ' + uni_chr1 + 'abcd' + uni_chr2 + '

    ', - formatted='

    ' + uni_chr1 + 'abcd' + uni_chr2 + '

    ', - text_content='whatever') - mail_ctx = SendMailContext(queue_doc = email) + message='

    ' + uni_chr1 + 'abcd' + uni_chr2 + '

    ', + text_content='whatever').process()[0] + mail_ctx = SendMailContext(queue_doc = queue_doc) result = mail_ctx.build_message(recipient_email = 'test@test.com') self.assertTrue(b"

    =EA=80=80abcd=DE=B4

    " in result) def test_prepare_message_returns_cr_lf(self): - email = get_email_queue( + queue_doc = QueueBuilder( recipients=['test@example.com'], sender='me@example.com', subject='Test Subject', - content='

    \n this is a test of newlines\n' + '

    ', - formatted='

    \n this is a test of newlines\n' + '

    ', - text_content='whatever') + message='

    \n this is a test of newlines\n' + '

    ', + text_content='whatever').process()[0] - mail_ctx = SendMailContext(queue_doc = email) + mail_ctx = SendMailContext(queue_doc = queue_doc) result = safe_decode(mail_ctx.build_message(recipient_email='test@test.com')) if PY3: diff --git a/frappe/tests/test_email.py b/frappe/tests/test_email.py index 74962dd84c..58ffe7531d 100644 --- a/frappe/tests/test_email.py +++ b/frappe/tests/test_email.py @@ -143,7 +143,8 @@ class TestEmail(unittest.TestCase): self.assertEqual(len(queue_recipients), 2) def test_unsubscribe(self): - from frappe.email.queue import unsubscribe, send + from frappe.email.queue import unsubscribe + from frappe.email.doctype.email_queue.email_queue import QueueBuilder unsubscribe(doctype="User", name="Administrator", email="test@example.com") self.assertTrue(frappe.db.get_value("Email Unsubscribe", @@ -152,11 +153,11 @@ class TestEmail(unittest.TestCase): before = frappe.db.sql("""select count(name) from `tabEmail Queue` where status='Not Sent'""")[0][0] - send(recipients=['test@example.com', 'test1@example.com'], - sender="admin@example.com", - reference_doctype='User', reference_name="Administrator", - subject='Testing Email Queue', message='This is mail is queued!', unsubscribe_message="Unsubscribe") - + builder = QueueBuilder(recipients=['test@example.com', 'test1@example.com'], + sender="admin@example.com", + reference_doctype='User', reference_name="Administrator", + subject='Testing Email Queue', message='This is mail is queued!', unsubscribe_message="Unsubscribe") + builder.process() # this is sent async (?) email_queue = frappe.db.sql("""select name from `tabEmail Queue` where status='Not Sent'""", diff --git a/frappe/utils/error.py b/frappe/utils/error.py index 2d8d6491a5..68577e47e5 100644 --- a/frappe/utils/error.py +++ b/frappe/utils/error.py @@ -215,7 +215,7 @@ def raise_error_on_no_output(error_message, error_type=None, keep_quiet=None): >>> @raise_error_on_no_output("Ingradients missing") ... def get_indradients(_raise_error=1): return ... - >>> get_indradients() + >>> get_ingradients() `Exception Name`: Ingradients missing """ def decorator_raise_error_on_no_output(func): From 43972e28a73b9d08cd073065fc94d0175cf872ed Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 1 Jun 2021 22:17:24 +0530 Subject: [PATCH 157/495] feat: group by tags in report view --- frappe/public/js/frappe/form/formatters.js | 11 ++++++++--- frappe/public/js/frappe/ui/group_by/group_by.js | 5 +++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js index 89c34ed80c..83fe9ffa9c 100644 --- a/frappe/public/js/frappe/form/formatters.js +++ b/frappe/public/js/frappe/form/formatters.js @@ -221,9 +221,13 @@ frappe.form.formatters = { Tag: function(value) { var html = ""; $.each((value || "").split(","), function(i, v) { - if(v) html+= ''+v +''; + if (v) html += ` + + ${v} + `; }); return html; }, @@ -310,6 +314,7 @@ frappe.form.get_formatter = function(fieldtype) { frappe.format = function(value, df, options, doc) { if(!df) df = {"fieldtype":"Data"}; + if (df.fieldname == '_user_tags') df.fieldtype = 'Tag' var fieldtype = df.fieldtype || "Data"; // format Dynamic Link as a Link diff --git a/frappe/public/js/frappe/ui/group_by/group_by.js b/frappe/public/js/frappe/ui/group_by/group_by.js index 3ebf9c9d3d..a113396643 100644 --- a/frappe/public/js/frappe/ui/group_by/group_by.js +++ b/frappe/public/js/frappe/ui/group_by/group_by.js @@ -381,10 +381,11 @@ frappe.ui.GroupBy = class { this.group_by_fields = {}; this.all_fields = {}; - let fields = this.report_view.meta.fields.filter((f) => + const fields = this.report_view.meta.fields.filter((f) => ['Select', 'Link', 'Data', 'Int', 'Check'].includes(f.fieldtype) ); - this.group_by_fields[this.doctype] = fields; + const tag_field = {fieldname:'_user_tags', fieldtype:'Data', label:__('Tags')}; + this.group_by_fields[this.doctype] = fields.concat(tag_field); this.all_fields[this.doctype] = this.report_view.meta.fields; const standard_fields_filter = (df) => From 0e3d8626c49d464aaa4e4a541c273678887352d8 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 2 Jun 2021 12:15:58 +0530 Subject: [PATCH 158/495] fix: Remove ellipsis to avoid cliping of useful content --- frappe/public/js/frappe/form/controls/link.js | 2 +- frappe/public/scss/common/awesomeplete.scss | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index af92f3b7a9..83f3f8dd70 100644 --- a/frappe/public/js/frappe/form/controls/link.js +++ b/frappe/public/js/frappe/form/controls/link.js @@ -141,7 +141,7 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat return $('
  • ') .data('item.autocomplete', d) .prop('aria-selected', 'false') - .html(`

    ${html}

    `) + .html(`

    ${html}

    `) .get(0); }, sort: function() { diff --git a/frappe/public/scss/common/awesomeplete.scss b/frappe/public/scss/common/awesomeplete.scss index 096da1e2fd..b9e8035d68 100644 --- a/frappe/public/scss/common/awesomeplete.scss +++ b/frappe/public/scss/common/awesomeplete.scss @@ -31,6 +31,7 @@ margin: 0; padding: var(--padding-xs); z-index: 1; + min-width: 250px; &> li { cursor: pointer; @@ -44,6 +45,8 @@ } p { margin-bottom: 0; + overflow-y: clip; + text-overflow: ellipsis; } } From e489c1b594697931972cf819c44a41b25311a75c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 2 Jun 2021 13:05:16 +0530 Subject: [PATCH 159/495] fix: Added select permission for Module Def doctype to All role --- frappe/core/doctype/module_def/module_def.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/module_def/module_def.json b/frappe/core/doctype/module_def/module_def.json index 7a8bfd76a7..4de046bbb6 100644 --- a/frappe/core/doctype/module_def/module_def.json +++ b/frappe/core/doctype/module_def/module_def.json @@ -55,7 +55,7 @@ "link_fieldname": "module" } ], - "modified": "2020-08-06 12:39:30.740379", + "modified": "2021-06-02 13:04:53.118716", "modified_by": "Administrator", "module": "Core", "name": "Module Def", @@ -69,6 +69,7 @@ "read": 1, "report": 1, "role": "Administrator", + "select": 1, "share": 1, "write": 1 }, @@ -78,7 +79,14 @@ "read": 1, "report": 1, "role": "System Manager", + "select": 1, "write": 1 + }, + { + "read": 1, + "report": 1, + "role": "All", + "select": 1 } ], "show_name_in_global_search": 1, From a457501422e06b22293ab2bfd19d8c2a8f509ace Mon Sep 17 00:00:00 2001 From: walstanb Date: Wed, 2 Jun 2021 16:20:51 +0530 Subject: [PATCH 160/495] fix: removed duplicate fields from Customize Form --- .../doctype/customize_form/customize_form.json | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json index 1807678673..b9dde88126 100644 --- a/frappe/custom/doctype/customize_form/customize_form.json +++ b/frappe/custom/doctype/customize_form/customize_form.json @@ -288,16 +288,6 @@ "fieldname": "autoname", "fieldtype": "Data", "label": "Auto Name" - }, - { - "fieldname": "default_email_template", - "fieldtype": "Link", - "label": "Default Email Template", - "options": "Email Template" - }, - { - "fieldname": "column_break_26", - "fieldtype": "Column Break" } ], "hide_toolbar": 1, @@ -306,7 +296,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-04-29 21:21:06.476372", + "modified": "2021-06-02 06:49:16.782806", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form", From 879ecaacabad73ea13f583e61d85f5a086a36f6c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 3 Jun 2021 10:40:36 +0530 Subject: [PATCH 161/495] fix: Custom page renderer logic --- frappe/website/path_resolver.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/frappe/website/path_resolver.py b/frappe/website/path_resolver.py index 0519d82735..bedd9f19ae 100644 --- a/frappe/website/path_resolver.py +++ b/frappe/website/path_resolver.py @@ -1,4 +1,5 @@ import re +import click from werkzeug.routing import Rule @@ -35,7 +36,7 @@ class PathResolver(): return frappe.flags.redirect_location, RedirectPage(self.path) endpoint = resolve_path(self.path) - custom_renderers = frappe.get_hooks('page_renderer') + custom_renderers = self.get_custom_page_renderers() renderers = custom_renderers + [StaticPage, WebFormPage, TemplatePage, ListPage, DocumentPage, PrintPage, NotFoundPage] for renderer in renderers: @@ -49,6 +50,27 @@ class PathResolver(): _endpoint, renderer_instance = self.resolve() return not isinstance(renderer_instance, NotFoundPage) + @staticmethod + def get_custom_page_renderers(): + custom_renderers = [] + for renderer_path in frappe.get_hooks('page_renderer') or []: + try: + renderer = frappe.get_attr(renderer_path) + if not hasattr(renderer, 'can_render'): + click.echo(f'{renderer.__name__} does not have can_render method') + continue + if not hasattr(renderer, 'render'): + click.echo(f'{renderer.__name__} does not have render method') + continue + + custom_renderers.append(renderer) + + except Exception: + click.echo(f'Failed to load page renderer. Import path: {renderer_path}') + + return custom_renderers + + def resolve_redirect(path, query_string=None): ''' From 254496450163cc308db0c9a23224b041daceb07d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 3 Jun 2021 10:41:22 +0530 Subject: [PATCH 162/495] test: Add test case to validate custom page renderer --- frappe/tests/test_website.py | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index 69425cdc45..84bf3ef8f1 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -3,9 +3,10 @@ from __future__ import unicode_literals import unittest import frappe -from frappe.website.serve import get_response -from frappe.website.utils import get_home_page, clear_website_cache from frappe.utils import set_request +from frappe.website.serve import get_response, get_response_content +from frappe.website.utils import (build_response, clear_website_cache, get_home_page) + class TestWebsite(unittest.TestCase): def setUp(self): @@ -178,6 +179,25 @@ class TestWebsite(unittest.TestCase): delattr(frappe.hooks, 'website_redirects') frappe.cache().delete_key('app_hooks') + def test_custom_page_renderer(self): + import frappe.hooks + frappe.hooks.page_renderer = ['frappe.tests.test_website.CustomPageRenderer'] + frappe.cache().delete_key('app_hooks') + set_request(method='GET', path='/custom') + response = get_response() + self.assertEqual(response.status_code, 3984) + + set_request(method='GET', path='/new') + content = get_response_content() + self.assertIn("
    Custom Page Response
    ", content) + + set_request(method='GET', path='/random') + response = get_response() + self.assertEqual(response.status_code, 404) + + delattr(frappe.hooks, 'page_renderer') + frappe.cache().delete_key('app_hooks') + def set_home_page_hook(key, value): from frappe import hooks @@ -189,3 +209,15 @@ def set_home_page_hook(key, value): setattr(hooks, key, value) frappe.cache().delete_key('app_hooks') +class CustomPageRenderer(): + def __init__(self, path, status_code=None): + self.path = path + # custom status code + self.status_code = 3984 + + def can_render(self): + if self.path in ('new', 'custom'): + return True + + def render(self): + return build_response(self.path, """
    Custom Page Response
    """, self.status_code) From 468b8fbd996142193107ecfc8f93a975646911d3 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 3 Jun 2021 11:48:49 +0530 Subject: [PATCH 163/495] fix: Remove unnecessary import --- frappe/website/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/website/utils.py b/frappe/website/utils.py index df3cdefa1f..0f5f182ea2 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -7,7 +7,6 @@ import re from functools import wraps import yaml -from past.builtins import cmp from six import iteritems from werkzeug.wrappers import Response From 73f320ee3974fe7bec4decb8e34cce403b6ecf03 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 3 Jun 2021 13:21:04 +0530 Subject: [PATCH 164/495] chore: Drop patches before added before v10 * This change will not allow direct upgrade from frappe versions older than v10 to v13 and newer * Removed patch records and execute commands that could've been guessed aren't needed by sleuthing and guessing. since 2017 had 3 major releases: v8, v9 and v10 unsure which execute lines are required so kept all that were last updated in that year --- frappe/patches.txt | 157 --------------------------------------------- 1 file changed, 157 deletions(-) diff --git a/frappe/patches.txt b/frappe/patches.txt index e70be0a37b..7605d8ea2b 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -1,11 +1,5 @@ frappe.patches.v12_0.remove_deprecated_fields_from_doctype #3 -execute:frappe.db.sql("""update `tabPatch Log` set patch=replace(patch, '.4_0.', '.v4_0.')""") #2014-05-12 -frappe.patches.v5_0.convert_to_barracuda_and_utf8mb4 execute:frappe.utils.global_search.setup_global_search_table() -frappe.patches.v8_0.update_global_search_table -frappe.patches.v7_0.update_auth -frappe.patches.v8_0.drop_in_dialog #2017-09-22 -frappe.patches.v7_2.remove_in_filter execute:frappe.reload_doc('core', 'doctype', 'doctype_action', force=True) #2019-09-23 execute:frappe.reload_doc('core', 'doctype', 'doctype_link', force=True) #2020-10-17 execute:frappe.reload_doc('core', 'doctype', 'doctype', force=True) #2017-09-22 @@ -14,7 +8,6 @@ frappe.patches.v11_0.drop_column_apply_user_permissions execute:frappe.reload_doc('core', 'doctype', 'custom_docperm') execute:frappe.reload_doc('core', 'doctype', 'docperm') #2018-05-29 execute:frappe.reload_doc('core', 'doctype', 'comment') -frappe.patches.v8_0.drop_is_custom_from_docperm execute:frappe.reload_doc('core', 'doctype', 'document_naming_rule', force=True) execute:frappe.reload_doc('core', 'doctype', 'module_def') #2020-08-28 execute:frappe.reload_doc('core', 'doctype', 'version') #2017-04-01 @@ -25,190 +18,40 @@ execute:frappe.reload_doc('core', 'doctype', 'communication') #2019-10-02 execute:frappe.reload_doc('core', 'doctype', 'server_script') frappe.patches.v11_0.replicate_old_user_permissions frappe.patches.v11_0.reload_and_rename_view_log #2019-01-03 -frappe.patches.v7_1.rename_scheduler_log_to_error_log -frappe.patches.v6_1.rename_file_data -frappe.patches.v7_0.re_route #2016-06-27 -frappe.patches.v8_0.update_records_in_global_search #11-05-2017 -frappe.patches.v8_0.update_published_in_global_search frappe.patches.v11_0.copy_fetch_data_from_options frappe.patches.v11_0.change_email_signature_fieldtype execute:frappe.reload_doc('core', 'doctype', 'activity_log') execute:frappe.reload_doc('core', 'doctype', 'deleted_document') execute:frappe.reload_doc('core', 'doctype', 'domain_settings') frappe.patches.v13_0.rename_custom_client_script -frappe.patches.v8_0.rename_page_role_to_has_role #2017-03-16 -frappe.patches.v7_2.setup_custom_perms #2017-01-19 -frappe.patches.v8_0.set_user_permission_for_page_and_report #2017-03-20 execute:frappe.reload_doc('core', 'doctype', 'role') #2017-05-23 execute:frappe.reload_doc('core', 'doctype', 'user') #2017-10-27 -execute:frappe.reload_doc('custom', 'doctype', 'custom_field') #2015-10-19 -execute:frappe.reload_doc('core', 'doctype', 'page') #2013-13-26 execute:frappe.reload_doc('core', 'doctype', 'report_column') execute:frappe.reload_doc('core', 'doctype', 'report_filter') execute:frappe.reload_doc('core', 'doctype', 'report') #2020-08-25 -execute:frappe.reload_doc('core', 'doctype', 'translation') #2016-03-03 -execute:frappe.reload_doc('email', 'doctype', 'email_alert') #2014-07-15 -execute:frappe.reload_doc('desk', 'doctype', 'todo') #2014-12-31-1 -execute:frappe.reload_doc('custom', 'doctype', 'property_setter') #2014-12-31-1 -execute:frappe.reload_doc('core', 'doctype', 'patch_log') #2016-10-31 -execute:frappe.reload_doctype("File") # 2015-10-19 execute:frappe.reload_doc('core', 'doctype', 'error_snapshot') -execute:frappe.clear_cache() -frappe.patches.v7_1.rename_scheduler_log_to_error_log -frappe.patches.v7_1.sync_language_doctype -frappe.patches.v7_0.rename_bulk_email_to_email_queue -frappe.patches.v7_1.rename_chinese_language_codes - -execute:frappe.db.sql("alter table `tabSessions` modify `user` varchar(255), engine=InnoDB") -execute:frappe.db.sql("delete from `tabDocField` where parent='0'") -frappe.patches.v4_0.change_varchar_length -frappe.patches.v6_4.reduce_varchar_length -frappe.patches.v5_2.change_checks_to_not_null -frappe.patches.v6_9.int_float_not_null #2015-11-25 -frappe.patches.v5_0.v4_to_v5 - -frappe.patches.v5_0.remove_shopping_cart_app -frappe.patches.v4_0.webnotes_to_frappe -execute:frappe.permissions.reset_perms("Module Def") -execute:import frappe.installer;frappe.installer.make_site_dirs() #2014-02-19 -frappe.patches.v4_0.rename_profile_to_user -frappe.patches.v4_0.deprecate_control_panel -frappe.patches.v4_0.remove_old_parent -frappe.patches.v4_0.rename_sitemap_to_route -frappe.patches.v4_0.website_sitemap_hierarchy -frappe.patches.v4_0.remove_index_sitemap -frappe.patches.v4_0.set_website_route_idx -frappe.patches.v4_0.add_delete_permission -frappe.patches.v4_0.set_todo_checked_as_closed -frappe.patches.v4_0.private_backups -frappe.patches.v4_0.set_module_in_report -frappe.patches.v4_0.update_datetime -frappe.patches.v4_0.file_manager_hooks execute:frappe.get_doc("User", "Guest").save() -frappe.patches.v4_0.update_custom_field_insert_after -frappe.patches.v4_0.deprecate_link_selects -frappe.patches.v4_0.set_user_gravatar -frappe.patches.v4_0.set_user_permissions -frappe.patches.v4_0.create_custom_field_for_owner_match -frappe.patches.v4_0.enable_scheduler_in_system_settings -execute:frappe.db.sql("update tabReport set apply_user_permissions=1") #2014-06-03 -frappe.patches.v4_0.replace_deprecated_timezones -execute:import frappe.website.render; frappe.website.render.clear_cache("login"); #2014-06-10 -frappe.patches.v4_0.fix_attach_field_file_url -execute:frappe.permissions.reset_perms("User") #2015-03-24 -execute:frappe.db.sql("""delete from `tabUserRole` where ifnull(parentfield, '')='' or ifnull(`role`, '')=''""") #2014-08-18 -frappe.patches.v4_0.remove_user_owner_custom_field -execute:frappe.delete_doc("DocType", "Website Template") -execute:frappe.db.sql("""update `tabProperty Setter` set property_type='Text' where property in ('options', 'default')""") #2014-06-20 -frappe.patches.v4_1.enable_outgoing_email_settings -execute:frappe.db.sql("""update `tabSingles` set `value`=`doctype` where `field`='name'""") #2014-07-04 -frappe.patches.v4_1.enable_print_as_pdf #2014-06-17 -execute:frappe.db.sql("""update `tabDocPerm` set email=1 where parent='User' and permlevel=0 and `role`='All' and `read`=1 and apply_user_permissions=1""") #2014-07-15 -execute:frappe.db.sql("""update `tabPrint Format` set print_format_type='Client' where ifnull(print_format_type, '')=''""") #2014-07-28 -frappe.patches.v4_1.file_manager_fix -frappe.patches.v4_2.print_with_letterhead execute:frappe.delete_doc("DocType", "Control Panel", force=1) -execute:frappe.reload_doc('website', 'doctype', 'web_form') #2014-09-04 -execute:frappe.reload_doc('website', 'doctype', 'web_form_field') #2014-09-04 -frappe.patches.v4_2.refactor_website_routing -frappe.patches.v4_2.set_assign_in_doc -frappe.patches.v4_3.remove_allow_on_submit_customization -frappe.patches.v5_0.rename_table_fieldnames -frappe.patches.v5_0.communication_parent -frappe.patches.v5_0.clear_website_group_and_notifications -frappe.patches.v5_0.update_shared -execute:frappe.reload_doc("core", "doctype", "docshare") #2015-07-21 -frappe.patches.v6_19.comment_feed_communication -frappe.patches.v6_16.star_to_like -frappe.patches.v5_0.bookmarks_to_stars -frappe.patches.v5_0.style_settings_to_website_theme -frappe.patches.v5_0.rename_ref_type_fieldnames -frappe.patches.v5_0.fix_email_alert -frappe.patches.v5_0.fix_null_date_datetime -frappe.patches.v5_0.force_sync_website execute:frappe.delete_doc("DocType", "Tag") execute:frappe.db.sql("delete from `tabProperty Setter` where `property` in ('idx', '_idx')") -frappe.patches.v5_0.move_scheduler_last_event_to_system_settings execute:frappe.db.sql("update tabUser set new_password='' where ifnull(new_password, '')!=''") -frappe.patches.v5_0.fix_text_editor_file_urls -frappe.patches.v5_0.modify_session -frappe.patches.v5_0.expire_old_scheduler_logs execute:frappe.permissions.reset_perms("DocType") execute:frappe.db.sql("delete from `tabProperty Setter` where `property` = 'idx'") -frappe.patches.v6_0.communication_status_and_permission -frappe.patches.v6_0.make_task_log_folder -frappe.patches.v6_0.document_type_rename -frappe.patches.v6_0.fix_ghana_currency -frappe.patches.v6_2.ignore_user_permissions_if_missing execute:frappe.db.sql("delete from tabSessions where user is null") -frappe.patches.v6_2.rename_backup_manager execute:frappe.delete_doc("DocType", "Backup Manager") -execute:frappe.db.sql("""update `tabCommunication` set parenttype=null, parent=null, parentfield=null""") #2015-10-22 execute:frappe.permissions.reset_perms("Web Page") -frappe.patches.v6_6.user_last_active -frappe.patches.v6_6.fix_file_url -frappe.patches.v6_11.rename_field_in_email_account -frappe.patches.v7_0.create_private_file_folder -frappe.patches.v6_15.remove_property_setter_for_previous_field #2015-12-29 -frappe.patches.v6_15.set_username execute:frappe.permissions.reset_perms("Error Snapshot") -frappe.patches.v6_16.feed_doc_owner -frappe.patches.v6_21.print_settings_repeat_header_footer -frappe.patches.v6_24.set_language_as_code -frappe.patches.v6_20x.update_insert_after -frappe.patches.v6_20x.set_allow_draft_for_print -frappe.patches.v6_20x.remove_roles_from_website_user -frappe.patches.v7_0.set_user_fullname -frappe.patches.v7_0.add_communication_in_doc -frappe.patches.v7_0.update_send_after_in_bulk_email -execute:frappe.db.sql('''delete from `tabSingles` where doctype="Email Settings"''') # 2016-06-13 execute:frappe.db.sql("delete from `tabWeb Page` where ifnull(template_path, '')!=''") -frappe.patches.v7_0.rename_newsletter_list_to_email_group -frappe.patches.v7_0.set_email_group -frappe.patches.v7_1.setup_integration_services #2016-10-27 -frappe.patches.v7_1.rename_chinese_language_codes execute:frappe.core.doctype.language.language.update_language_names() # 2017-04-12 execute:frappe.db.set_value("Print Settings", "Print Settings", "add_draft_heading", 1) -frappe.patches.v7_0.cleanup_list_settings execute:frappe.db.set_default('language', '') -frappe.patches.v7_1.refactor_integration_broker -frappe.patches.v7_1.set_backup_limit -frappe.patches.v7_2.set_doctype_engine -frappe.patches.v7_2.merge_knowledge_base -frappe.patches.v7_0.update_report_builder_json -frappe.patches.v7_2.set_in_standard_filter_property #1 -frappe.patches.v8_0.drop_unwanted_indexes execute:frappe.db.sql("update tabCommunication set communication_date = creation where time(communication_date) = 0") -frappe.patches.v7_2.fix_email_queue_recipient -frappe.patches.v7_2.update_feedback_request # 2017-02-27 execute:frappe.rename_doc('Country', 'Macedonia, Republic of', 'Macedonia', ignore_if_exists=True) execute:frappe.rename_doc('Country', 'Iran, Islamic Republic of', 'Iran', ignore_if_exists=True) execute:frappe.rename_doc('Country', 'Tanzania, United Republic of', 'Tanzania', ignore_if_exists=True) execute:frappe.rename_doc('Country', 'Syrian Arab Republic', 'Syria', ignore_if_exists=True) -frappe.patches.v8_0.rename_listsettings_to_usersettings -frappe.patches.v7_2.update_communications -frappe.patches.v8_0.deprecate_integration_broker -frappe.patches.v8_0.update_gender_and_salutation -frappe.patches.v8_0.setup_email_inbox #2017-03-29 -frappe.patches.v8_0.newsletter_childtable_migrate -frappe.patches.v8_0.set_doctype_values_in_custom_role -frappe.patches.v8_0.install_new_build_system_requirements -frappe.patches.v8_0.set_currency_field_precision # 2017-05-09 execute:frappe.reload_doc('desk', 'doctype', 'notification_log') -frappe.patches.v8_0.rename_print_to_printing -frappe.patches.v7_1.disabled_print_settings_for_custom_print_format execute:frappe.db.sql('update tabReport set module="Desk" where name="ToDo"') -frappe.patches.v8_1.enable_allow_error_traceback_in_system_settings -frappe.patches.v8_1.update_format_options_in_auto_email_report -frappe.patches.v8_1.delete_custom_docperm_if_doctype_not_exists -frappe.patches.v8_5.delete_email_group_member_with_invalid_emails -frappe.patches.v8_x.update_user_permission -frappe.patches.v8_5.patch_event_colors -frappe.patches.v8_10.delete_static_web_page_from_global_search -frappe.patches.v9_1.add_sms_sender_name_as_parameters -frappe.patches.v9_1.resave_domain_settings -frappe.patches.v9_1.revert_domain_settings -frappe.patches.v9_1.move_feed_to_activity_log execute:frappe.delete_doc('Page', 'data-import-tool', ignore_missing=True) frappe.patches.v10_0.reload_countries_and_currencies # 2021-02-03 frappe.patches.v10_0.refactor_social_login_keys From 0e811e0193e6773f73b41a6112f189a38888bbb0 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 3 Jun 2021 13:24:29 +0530 Subject: [PATCH 165/495] chore: Drop unlinked patches (pre v10) --- frappe/patches/v4_0/__init__.py | 0 frappe/patches/v4_0/add_delete_permission.py | 13 - frappe/patches/v4_0/change_varchar_length.py | 25 -- .../create_custom_field_for_owner_match.py | 39 --- .../patches/v4_0/deprecate_control_panel.py | 9 - frappe/patches/v4_0/deprecate_link_selects.py | 12 - .../enable_scheduler_in_system_settings.py | 13 - frappe/patches/v4_0/file_manager_hooks.py | 33 -- .../patches/v4_0/fix_attach_field_file_url.py | 12 - frappe/patches/v4_0/private_backups.py | 10 - frappe/patches/v4_0/remove_index_sitemap.py | 5 - frappe/patches/v4_0/remove_old_parent.py | 9 - .../v4_0/remove_user_owner_custom_field.py | 9 - frappe/patches/v4_0/rename_profile_to_user.py | 15 - .../patches/v4_0/rename_sitemap_to_route.py | 25 -- .../v4_0/replace_deprecated_timezones.py | 20 -- frappe/patches/v4_0/set_module_in_report.py | 9 - .../v4_0/set_todo_checked_as_closed.py | 9 - frappe/patches/v4_0/set_user_gravatar.py | 10 - frappe/patches/v4_0/set_user_permissions.py | 23 -- frappe/patches/v4_0/set_website_route_idx.py | 25 -- .../v4_0/update_custom_field_insert_after.py | 17 - frappe/patches/v4_0/update_datetime.py | 12 - frappe/patches/v4_0/webnotes_to_frappe.py | 12 - .../patches/v4_0/website_sitemap_hierarchy.py | 19 -- frappe/patches/v4_1/__init__.py | 0 .../v4_1/enable_outgoing_email_settings.py | 9 - frappe/patches/v4_1/enable_print_as_pdf.py | 25 -- frappe/patches/v4_1/file_manager_fix.py | 97 ------ frappe/patches/v4_2/__init__.py | 0 frappe/patches/v4_2/print_with_letterhead.py | 10 - .../patches/v4_2/refactor_website_routing.py | 8 - frappe/patches/v4_2/set_assign_in_doc.py | 11 - frappe/patches/v4_3/__init__.py | 0 .../remove_allow_on_submit_customization.py | 10 - frappe/patches/v5_0/__init__.py | 0 frappe/patches/v5_0/bookmarks_to_stars.py | 32 -- .../clear_website_group_and_notifications.py | 9 - frappe/patches/v5_0/communication_parent.py | 6 - .../v5_0/convert_to_barracuda_and_utf8mb4.py | 16 - .../patches/v5_0/expire_old_scheduler_logs.py | 8 - frappe/patches/v5_0/fix_email_alert.py | 14 - frappe/patches/v5_0/fix_null_date_datetime.py | 20 -- .../patches/v5_0/fix_text_editor_file_urls.py | 43 --- frappe/patches/v5_0/force_sync_website.py | 5 - frappe/patches/v5_0/modify_session.py | 6 - ...scheduler_last_event_to_system_settings.py | 8 - .../patches/v5_0/remove_shopping_cart_app.py | 5 - .../v5_0/rename_ref_type_fieldnames.py | 18 - .../patches/v5_0/rename_table_fieldnames.py | 29 -- .../v5_0/style_settings_to_website_theme.py | 59 ---- frappe/patches/v5_0/update_shared.py | 37 --- frappe/patches/v5_0/v4_to_v5.py | 17 - frappe/patches/v5_2/__init__.py | 0 .../patches/v5_2/change_checks_to_not_null.py | 34 -- frappe/patches/v5_3/__init__.py | 0 .../patches/v5_3/rename_chinese_languages.py | 12 - frappe/patches/v6_0/__init__.py | 0 .../communication_status_and_permission.py | 19 -- frappe/patches/v6_0/document_type_rename.py | 8 - frappe/patches/v6_0/fix_ghana_currency.py | 6 - frappe/patches/v6_0/make_task_log_folder.py | 7 - frappe/patches/v6_1/__init__.py | 0 frappe/patches/v6_1/rename_file_data.py | 37 --- frappe/patches/v6_11/__init__.py | 0 .../v6_11/rename_field_in_email_account.py | 7 - frappe/patches/v6_15/__init__.py | 0 ...move_property_setter_for_previous_field.py | 86 ----- frappe/patches/v6_15/set_username.py | 16 - frappe/patches/v6_16/__init__.py | 0 frappe/patches/v6_16/feed_doc_owner.py | 31 -- frappe/patches/v6_16/star_to_like.py | 15 - frappe/patches/v6_19/__init__.py | 0 .../v6_19/comment_feed_communication.py | 307 ------------------ frappe/patches/v6_2/__init__.py | 0 .../ignore_user_permissions_if_missing.py | 8 - frappe/patches/v6_2/rename_backup_manager.py | 20 -- frappe/patches/v6_20x/__init__.py | 0 .../v6_20x/remove_roles_from_website_user.py | 11 - .../v6_20x/set_allow_draft_for_print.py | 5 - frappe/patches/v6_20x/update_insert_after.py | 27 -- frappe/patches/v6_21/__init__.py | 0 .../print_settings_repeat_header_footer.py | 6 - frappe/patches/v6_24/__init__.py | 0 frappe/patches/v6_24/set_language_as_code.py | 8 - frappe/patches/v6_4/__init__.py | 0 frappe/patches/v6_4/reduce_varchar_length.py | 36 -- .../patches/v6_4/rename_bengali_language.py | 6 - frappe/patches/v6_6/__init__.py | 0 frappe/patches/v6_6/fix_file_url.py | 36 -- frappe/patches/v6_6/rename_slovak_language.py | 6 - frappe/patches/v6_6/user_last_active.py | 6 - frappe/patches/v6_9/__init__.py | 0 frappe/patches/v6_9/int_float_not_null.py | 30 -- .../patches/v6_9/rename_burmese_language.py | 6 - frappe/patches/v7_0/__init__.py | 0 .../patches/v7_0/add_communication_in_doc.py | 14 - frappe/patches/v7_0/cleanup_list_settings.py | 20 -- .../v7_0/create_private_file_folder.py | 6 - frappe/patches/v7_0/re_route.py | 23 -- .../v7_0/rename_bulk_email_to_email_queue.py | 5 - .../rename_newsletter_list_to_email_group.py | 6 - frappe/patches/v7_0/set_email_group.py | 10 - frappe/patches/v7_0/set_user_fullname.py | 10 - frappe/patches/v7_0/update_auth.py | 42 --- .../v7_0/update_report_builder_json.py | 11 - .../v7_0/update_send_after_in_bulk_email.py | 6 - frappe/patches/v7_1/__init__.py | 0 ..._print_settings_for_custom_print_format.py | 16 - .../v7_1/refactor_integration_broker.py | 51 --- .../v7_1/rename_chinese_language_codes.py | 11 - .../v7_1/rename_scheduler_log_to_error_log.py | 11 - frappe/patches/v7_1/set_backup_limit.py | 10 - .../v7_1/setup_integration_services.py | 118 ------- frappe/patches/v7_1/sync_language_doctype.py | 22 -- frappe/patches/v7_2/__init__.py | 0 .../patches/v7_2/fix_email_queue_recipient.py | 6 - frappe/patches/v7_2/merge_knowledge_base.py | 24 -- frappe/patches/v7_2/remove_in_filter.py | 7 - frappe/patches/v7_2/set_doctype_engine.py | 7 - .../v7_2/set_in_standard_filter_property.py | 20 -- frappe/patches/v7_2/setup_custom_perms.py | 13 - frappe/patches/v7_2/setup_ldap_config.py | 22 -- frappe/patches/v7_2/update_communications.py | 10 - .../patches/v7_2/update_feedback_request.py | 10 - frappe/patches/v8_0/__init__.py | 0 .../v8_0/deprecate_integration_broker.py | 51 --- frappe/patches/v8_0/drop_in_dialog.py | 7 - .../v8_0/drop_is_custom_from_docperm.py | 8 - frappe/patches/v8_0/drop_unwanted_indexes.py | 16 - .../install_new_build_system_requirements.py | 21 -- .../v8_0/newsletter_childtable_migrate.py | 23 -- .../rename_listsettings_to_usersettings.py | 45 --- .../v8_0/rename_page_role_to_has_role.py | 47 --- .../patches/v8_0/rename_print_to_printing.py | 13 - frappe/patches/v8_0/set_allow_traceback.py | 6 - .../v8_0/set_currency_field_precision.py | 21 -- .../v8_0/set_doctype_values_in_custom_role.py | 12 - ...set_user_permission_for_page_and_report.py | 54 --- frappe/patches/v8_0/setup_email_inbox.py | 26 -- .../v8_0/update_gender_and_salutation.py | 14 - .../v8_0/update_global_search_table.py | 13 - .../v8_0/update_published_in_global_search.py | 12 - .../v8_0/update_records_in_global_search.py | 12 - frappe/patches/v8_1/__init__.py | 0 ...te_custom_docperm_if_doctype_not_exists.py | 7 - ...llow_error_traceback_in_system_settings.py | 14 - ...ate_format_options_in_auto_email_report.py | 13 - frappe/patches/v8_10/__init__.py | 0 ...lete_static_web_page_from_global_search.py | 5 - frappe/patches/v8_5/__init__.py | 0 ..._email_group_member_with_invalid_emails.py | 19 -- frappe/patches/v8_5/patch_event_colors.py | 25 -- frappe/patches/v8_7/__init__.py | 0 frappe/patches/v8_x/__init__.py | 0 frappe/patches/v8_x/update_user_permission.py | 28 -- frappe/patches/v9_1/__init__.py | 1 - .../v9_1/add_sms_sender_name_as_parameters.py | 18 - .../patches/v9_1/move_feed_to_activity_log.py | 24 -- frappe/patches/v9_1/resave_domain_settings.py | 13 - frappe/patches/v9_1/revert_domain_settings.py | 11 - 161 files changed, 2773 deletions(-) delete mode 100644 frappe/patches/v4_0/__init__.py delete mode 100644 frappe/patches/v4_0/add_delete_permission.py delete mode 100644 frappe/patches/v4_0/change_varchar_length.py delete mode 100644 frappe/patches/v4_0/create_custom_field_for_owner_match.py delete mode 100644 frappe/patches/v4_0/deprecate_control_panel.py delete mode 100644 frappe/patches/v4_0/deprecate_link_selects.py delete mode 100644 frappe/patches/v4_0/enable_scheduler_in_system_settings.py delete mode 100644 frappe/patches/v4_0/file_manager_hooks.py delete mode 100644 frappe/patches/v4_0/fix_attach_field_file_url.py delete mode 100644 frappe/patches/v4_0/private_backups.py delete mode 100644 frappe/patches/v4_0/remove_index_sitemap.py delete mode 100644 frappe/patches/v4_0/remove_old_parent.py delete mode 100644 frappe/patches/v4_0/remove_user_owner_custom_field.py delete mode 100644 frappe/patches/v4_0/rename_profile_to_user.py delete mode 100644 frappe/patches/v4_0/rename_sitemap_to_route.py delete mode 100644 frappe/patches/v4_0/replace_deprecated_timezones.py delete mode 100644 frappe/patches/v4_0/set_module_in_report.py delete mode 100644 frappe/patches/v4_0/set_todo_checked_as_closed.py delete mode 100644 frappe/patches/v4_0/set_user_gravatar.py delete mode 100644 frappe/patches/v4_0/set_user_permissions.py delete mode 100644 frappe/patches/v4_0/set_website_route_idx.py delete mode 100644 frappe/patches/v4_0/update_custom_field_insert_after.py delete mode 100644 frappe/patches/v4_0/update_datetime.py delete mode 100644 frappe/patches/v4_0/webnotes_to_frappe.py delete mode 100644 frappe/patches/v4_0/website_sitemap_hierarchy.py delete mode 100644 frappe/patches/v4_1/__init__.py delete mode 100644 frappe/patches/v4_1/enable_outgoing_email_settings.py delete mode 100644 frappe/patches/v4_1/enable_print_as_pdf.py delete mode 100644 frappe/patches/v4_1/file_manager_fix.py delete mode 100644 frappe/patches/v4_2/__init__.py delete mode 100644 frappe/patches/v4_2/print_with_letterhead.py delete mode 100644 frappe/patches/v4_2/refactor_website_routing.py delete mode 100644 frappe/patches/v4_2/set_assign_in_doc.py delete mode 100644 frappe/patches/v4_3/__init__.py delete mode 100644 frappe/patches/v4_3/remove_allow_on_submit_customization.py delete mode 100644 frappe/patches/v5_0/__init__.py delete mode 100644 frappe/patches/v5_0/bookmarks_to_stars.py delete mode 100644 frappe/patches/v5_0/clear_website_group_and_notifications.py delete mode 100644 frappe/patches/v5_0/communication_parent.py delete mode 100644 frappe/patches/v5_0/convert_to_barracuda_and_utf8mb4.py delete mode 100644 frappe/patches/v5_0/expire_old_scheduler_logs.py delete mode 100644 frappe/patches/v5_0/fix_email_alert.py delete mode 100644 frappe/patches/v5_0/fix_null_date_datetime.py delete mode 100644 frappe/patches/v5_0/fix_text_editor_file_urls.py delete mode 100644 frappe/patches/v5_0/force_sync_website.py delete mode 100644 frappe/patches/v5_0/modify_session.py delete mode 100644 frappe/patches/v5_0/move_scheduler_last_event_to_system_settings.py delete mode 100644 frappe/patches/v5_0/remove_shopping_cart_app.py delete mode 100644 frappe/patches/v5_0/rename_ref_type_fieldnames.py delete mode 100644 frappe/patches/v5_0/rename_table_fieldnames.py delete mode 100644 frappe/patches/v5_0/style_settings_to_website_theme.py delete mode 100644 frappe/patches/v5_0/update_shared.py delete mode 100644 frappe/patches/v5_0/v4_to_v5.py delete mode 100644 frappe/patches/v5_2/__init__.py delete mode 100644 frappe/patches/v5_2/change_checks_to_not_null.py delete mode 100644 frappe/patches/v5_3/__init__.py delete mode 100644 frappe/patches/v5_3/rename_chinese_languages.py delete mode 100644 frappe/patches/v6_0/__init__.py delete mode 100644 frappe/patches/v6_0/communication_status_and_permission.py delete mode 100644 frappe/patches/v6_0/document_type_rename.py delete mode 100644 frappe/patches/v6_0/fix_ghana_currency.py delete mode 100644 frappe/patches/v6_0/make_task_log_folder.py delete mode 100644 frappe/patches/v6_1/__init__.py delete mode 100644 frappe/patches/v6_1/rename_file_data.py delete mode 100644 frappe/patches/v6_11/__init__.py delete mode 100644 frappe/patches/v6_11/rename_field_in_email_account.py delete mode 100644 frappe/patches/v6_15/__init__.py delete mode 100644 frappe/patches/v6_15/remove_property_setter_for_previous_field.py delete mode 100644 frappe/patches/v6_15/set_username.py delete mode 100644 frappe/patches/v6_16/__init__.py delete mode 100644 frappe/patches/v6_16/feed_doc_owner.py delete mode 100644 frappe/patches/v6_16/star_to_like.py delete mode 100644 frappe/patches/v6_19/__init__.py delete mode 100644 frappe/patches/v6_19/comment_feed_communication.py delete mode 100644 frappe/patches/v6_2/__init__.py delete mode 100644 frappe/patches/v6_2/ignore_user_permissions_if_missing.py delete mode 100644 frappe/patches/v6_2/rename_backup_manager.py delete mode 100644 frappe/patches/v6_20x/__init__.py delete mode 100644 frappe/patches/v6_20x/remove_roles_from_website_user.py delete mode 100644 frappe/patches/v6_20x/set_allow_draft_for_print.py delete mode 100644 frappe/patches/v6_20x/update_insert_after.py delete mode 100644 frappe/patches/v6_21/__init__.py delete mode 100644 frappe/patches/v6_21/print_settings_repeat_header_footer.py delete mode 100644 frappe/patches/v6_24/__init__.py delete mode 100644 frappe/patches/v6_24/set_language_as_code.py delete mode 100644 frappe/patches/v6_4/__init__.py delete mode 100644 frappe/patches/v6_4/reduce_varchar_length.py delete mode 100644 frappe/patches/v6_4/rename_bengali_language.py delete mode 100644 frappe/patches/v6_6/__init__.py delete mode 100644 frappe/patches/v6_6/fix_file_url.py delete mode 100644 frappe/patches/v6_6/rename_slovak_language.py delete mode 100644 frappe/patches/v6_6/user_last_active.py delete mode 100644 frappe/patches/v6_9/__init__.py delete mode 100644 frappe/patches/v6_9/int_float_not_null.py delete mode 100644 frappe/patches/v6_9/rename_burmese_language.py delete mode 100644 frappe/patches/v7_0/__init__.py delete mode 100644 frappe/patches/v7_0/add_communication_in_doc.py delete mode 100644 frappe/patches/v7_0/cleanup_list_settings.py delete mode 100644 frappe/patches/v7_0/create_private_file_folder.py delete mode 100644 frappe/patches/v7_0/re_route.py delete mode 100644 frappe/patches/v7_0/rename_bulk_email_to_email_queue.py delete mode 100644 frappe/patches/v7_0/rename_newsletter_list_to_email_group.py delete mode 100644 frappe/patches/v7_0/set_email_group.py delete mode 100644 frappe/patches/v7_0/set_user_fullname.py delete mode 100644 frappe/patches/v7_0/update_auth.py delete mode 100644 frappe/patches/v7_0/update_report_builder_json.py delete mode 100644 frappe/patches/v7_0/update_send_after_in_bulk_email.py delete mode 100644 frappe/patches/v7_1/__init__.py delete mode 100644 frappe/patches/v7_1/disabled_print_settings_for_custom_print_format.py delete mode 100644 frappe/patches/v7_1/refactor_integration_broker.py delete mode 100644 frappe/patches/v7_1/rename_chinese_language_codes.py delete mode 100644 frappe/patches/v7_1/rename_scheduler_log_to_error_log.py delete mode 100644 frappe/patches/v7_1/set_backup_limit.py delete mode 100644 frappe/patches/v7_1/setup_integration_services.py delete mode 100644 frappe/patches/v7_1/sync_language_doctype.py delete mode 100644 frappe/patches/v7_2/__init__.py delete mode 100644 frappe/patches/v7_2/fix_email_queue_recipient.py delete mode 100644 frappe/patches/v7_2/merge_knowledge_base.py delete mode 100644 frappe/patches/v7_2/remove_in_filter.py delete mode 100644 frappe/patches/v7_2/set_doctype_engine.py delete mode 100644 frappe/patches/v7_2/set_in_standard_filter_property.py delete mode 100644 frappe/patches/v7_2/setup_custom_perms.py delete mode 100644 frappe/patches/v7_2/setup_ldap_config.py delete mode 100644 frappe/patches/v7_2/update_communications.py delete mode 100644 frappe/patches/v7_2/update_feedback_request.py delete mode 100644 frappe/patches/v8_0/__init__.py delete mode 100644 frappe/patches/v8_0/deprecate_integration_broker.py delete mode 100644 frappe/patches/v8_0/drop_in_dialog.py delete mode 100644 frappe/patches/v8_0/drop_is_custom_from_docperm.py delete mode 100644 frappe/patches/v8_0/drop_unwanted_indexes.py delete mode 100644 frappe/patches/v8_0/install_new_build_system_requirements.py delete mode 100644 frappe/patches/v8_0/newsletter_childtable_migrate.py delete mode 100644 frappe/patches/v8_0/rename_listsettings_to_usersettings.py delete mode 100644 frappe/patches/v8_0/rename_page_role_to_has_role.py delete mode 100644 frappe/patches/v8_0/rename_print_to_printing.py delete mode 100644 frappe/patches/v8_0/set_allow_traceback.py delete mode 100644 frappe/patches/v8_0/set_currency_field_precision.py delete mode 100644 frappe/patches/v8_0/set_doctype_values_in_custom_role.py delete mode 100644 frappe/patches/v8_0/set_user_permission_for_page_and_report.py delete mode 100644 frappe/patches/v8_0/setup_email_inbox.py delete mode 100644 frappe/patches/v8_0/update_gender_and_salutation.py delete mode 100644 frappe/patches/v8_0/update_global_search_table.py delete mode 100644 frappe/patches/v8_0/update_published_in_global_search.py delete mode 100644 frappe/patches/v8_0/update_records_in_global_search.py delete mode 100644 frappe/patches/v8_1/__init__.py delete mode 100644 frappe/patches/v8_1/delete_custom_docperm_if_doctype_not_exists.py delete mode 100644 frappe/patches/v8_1/enable_allow_error_traceback_in_system_settings.py delete mode 100644 frappe/patches/v8_1/update_format_options_in_auto_email_report.py delete mode 100644 frappe/patches/v8_10/__init__.py delete mode 100644 frappe/patches/v8_10/delete_static_web_page_from_global_search.py delete mode 100644 frappe/patches/v8_5/__init__.py delete mode 100644 frappe/patches/v8_5/delete_email_group_member_with_invalid_emails.py delete mode 100644 frappe/patches/v8_5/patch_event_colors.py delete mode 100644 frappe/patches/v8_7/__init__.py delete mode 100644 frappe/patches/v8_x/__init__.py delete mode 100644 frappe/patches/v8_x/update_user_permission.py delete mode 100644 frappe/patches/v9_1/__init__.py delete mode 100644 frappe/patches/v9_1/add_sms_sender_name_as_parameters.py delete mode 100644 frappe/patches/v9_1/move_feed_to_activity_log.py delete mode 100644 frappe/patches/v9_1/resave_domain_settings.py delete mode 100644 frappe/patches/v9_1/revert_domain_settings.py diff --git a/frappe/patches/v4_0/__init__.py b/frappe/patches/v4_0/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v4_0/add_delete_permission.py b/frappe/patches/v4_0/add_delete_permission.py deleted file mode 100644 index 9e375a431d..0000000000 --- a/frappe/patches/v4_0/add_delete_permission.py +++ /dev/null @@ -1,13 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doc("core", "doctype", "docperm") - - # delete same as cancel (map old permissions) - frappe.db.sql("""update tabDocPerm set `delete`=ifnull(`cancel`,0)""") - - # can't cancel if can't submit - frappe.db.sql("""update tabDocPerm set `cancel`=0 where ifnull(`submit`,0)=0""") - - frappe.clear_cache() \ No newline at end of file diff --git a/frappe/patches/v4_0/change_varchar_length.py b/frappe/patches/v4_0/change_varchar_length.py deleted file mode 100644 index 914034ccba..0000000000 --- a/frappe/patches/v4_0/change_varchar_length.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe - -def execute(): - frappe.db.sql('update tabDocField set search_index=0 where fieldtype="Small Text"') - frappe.db.sql('update tabDocField set in_list_view=0 where fieldtype="Image"') - - for dt in frappe.db.sql_list("""select name from `tabDocType` where issingle=0"""): - desc = dict((d["Field"], d) for d in frappe.db.sql("desc `tab{}`".format(dt), as_dict=True)) - alter_table = [] - - if desc["name"]["Type"] != "varchar(255)": - alter_table.append("change `name` `name` varchar(255) not null") - - for fieldname in ("modified_by", "owner", "parent", "parentfield", "parenttype"): - if desc[fieldname]["Type"] != "varchar(255)": - alter_table.append("change `{fieldname}` `{fieldname}` varchar(255)".format(fieldname=fieldname)) - - if alter_table: - alter_table_query = "alter table `tab{doctype}` {alter_table}".format(doctype=dt, alter_table=",\n".join(alter_table)) - # print alter_table_query - frappe.db.sql_ddl(alter_table_query) - diff --git a/frappe/patches/v4_0/create_custom_field_for_owner_match.py b/frappe/patches/v4_0/create_custom_field_for_owner_match.py deleted file mode 100644 index 438e280669..0000000000 --- a/frappe/patches/v4_0/create_custom_field_for_owner_match.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt -import frappe -from frappe.custom.doctype.custom_field.custom_field import create_custom_field - -def execute(): - if "match" in frappe.db.get_table_columns("DocPerm"): - create_custom_field_for_owner_match() - -def create_custom_field_for_owner_match(): - docperm_meta = frappe.get_meta('DocPerm') - if docperm_meta.get_field('apply_user_permissions'): - frappe.db.sql("""update `tabDocPerm` set apply_user_permissions=1 where `match`='owner'""") - - for dt in frappe.db.sql_list("""select distinct parent from `tabDocPerm` - where `match`='owner' and permlevel=0 and parent != 'User'"""): - - # a link field pointing to User already exists - if (frappe.db.get_value("DocField", {"parent": dt, "fieldtype": "Link", "options": "User", "default": "__user"}) - or frappe.db.get_value("Custom Field", {"dt": dt, "fieldtype": "Link", "options": "User", "default": "__user"})): - print("User link field already exists for", dt) - continue - - fieldname = "{}_owner".format(frappe.scrub(dt)) - - create_custom_field(dt, frappe._dict({ - "permlevel": 0, - "label": "{} Owner".format(dt), - "fieldname": fieldname, - "fieldtype": "Link", - "options": "User", - "default": "__user" - })) - - frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=owner""".format(doctype=dt, - fieldname=fieldname)) - - # commit is required so that we don't lose these changes because of an error in next loop's ddl - frappe.db.commit() diff --git a/frappe/patches/v4_0/deprecate_control_panel.py b/frappe/patches/v4_0/deprecate_control_panel.py deleted file mode 100644 index 29ec8d35f6..0000000000 --- a/frappe/patches/v4_0/deprecate_control_panel.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe - -def execute(): - frappe.db.sql("update `tabDefaultValue` set parenttype='__default' where parenttype='Control Panel'") - frappe.db.sql("update `tabDefaultValue` set parent='__default' where parent='Control Panel'") - frappe.clear_cache() diff --git a/frappe/patches/v4_0/deprecate_link_selects.py b/frappe/patches/v4_0/deprecate_link_selects.py deleted file mode 100644 index 837144a6ba..0000000000 --- a/frappe/patches/v4_0/deprecate_link_selects.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe - -def execute(): - for name in frappe.db.sql_list("""select name from `tabCustom Field` - where fieldtype="Select" and options like "link:%" """): - custom_field = frappe.get_doc("Custom Field", name) - custom_field.fieldtype = "Link" - custom_field.options = custom_field.options[5:] - custom_field.save() diff --git a/frappe/patches/v4_0/enable_scheduler_in_system_settings.py b/frappe/patches/v4_0/enable_scheduler_in_system_settings.py deleted file mode 100644 index 68c74edb4f..0000000000 --- a/frappe/patches/v4_0/enable_scheduler_in_system_settings.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe -from frappe.utils.scheduler import disable_scheduler, enable_scheduler -from frappe.utils import cint - -def execute(): - frappe.reload_doc("core", "doctype", "system_settings") - if cint(frappe.db.get_global("disable_scheduler")): - disable_scheduler() - else: - enable_scheduler() diff --git a/frappe/patches/v4_0/file_manager_hooks.py b/frappe/patches/v4_0/file_manager_hooks.py deleted file mode 100644 index ccf95d1d31..0000000000 --- a/frappe/patches/v4_0/file_manager_hooks.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt -import frappe -import os -from frappe.core.doctype.file.file import get_content_hash - - -def execute(): - frappe.reload_doc('core', 'doctype', 'file_data') - for name, file_name, file_url in frappe.db.sql( - """select name, file_name, file_url from `tabFile` - where file_name is not null"""): - b = frappe.get_doc('File', name) - old_file_name = b.file_name - b.file_name = os.path.basename(old_file_name) - if old_file_name.startswith('files/') or old_file_name.startswith('/files/'): - b.file_url = os.path.normpath('/' + old_file_name) - else: - b.file_url = os.path.normpath('/files/' + old_file_name) - try: - _file = frappe.get_doc("File", {"file_name": name}) - content = _file.get_content() - b.content_hash = get_content_hash(content) - except IOError: - print('Warning: Error processing ', name) - _file_name = old_file_name - b.content_hash = None - - try: - b.save() - except frappe.DuplicateEntryError: - frappe.delete_doc(b.doctype, b.name) - diff --git a/frappe/patches/v4_0/fix_attach_field_file_url.py b/frappe/patches/v4_0/fix_attach_field_file_url.py deleted file mode 100644 index 61c35b120d..0000000000 --- a/frappe/patches/v4_0/fix_attach_field_file_url.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe - -def execute(): - attach_fields = (frappe.db.sql("""select parent, fieldname from `tabDocField` where fieldtype in ('Attach', 'Attach Image')""") + - frappe.db.sql("""select dt, fieldname from `tabCustom Field` where fieldtype in ('Attach', 'Attach Image')""")) - - for doctype, fieldname in attach_fields: - frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=concat("/", `{fieldname}`) - where `{fieldname}` like 'files/%'""".format(doctype=doctype, fieldname=fieldname)) diff --git a/frappe/patches/v4_0/private_backups.py b/frappe/patches/v4_0/private_backups.py deleted file mode 100644 index 7920564677..0000000000 --- a/frappe/patches/v4_0/private_backups.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe -from frappe.installer import make_site_dirs - -def execute(): - make_site_dirs() - if frappe.local.conf.backup_path and frappe.local.conf.backup_path.startswith("public"): - raise Exception("Backups path in conf set to public directory") diff --git a/frappe/patches/v4_0/remove_index_sitemap.py b/frappe/patches/v4_0/remove_index_sitemap.py deleted file mode 100644 index 8f48729276..0000000000 --- a/frappe/patches/v4_0/remove_index_sitemap.py +++ /dev/null @@ -1,5 +0,0 @@ - -import frappe - -def execute(): - pass diff --git a/frappe/patches/v4_0/remove_old_parent.py b/frappe/patches/v4_0/remove_old_parent.py deleted file mode 100644 index f2d125953a..0000000000 --- a/frappe/patches/v4_0/remove_old_parent.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe - -def execute(): - for doctype in frappe.db.sql_list("""select name from `tabDocType` where istable=1"""): - frappe.db.sql("""delete from `tab{0}` where parent like "old_par%:%" """.format(doctype)) - frappe.db.sql("""delete from `tabDocField` where parent="0" """) diff --git a/frappe/patches/v4_0/remove_user_owner_custom_field.py b/frappe/patches/v4_0/remove_user_owner_custom_field.py deleted file mode 100644 index 4620f055d9..0000000000 --- a/frappe/patches/v4_0/remove_user_owner_custom_field.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe - -def execute(): - user_owner = frappe.db.get_value("Custom Field", {"fieldname": "user_owner"}) - if user_owner: - frappe.delete_doc("Custom Field", user_owner) diff --git a/frappe/patches/v4_0/rename_profile_to_user.py b/frappe/patches/v4_0/rename_profile_to_user.py deleted file mode 100644 index 3e6f269329..0000000000 --- a/frappe/patches/v4_0/rename_profile_to_user.py +++ /dev/null @@ -1,15 +0,0 @@ - -import frappe - -from frappe.model.utils.rename_field import rename_field -from frappe.model.meta import get_table_columns - -def execute(): - tables = frappe.db.sql_list("show tables") - if "tabUser" not in tables: - frappe.rename_doc("DocType", "Profile", "User", force=True) - - frappe.reload_doc("website", "doctype", "blogger") - - if "profile" in get_table_columns("Blogger"): - rename_field("Blogger", "profile", "user") diff --git a/frappe/patches/v4_0/rename_sitemap_to_route.py b/frappe/patches/v4_0/rename_sitemap_to_route.py deleted file mode 100644 index b4606672bc..0000000000 --- a/frappe/patches/v4_0/rename_sitemap_to_route.py +++ /dev/null @@ -1,25 +0,0 @@ - -import frappe - -from frappe.model.utils.rename_field import rename_field - -def execute(): - tables = frappe.db.sql_list("show tables") - for doctype in ("Website Sitemap", "Website Sitemap Config"): - if "tab{}".format(doctype) in tables: - frappe.delete_doc("DocType", doctype, force=1) - frappe.db.sql("drop table `tab{}`".format(doctype)) - - for d in ("Blog Category", "Blog Post", "Web Page"): - frappe.reload_doc("website", "doctype", frappe.scrub(d)) - rename_field_if_exists(d, "parent_website_sitemap", "parent_website_route") - - for d in ("blog_category", "blog_post", "web_page", "post", "user_vote"): - frappe.reload_doc("website", "doctype", d) - -def rename_field_if_exists(doctype, old_fieldname, new_fieldname): - try: - rename_field(doctype, old_fieldname, new_fieldname) - except frappe.db.ProgrammingError as e: - if not frappe.db.is_column_missing(e): - raise diff --git a/frappe/patches/v4_0/replace_deprecated_timezones.py b/frappe/patches/v4_0/replace_deprecated_timezones.py deleted file mode 100644 index a3d8ecbf2b..0000000000 --- a/frappe/patches/v4_0/replace_deprecated_timezones.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe -from frappe.utils.momentjs import data as momentjs_data - -def execute(): - frappe.reload_doc("core", "doctype", "user") - - ss = frappe.get_doc("System Settings", "System Settings") - if ss.time_zone in momentjs_data.get("links"): - ss.time_zone = momentjs_data["links"][ss.time_zone] - ss.flags.ignore_mandatory = True - ss.save() - - for user, time_zone in frappe.db.sql("select name, time_zone from `tabUser` where ifnull(time_zone, '')!=''"): - if time_zone in momentjs_data.get("links"): - user = frappe.get_doc("User", user) - user.time_zone = momentjs_data["links"][user.time_zone] - user.save() diff --git a/frappe/patches/v4_0/set_module_in_report.py b/frappe/patches/v4_0/set_module_in_report.py deleted file mode 100644 index 6c670f4c8e..0000000000 --- a/frappe/patches/v4_0/set_module_in_report.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe - -def execute(): - frappe.reload_doc("core", "doctype", "report") - frappe.db.sql("""update `tabReport` r set r.module=(select d.module from `tabDocType` d - where d.name=r.ref_doctype) where ifnull(r.module, '')=''""") \ No newline at end of file diff --git a/frappe/patches/v4_0/set_todo_checked_as_closed.py b/frappe/patches/v4_0/set_todo_checked_as_closed.py deleted file mode 100644 index 5f02e1447b..0000000000 --- a/frappe/patches/v4_0/set_todo_checked_as_closed.py +++ /dev/null @@ -1,9 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doc("core", "doctype", "todo") - try: - frappe.db.sql("""update tabToDo set status = if(ifnull(checked,0)=0, 'Open', 'Closed')""") - except: - pass diff --git a/frappe/patches/v4_0/set_user_gravatar.py b/frappe/patches/v4_0/set_user_gravatar.py deleted file mode 100644 index 348991c320..0000000000 --- a/frappe/patches/v4_0/set_user_gravatar.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe - -def execute(): - for name in frappe.db.sql_list("select name from `tabUser` where ifnull(user_image, '')=''"): - user = frappe.get_doc("User", name) - user.update_gravatar() - user.db_set("user_image", user.user_image) diff --git a/frappe/patches/v4_0/set_user_permissions.py b/frappe/patches/v4_0/set_user_permissions.py deleted file mode 100644 index ef6f3a27e5..0000000000 --- a/frappe/patches/v4_0/set_user_permissions.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe -import frappe.permissions - -def execute(): - frappe.reload_doc("core", "doctype", "docperm") - table_columns = frappe.db.get_table_columns("DocPerm") - - if "restricted" in table_columns: - frappe.db.sql("""update `tabDocPerm` set apply_user_permissions=1 where apply_user_permissions=0 - and restricted=1""") - - if "match" in table_columns: - frappe.db.sql("""update `tabDocPerm` set apply_user_permissions=1 - where apply_user_permissions=0 and ifnull(`match`, '')!=''""") - - # change Restriction to User Permission in tabDefaultValue - frappe.db.sql("""update `tabDefaultValue` set parenttype='User Permission' where parenttype='Restriction'""") - - frappe.clear_cache() - diff --git a/frappe/patches/v4_0/set_website_route_idx.py b/frappe/patches/v4_0/set_website_route_idx.py deleted file mode 100644 index c3dba712d8..0000000000 --- a/frappe/patches/v4_0/set_website_route_idx.py +++ /dev/null @@ -1,25 +0,0 @@ - -import frappe - -def execute(): - pass - # from frappe.website.doctype.website_template.website_template import \ - # get_pages_and_generators, get_template_controller - # - # frappe.reload_doc("website", "doctype", "website_template") - # frappe.reload_doc("website", "doctype", "website_route") - # - # for app in frappe.get_installed_apps(): - # pages, generators = get_pages_and_generators(app) - # for g in generators: - # doctype = frappe.get_attr(get_template_controller(app, g["path"], g["fname"]) + ".doctype") - # module = frappe.db.get_value("DocType", doctype, "module") - # frappe.reload_doc(frappe.scrub(module), "doctype", frappe.scrub(doctype)) - # - # frappe.db.sql("""update `tabBlog Category` set `title`=`name` where ifnull(`title`, '')=''""") - # frappe.db.sql("""update `tabWebsite Route` set idx=null""") - # for doctype in ["Blog Category", "Blog Post", "Web Page", "Website Group"]: - # frappe.db.sql("""update `tab{}` set idx=null""".format(doctype)) - # - # from frappe.website.doctype.website_template.website_template import rebuild_website_template - # rebuild_website_template() diff --git a/frappe/patches/v4_0/update_custom_field_insert_after.py b/frappe/patches/v4_0/update_custom_field_insert_after.py deleted file mode 100644 index 4cb50956d6..0000000000 --- a/frappe/patches/v4_0/update_custom_field_insert_after.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe - -def execute(): - for d in frappe.db.sql("""select name, dt, insert_after from `tabCustom Field` - where docstatus < 2""", as_dict=1): - dt_meta = frappe.get_meta(d.dt) - if not dt_meta.get_field(d.insert_after): - cf = frappe.get_doc("Custom Field", d.name) - df = dt_meta.get("fields", {"label": d.insert_after}) - if df: - cf.insert_after = df[0].fieldname - else: - cf.insert_after = None - cf.save() diff --git a/frappe/patches/v4_0/update_datetime.py b/frappe/patches/v4_0/update_datetime.py deleted file mode 100644 index 4034d8f665..0000000000 --- a/frappe/patches/v4_0/update_datetime.py +++ /dev/null @@ -1,12 +0,0 @@ - -import frappe - -def execute(): - for table in frappe.db.sql_list("show tables"): - for field in frappe.db.sql("desc `%s`" % table): - if field[1]=="datetime": - frappe.db.sql("alter table `%s` change `%s` `%s` datetime(6)" % \ - (table, field[0], field[0])) - elif field[1]=="time": - frappe.db.sql("alter table `%s` change `%s` `%s` time(6)" % \ - (table, field[0], field[0])) diff --git a/frappe/patches/v4_0/webnotes_to_frappe.py b/frappe/patches/v4_0/webnotes_to_frappe.py deleted file mode 100644 index c29f6f603e..0000000000 --- a/frappe/patches/v4_0/webnotes_to_frappe.py +++ /dev/null @@ -1,12 +0,0 @@ - -import frappe, json - -def execute(): - frappe.clear_cache() - installed = frappe.get_installed_apps() - if "webnotes" in installed: - installed.remove("webnotes") - if "frappe" not in installed: - installed = ["frappe"] + installed - frappe.db.set_global("installed_apps", json.dumps(installed)) - frappe.clear_cache() diff --git a/frappe/patches/v4_0/website_sitemap_hierarchy.py b/frappe/patches/v4_0/website_sitemap_hierarchy.py deleted file mode 100644 index 6404986245..0000000000 --- a/frappe/patches/v4_0/website_sitemap_hierarchy.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe - -def execute(): - # frappe.db.sql("""update `tabWebsite Route` ws set ref_doctype=(select wsc.ref_doctype - # from `tabWebsite Template` wsc where wsc.name=ws.website_template) - # where ifnull(page_or_generator, '')!='Page'""") - - frappe.reload_doc("website", "doctype", "website_settings") - - # original_home_page = frappe.db.get_value("Website Settings", "Website Settings", "home_page") - # - # home_page = frappe.db.sql("""select name from `tabWebsite Route` - # where (name=%s or docname=%s) and name!='index'""", (original_home_page, original_home_page)) - # home_page = home_page[0][0] if home_page else original_home_page - # - # frappe.db.set_value("Website Settings", "Website Settings", "home_page", home_page) diff --git a/frappe/patches/v4_1/__init__.py b/frappe/patches/v4_1/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v4_1/enable_outgoing_email_settings.py b/frappe/patches/v4_1/enable_outgoing_email_settings.py deleted file mode 100644 index ffa891ae7c..0000000000 --- a/frappe/patches/v4_1/enable_outgoing_email_settings.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe - -def execute(): - frappe.reload_doc("core", "doctype", "outgoing_email_settings") - if (frappe.db.get_value("Outgoing Email Settings", "Outgoing Email Settings", "mail_server") or "").strip(): - frappe.db.set_value("Outgoing Email Settings", "Outgoing Email Settings", "enabled", 1) diff --git a/frappe/patches/v4_1/enable_print_as_pdf.py b/frappe/patches/v4_1/enable_print_as_pdf.py deleted file mode 100644 index e5a8f830f6..0000000000 --- a/frappe/patches/v4_1/enable_print_as_pdf.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe - -def execute(): - frappe.reload_doc("core", "doctype", "print_settings") - print_settings = frappe.get_doc("Print Settings") - print_settings.print_style = "Modern" - - try: - import pdfkit - except ImportError: - pass - else: - # if someone has already configured in Outgoing Email Settings - outgoing_email_settings = frappe.db.get_singles_dict("Outgoing Email Settings") - if "send_print_as_pdf" in outgoing_email_settings: - print_settings.send_print_as_pdf = outgoing_email_settings.send_print_as_pdf - print_settings.pdf_page_size = outgoing_email_settings.pdf_page_size - - else: - print_settings.send_print_as_pdf = 1 - - print_settings.save() diff --git a/frappe/patches/v4_1/file_manager_fix.py b/frappe/patches/v4_1/file_manager_fix.py deleted file mode 100644 index 18f44203f2..0000000000 --- a/frappe/patches/v4_1/file_manager_fix.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt -import frappe -import os -from frappe.core.doctype.file.file import get_content_hash, get_file_name -from frappe.utils import get_files_path, get_site_path - -# The files missed by the previous patch might have been replaced with new files -# with the same filename -# -# This patch does the following, -# * Detect which files were replaced and rename them with name{hash:5}.extn and -# update filedata record for the new file -# -# * make missing_files.txt in site dir with files that should be recovered from -# a backup from a time before version 3 migration -# -# * Patch remaining unpatched File records. - - -def execute(): - frappe.db.auto_commit_on_many_writes = True - rename_replacing_files() - for name, file_name, file_url in frappe.db.sql( - """select name, file_name, file_url from `tabFile` - where ifnull(file_name, '')!='' and ifnull(content_hash, '')=''"""): - b = frappe.get_doc('File', name) - old_file_name = b.file_name - b.file_name = os.path.basename(old_file_name) - if old_file_name.startswith('files/') or old_file_name.startswith('/files/'): - b.file_url = os.path.normpath('/' + old_file_name) - else: - b.file_url = os.path.normpath('/files/' + old_file_name) - try: - _file = frappe.get_doc("File", {"file_name": name}) - content = _file.get_content() - b.content_hash = get_content_hash(content) - except IOError: - print('Warning: Error processing ', name) - b.content_hash = None - b.flags.ignore_duplicate_entry_error = True - b.save() - frappe.db.auto_commit_on_many_writes = False - -def get_replaced_files(): - ret = [] - new_files = dict(frappe.db.sql("select name, file_name from `tabFile` where file_name not like 'files/%'")) - old_files = dict(frappe.db.sql("select name, file_name from `tabFile` where ifnull(content_hash, '')=''")) - invfiles = invert_dict(new_files) - - for nname, nfilename in new_files.items(): - if 'files/' + nfilename in old_files.values(): - ret.append((nfilename, invfiles[nfilename])) - return ret - -def rename_replacing_files(): - replaced_files = get_replaced_files() - if len(replaced_files): - missing_files = [v[0] for v in replaced_files] - with open(get_site_path('missing_files.txt'), 'w') as f: - f.write(('\n'.join(missing_files) + '\n').encode('utf-8')) - - for file_name, file_datas in replaced_files: - print ('processing ' + file_name) - content_hash = frappe.db.get_value('File', file_datas[0], 'content_hash') - if not content_hash: - continue - new_file_name = get_file_name(file_name, content_hash) - if os.path.exists(get_files_path(new_file_name)): - continue - print('skipping ' + file_name) - try: - os.rename(get_files_path(file_name), get_files_path(new_file_name)) - except OSError: - print('Error renaming ', file_name) - for name in file_datas: - f = frappe.get_doc('File', name) - f.file_name = new_file_name - f.file_url = '/files/' + new_file_name - f.save() - -def invert_dict(ddict): - ret = {} - for k,v in ddict.items(): - if not ret.get(v): - ret[v] = [k] - else: - ret[v].append(k) - return ret - -def get_file_name(fname, hash): - if '.' in fname: - partial, extn = fname.rsplit('.', 1) - else: - partial = fname - extn = '' - return '{partial}{suffix}.{extn}'.format(partial=partial, extn=extn, suffix=hash[:5]) diff --git a/frappe/patches/v4_2/__init__.py b/frappe/patches/v4_2/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v4_2/print_with_letterhead.py b/frappe/patches/v4_2/print_with_letterhead.py deleted file mode 100644 index 111f6c762e..0000000000 --- a/frappe/patches/v4_2/print_with_letterhead.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe - -def execute(): - frappe.reload_doc("core", "doctype", "print_settings") - print_settings = frappe.get_doc("Print Settings") - print_settings.with_letterhead = 1 - print_settings.save() diff --git a/frappe/patches/v4_2/refactor_website_routing.py b/frappe/patches/v4_2/refactor_website_routing.py deleted file mode 100644 index 77eea3d429..0000000000 --- a/frappe/patches/v4_2/refactor_website_routing.py +++ /dev/null @@ -1,8 +0,0 @@ - -import frappe - -def execute(): - # clear all static web pages - frappe.delete_doc("DocType", "Website Route", force=1) - frappe.delete_doc("Page", "sitemap-browser", force=1) - frappe.db.sql("drop table if exists `tabWebsite Route`") diff --git a/frappe/patches/v4_2/set_assign_in_doc.py b/frappe/patches/v4_2/set_assign_in_doc.py deleted file mode 100644 index 8fbd37c5c5..0000000000 --- a/frappe/patches/v4_2/set_assign_in_doc.py +++ /dev/null @@ -1,11 +0,0 @@ - -import frappe - -def execute(): - for name in frappe.db.sql_list("""select name from `tabToDo` - where ifnull(reference_type, '')!='' and ifnull(reference_name, '')!=''"""): - try: - frappe.get_doc("ToDo", name).on_update() - except Exception as e: - if not frappe.db.is_table_missing(e): - raise diff --git a/frappe/patches/v4_3/__init__.py b/frappe/patches/v4_3/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v4_3/remove_allow_on_submit_customization.py b/frappe/patches/v4_3/remove_allow_on_submit_customization.py deleted file mode 100644 index a762fd10ab..0000000000 --- a/frappe/patches/v4_3/remove_allow_on_submit_customization.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe - -def execute(): - for d in frappe.get_all("Property Setter", fields=["name", "doc_type"], - filters={"doctype_or_field": "DocField", "property": "allow_on_submit", "value": "1"}): - frappe.delete_doc("Property Setter", d.name) - frappe.clear_cache(doctype=d.doc_type) diff --git a/frappe/patches/v5_0/__init__.py b/frappe/patches/v5_0/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v5_0/bookmarks_to_stars.py b/frappe/patches/v5_0/bookmarks_to_stars.py deleted file mode 100644 index 0d2c13525e..0000000000 --- a/frappe/patches/v5_0/bookmarks_to_stars.py +++ /dev/null @@ -1,32 +0,0 @@ - -import json -import frappe -import frappe.defaults -from frappe.desk.like import _toggle_like - -def execute(): - for user in frappe.get_all("User"): - username = user["name"] - bookmarks = frappe.db.get_default("_bookmarks", username) - - if not bookmarks: - continue - - if isinstance(bookmarks, str): - bookmarks = json.loads(bookmarks) - - for opts in bookmarks: - route = (opts.get("route") or "").strip("#/ ") - - if route and route.startswith("Form"): - try: - view, doctype, docname = opts["route"].split("/") - except ValueError: - continue - - if frappe.db.exists(doctype, docname): - if (doctype=="DocType" - or int(frappe.db.get_value("DocType", doctype, "issingle") or 0) - or not frappe.db.table_exists(doctype)): - continue - _toggle_like(doctype, docname, add="Yes", user=username) diff --git a/frappe/patches/v5_0/clear_website_group_and_notifications.py b/frappe/patches/v5_0/clear_website_group_and_notifications.py deleted file mode 100644 index 3d3d0c0d16..0000000000 --- a/frappe/patches/v5_0/clear_website_group_and_notifications.py +++ /dev/null @@ -1,9 +0,0 @@ - -import frappe - -def execute(): - frappe.delete_doc("DocType", "Post") - frappe.delete_doc("DocType", "Website Group") - frappe.delete_doc("DocType", "Website Route Permission") - frappe.delete_doc("DocType", "User Vote") - frappe.delete_doc("DocType", "Notification Count") diff --git a/frappe/patches/v5_0/communication_parent.py b/frappe/patches/v5_0/communication_parent.py deleted file mode 100644 index 3c73d91972..0000000000 --- a/frappe/patches/v5_0/communication_parent.py +++ /dev/null @@ -1,6 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doc("core", "doctype", "communication") - frappe.db.sql("""update tabCommunication set reference_doctype = parenttype, reference_name = parent""") diff --git a/frappe/patches/v5_0/convert_to_barracuda_and_utf8mb4.py b/frappe/patches/v5_0/convert_to_barracuda_and_utf8mb4.py deleted file mode 100644 index 6fa6434f98..0000000000 --- a/frappe/patches/v5_0/convert_to_barracuda_and_utf8mb4.py +++ /dev/null @@ -1,16 +0,0 @@ - -import frappe -from frappe.database.mariadb.setup_db import check_database_settings -from frappe.model.meta import trim_tables - -def execute(): - check_database_settings() - - for table in frappe.db.get_tables(): - frappe.db.sql_ddl("""alter table `{0}` ENGINE=InnoDB ROW_FORMAT=COMPRESSED""".format(table)) - try: - frappe.db.sql_ddl("""alter table `{0}` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci""".format(table)) - except: - # if row size gets too large, let it be old charset! - pass - diff --git a/frappe/patches/v5_0/expire_old_scheduler_logs.py b/frappe/patches/v5_0/expire_old_scheduler_logs.py deleted file mode 100644 index 0262acd346..0000000000 --- a/frappe/patches/v5_0/expire_old_scheduler_logs.py +++ /dev/null @@ -1,8 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doctype("Error Log") - - from frappe.core.doctype.error_log.error_log import set_old_logs_as_seen - set_old_logs_as_seen() diff --git a/frappe/patches/v5_0/fix_email_alert.py b/frappe/patches/v5_0/fix_email_alert.py deleted file mode 100644 index e7366e8b66..0000000000 --- a/frappe/patches/v5_0/fix_email_alert.py +++ /dev/null @@ -1,14 +0,0 @@ -import frappe - -def execute(): - frappe.reload_doctype("Notification") - for e in frappe.get_all("Notification"): - notification = frappe.get_doc("Notification", e.name) - if notification.event == "Date Change": - if notification.days_in_advance < 0: - notification.event = "Days After" - notification.days_in_advance = -email_alert.days_in_advance - else: - notification.event = "Days Before" - - notification.save() diff --git a/frappe/patches/v5_0/fix_null_date_datetime.py b/frappe/patches/v5_0/fix_null_date_datetime.py deleted file mode 100644 index 078cba079a..0000000000 --- a/frappe/patches/v5_0/fix_null_date_datetime.py +++ /dev/null @@ -1,20 +0,0 @@ - -import frappe - -def execute(): - for table in frappe.db.get_tables(): - changed = False - desc = frappe.db.sql("desc `{table}`".format(table=table), as_dict=True) - for field in desc: - if field["Type"] == "date": - frappe.db.sql("""update `{table}` set `{fieldname}`=null where `{fieldname}`='0000-00-00'""".format( - table=table, fieldname=field["Field"])) - changed = True - - elif field["Type"] == "datetime(6)": - frappe.db.sql("""update `{table}` set `{fieldname}`=null where `{fieldname}`='0000-00-00 00:00:00.000000'""".format( - table=table, fieldname=field["Field"])) - changed = True - - if changed: - frappe.db.commit() diff --git a/frappe/patches/v5_0/fix_text_editor_file_urls.py b/frappe/patches/v5_0/fix_text_editor_file_urls.py deleted file mode 100644 index 43f0c9d8a5..0000000000 --- a/frappe/patches/v5_0/fix_text_editor_file_urls.py +++ /dev/null @@ -1,43 +0,0 @@ -import frappe -import re - -def execute(): - """Fix relative urls for image src="files/" to src="/files/" in DocTypes with text editor fields""" - doctypes_with_text_fields = frappe.get_all("DocField", fields=["parent", "fieldname"], - filters={"fieldtype": "Text Editor"}) - - done = [] - for opts in doctypes_with_text_fields: - if opts in done: - continue - - try: - result = frappe.get_all(opts.parent, fields=["name", opts.fieldname]) - except frappe.db.SQLError: - # bypass single tables - continue - - for data in result: - old_value = data[opts.fieldname] - if not old_value: - continue - - html = scrub_relative_urls(old_value) - if html != old_value: - # print_diff(html, old_value) - frappe.db.set_value(opts.parent, data.name, opts.fieldname, html, update_modified=False) - - done.append(opts) - -def scrub_relative_urls(html): - """prepend a slash before a relative url""" - try: - return re.sub(r'src[\s]*=[\s]*[\'"]files/([^\'"]*)[\'"]', r'src="/files/\g<1>"', html) - except: - print("Error", html) - raise - -def print_diff(html, old_value): - import difflib - diff = difflib.unified_diff(old_value.splitlines(1), html.splitlines(1), lineterm='') - print('\n'.join(list(diff))) diff --git a/frappe/patches/v5_0/force_sync_website.py b/frappe/patches/v5_0/force_sync_website.py deleted file mode 100644 index 8f48729276..0000000000 --- a/frappe/patches/v5_0/force_sync_website.py +++ /dev/null @@ -1,5 +0,0 @@ - -import frappe - -def execute(): - pass diff --git a/frappe/patches/v5_0/modify_session.py b/frappe/patches/v5_0/modify_session.py deleted file mode 100644 index 1c2ff0d6e6..0000000000 --- a/frappe/patches/v5_0/modify_session.py +++ /dev/null @@ -1,6 +0,0 @@ - -import frappe - -def execute(): - if "device" not in frappe.db.get_table_columns("Sessions"): - frappe.db.sql("alter table tabSessions add column `device` varchar(255) default 'desktop'") diff --git a/frappe/patches/v5_0/move_scheduler_last_event_to_system_settings.py b/frappe/patches/v5_0/move_scheduler_last_event_to_system_settings.py deleted file mode 100644 index bdc52e6152..0000000000 --- a/frappe/patches/v5_0/move_scheduler_last_event_to_system_settings.py +++ /dev/null @@ -1,8 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doctype('System Settings') - last = frappe.db.get_global('scheduler_last_event') - frappe.db.set_value('System Settings', 'System Settings', 'scheduler_last_event', last) - diff --git a/frappe/patches/v5_0/remove_shopping_cart_app.py b/frappe/patches/v5_0/remove_shopping_cart_app.py deleted file mode 100644 index ed9414159e..0000000000 --- a/frappe/patches/v5_0/remove_shopping_cart_app.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt -def execute(): - from frappe.installer import remove_from_installed_apps - remove_from_installed_apps("shopping_cart") diff --git a/frappe/patches/v5_0/rename_ref_type_fieldnames.py b/frappe/patches/v5_0/rename_ref_type_fieldnames.py deleted file mode 100644 index 01e36af8a9..0000000000 --- a/frappe/patches/v5_0/rename_ref_type_fieldnames.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import frappe - -def execute(): - try: - frappe.db.sql("alter table `tabEmail Queue` change `ref_docname` `reference_name` varchar(255)") - except Exception as e: - if not frappe.db.is_table_or_column_missing(e): - raise - - try: - frappe.db.sql("alter table `tabEmail Queue` change `ref_doctype` `reference_doctype` varchar(255)") - except Exception as e: - if not frappe.db.is_table_or_column_missing(e): - raise - frappe.reload_doctype("Email Queue") diff --git a/frappe/patches/v5_0/rename_table_fieldnames.py b/frappe/patches/v5_0/rename_table_fieldnames.py deleted file mode 100644 index 79703bbba2..0000000000 --- a/frappe/patches/v5_0/rename_table_fieldnames.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe -from frappe.model.utils.rename_field import rename_field -from frappe.modules import scrub, get_doctype_module - -rename_map = { - "Customize Form": [ - ["customize_form_fields", "fields"] - ], - "Email Alert": [ - ["email_alert_recipients", "recipients"] - ], - "Workflow": [ - ["workflow_document_states", "states"], - ["workflow_transitions", "transitions"] - ] -} - -def execute(): - frappe.reload_doc("custom", "doctype", "customize_form") - frappe.reload_doc("email", "doctype", "notification") - frappe.reload_doc("desk", "doctype", "event") - frappe.reload_doc("workflow", "doctype", "workflow") - - for dt, field_list in rename_map.items(): - for field in field_list: - rename_field(dt, field[0], field[1]) diff --git a/frappe/patches/v5_0/style_settings_to_website_theme.py b/frappe/patches/v5_0/style_settings_to_website_theme.py deleted file mode 100644 index 73ee28c1fc..0000000000 --- a/frappe/patches/v5_0/style_settings_to_website_theme.py +++ /dev/null @@ -1,59 +0,0 @@ - -import frappe -from frappe import _ -from frappe.utils import cint - -def execute(): - frappe.reload_doc("website", "doctype", "website_theme") - frappe.reload_doc("website", "website_theme", "standard") - frappe.reload_doctype("Website Settings") - migrate_style_settings() - frappe.delete_doc("website", "doctype", "style_settings") - -def migrate_style_settings(): - style_settings = frappe.db.get_singles_dict("Style Settings") - standard_website_theme = frappe.get_doc("Website Theme", "Standard") - - website_theme = frappe.copy_doc(standard_website_theme) - website_theme.custom = 1 - website_theme.theme = _("Custom") - - if style_settings: - map_color_fields(style_settings, website_theme) - map_other_fields(style_settings, website_theme) - - website_theme.no_sidebar = cint(frappe.db.get_single_value("Website Settings", "no_sidebar")) - - website_theme.save() - website_theme.set_as_default() - -def map_color_fields(style_settings, website_theme): - color_fields_map = { - "page_text": "text_color", - "page_links": "link_color", - "top_bar_background": "top_bar_color", - "top_bar_foreground": "top_bar_text_color", - "footer_background": "footer_color", - "footer_color": "footer_text_color", - } - - for from_fieldname, to_fieldname in color_fields_map.items(): - from_value = style_settings.get(from_fieldname) - - if from_value: - website_theme.set(to_fieldname, "#{0}".format(from_value)) - -def map_other_fields(style_settings, website_theme): - other_fields_map = { - "heading_text_as": "heading_style", - "google_web_font_for_heading": "heading_webfont", - "google_web_font_for_text": "text_webfont", - "add_css": "css" - } - - for from_fieldname, to_fieldname in other_fields_map.items(): - website_theme.set(to_fieldname, style_settings.get(from_fieldname)) - - for fieldname in ("apply_style", "background_image", "background_color", - "font_size"): - website_theme.set(fieldname, style_settings.get(fieldname)) diff --git a/frappe/patches/v5_0/update_shared.py b/frappe/patches/v5_0/update_shared.py deleted file mode 100644 index e549d7271d..0000000000 --- a/frappe/patches/v5_0/update_shared.py +++ /dev/null @@ -1,37 +0,0 @@ - -import frappe -import frappe.share - -def execute(): - frappe.reload_doc("core", "doctype", "docperm") - frappe.reload_doc("core", "doctype", "docshare") - frappe.reload_doc('email', 'doctype', 'email_account') - - # default share to all writes - frappe.db.sql("""update tabDocPerm set `share`=1 where ifnull(`write`,0)=1 and ifnull(`permlevel`,0)=0""") - - # every user must have access to his / her own detail - users = frappe.get_all("User", filters={"user_type": "System User"}) - usernames = [user.name for user in users] - for user in usernames: - frappe.share.add("User", user, user, write=1, share=1) - - # move event user to shared - if frappe.db.exists("DocType", "Event User"): - for event in frappe.get_all("Event User", fields=["parent", "person"]): - if event.person in usernames: - if not frappe.db.exists("Event", event.parent): - frappe.db.sql("delete from `tabEvent User` where parent = %s",event.parent) - else: - frappe.share.add("Event", event.parent, event.person, write=1) - - frappe.delete_doc("DocType", "Event User") - - # move note user to shared - if frappe.db.exists("DocType", "Note User"): - for note in frappe.get_all("Note User", fields=["parent", "user", "permission"]): - perm = {"read": 1} if note.permission=="Read" else {"write": 1} - if note.user in usernames: - frappe.share.add("Note", note.parent, note.user, **perm) - - frappe.delete_doc("DocType", "Note User") diff --git a/frappe/patches/v5_0/v4_to_v5.py b/frappe/patches/v5_0/v4_to_v5.py deleted file mode 100644 index 479acc6d63..0000000000 --- a/frappe/patches/v5_0/v4_to_v5.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe - -def execute(): - changed = ( - ("desk", ("feed", "event", "todo", "note")), - ("custom", ("custom_field", "custom_script", "customize_form", - "customize_form_field", "property_setter")), - ("email", ("email_queue", "notification", "notification_recipient", "standard_reply")), - ("geo", ("country", "currency")), - ("print", ("letter_head", "print_format", "print_settings")) - ) - for module in changed: - for doctype in module[1]: - frappe.reload_doc(module[0], "doctype", doctype) diff --git a/frappe/patches/v5_2/__init__.py b/frappe/patches/v5_2/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v5_2/change_checks_to_not_null.py b/frappe/patches/v5_2/change_checks_to_not_null.py deleted file mode 100644 index 32be3aa752..0000000000 --- a/frappe/patches/v5_2/change_checks_to_not_null.py +++ /dev/null @@ -1,34 +0,0 @@ - -import frappe -from frappe.utils import cint -from frappe.model import default_fields - -def execute(): - for table in frappe.db.get_tables(): - doctype = table[3:] - if frappe.db.exists("DocType", doctype): - fieldnames = [df["fieldname"] for df in - frappe.get_all("DocField", fields=["fieldname"], filters={"parent": doctype})] - custom_fieldnames = [df["fieldname"] for df in - frappe.get_all("Custom Field", fields=["fieldname"], filters={"dt": doctype})] - - else: - fieldnames = custom_fieldnames = [] - - for column in frappe.db.sql("""desc `{0}`""".format(table), as_dict=True): - if column["Type"]=="int(1)": - fieldname = column["Field"] - - # only change for defined fields, ignore old fields that don't exist in meta - if not (fieldname in default_fields or fieldname in fieldnames or fieldname in custom_fieldnames): - continue - - # set 0 - frappe.db.sql("""update `{table}` set `{column}`=0 where `{column}` is null"""\ - .format(table=table, column=fieldname)) - frappe.db.commit() - - # change definition - frappe.db.sql_ddl("""alter table `{table}` - modify `{column}` int(1) not null default {default}"""\ - .format(table=table, column=fieldname, default=cint(column["Default"]))) diff --git a/frappe/patches/v5_3/__init__.py b/frappe/patches/v5_3/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v5_3/rename_chinese_languages.py b/frappe/patches/v5_3/rename_chinese_languages.py deleted file mode 100644 index f720fb7538..0000000000 --- a/frappe/patches/v5_3/rename_chinese_languages.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -import frappe -from frappe.translate import rename_language - -def execute(): - language_map = { - "中国(简体)": "簡體中文", - "中國(繁體)": "正體中文" - } - - for old_name, new_name in language_map.items(): - rename_language(old_name, new_name) diff --git a/frappe/patches/v6_0/__init__.py b/frappe/patches/v6_0/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v6_0/communication_status_and_permission.py b/frappe/patches/v6_0/communication_status_and_permission.py deleted file mode 100644 index 435dcc21a5..0000000000 --- a/frappe/patches/v6_0/communication_status_and_permission.py +++ /dev/null @@ -1,19 +0,0 @@ - -import frappe -from frappe.permissions import reset_perms - -def execute(): - frappe.reload_doctype("Communication") - - # set status = "Linked" - frappe.db.sql("""update `tabCommunication` set status='Linked' - where ifnull(reference_doctype, '')!='' and ifnull(reference_name, '')!=''""") - - frappe.db.sql("""update `tabCommunication` set status='Closed' - where status='Archived'""") - - # reset permissions if owner of all DocPerms is Administrator - if not frappe.db.sql("""select name from `tabDocPerm` - where parent='Communication' and ifnull(owner, '')!='Administrator'"""): - - reset_perms("Communication") diff --git a/frappe/patches/v6_0/document_type_rename.py b/frappe/patches/v6_0/document_type_rename.py deleted file mode 100644 index 53eec5d85c..0000000000 --- a/frappe/patches/v6_0/document_type_rename.py +++ /dev/null @@ -1,8 +0,0 @@ - -import frappe - -def execute(): - frappe.db.sql("""update tabDocType set document_type='Document' - where document_type='Transaction'""") - frappe.db.sql("""update tabDocType set document_type='Setup' - where document_type='Master'""") diff --git a/frappe/patches/v6_0/fix_ghana_currency.py b/frappe/patches/v6_0/fix_ghana_currency.py deleted file mode 100644 index 50feb3ca3f..0000000000 --- a/frappe/patches/v6_0/fix_ghana_currency.py +++ /dev/null @@ -1,6 +0,0 @@ -def execute(): - from frappe.geo.country_info import get_all - import frappe.utils.install - - countries = get_all() - frappe.utils.install.add_country_and_currency("Ghana", frappe._dict(countries["Ghana"])) diff --git a/frappe/patches/v6_0/make_task_log_folder.py b/frappe/patches/v6_0/make_task_log_folder.py deleted file mode 100644 index b5ed547d71..0000000000 --- a/frappe/patches/v6_0/make_task_log_folder.py +++ /dev/null @@ -1,7 +0,0 @@ - -import frappe.utils, os - -def execute(): - path = frappe.utils.get_site_path('task-logs') - if not os.path.exists(path): - os.makedirs(path) diff --git a/frappe/patches/v6_1/__init__.py b/frappe/patches/v6_1/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v6_1/rename_file_data.py b/frappe/patches/v6_1/rename_file_data.py deleted file mode 100644 index 3c62217e8d..0000000000 --- a/frappe/patches/v6_1/rename_file_data.py +++ /dev/null @@ -1,37 +0,0 @@ -import frappe - -def execute(): - from frappe.core.doctype.file.file import make_home_folder - - if not frappe.db.exists("DocType", "File"): - frappe.rename_doc("DocType", "File Data", "File") - frappe.reload_doctype("File") - - if not frappe.db.exists("File", {"is_home_folder": 1}): - make_home_folder() - - # make missing folders and set parent folder - for file in frappe.get_all("File", filters={"is_folder": 0}): - file = frappe.get_doc("File", file.name) - file.flags.ignore_folder_validate = True - file.flags.ignore_file_validate = True - file.flags.ignore_duplicate_entry_error = True - file.flags.ignore_links = True - file.set_folder_name() - try: - file.save() - except: - print(frappe.get_traceback()) - raise - - from frappe.utils.nestedset import rebuild_tree - rebuild_tree("File", "folder") - - # reset file size - for folder in frappe.db.sql("""select name from tabFile f1 where is_folder = 1 and - (select count(*) from tabFile f2 where f2.folder = f1.name and f2.is_folder = 1) = 0"""): - folder = frappe.get_doc("File", folder[0]) - folder.save() - - - diff --git a/frappe/patches/v6_11/__init__.py b/frappe/patches/v6_11/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v6_11/rename_field_in_email_account.py b/frappe/patches/v6_11/rename_field_in_email_account.py deleted file mode 100644 index 8e600cc2b9..0000000000 --- a/frappe/patches/v6_11/rename_field_in_email_account.py +++ /dev/null @@ -1,7 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doc("email", "doctype", "email_account") - if frappe.db.has_column('Email Account', 'pop3_server'): - frappe.db.sql("update `tabEmail Account` set email_server = pop3_server") diff --git a/frappe/patches/v6_15/__init__.py b/frappe/patches/v6_15/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v6_15/remove_property_setter_for_previous_field.py b/frappe/patches/v6_15/remove_property_setter_for_previous_field.py deleted file mode 100644 index 9f0cd69489..0000000000 --- a/frappe/patches/v6_15/remove_property_setter_for_previous_field.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import frappe, json -from frappe.utils import cstr - -def execute(): - # deprecated on 2016-03-09 - # using insert_after instead - return - - frappe.db.sql("""delete from `tabProperty Setter` where property='previous_field'""") - - all_custom_fields = frappe._dict() - for d in frappe.db.sql("""select name, dt, fieldname, insert_after from `tabCustom Field` - where insert_after is not null and insert_after != ''""", as_dict=1): - all_custom_fields.setdefault(d.dt, frappe._dict()).setdefault(d.fieldname, d.insert_after) - - for dt, custom_fields in all_custom_fields.items(): - _idx = [] - existing_ps = frappe.db.get_value("Property Setter", - {"doc_type": dt, "property": "_idx"}, ["name", "value", "creation"], as_dict=1) - - # if no existsing property setter, build based on meta - if not existing_ps: - _idx = get_sorted_fields(dt, custom_fields) - else: - _idx = json.loads(existing_ps.value) - - idx_needs_to_be_fixed = False - for fieldname, insert_after in custom_fields.items(): - # Delete existing property setter if field is not there - if fieldname not in _idx: - idx_needs_to_be_fixed = True - break - else: - previous_field = _idx[_idx.index(fieldname) - 1] - - if previous_field != insert_after and cstr(existing_ps.creation) >= "2015-12-28": - idx_needs_to_be_fixed = True - break - - if idx_needs_to_be_fixed: - frappe.delete_doc("Property Setter", existing_ps.name) - _idx = get_sorted_fields(dt, custom_fields) - - if _idx: - frappe.make_property_setter({ - "doctype":dt, - "doctype_or_field": "DocType", - "property": "_idx", - "value": json.dumps(_idx), - "property_type": "Text" - }, validate_fields_for_doctype=False) - - -def get_sorted_fields(doctype, custom_fields): - """sort on basis of insert_after""" - fields_dict = frappe.get_meta(doctype).get("fields") - - standard_fields_count = frappe.db.sql("""select count(name) from `tabDocField` - where parent=%s""", doctype)[0][0] - - newlist = [] - pending = [d.fieldname for d in fields_dict] - - maxloops = len(custom_fields) + 20 - while (pending and maxloops>0): - maxloops -= 1 - for fieldname in pending[:]: - if fieldname in custom_fields and len(newlist) >= standard_fields_count: - # field already added - for n in newlist: - if n==custom_fields.get(fieldname): - newlist.insert(newlist.index(n)+1, fieldname) - pending.remove(fieldname) - break - else: - newlist.append(fieldname) - pending.remove(fieldname) - - # recurring at end - if pending: - newlist += pending - - return newlist diff --git a/frappe/patches/v6_15/set_username.py b/frappe/patches/v6_15/set_username.py deleted file mode 100644 index ebf01763d0..0000000000 --- a/frappe/patches/v6_15/set_username.py +++ /dev/null @@ -1,16 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doctype("User") - - # give preference to System Users - users = frappe.db.sql_list("""select name from `tabUser` order by if(user_type='System User', 0, 1)""") - for name in users: - user = frappe.get_doc("User", name) - if user.username or not user.first_name: - continue - - username = user.suggest_username() - if username: - user.db_set("username", username, update_modified=False) diff --git a/frappe/patches/v6_16/__init__.py b/frappe/patches/v6_16/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v6_16/feed_doc_owner.py b/frappe/patches/v6_16/feed_doc_owner.py deleted file mode 100644 index b7e738b6d9..0000000000 --- a/frappe/patches/v6_16/feed_doc_owner.py +++ /dev/null @@ -1,31 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doctype("Communication") - - for doctype, name in frappe.db.sql("""select distinct reference_doctype, reference_name - from `tabCommunication` - where - (reference_doctype is not null and reference_doctype != '') - and (reference_name is not null and reference_name != '') - and (reference_owner is null or reference_owner = '') - for update"""): - - owner = frappe.db.get_value(doctype, name, "owner") - - if not owner: - continue - - frappe.db.sql("""update `tabCommunication` - set reference_owner=%(owner)s - where - reference_doctype=%(doctype)s - and reference_name=%(name)s - and (reference_owner is null or reference_owner = '')""".format(doctype=doctype), { - "doctype": doctype, - "name": name, - "owner": owner - }) - - frappe.db.commit() diff --git a/frappe/patches/v6_16/star_to_like.py b/frappe/patches/v6_16/star_to_like.py deleted file mode 100644 index f3fc6310d9..0000000000 --- a/frappe/patches/v6_16/star_to_like.py +++ /dev/null @@ -1,15 +0,0 @@ - -import frappe -from frappe.database.schema import add_column - -def execute(): - frappe.db.sql("""update `tabSingles` set field='_liked_by' where field='_starred_by'""") - frappe.db.commit() - - for table in frappe.db.get_tables(): - columns = [r[0] for r in frappe.db.sql("DESC `{0}`".format(table))] - if "_starred_by" in columns and '_liked_by' not in columns: - frappe.db.sql_ddl("""alter table `{0}` change `_starred_by` `_liked_by` Text """.format(table)) - - if not frappe.db.has_column("Communication", "_liked_by"): - add_column("Communication", "_liked_by", "Text") diff --git a/frappe/patches/v6_19/__init__.py b/frappe/patches/v6_19/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v6_19/comment_feed_communication.py b/frappe/patches/v6_19/comment_feed_communication.py deleted file mode 100644 index 64c5ad9c4c..0000000000 --- a/frappe/patches/v6_19/comment_feed_communication.py +++ /dev/null @@ -1,307 +0,0 @@ - -import frappe -from frappe import _ -from frappe.model.rename_doc import get_link_fields -from frappe.model.dynamic_links import dynamic_link_queries -from frappe.permissions import reset_perms - -def execute(): - # comments stay comments in v12 - return - - frappe.reload_doctype("DocType") - frappe.reload_doctype("Communication") - reset_perms("Communication") - - migrate_comments() - frappe.delete_doc("DocType", "Comment") - # frappe.db.sql_ddl("drop table `tabComment`") - - migrate_feed() - frappe.delete_doc("DocType", "Feed") - # frappe.db.sql_ddl("drop table `tabFeed`") - - update_timeline_doc_for("Blogger") - -def migrate_comments(): - from_fields = "" - to_fields = "" - - if "reference_doctype" in frappe.db.get_table_columns("Comment"): - from_fields = "reference_doctype as link_doctype, reference_name as link_name," - to_fields = "link_doctype, link_name," - - # comments - frappe.db.sql("""insert ignore into `tabCommunication` ( - subject, - content, - sender, - sender_full_name, - comment_type, - communication_date, - reference_doctype, - reference_name, - {to_fields} - - name, - user, - owner, - creation, - modified_by, - modified, - status, - sent_or_received, - communication_type, - seen - ) - select - substring(comment, 1, 100) as subject, - comment as content, - comment_by as sender, - comment_by_fullname as sender_full_name, - comment_type, - ifnull(timestamp(comment_date, comment_time), creation) as communication_date, - comment_doctype as reference_doctype, - comment_docname as reference_name, - {from_fields} - - name, - owner as user, - owner, - creation, - modified_by, - modified, - 'Linked' as status, - 'Sent' as sent_or_received, - 'Comment' as communication_type, - 1 as seen - from `tabComment` where comment_doctype is not null and comment_doctype not in ('Message', 'My Company')""" - .format(to_fields=to_fields, from_fields=from_fields)) - - # chat and assignment notifications - frappe.db.sql("""insert ignore into `tabCommunication` ( - subject, - content, - sender, - sender_full_name, - comment_type, - communication_date, - reference_doctype, - reference_name, - {to_fields} - - name, - user, - owner, - creation, - modified_by, - modified, - status, - sent_or_received, - communication_type, - seen - ) - select - case - when parenttype='Assignment' then %(assignment)s - else substring(comment, 1, 100) - end - as subject, - comment as content, - comment_by as sender, - comment_by_fullname as sender_full_name, - comment_type, - ifnull(timestamp(comment_date, comment_time), creation) as communication_date, - 'User' as reference_doctype, - comment_docname as reference_name, - {from_fields} - - name, - owner as user, - owner, - creation, - modified_by, - modified, - 'Linked' as status, - 'Sent' as sent_or_received, - case - when parenttype='Assignment' then 'Notification' - else 'Chat' - end - as communication_type, - 1 as seen - from `tabComment` where comment_doctype in ('Message', 'My Company')""" - .format(to_fields=to_fields, from_fields=from_fields), {"assignment": _("Assignment")}) - -def migrate_feed(): - # migrate delete feed - for doctype in frappe.db.sql("""select distinct doc_type from `tabFeed` where subject=%(deleted)s""", {"deleted": _("Deleted")}): - frappe.db.sql("""insert ignore into `tabCommunication` ( - subject, - sender, - sender_full_name, - comment_type, - communication_date, - reference_doctype, - - name, - user, - owner, - creation, - modified_by, - modified, - status, - sent_or_received, - communication_type, - seen - ) - select - concat_ws(" ", %(_doctype)s, doc_name) as subject, - owner as sender, - full_name as sender_full_name, - 'Deleted' as comment_type, - creation as communication_date, - doc_type as reference_doctype, - - name, - owner as user, - owner, - creation, - modified_by, - modified, - 'Linked' as status, - 'Sent' as sent_or_received, - 'Comment' as communication_type, - 1 as seen - from `tabFeed` where subject=%(deleted)s and doc_type=%(doctype)s""", { - "deleted": _("Deleted"), - "doctype": doctype, - "_doctype": _(doctype) - }) - - # migrate feed type login or empty - frappe.db.sql("""insert ignore into `tabCommunication` ( - subject, - sender, - sender_full_name, - comment_type, - communication_date, - reference_doctype, - reference_name, - - name, - user, - owner, - creation, - modified_by, - modified, - status, - sent_or_received, - communication_type, - seen - ) - select - subject, - owner as sender, - full_name as sender_full_name, - case - when feed_type='Login' then 'Info' - else 'Updated' - end as comment_type, - creation as communication_date, - doc_type as reference_doctype, - doc_name as reference_name, - - name, - owner as user, - owner, - creation, - modified_by, - modified, - 'Linked' as status, - 'Sent' as sent_or_received, - 'Comment' as communication_type, - 1 as seen - from `tabFeed` where (feed_type in ('Login', '') or feed_type is null)""") - -def update_timeline_doc_for(timeline_doctype): - """NOTE: This method may be used by other apps for patching. It also has COMMIT after each update.""" - - # find linked doctypes - # link fields - update_for_linked_docs(timeline_doctype) - - # dynamic link fields - update_for_dynamically_linked_docs(timeline_doctype) - -def update_for_linked_docs(timeline_doctype): - for df in get_link_fields(timeline_doctype): - if df.issingle: - continue - - reference_doctype = df.parent - - if not is_valid_timeline_doctype(reference_doctype, timeline_doctype): - continue - - for doc in frappe.get_all(reference_doctype, fields=["name", df.fieldname]): - timeline_name = doc.get(df.fieldname) - update_communication(timeline_doctype, timeline_name, reference_doctype, doc.name) - -def update_for_dynamically_linked_docs(timeline_doctype): - dynamic_link_fields = [] - for query in dynamic_link_queries: - for df in frappe.db.sql(query, as_dict=True): - dynamic_link_fields.append(df) - - for df in dynamic_link_fields: - reference_doctype = df.parent - - if not is_valid_timeline_doctype(reference_doctype, timeline_doctype): - continue - - try: - docs = frappe.get_all(reference_doctype, fields=["name", df.fieldname], - filters={ df.options: timeline_doctype }) - except frappe.db.SQLError as e: - if frappe.db.is_table_missing(e): - # single - continue - else: - raise - - for doc in docs: - timeline_name = doc.get(df.fieldname) - update_communication(timeline_doctype, timeline_name, reference_doctype, doc.name) - -def update_communication(timeline_doctype, timeline_name, reference_doctype, reference_name): - if not timeline_name: - return - - frappe.db.sql("""update `tabCommunication` set timeline_doctype=%(timeline_doctype)s, timeline_name=%(timeline_name)s - where (reference_doctype=%(reference_doctype)s and reference_name=%(reference_name)s) - and (timeline_doctype is null or timeline_doctype='') - and (timeline_name is null or timeline_name='')""", { - "timeline_doctype": timeline_doctype, - "timeline_name": timeline_name, - "reference_doctype": reference_doctype, - "reference_name": reference_name - }) - - frappe.db.commit() - -def is_valid_timeline_doctype(reference_doctype, timeline_doctype): - # for reloading timeline_field - frappe.reload_doctype(reference_doctype) - - # make sure the timeline field's doctype is same as timeline doctype - meta = frappe.get_meta(reference_doctype) - if not meta.timeline_field: - return False - - doctype = meta.get_link_doctype(meta.timeline_field) - if doctype != timeline_doctype: - return False - - - return True diff --git a/frappe/patches/v6_2/__init__.py b/frappe/patches/v6_2/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v6_2/ignore_user_permissions_if_missing.py b/frappe/patches/v6_2/ignore_user_permissions_if_missing.py deleted file mode 100644 index e216dc36b6..0000000000 --- a/frappe/patches/v6_2/ignore_user_permissions_if_missing.py +++ /dev/null @@ -1,8 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doctype("System Settings") - system_settings = frappe.get_doc("System Settings") - system_settings.flags.ignore_mandatory = 1 - system_settings.save() diff --git a/frappe/patches/v6_2/rename_backup_manager.py b/frappe/patches/v6_2/rename_backup_manager.py deleted file mode 100644 index df2fa72c05..0000000000 --- a/frappe/patches/v6_2/rename_backup_manager.py +++ /dev/null @@ -1,20 +0,0 @@ - -import frappe - -def execute(): - unset = False - frappe.reload_doc("integrations", "doctype", "dropbox_backup") - - dropbox_backup = frappe.get_doc("Dropbox Backup", "Dropbox Backup") - for df in dropbox_backup.meta.fields: - value = frappe.db.get_single_value("Backup Manager", df.fieldname) - if value: - if df.fieldname=="upload_backups_to_dropbox" and value=="Never": - value = "Daily" - unset = True - dropbox_backup.set(df.fieldname, value) - - if unset: - dropbox_backup.set("send_backups_to_dropbox", 0) - - dropbox_backup.save() diff --git a/frappe/patches/v6_20x/__init__.py b/frappe/patches/v6_20x/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v6_20x/remove_roles_from_website_user.py b/frappe/patches/v6_20x/remove_roles_from_website_user.py deleted file mode 100644 index 19009ff455..0000000000 --- a/frappe/patches/v6_20x/remove_roles_from_website_user.py +++ /dev/null @@ -1,11 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doc("core", "doctype", "user_email") - frappe.reload_doc("core", "doctype", "user") - for user_name in frappe.get_all('User', filters={'user_type': 'Website User'}): - user = frappe.get_doc('User', user_name) - if user.roles: - user.roles = [] - user.save() diff --git a/frappe/patches/v6_20x/set_allow_draft_for_print.py b/frappe/patches/v6_20x/set_allow_draft_for_print.py deleted file mode 100644 index 0b604567ec..0000000000 --- a/frappe/patches/v6_20x/set_allow_draft_for_print.py +++ /dev/null @@ -1,5 +0,0 @@ - -import frappe - -def execute(): - frappe.db.set_value("Print Settings", "Print Settings", "allow_print_for_draft", 1) \ No newline at end of file diff --git a/frappe/patches/v6_20x/update_insert_after.py b/frappe/patches/v6_20x/update_insert_after.py deleted file mode 100644 index 37820b2437..0000000000 --- a/frappe/patches/v6_20x/update_insert_after.py +++ /dev/null @@ -1,27 +0,0 @@ - -import frappe, json - -def execute(): - for ps in frappe.get_all('Property Setter', filters={'property': '_idx'}, - fields = ['doc_type', 'value']): - custom_fields = frappe.get_all('Custom Field', - filters = {'dt': ps.doc_type}, fields=['name', 'fieldname']) - - if custom_fields: - _idx = json.loads(ps.value) - - for custom_field in custom_fields: - if custom_field.fieldname in _idx: - custom_field_idx = _idx.index(custom_field.fieldname) - if custom_field_idx == 0: - prev_fieldname = "" - - else: - prev_fieldname = _idx[custom_field_idx - 1] - - else: - prev_fieldname = _idx[-1] - custom_field_idx = len(_idx) - - frappe.db.set_value('Custom Field', custom_field.name, 'insert_after', prev_fieldname) - frappe.db.set_value('Custom Field', custom_field.name, 'idx', custom_field_idx) diff --git a/frappe/patches/v6_21/__init__.py b/frappe/patches/v6_21/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v6_21/print_settings_repeat_header_footer.py b/frappe/patches/v6_21/print_settings_repeat_header_footer.py deleted file mode 100644 index 0919c35903..0000000000 --- a/frappe/patches/v6_21/print_settings_repeat_header_footer.py +++ /dev/null @@ -1,6 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doctype('Print Settings') - frappe.db.set_value('Print Settings', 'Print Settings', 'repeat_header_footer', 1) diff --git a/frappe/patches/v6_24/__init__.py b/frappe/patches/v6_24/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v6_24/set_language_as_code.py b/frappe/patches/v6_24/set_language_as_code.py deleted file mode 100644 index 6f862ede2e..0000000000 --- a/frappe/patches/v6_24/set_language_as_code.py +++ /dev/null @@ -1,8 +0,0 @@ - -import frappe - -from frappe.translate import get_lang_dict - -# migrate language from name to code -def execute(): - return diff --git a/frappe/patches/v6_4/__init__.py b/frappe/patches/v6_4/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v6_4/reduce_varchar_length.py b/frappe/patches/v6_4/reduce_varchar_length.py deleted file mode 100644 index 7edde55778..0000000000 --- a/frappe/patches/v6_4/reduce_varchar_length.py +++ /dev/null @@ -1,36 +0,0 @@ -import frappe - -def execute(): - for doctype in frappe.get_all("DocType", filters={"issingle": 0}): - doctype = doctype.name - if not frappe.db.table_exists(doctype): - continue - - for column in frappe.db.sql("desc `tab{doctype}`".format(doctype=doctype), as_dict=True): - fieldname = column["Field"] - column_type = column["Type"] - - if not column_type.startswith("varchar"): - continue - - max_length = frappe.db.sql("""select max(char_length(`{fieldname}`)) from `tab{doctype}`"""\ - .format(fieldname=fieldname, doctype=doctype)) - - max_length = max_length[0][0] if max_length else None - - if max_length and 140 < max_length <= 255: - print( - "setting length of '{fieldname}' in '{doctype}' as {length}".format( - fieldname=fieldname, doctype=doctype, length=max_length) - ) - - # create property setter for length - frappe.make_property_setter({ - "doctype": doctype, - "fieldname": fieldname, - "property": "length", - "value": max_length, - "property_type": "Int" - }) - - frappe.clear_cache(doctype=doctype) diff --git a/frappe/patches/v6_4/rename_bengali_language.py b/frappe/patches/v6_4/rename_bengali_language.py deleted file mode 100644 index f872dea1b9..0000000000 --- a/frappe/patches/v6_4/rename_bengali_language.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -import frappe -from frappe.translate import rename_language - -def execute(): - rename_language("বাঙালি", "বাংলা") \ No newline at end of file diff --git a/frappe/patches/v6_6/__init__.py b/frappe/patches/v6_6/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v6_6/fix_file_url.py b/frappe/patches/v6_6/fix_file_url.py deleted file mode 100644 index 48e292f4d4..0000000000 --- a/frappe/patches/v6_6/fix_file_url.py +++ /dev/null @@ -1,36 +0,0 @@ - -import frappe -from frappe.model.meta import is_single - -def execute(): - """Fix old style file urls that start with files/""" - fix_file_urls() - fix_attach_field_urls() - -def fix_file_urls(): - for file in frappe.db.sql_list("""select name from `tabFile` where file_url like 'files/%'"""): - file = frappe.get_doc("File", file) - file.db_set("file_url", "/" + file.file_url, update_modified=False) - try: - file.validate_file() - file.db_set("file_name", file.file_name, update_modified=False) - if not file.content_hash: - file.generate_content_hash() - file.db_set("content_hash", file.content_hash, update_modified=False) - - except IOError: - pass - -def fix_attach_field_urls(): - # taken from an old patch - attach_fields = (frappe.db.sql("""select parent, fieldname from `tabDocField` where fieldtype in ('Attach', 'Attach Image')""") + - frappe.db.sql("""select dt, fieldname from `tabCustom Field` where fieldtype in ('Attach', 'Attach Image')""")) - - for doctype, fieldname in attach_fields: - if is_single(doctype): - frappe.db.sql("""update `tabSingles` set value=concat("/", `value`) - where doctype=%(doctype)s and field=%(fieldname)s - and value like 'files/%%'""", {"doctype": doctype, "fieldname": fieldname}) - else: - frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=concat("/", `{fieldname}`) - where `{fieldname}` like 'files/%'""".format(doctype=doctype, fieldname=fieldname)) diff --git a/frappe/patches/v6_6/rename_slovak_language.py b/frappe/patches/v6_6/rename_slovak_language.py deleted file mode 100644 index 198949e79c..0000000000 --- a/frappe/patches/v6_6/rename_slovak_language.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -import frappe -from frappe.translate import rename_language - -def execute(): - rename_language("slovenčina", "slovenčina (Slovak)") diff --git a/frappe/patches/v6_6/user_last_active.py b/frappe/patches/v6_6/user_last_active.py deleted file mode 100644 index b9f63fa45e..0000000000 --- a/frappe/patches/v6_6/user_last_active.py +++ /dev/null @@ -1,6 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doctype("User") - frappe.db.sql("update `tabUser` set last_active=last_login") diff --git a/frappe/patches/v6_9/__init__.py b/frappe/patches/v6_9/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v6_9/int_float_not_null.py b/frappe/patches/v6_9/int_float_not_null.py deleted file mode 100644 index c414d6b583..0000000000 --- a/frappe/patches/v6_9/int_float_not_null.py +++ /dev/null @@ -1,30 +0,0 @@ - -import frappe -from frappe.utils import cint, flt - -def execute(): - for doctype in frappe.get_all("DocType", filters={"issingle": 0}): - doctype = doctype.name - meta = frappe.get_meta(doctype) - - for column in frappe.db.sql("desc `tab{doctype}`".format(doctype=doctype), as_dict=True): - fieldname = column["Field"] - column_type = column["Type"] - - if not (column_type.startswith("int") or column_type.startswith("decimal")): - continue - - frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=0 where `{fieldname}` is null"""\ - .format(doctype=doctype, fieldname=fieldname)) - - # alter table - if column["Null"]=='YES': - if not meta.get_field(fieldname): - continue - - default = cint(column["Default"]) if column_type.startswith("int") else flt(column["Default"]) - frappe.db.sql_ddl("""alter table `tab{doctype}` - change `{fieldname}` `{fieldname}` {column_type} not null default '{default}'""".format( - doctype=doctype, fieldname=fieldname, column_type=column_type, default=default)) - - diff --git a/frappe/patches/v6_9/rename_burmese_language.py b/frappe/patches/v6_9/rename_burmese_language.py deleted file mode 100644 index 5e1333077e..0000000000 --- a/frappe/patches/v6_9/rename_burmese_language.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -import frappe -from frappe.translate import rename_language - -def execute(): - rename_language("Melayu", "မြန်မာ") diff --git a/frappe/patches/v7_0/__init__.py b/frappe/patches/v7_0/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v7_0/add_communication_in_doc.py b/frappe/patches/v7_0/add_communication_in_doc.py deleted file mode 100644 index 8be229fe3a..0000000000 --- a/frappe/patches/v7_0/add_communication_in_doc.py +++ /dev/null @@ -1,14 +0,0 @@ - -import frappe - -from frappe.core.doctype.comment.comment import update_comment_in_doc - -def execute(): - for d in frappe.db.get_all("Communication", - fields = ['name', 'reference_doctype', 'reference_name', 'SUBSTRING(content,1,102)', 'communication_type'], - filters = {"reference_name":None,"reference_doctype":None,'communication_type': 'Communication'}): - - try: - update_comment_in_doc(d) - except frappe.ImplicitCommitError: - pass diff --git a/frappe/patches/v7_0/cleanup_list_settings.py b/frappe/patches/v7_0/cleanup_list_settings.py deleted file mode 100644 index 9fe2e71ed1..0000000000 --- a/frappe/patches/v7_0/cleanup_list_settings.py +++ /dev/null @@ -1,20 +0,0 @@ - -import frappe, json - -def execute(): - if frappe.db.table_exists("__ListSettings"): - list_settings = frappe.db.sql("select user, doctype, data from __ListSettings", as_dict=1) - for ls in list_settings: - if ls and ls.data: - data = json.loads(ls.data) - if "fields" not in data: - continue - fields = data["fields"] - for field in fields: - if "name as" in field: - fields.remove(field) - data["fields"] = fields - - frappe.db.sql("update __ListSettings set data = %s where user=%s and doctype=%s", - (json.dumps(data), ls.user, ls.doctype)) - diff --git a/frappe/patches/v7_0/create_private_file_folder.py b/frappe/patches/v7_0/create_private_file_folder.py deleted file mode 100644 index e89beb5d0f..0000000000 --- a/frappe/patches/v7_0/create_private_file_folder.py +++ /dev/null @@ -1,6 +0,0 @@ - -import frappe, os - -def execute(): - if not os.path.exists(os.path.join(frappe.local.site_path, 'private', 'files')): - frappe.create_folder(os.path.join(frappe.local.site_path, 'private', 'files')) \ No newline at end of file diff --git a/frappe/patches/v7_0/re_route.py b/frappe/patches/v7_0/re_route.py deleted file mode 100644 index 8a4daaea86..0000000000 --- a/frappe/patches/v7_0/re_route.py +++ /dev/null @@ -1,23 +0,0 @@ - -import frappe -from frappe.model.base_document import get_controller - -def execute(): - update_routes(['Blog Post', 'Blog Category', 'Web Page']) - -def update_routes(doctypes): - """Patch old routing system""" - for d in doctypes: - frappe.reload_doctype(d) - c = get_controller(d) - - condition = '' - if c.website.condition_field: - condition = 'where {0}=1'.format(c.website.condition_field) - - try: - frappe.db.sql("""update ignore `tab{0}` set route = concat(ifnull(parent_website_route, ""), - if(ifnull(parent_website_route, "")="", "", "/"), page_name) {1}""".format(d, condition)) - - except Exception as e: - if not frappe.db.is_missing_column(e): raise diff --git a/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py b/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py deleted file mode 100644 index 42f2dfe4c2..0000000000 --- a/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py +++ /dev/null @@ -1,5 +0,0 @@ - -import frappe - -def execute(): - frappe.rename_doc('DocType', 'Bulk Email', 'Email Queue') \ No newline at end of file diff --git a/frappe/patches/v7_0/rename_newsletter_list_to_email_group.py b/frappe/patches/v7_0/rename_newsletter_list_to_email_group.py deleted file mode 100644 index 5e40d9df35..0000000000 --- a/frappe/patches/v7_0/rename_newsletter_list_to_email_group.py +++ /dev/null @@ -1,6 +0,0 @@ - -import frappe - -def execute(): - frappe.rename_doc('DocType', 'Newsletter List', 'Email Group') - frappe.rename_doc('DocType', 'Newsletter List Subscriber', 'Email Group Member') \ No newline at end of file diff --git a/frappe/patches/v7_0/set_email_group.py b/frappe/patches/v7_0/set_email_group.py deleted file mode 100644 index 251e9a27b6..0000000000 --- a/frappe/patches/v7_0/set_email_group.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe - -def execute(): - frappe.reload_doc("email", "doctype", "email_group_member") - if "newsletter_list" in frappe.db.get_table_columns("Email Group Member"): - frappe.db.sql("""update `tabEmail Group Member` set email_group = newsletter_list - where email_group is null or email_group = ''""") \ No newline at end of file diff --git a/frappe/patches/v7_0/set_user_fullname.py b/frappe/patches/v7_0/set_user_fullname.py deleted file mode 100644 index e69c180c27..0000000000 --- a/frappe/patches/v7_0/set_user_fullname.py +++ /dev/null @@ -1,10 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doc("Core", "DocType", "User") - - for user in frappe.db.get_all('User'): - user = frappe.get_doc('User', user.name) - user.set_full_name() - user.db_set('full_name', user.full_name, update_modified = False) \ No newline at end of file diff --git a/frappe/patches/v7_0/update_auth.py b/frappe/patches/v7_0/update_auth.py deleted file mode 100644 index 098081563f..0000000000 --- a/frappe/patches/v7_0/update_auth.py +++ /dev/null @@ -1,42 +0,0 @@ - -import frappe -from frappe.utils.password import create_auth_table, set_encrypted_password - -def execute(): - if '__OldAuth' not in frappe.db.get_tables(): - frappe.db.sql_ddl('''alter table `__Auth` rename `__OldAuth`''') - - create_auth_table() - - # user passwords - frappe.db.sql('''insert ignore into `__Auth` (doctype, name, fieldname, `password`) - (select 'User', `name`, 'password', `password` from `__OldAuth`)''') - - frappe.db.commit() - - # other password fields - for doctype in frappe.db.sql_list('''select distinct parent from `tabDocField` - where fieldtype="Password" and parent != "User"'''): - - frappe.reload_doctype(doctype) - meta = frappe.get_meta(doctype) - - for df in meta.get('fields', {'fieldtype': 'Password'}): - if meta.issingle: - password = frappe.db.get_value(doctype, doctype, df.fieldname) - if password: - set_encrypted_password(doctype, doctype, password, fieldname=df.fieldname) - frappe.db.set_value(doctype, doctype, df.fieldname, '*'*len(password)) - - else: - for d in frappe.db.sql('''select name, `{fieldname}` from `tab{doctype}` - where `{fieldname}` is not null'''.format(fieldname=df.fieldname, doctype=doctype), as_dict=True): - - set_encrypted_password(doctype, d.name, d.get(df.fieldname), fieldname=df.fieldname) - - frappe.db.sql('''update `tab{doctype}` set `{fieldname}`=repeat("*", char_length(`{fieldname}`))''' - .format(doctype=doctype, fieldname=df.fieldname)) - - frappe.db.commit() - - frappe.db.sql_ddl('''drop table `__OldAuth`''') diff --git a/frappe/patches/v7_0/update_report_builder_json.py b/frappe/patches/v7_0/update_report_builder_json.py deleted file mode 100644 index 01a6126de7..0000000000 --- a/frappe/patches/v7_0/update_report_builder_json.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe - -def execute(): - for report in frappe.db.sql_list(""" select name from `tabReport` where report_type = 'Report Builder' - and is_standard = 'No' and `json` != '' and `json` is not null """): - doc = frappe.get_doc("Report", report) - doc.update_report_json() - doc.db_set("json", doc.json, update_modified=False) \ No newline at end of file diff --git a/frappe/patches/v7_0/update_send_after_in_bulk_email.py b/frappe/patches/v7_0/update_send_after_in_bulk_email.py deleted file mode 100644 index b9da83eaab..0000000000 --- a/frappe/patches/v7_0/update_send_after_in_bulk_email.py +++ /dev/null @@ -1,6 +0,0 @@ - -import frappe -from frappe.utils import now_datetime - -def execute(): - frappe.db.sql('update `tabEmail Queue` set send_after=%s where send_after is null', now_datetime()) \ No newline at end of file diff --git a/frappe/patches/v7_1/__init__.py b/frappe/patches/v7_1/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v7_1/disabled_print_settings_for_custom_print_format.py b/frappe/patches/v7_1/disabled_print_settings_for_custom_print_format.py deleted file mode 100644 index 6ab9340845..0000000000 --- a/frappe/patches/v7_1/disabled_print_settings_for_custom_print_format.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe - -def execute(): - frappe.reload_doctype('Print Format') - frappe.db.sql(""" - update - `tabPrint Format` - set - align_labels_right = 0, line_breaks = 0, show_section_headings = 0 - where - custom_format = 1 - """) diff --git a/frappe/patches/v7_1/refactor_integration_broker.py b/frappe/patches/v7_1/refactor_integration_broker.py deleted file mode 100644 index 05ccae5d46..0000000000 --- a/frappe/patches/v7_1/refactor_integration_broker.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe -import json - -def execute(): - for doctype_name in ["Razorpay Log", "Razorpay Payment", "Razorpay Settings"]: - delete_doc("DocType", doctype_name) - - reload_doctypes() - setup_services() - -def delete_doc(doctype, doctype_name): - frappe.delete_doc(doctype, doctype_name) - -def reload_doctypes(): - for doctype in ("razorpay_settings", "paypal_settings", "dropbox_settings", "ldap_settings"): - frappe.reload_doc("integrations", "doctype", doctype) - -def setup_services(): - for service in [{"old_name": "Razorpay", "new_name": "Razorpay"}, - {"old_name": "PayPal", "new_name": "PayPal"}, - {"old_name": "Dropbox Integration", "new_name": "Dropbox"}, - {"old_name": "LDAP Auth", "new_name": "LDAP"}]: - - try: - service_doc = frappe.get_doc("Integration Service", service["old_name"]) - settings = json.loads(service_doc.custom_settings_json) - - service_settings = frappe.new_doc("{0} Settings".format(service["new_name"])) - service_settings.update(settings) - - service_settings.flags.ignore_mandatory = True - service_settings.save(ignore_permissions=True) - - if service["old_name"] in ["Dropbox Integration", "LDAP Auth"]: - delete_doc("Integration Service", service["old_name"]) - - new_service_doc = frappe.get_doc({ - "doctype": "Integration Service", - "service": service["new_name"], - "enabled": 1 - }) - - new_service_doc.flags.ignore_mandatory = True - new_service_doc.save(ignore_permissions=True) - - except Exception: - pass diff --git a/frappe/patches/v7_1/rename_chinese_language_codes.py b/frappe/patches/v7_1/rename_chinese_language_codes.py deleted file mode 100644 index 91ed73ccae..0000000000 --- a/frappe/patches/v7_1/rename_chinese_language_codes.py +++ /dev/null @@ -1,11 +0,0 @@ - -import frappe - -def execute(): - frappe.rename_doc('Language', 'zh-cn', 'zh', force=True, - merge=True if frappe.db.exists('Language', 'zh') else False) - if frappe.db.get_value('Language', 'zh-tw') == 'zh-tw': - frappe.rename_doc('Language', 'zh-tw', 'zh-TW', force=True) - - frappe.db.set_value('Language', 'zh', 'language_code', 'zh') - frappe.db.set_value('Language', 'zh-TW', 'language_code', 'zh-TW') \ No newline at end of file diff --git a/frappe/patches/v7_1/rename_scheduler_log_to_error_log.py b/frappe/patches/v7_1/rename_scheduler_log_to_error_log.py deleted file mode 100644 index c0c9e03565..0000000000 --- a/frappe/patches/v7_1/rename_scheduler_log_to_error_log.py +++ /dev/null @@ -1,11 +0,0 @@ - -import frappe - -def execute(): - if not 'tabError Log' in frappe.db.get_tables(): - frappe.rename_doc('DocType', 'Scheduler Log', 'Error Log') - frappe.db.sql("""delete from `tabError Log` where datediff(curdate(), creation) > 30""") - frappe.db.commit() - frappe.db.sql('alter table `tabError Log` change column name name varchar(140)') - frappe.db.sql('alter table `tabError Log` change column parent parent varchar(140)') - frappe.db.sql('alter table `tabError Log` engine=MyISAM') diff --git a/frappe/patches/v7_1/set_backup_limit.py b/frappe/patches/v7_1/set_backup_limit.py deleted file mode 100644 index ce502393b2..0000000000 --- a/frappe/patches/v7_1/set_backup_limit.py +++ /dev/null @@ -1,10 +0,0 @@ - -from frappe.utils import cint -import frappe - -def execute(): - frappe.reload_doctype('System Settings') - backup_limit = frappe.db.get_single_value('System Settings', 'backup_limit') - - if cint(backup_limit) == 0: - frappe.db.set_value('System Settings', 'System Settings', 'backup_limit', 3) diff --git a/frappe/patches/v7_1/setup_integration_services.py b/frappe/patches/v7_1/setup_integration_services.py deleted file mode 100644 index 9f4c8a3915..0000000000 --- a/frappe/patches/v7_1/setup_integration_services.py +++ /dev/null @@ -1,118 +0,0 @@ - -import frappe -from frappe.exceptions import DataError -from frappe.utils.password import get_decrypted_password -from frappe.utils import cstr -import os - -app_list = [ - {"app_name": "razorpay_integration", "service_name": "Razorpay", "doctype": "Razorpay Settings", "remove": True}, - {"app_name": "paypal_integration", "service_name": "PayPal", "doctype": "PayPal Settings", "remove": True}, - {"app_name": "frappe", "service_name": "Dropbox", "doctype": "Dropbox Backup", "remove": False} -] - -def execute(): - installed_apps = frappe.get_installed_apps() - - for app_details in app_list: - if app_details["app_name"] in installed_apps: - settings = get_app_settings(app_details) - if app_details["remove"]: - uninstall_app(app_details["app_name"]) - - try: - setup_integration_service(app_details, settings) - except DataError: - pass - - frappe.delete_doc("DocType", "Dropbox Backup") - -def setup_integration_service(app_details, settings=None): - if not settings: - return - - setup_service_settings(app_details["service_name"], settings) - - doc_path = frappe.get_app_path("frappe", "integration_broker", "doctype", - "integration_service", "integration_service.json") - - if not os.path.exists(doc_path): - return - - frappe.reload_doc("integration_broker", "doctype", "integration_service") - - if frappe.db.exists("Integration Service", app_details["service_name"]): - integration_service = frappe.get_doc("Integration Service", app_details["service_name"]) - else: - integration_service = frappe.new_doc("Integration Service") - integration_service.service = app_details["service_name"] - - integration_service.enabled = 1 - integration_service.flags.ignore_mandatory = True - integration_service.save(ignore_permissions=True) - -def get_app_settings(app_details): - parameters = {} - doctype = docname = app_details["doctype"] - - app_settings = get_parameters(app_details) - if app_settings: - settings = app_settings["settings"] - frappe.reload_doc("integrations", "doctype", "{0}_settings".format(app_details["service_name"].lower())) - controller = frappe.get_meta("{0} Settings".format(app_details["service_name"])) - - for d in controller.fields: - if settings.get(d.fieldname): - if ''.join(set(cstr(settings.get(d.fieldname)))) == '*': - setattr(settings, d.fieldname, get_decrypted_password(doctype, docname, d.fieldname, raise_exception=True)) - - parameters.update({d.fieldname : settings.get(d.fieldname)}) - - return parameters - -def uninstall_app(app_name): - from frappe.installer import remove_from_installed_apps - remove_from_installed_apps(app_name) - -def get_parameters(app_details): - if app_details["service_name"] == "Razorpay": - return {"settings": frappe.get_doc(app_details["doctype"])} - - elif app_details["service_name"] == "PayPal": - if frappe.conf.paypal_username and frappe.conf.paypal_password and frappe.conf.paypal_signature: - return { - "settings": { - "api_username": frappe.conf.paypal_username, - "api_password": frappe.conf.paypal_password, - "signature": frappe.conf.paypal_signature - } - } - else: - return {"settings": frappe.get_doc(app_details["doctype"])} - - elif app_details["service_name"] == "Dropbox": - doc = frappe.db.get_value(app_details["doctype"], None, - ["dropbox_access_key", "dropbox_access_secret", "upload_backups_to_dropbox"], as_dict=1) - - if not doc: - return - - if not (frappe.conf.dropbox_access_key and frappe.conf.dropbox_secret_key): - return - - return { - "settings": { - "app_access_key": frappe.conf.dropbox_access_key, - "app_secret_key": frappe.conf.dropbox_secret_key, - "dropbox_access_key": doc.dropbox_access_key, - "dropbox_access_secret": doc.dropbox_access_secret, - "backup_frequency": doc.upload_backups_to_dropbox, - "enabled": doc.send_backups_to_dropbox - } - } - -def setup_service_settings(service_name, settings): - service_doc = frappe.get_doc("{0} Settings".format(service_name)) - service_doc.update(settings) - service_doc.flags.ignore_mandatory = True - service_doc.save(ignore_permissions=True) \ No newline at end of file diff --git a/frappe/patches/v7_1/sync_language_doctype.py b/frappe/patches/v7_1/sync_language_doctype.py deleted file mode 100644 index a5e9ad1cb1..0000000000 --- a/frappe/patches/v7_1/sync_language_doctype.py +++ /dev/null @@ -1,22 +0,0 @@ - -import frappe -from frappe.translate import get_lang_dict - -def execute(): - frappe.reload_doc('core', 'doctype', 'language') - - from frappe.core.doctype.language.language import sync_languages - sync_languages() - - # move language from old style to new style for old accounts - # i.e. from "english" to "en" - - lang_dict = get_lang_dict() - language = frappe.db.get_value('System Settings', None, 'language') - if language: - frappe.db.set_value('System Settings', None, 'language', lang_dict.get('language') or 'en') - - for user in frappe.get_all('User', fields=['name', 'language']): - if user.language: - frappe.db.set_value('User', user.name, 'language', - lang_dict.get('language') or 'en', update_modified=False) diff --git a/frappe/patches/v7_2/__init__.py b/frappe/patches/v7_2/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v7_2/fix_email_queue_recipient.py b/frappe/patches/v7_2/fix_email_queue_recipient.py deleted file mode 100644 index 021397031b..0000000000 --- a/frappe/patches/v7_2/fix_email_queue_recipient.py +++ /dev/null @@ -1,6 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doc('email', 'doctype', 'email_queue_recipient') - frappe.db.sql('update `tabEmail Queue Recipient` set parenttype="recipients"') \ No newline at end of file diff --git a/frappe/patches/v7_2/merge_knowledge_base.py b/frappe/patches/v7_2/merge_knowledge_base.py deleted file mode 100644 index 04e6c16213..0000000000 --- a/frappe/patches/v7_2/merge_knowledge_base.py +++ /dev/null @@ -1,24 +0,0 @@ - -import frappe - -from frappe.patches.v7_0.re_route import update_routes -from frappe.installer import remove_from_installed_apps - -def execute(): - if 'knowledge_base' in frappe.get_installed_apps(): - frappe.reload_doc('website', 'doctype', 'help_category') - frappe.reload_doc('website', 'doctype', 'help_article') - update_routes(['Help Category', 'Help Article']) - remove_from_installed_apps('knowledge_base') - - # remove module def - if frappe.db.exists('Module Def', 'Knowledge Base'): - frappe.delete_doc('Module Def', 'Knowledge Base') - - # set missing routes - for doctype in ('Help Category', 'Help Article'): - for d in frappe.get_all(doctype, fields=['name', 'route']): - if not d.route: - doc = frappe.get_doc(doctype, d.name) - doc.set_route() - doc.db_update() \ No newline at end of file diff --git a/frappe/patches/v7_2/remove_in_filter.py b/frappe/patches/v7_2/remove_in_filter.py deleted file mode 100644 index 306879f996..0000000000 --- a/frappe/patches/v7_2/remove_in_filter.py +++ /dev/null @@ -1,7 +0,0 @@ - -import frappe - -def execute(): - if frappe.db.has_column('DocField', 'in_filter'): - frappe.db.sql('alter table tabDocField drop column in_filter') - frappe.clear_cache(doctype="DocField") \ No newline at end of file diff --git a/frappe/patches/v7_2/set_doctype_engine.py b/frappe/patches/v7_2/set_doctype_engine.py deleted file mode 100644 index e0df9cff87..0000000000 --- a/frappe/patches/v7_2/set_doctype_engine.py +++ /dev/null @@ -1,7 +0,0 @@ - -import frappe - -def execute(): - for t in frappe.db.sql('show table status'): - if t[0].startswith('tab'): - frappe.db.sql('update tabDocType set engine=%s where name=%s', (t[1], t[0][3:])) \ No newline at end of file diff --git a/frappe/patches/v7_2/set_in_standard_filter_property.py b/frappe/patches/v7_2/set_in_standard_filter_property.py deleted file mode 100644 index 568f43d2aa..0000000000 --- a/frappe/patches/v7_2/set_in_standard_filter_property.py +++ /dev/null @@ -1,20 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doc('custom', 'doctype', 'custom_field', force=True) - - try: - frappe.db.sql('update `tabCustom Field` set in_standard_filter = in_filter_dash') - except Exception as e: - if not frappe.db.is_missing_column(e): raise e - - for doctype in frappe.get_all("DocType", {"istable": 0, "issingle": 0, "custom": 0}): - try: - frappe.reload_doctype(doctype.name, force=True) - except KeyError: - pass - except frappe.db.DataError: - pass - except Exception: - pass diff --git a/frappe/patches/v7_2/setup_custom_perms.py b/frappe/patches/v7_2/setup_custom_perms.py deleted file mode 100644 index 1f46072782..0000000000 --- a/frappe/patches/v7_2/setup_custom_perms.py +++ /dev/null @@ -1,13 +0,0 @@ - -import frappe -from frappe.permissions import setup_custom_perms -from frappe.core.page.permission_manager.permission_manager import get_standard_permissions -from frappe.utils.reset_doc import setup_perms_for - -''' -Copy DocPerm to Custom DocPerm where permissions are set differently -''' - -def execute(): - for d in frappe.db.get_all('DocType', dict(istable=0, issingle=0, custom=0)): - setup_perms_for(d.name) diff --git a/frappe/patches/v7_2/setup_ldap_config.py b/frappe/patches/v7_2/setup_ldap_config.py deleted file mode 100644 index c9ad3e6714..0000000000 --- a/frappe/patches/v7_2/setup_ldap_config.py +++ /dev/null @@ -1,22 +0,0 @@ - -import frappe -from frappe.utils import cint - -def execute(): - frappe.reload_doc("integrations", "doctype", "ldap_settings") - - if not frappe.db.exists("DocType", "Integration Service"): - return - - if not frappe.db.exists("Integration Service", "LDAP"): - return - - if not cint(frappe.db.get_value("Integration Service", "LDAP", 'enabled')): - return - - import ldap - try: - ldap_settings = frappe.get_doc("LDAP Settings") - ldap_settings.save(ignore_permissions=True) - except ldap.LDAPError: - pass diff --git a/frappe/patches/v7_2/update_communications.py b/frappe/patches/v7_2/update_communications.py deleted file mode 100644 index 114e531324..0000000000 --- a/frappe/patches/v7_2/update_communications.py +++ /dev/null @@ -1,10 +0,0 @@ - -import frappe - -def execute(): - """ - in communication move feedback details to content - remove Guest None from sender full name - setup feedback request trigger's is_manual field - """ - return diff --git a/frappe/patches/v7_2/update_feedback_request.py b/frappe/patches/v7_2/update_feedback_request.py deleted file mode 100644 index 9bc656bf67..0000000000 --- a/frappe/patches/v7_2/update_feedback_request.py +++ /dev/null @@ -1,10 +0,0 @@ - -import frappe - -def execute(): - """ - rename feedback request documents, - update the feedback request and save the rating and communication - reference in Feedback Request document - """ - return diff --git a/frappe/patches/v8_0/__init__.py b/frappe/patches/v8_0/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v8_0/deprecate_integration_broker.py b/frappe/patches/v8_0/deprecate_integration_broker.py deleted file mode 100644 index 9aeee17837..0000000000 --- a/frappe/patches/v8_0/deprecate_integration_broker.py +++ /dev/null @@ -1,51 +0,0 @@ - -import frappe -from frappe.integrations.utils import create_payment_gateway - -def execute(): - setup_enabled_integrations() - - for doctype in ["integration_request", "oauth_authorization_code", "oauth_bearer_token", "oauth_client"]: - frappe.reload_doc('integrations', 'doctype', doctype) - - frappe.reload_doc("core", "doctype", "payment_gateway") - update_doctype_module() - create_payment_gateway_master_records() - - for doctype in ["Integration Service", "Integration Service Parameter"]: - frappe.delete_doc("DocType", doctype) - - if not frappe.db.get_value("DocType", {"module": "Integration Broker"}, "name"): - frappe.delete_doc("Module Def", "Integration Broker") - -def setup_enabled_integrations(): - if not frappe.db.exists("DocType", "Integration Service"): - return - - for service in frappe.get_all("Integration Service", - filters={"enabled": 1, "service": ('in', ("Dropbox", "LDAP"))}, fields=["name"]): - - doctype = "{0} Settings".format(service.name) - frappe.db.set_value(doctype, doctype, 'enabled', 1) - -def update_doctype_module(): - frappe.db.sql("""update tabDocType set module='Integrations' - where name in ('Integration Request', 'Oauth Authorization Code', - 'Oauth Bearer Token', 'Oauth Client') """) - - frappe.db.sql(""" update tabDocType set module='Core' where name = 'Payment Gateway'""") - -def create_payment_gateway_master_records(): - for payment_gateway in ["Razorpay", "PayPal"]: - doctype = "{0} Settings".format(payment_gateway) - doc = frappe.get_doc(doctype) - doc_meta = frappe.get_meta(doctype) - all_mandatory_fields_has_value = True - - for d in doc_meta.fields: - if d.reqd and not doc.get(d.fieldname): - all_mandatory_fields_has_value = False - break - - if all_mandatory_fields_has_value: - create_payment_gateway(payment_gateway) diff --git a/frappe/patches/v8_0/drop_in_dialog.py b/frappe/patches/v8_0/drop_in_dialog.py deleted file mode 100644 index 5022333d22..0000000000 --- a/frappe/patches/v8_0/drop_in_dialog.py +++ /dev/null @@ -1,7 +0,0 @@ - -import frappe - -def execute(): - if frappe.db.has_column('DocType', 'in_dialog'): - frappe.db.sql('alter table tabDocType drop column in_dialog') - frappe.clear_cache(doctype="DocType") \ No newline at end of file diff --git a/frappe/patches/v8_0/drop_is_custom_from_docperm.py b/frappe/patches/v8_0/drop_is_custom_from_docperm.py deleted file mode 100644 index 0f17bbef5c..0000000000 --- a/frappe/patches/v8_0/drop_is_custom_from_docperm.py +++ /dev/null @@ -1,8 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doctype('DocPerm') - if frappe.db.has_column('DocPerm', 'is_custom'): - frappe.db.commit() - frappe.db.sql('alter table `tabDocPerm` drop column is_custom') \ No newline at end of file diff --git a/frappe/patches/v8_0/drop_unwanted_indexes.py b/frappe/patches/v8_0/drop_unwanted_indexes.py deleted file mode 100644 index 655bce1a4b..0000000000 --- a/frappe/patches/v8_0/drop_unwanted_indexes.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt -# -*- coding: utf-8 -*- - -import frappe - -def execute(): - # communication - unwanted_indexes = ["communication_date_index", "message_id_index", "modified_index", - "creation_index", "reference_owner", "communication_date"] - - for k in unwanted_indexes: - try: - frappe.db.sql("drop index {0} on `tabCommunication`".format(k)) - except: - pass \ No newline at end of file diff --git a/frappe/patches/v8_0/install_new_build_system_requirements.py b/frappe/patches/v8_0/install_new_build_system_requirements.py deleted file mode 100644 index 75ccfa87cd..0000000000 --- a/frappe/patches/v8_0/install_new_build_system_requirements.py +++ /dev/null @@ -1,21 +0,0 @@ -from subprocess import Popen, call, PIPE - -def execute(): - # update nodejs version if brew exists - p = Popen(['which', 'brew'], stdout=PIPE, stderr=PIPE) - output, err = p.communicate() - if output: - call(['brew', 'upgrade', 'node']) - else: - print('Please update your NodeJS version') - - call([ - 'npm', 'install', - 'babel-core', - 'less', - 'chokidar', - 'babel-preset-es2015', - 'babel-preset-es2016', - 'babel-preset-es2017', - 'babel-preset-babili' - ]) \ No newline at end of file diff --git a/frappe/patches/v8_0/newsletter_childtable_migrate.py b/frappe/patches/v8_0/newsletter_childtable_migrate.py deleted file mode 100644 index 67ff5e586f..0000000000 --- a/frappe/patches/v8_0/newsletter_childtable_migrate.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe - -def execute(): - frappe.reload_doc('email', 'doctype', 'newsletter_email_group') - frappe.reload_doctype('Newsletter') - - if "email_group" not in frappe.db.get_table_columns("Newsletter"): - return - - newsletters = frappe.get_all("Newsletter", fields=["name", "email_group"]) - for newsletter in newsletters: - if newsletter.email_group: - newsletter_doc = frappe.get_doc("Newsletter", newsletter.name) - if not newsletter_doc.get("email_group"): - newsletter_doc.append("email_group", { - "email_group": newsletter.email_group, - }) - newsletter_doc.flags.ignore_validate = True - newsletter_doc.flags.ignore_mandatory = True - newsletter_doc.save() diff --git a/frappe/patches/v8_0/rename_listsettings_to_usersettings.py b/frappe/patches/v8_0/rename_listsettings_to_usersettings.py deleted file mode 100644 index 9545953e34..0000000000 --- a/frappe/patches/v8_0/rename_listsettings_to_usersettings.py +++ /dev/null @@ -1,45 +0,0 @@ - -from frappe.model.utils.user_settings import update_user_settings -import frappe, json - - -def execute(): - if frappe.db.table_exists("__ListSettings"): - for us in frappe.db.sql('''select user, doctype, data from __ListSettings''', as_dict=True): - try: - data = json.loads(us.data) - except: - continue - - if 'List' in data: - continue - - if 'limit' in data: - data['page_length'] = data['limit'] - del data['limit'] - - new_data = dict(List=data) - new_data = json.dumps(new_data) - - frappe.db.sql('''update __ListSettings - set data=%(new_data)s - where user=%(user)s - and doctype=%(doctype)s''', - {'new_data': new_data, 'user': us.user, 'doctype': us.doctype}) - - frappe.db.sql("RENAME TABLE __ListSettings to __UserSettings") - else: - if not frappe.db.table_exists("__UserSettings"): - frappe.db.create_user_settings_table() - - for user in frappe.db.get_all('User', {'user_type': 'System User'}): - defaults = frappe.defaults.get_defaults_for(user.name) - for key, value in defaults.items(): - if key.startswith('_list_settings:'): - doctype = key.replace('_list_settings:', '') - columns = ['`tab{1}`.`{0}`'.format(*c) for c in json.loads(value)] - for col in columns: - if "name as" in col: - columns.remove(col) - - update_user_settings(doctype, {'fields': columns}) \ No newline at end of file diff --git a/frappe/patches/v8_0/rename_page_role_to_has_role.py b/frappe/patches/v8_0/rename_page_role_to_has_role.py deleted file mode 100644 index 49006ea419..0000000000 --- a/frappe/patches/v8_0/rename_page_role_to_has_role.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe - -def execute(): - if not frappe.db.exists('DocType', 'Has Role'): - frappe.rename_doc('DocType', 'Page Role', 'Has Role') - reload_doc() - set_ref_doctype_roles_to_report() - copy_user_roles_to_has_roles() - remove_doctypes() - -def reload_doc(): - frappe.reload_doc("core", 'doctype', "page") - frappe.reload_doc("core", 'doctype', "report") - frappe.reload_doc("core", 'doctype', "user") - frappe.reload_doc("core", 'doctype', "has_role") - -def set_ref_doctype_roles_to_report(): - for data in frappe.get_all('Report', fields=["name"]): - doc = frappe.get_doc('Report', data.name) - if frappe.db.exists("DocType", doc.ref_doctype): - try: - doc.set_doctype_roles() - for row in doc.roles: - row.db_update() - except: - pass - -def copy_user_roles_to_has_roles(): - if frappe.db.exists('DocType', 'UserRole'): - for data in frappe.get_all('User', fields = ["name"]): - doc = frappe.get_doc('User', data.name) - doc.set('roles',[]) - for args in frappe.get_all('UserRole', fields = ["role"], - filters = {'parent': data.name, 'parenttype': 'User'}): - doc.append('roles', { - 'role': args.role - }) - for role in doc.roles: - role.db_update() - -def remove_doctypes(): - for doctype in ['UserRole', 'Event Role']: - if frappe.db.exists('DocType', doctype): - frappe.delete_doc('DocType', doctype) \ No newline at end of file diff --git a/frappe/patches/v8_0/rename_print_to_printing.py b/frappe/patches/v8_0/rename_print_to_printing.py deleted file mode 100644 index 56889d630e..0000000000 --- a/frappe/patches/v8_0/rename_print_to_printing.py +++ /dev/null @@ -1,13 +0,0 @@ - -import frappe - -def execute(): - if frappe.db.exists('Module Def', 'Print'): - frappe.reload_doc('printing', 'doctype', 'print_format') - frappe.reload_doc('printing', 'doctype', 'print_settings') - frappe.reload_doc('printing', 'doctype', 'print_heading') - frappe.reload_doc('printing', 'doctype', 'letter_head') - frappe.reload_doc('printing', 'page', 'print_format_builder') - frappe.db.sql("""update `tabPrint Format` set module='Printing' where module='Print'""") - - frappe.delete_doc('Module Def', 'Print') \ No newline at end of file diff --git a/frappe/patches/v8_0/set_allow_traceback.py b/frappe/patches/v8_0/set_allow_traceback.py deleted file mode 100644 index bb72e7dde6..0000000000 --- a/frappe/patches/v8_0/set_allow_traceback.py +++ /dev/null @@ -1,6 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doc('core', 'doctype', 'system_settings') - frappe.db.sql("update `tabSystem Settings` set allow_error_traceback=1") diff --git a/frappe/patches/v8_0/set_currency_field_precision.py b/frappe/patches/v8_0/set_currency_field_precision.py deleted file mode 100644 index 57b12ffdee..0000000000 --- a/frappe/patches/v8_0/set_currency_field_precision.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe -from frappe.utils import get_number_format_info - -def execute(): - frappe.reload_doc('core', 'doctype', 'system_settings', force=True) - if not frappe.db.get_value("System Settings", None, "currency_precision"): - default_currency = frappe.db.get_default("currency") - number_format = frappe.db.get_value("Currency", default_currency, "number_format", cache=True) \ - or frappe.db.get_default("number_format") - if number_format: - precision = get_number_format_info(number_format)[2] - else: - precision = 2 - - ss = frappe.get_doc("System Settings") - ss.currency_precision = precision - ss.flags.ignore_mandatory = True - ss.save() diff --git a/frappe/patches/v8_0/set_doctype_values_in_custom_role.py b/frappe/patches/v8_0/set_doctype_values_in_custom_role.py deleted file mode 100644 index 50e7eb83e1..0000000000 --- a/frappe/patches/v8_0/set_doctype_values_in_custom_role.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe - -def execute(): - frappe.reload_doctype('Custom Role') - - # set ref doctype in custom role for reports - frappe.db.sql(""" update `tabCustom Role` set - `tabCustom Role`.ref_doctype = (select ref_doctype from `tabReport` where name = `tabCustom Role`.report) - where `tabCustom Role`.report is not null""") diff --git a/frappe/patches/v8_0/set_user_permission_for_page_and_report.py b/frappe/patches/v8_0/set_user_permission_for_page_and_report.py deleted file mode 100644 index 55789a8301..0000000000 --- a/frappe/patches/v8_0/set_user_permission_for_page_and_report.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe - -def execute(): - if not frappe.db.exists('DocType', 'Custom Role'): - frappe.reload_doc("core", 'doctype', "custom_role") - set_user_permission_for_page_and_report() - - update_ref_doctype_in_custom_role() - -def update_ref_doctype_in_custom_role(): - frappe.reload_doc("core", 'doctype', "custom_role") - frappe.db.sql("""update `tabCustom Role` - set - ref_doctype = (select ref_doctype from tabReport where name = `tabCustom Role`.report) - where report is not null""") - -def set_user_permission_for_page_and_report(): - make_custom_roles_for_page_and_report() - -def make_custom_roles_for_page_and_report(): - for doctype in ['Page', 'Report']: - for data in get_data(doctype): - doc = frappe.get_doc(doctype, data.name) - roles = get_roles(doctype, data, doc) - make_custom_roles(doctype, doc.name, roles) - -def get_data(doctype): - fields = ["name"] if doctype == 'Page' else ["name", "ref_doctype"] - return frappe.get_all(doctype, fields = fields) - -def get_roles(doctype, data, doc): - roles = [] - if doctype == 'Page': - for d in doc.roles: - if frappe.db.exists('Role', d.role): - roles.append({'role': d.role}) - else: - out = frappe.get_all('Custom DocPerm', fields='distinct role', filters=dict(parent = data.ref_doctype)) - for d in out: - roles.append({'role': d.role}) - return roles - -def make_custom_roles(doctype, name, roles): - field = doctype.lower() - - if roles: - custom_permission = frappe.get_doc({ - 'doctype': 'Custom Role', - field : name, - 'roles' : roles - }).insert() diff --git a/frappe/patches/v8_0/setup_email_inbox.py b/frappe/patches/v8_0/setup_email_inbox.py deleted file mode 100644 index ad99068eb9..0000000000 --- a/frappe/patches/v8_0/setup_email_inbox.py +++ /dev/null @@ -1,26 +0,0 @@ - -import frappe, json -from frappe.core.doctype.user.user import ask_pass_update, setup_user_email_inbox - -def execute(): - """ - depricate email inbox page if exists - remove desktop icon for email inbox page if exists - patch to remove Custom DocPerm for communication - add user inbox child table entry for existing email account in not exists - """ - - if frappe.db.exists("Page", "email_inbox"): - frappe.delete_doc("Page", "email_inbox") - - frappe.db.sql("""update `tabCustom DocPerm` set `write`=0, email=1 where parent='Communication'""") - - frappe.reload_doc("core", "doctype", "user_email") - frappe.reload_doc("email", "doctype", "email_account") - - email_accounts = frappe.get_all("Email Account", filters={"enable_incoming": 1}, - fields=["name", "email_id", "awaiting_password", "enable_outgoing"]) - - for email_account in email_accounts: - setup_user_email_inbox(email_account.get("name"), email_account.get("awaiting_password"), - email_account.get("email_id"), email_account.get("enabled_outgoing")) diff --git a/frappe/patches/v8_0/update_gender_and_salutation.py b/frappe/patches/v8_0/update_gender_and_salutation.py deleted file mode 100644 index 913e0f714b..0000000000 --- a/frappe/patches/v8_0/update_gender_and_salutation.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors - -import frappe -from frappe.desk.page.setup_wizard.install_fixtures import update_genders, update_salutations - -def execute(): - frappe.db.set_value("DocType", "Contact", "module", "Contacts") - frappe.db.set_value("DocType", "Address", "module", "Contacts") - frappe.db.set_value("DocType", "Address Template", "module", "Contacts") - frappe.reload_doc('contacts', 'doctype', 'gender') - frappe.reload_doc('contacts', 'doctype', 'salutation') - - update_genders() - update_salutations() \ No newline at end of file diff --git a/frappe/patches/v8_0/update_global_search_table.py b/frappe/patches/v8_0/update_global_search_table.py deleted file mode 100644 index 4d5c8be9cf..0000000000 --- a/frappe/patches/v8_0/update_global_search_table.py +++ /dev/null @@ -1,13 +0,0 @@ - -import frappe - -def execute(): - if not 'published' in frappe.db.get_db_table_columns('__global_search'): - frappe.db.sql('''alter table __global_search - add column `title` varchar(140)''') - - frappe.db.sql('''alter table __global_search - add column `route` varchar(140)''') - - frappe.db.sql('''alter table __global_search - add column `published` int(1) not null default 0''') diff --git a/frappe/patches/v8_0/update_published_in_global_search.py b/frappe/patches/v8_0/update_published_in_global_search.py deleted file mode 100644 index ae86cb8b24..0000000000 --- a/frappe/patches/v8_0/update_published_in_global_search.py +++ /dev/null @@ -1,12 +0,0 @@ - -import frappe - -def execute(): - from frappe.website.router import get_doctypes_with_web_view - from frappe.utils.global_search import rebuild_for_doctype - - for doctype in get_doctypes_with_web_view(): - try: - rebuild_for_doctype(doctype) - except frappe.DoesNotExistError: - pass diff --git a/frappe/patches/v8_0/update_records_in_global_search.py b/frappe/patches/v8_0/update_records_in_global_search.py deleted file mode 100644 index 316f84b2f0..0000000000 --- a/frappe/patches/v8_0/update_records_in_global_search.py +++ /dev/null @@ -1,12 +0,0 @@ - -import frappe -from frappe.utils.global_search import get_doctypes_with_global_search, rebuild_for_doctype -from frappe.utils import update_progress_bar - -def execute(): - frappe.cache().delete_value('doctypes_with_global_search') - doctypes_with_global_search = get_doctypes_with_global_search(with_child_tables=False) - - for i, doctype in enumerate(doctypes_with_global_search): - update_progress_bar("Updating Global Search", i, len(doctypes_with_global_search)) - rebuild_for_doctype(doctype) diff --git a/frappe/patches/v8_1/__init__.py b/frappe/patches/v8_1/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v8_1/delete_custom_docperm_if_doctype_not_exists.py b/frappe/patches/v8_1/delete_custom_docperm_if_doctype_not_exists.py deleted file mode 100644 index 510018eb47..0000000000 --- a/frappe/patches/v8_1/delete_custom_docperm_if_doctype_not_exists.py +++ /dev/null @@ -1,7 +0,0 @@ - -import frappe - -def execute(): - frappe.db.sql("""delete from `tabCustom DocPerm` - where parent not in ( select name from `tabDocType` ) - """) diff --git a/frappe/patches/v8_1/enable_allow_error_traceback_in_system_settings.py b/frappe/patches/v8_1/enable_allow_error_traceback_in_system_settings.py deleted file mode 100644 index 513bb274bc..0000000000 --- a/frappe/patches/v8_1/enable_allow_error_traceback_in_system_settings.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe - -def execute(): - """ enable the allow_enable_traceback property in system settings """ - - frappe.reload_doc("core", "doctype", "system_settings") - doc = frappe.get_doc("System Settings", "System Settings") - doc.allow_error_traceback = 1 - doc.flags.ignore_permissions=True - doc.flags.ignore_mandatory=True - doc.save() \ No newline at end of file diff --git a/frappe/patches/v8_1/update_format_options_in_auto_email_report.py b/frappe/patches/v8_1/update_format_options_in_auto_email_report.py deleted file mode 100644 index 8bea2b7bf5..0000000000 --- a/frappe/patches/v8_1/update_format_options_in_auto_email_report.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe - -def execute(): - """ change the XLS option as XLSX in the auto email report """ - - frappe.reload_doc("email", "doctype", "auto_email_report") - - auto_email_list = frappe.get_all("Auto Email Report", filters={"format": "XLS"}) - for auto_email in auto_email_list: - frappe.db.set_value("Auto Email Report", auto_email.name, "format", "XLSX") diff --git a/frappe/patches/v8_10/__init__.py b/frappe/patches/v8_10/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v8_10/delete_static_web_page_from_global_search.py b/frappe/patches/v8_10/delete_static_web_page_from_global_search.py deleted file mode 100644 index aa6a053412..0000000000 --- a/frappe/patches/v8_10/delete_static_web_page_from_global_search.py +++ /dev/null @@ -1,5 +0,0 @@ - -import frappe - -def execute(): - frappe.db.sql("""delete from `__global_search` where doctype='Static Web Page'"""); \ No newline at end of file diff --git a/frappe/patches/v8_5/__init__.py b/frappe/patches/v8_5/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v8_5/delete_email_group_member_with_invalid_emails.py b/frappe/patches/v8_5/delete_email_group_member_with_invalid_emails.py deleted file mode 100644 index 5851e2855b..0000000000 --- a/frappe/patches/v8_5/delete_email_group_member_with_invalid_emails.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe -from frappe.utils import validate_email_address - -def execute(): - ''' update/delete the email group member with the wrong email ''' - - email_group_members = frappe.get_all("Email Group Member", fields=["name", "email"]) - for member in email_group_members: - validated_email = validate_email_address(member.email) - if (validated_email==member.email): - pass - else: - try: - frappe.db.set_value("Email Group Member", member.name, "email", validated_email) - except Exception: - frappe.delete_doc(doctype="Email Group Member", name=member.name, force=1, ignore_permissions=True) \ No newline at end of file diff --git a/frappe/patches/v8_5/patch_event_colors.py b/frappe/patches/v8_5/patch_event_colors.py deleted file mode 100644 index 3c34f7946b..0000000000 --- a/frappe/patches/v8_5/patch_event_colors.py +++ /dev/null @@ -1,25 +0,0 @@ - -import frappe - -def execute(): - - if not frappe.db.sql("SHOW COLUMNS FROM `tabEvent` LIKE 'color';"): - return - - colors = ['red', 'green', 'blue', 'yellow', 'skyblue', 'orange'] - hex_colors = ['#ffc4c4', '#cef6d1', '#d2d2ff', '#fffacd', '#d2f1ff', '#ffd2c2'] - - def get_hex_for_color(color): - index = colors.index(color) - return hex_colors[index] - - query = ''' - update tabEvent - set color='{hex}' - where color='{color}' - ''' - - for color in colors: - frappe.db.sql(query.format(color=color, hex=get_hex_for_color(color))) - - frappe.db.commit() diff --git a/frappe/patches/v8_7/__init__.py b/frappe/patches/v8_7/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v8_x/__init__.py b/frappe/patches/v8_x/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/patches/v8_x/update_user_permission.py b/frappe/patches/v8_x/update_user_permission.py deleted file mode 100644 index 387751500f..0000000000 --- a/frappe/patches/v8_x/update_user_permission.py +++ /dev/null @@ -1,28 +0,0 @@ - -import frappe - -def execute(): - frappe.reload_doc('core', 'doctype', 'user_permission') - frappe.delete_doc('core', 'page', 'user-permissions') - for perm in frappe.db.sql(""" - select - name, parent, defkey, defvalue - from - tabDefaultValue - where - parent not in ('__default', '__global') - and - substr(defkey,1,1)!='_' - and - parenttype='User Permission' - """, as_dict=True): - if frappe.db.exists(perm.defkey, perm.defvalue) and frappe.db.exists('User', perm.parent): - frappe.get_doc(dict( - doctype='User Permission', - user=perm.parent, - allow=perm.defkey, - for_value=perm.defvalue, - apply_for_all_roles=0, - )).insert(ignore_permissions = True) - - frappe.db.sql('delete from tabDefaultValue where parenttype="User Permission"') diff --git a/frappe/patches/v9_1/__init__.py b/frappe/patches/v9_1/__init__.py deleted file mode 100644 index 8b13789179..0000000000 --- a/frappe/patches/v9_1/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/frappe/patches/v9_1/add_sms_sender_name_as_parameters.py b/frappe/patches/v9_1/add_sms_sender_name_as_parameters.py deleted file mode 100644 index f63e86a340..0000000000 --- a/frappe/patches/v9_1/add_sms_sender_name_as_parameters.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe - -def execute(): - frappe.reload_doc("core", "doctype", "sms_parameter") - sms_sender_name = frappe.db.get_single_value("SMS Settings", "sms_sender_name") - if sms_sender_name: - frappe.reload_doc("core", "doctype", "sms_settings") - sms_settings = frappe.get_doc("SMS Settings") - sms_settings.append("parameters", { - "parameter": "sender_name", - "value": sms_sender_name - }) - sms_settings.flags.ignore_mandatory = True - sms_settings.flags.ignore_permissions = True - sms_settings.save() diff --git a/frappe/patches/v9_1/move_feed_to_activity_log.py b/frappe/patches/v9_1/move_feed_to_activity_log.py deleted file mode 100644 index a549296357..0000000000 --- a/frappe/patches/v9_1/move_feed_to_activity_log.py +++ /dev/null @@ -1,24 +0,0 @@ - -import frappe -from frappe.utils.background_jobs import enqueue - -def execute(): - comm_records_count = frappe.db.count("Communication", {"comment_type": "Updated"}) - if comm_records_count > 100000: - enqueue(method=move_data_from_communication_to_activity_log, queue='short', now=True) - else: - move_data_from_communication_to_activity_log() - -def move_data_from_communication_to_activity_log(): - frappe.reload_doc("core", "doctype", "communication") - frappe.reload_doc("core", "doctype", "activity_log") - - frappe.db.sql("""insert into `tabActivity Log` (name, owner, modified, creation, status, communication_date, - reference_doctype, reference_name, timeline_doctype, timeline_name, link_doctype, link_name, subject, content, user) - select name, owner, modified, creation, status, communication_date, - reference_doctype, reference_name, timeline_doctype, timeline_name, link_doctype, link_name, subject, content, user - from `tabCommunication` - where comment_type = 'Updated'""") - - frappe.db.sql("""delete from `tabCommunication` where comment_type = 'Updated'""") - frappe.delete_doc("DocType", "Authentication Log") \ No newline at end of file diff --git a/frappe/patches/v9_1/resave_domain_settings.py b/frappe/patches/v9_1/resave_domain_settings.py deleted file mode 100644 index 5814871c2e..0000000000 --- a/frappe/patches/v9_1/resave_domain_settings.py +++ /dev/null @@ -1,13 +0,0 @@ - -import frappe - -def execute(): - domain_settings = frappe.get_doc('Domain Settings') - active_domains = [d.domain for d in domain_settings.active_domains] - try: - for d in ('Education', 'Healthcare', 'Hospitality'): - if d in active_domains and frappe.db.exists('Domain', d): - domain = frappe.get_doc('Domain', d) - domain.setup_domain() - except frappe.LinkValidationError: - pass diff --git a/frappe/patches/v9_1/revert_domain_settings.py b/frappe/patches/v9_1/revert_domain_settings.py deleted file mode 100644 index 99c5561d78..0000000000 --- a/frappe/patches/v9_1/revert_domain_settings.py +++ /dev/null @@ -1,11 +0,0 @@ - -import frappe - -def execute(): - domain_settings = frappe.get_doc('Domain Settings') - active_domains = [d.domain for d in domain_settings.active_domains] - - for domain_name in ('Education', 'Healthcare', 'Hospitality'): - if frappe.db.exists('Domain', domain_name) and domain_name not in active_domains: - domain = frappe.get_doc('Domain', domain_name) - domain.remove_domain() \ No newline at end of file From 95ab1a60b435d8f80b3b420fdd031e7d66a740bd Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 3 Jun 2021 17:38:03 +0530 Subject: [PATCH 166/495] ci: Add frappe patch test * Generated v10 backup archive * used old v7 erpnext backup hosted via build.erpnext.com * upgraded to v10 frappe + erpnext * uninstalled and deleted erpnext related data * Hosted backup on https://frappeframework.com/files/v10-frappe.sql.gz --- .github/workflows/patch-mariadb-tests.yml | 83 +++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 .github/workflows/patch-mariadb-tests.yml diff --git a/.github/workflows/patch-mariadb-tests.yml b/.github/workflows/patch-mariadb-tests.yml new file mode 100644 index 0000000000..e8627a01fb --- /dev/null +++ b/.github/workflows/patch-mariadb-tests.yml @@ -0,0 +1,83 @@ +name: Patch + +on: [pull_request, workflow_dispatch] + +jobs: + test: + runs-on: ubuntu-18.04 + + name: Patch Test + + services: + mysql: + image: mariadb:10.3 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: YES + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + + steps: + - name: Clone + uses: actions/checkout@v2 + + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.7 + + - name: Add to Hosts + run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts + + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v2 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install Dependencies + run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh + env: + BEFORE: ${{ env.GITHUB_EVENT_PATH.before }} + AFTER: ${{ env.GITHUB_EVENT_PATH.after }} + TYPE: server + + - name: Install + run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh + env: + DB: mariadb + TYPE: server + + - name: Run Patch Tests + run: | + cd ~/frappe-bench/ + wget https://frappeframework.com/files/v10-frappe.sql.gz + bench --site test_site --force restore ~/frappe-bench/v10-frappe.sql.gz + bench --site test_site migrate From 5aeef088adb377d0506d13caace63e92ed67ebe4 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 3 Jun 2021 18:47:31 +0530 Subject: [PATCH 167/495] fix: Remove reloading statements for non existing dts --- frappe/patches/v11_0/create_contact_for_user.py | 1 - frappe/patches/v11_0/rename_google_maps_doctype.py | 1 - 2 files changed, 2 deletions(-) diff --git a/frappe/patches/v11_0/create_contact_for_user.py b/frappe/patches/v11_0/create_contact_for_user.py index 21e681a83e..5a483b630e 100644 --- a/frappe/patches/v11_0/create_contact_for_user.py +++ b/frappe/patches/v11_0/create_contact_for_user.py @@ -8,7 +8,6 @@ def execute(): frappe.reload_doc('integrations', 'doctype', 'google_contacts') frappe.reload_doc('contacts', 'doctype', 'contact') frappe.reload_doc('core', 'doctype', 'dynamic_link') - frappe.reload_doc('communication', 'doctype', 'call_log') contact_meta = frappe.get_meta("Contact") if contact_meta.has_field("phone_nos") and contact_meta.has_field("email_ids"): diff --git a/frappe/patches/v11_0/rename_google_maps_doctype.py b/frappe/patches/v11_0/rename_google_maps_doctype.py index 4e8aee6280..8091154b9c 100644 --- a/frappe/patches/v11_0/rename_google_maps_doctype.py +++ b/frappe/patches/v11_0/rename_google_maps_doctype.py @@ -5,4 +5,3 @@ from frappe.model.rename_doc import rename_doc def execute(): if frappe.db.exists("DocType","Google Maps") and not frappe.db.exists("DocType","Google Maps Settings"): rename_doc('DocType', 'Google Maps', 'Google Maps Settings') - frappe.reload_doc('integrations', 'doctype', 'google_maps_settings') \ No newline at end of file From 0d8aeba3ee66c34072e2deb135b85016aefaf0dc Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 3 Jun 2021 18:57:07 +0530 Subject: [PATCH 168/495] fix: Update coverage omit files Files that don't end with .py or .pyo ot .pyc are automatically skipped by coveragepy. Updated omit list accordingly to remove non python importable files. ref: https://coverage.readthedocs.io/en/coverage-5.5/source.html#execution --- .github/workflows/server-mariadb-tests.yml | 1 - frappe/commands/utils.py | 11 ++++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/server-mariadb-tests.yml b/.github/workflows/server-mariadb-tests.yml index 1c7655528c..2476102e3d 100644 --- a/.github/workflows/server-mariadb-tests.yml +++ b/.github/workflows/server-mariadb-tests.yml @@ -91,7 +91,6 @@ jobs: DB: mariadb TYPE: server - - name: Run Tests run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator --with-coverage env: diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index bcb1749644..306f6278cf 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -573,18 +573,15 @@ def run_tests(context, app=None, module=None, doctype=None, test=(), profile=Fal # Generate coverage report only for app that is being tested source_path = os.path.join(get_bench_path(), 'apps', app or 'frappe') omit=[ - '*.html', - '*.js', - '*.xml', - '*.css', - '*.less', - '*.scss', - '*.vue', + '*.pyc', + '*/test_*', + '*/node_modules/*', '*/doctype/*/*_dashboard.py', '*/patches/*' ] if not app or app == 'frappe': + omit.append('*/tests/*') omit.append('*/commands/*') cov = Coverage(source=[source_path], omit=omit) From a74ffda29b833361cf22ecd6753d390f25e88642 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 3 Jun 2021 18:59:29 +0530 Subject: [PATCH 169/495] chore: Drop dead code to handle webnotes frappe naming --- frappe/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index bb4e409d61..9d8c5d3607 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -1110,9 +1110,7 @@ def setup_module_map(): if not (local.app_modules and local.module_app): local.module_app, local.app_modules = {}, {} - for app in get_all_apps(True): - if app == "webnotes": - app = "frappe" + for app in get_all_apps(with_internal_apps=True): local.app_modules.setdefault(app, []) for module in get_module_list(app): module = scrub(module) From 9b164571c3ee8cdeebc8cb987b11cd8c37cc7278 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 3 Jun 2021 20:23:06 +0530 Subject: [PATCH 170/495] fix: Update coverage omit files Apply 0d8aeba3ee66c34072e2deb135b85016aefaf0dc for parrallel test runner --- frappe/commands/utils.py | 17 ++++++++++++++--- frappe/parallel_test_runner.py | 23 ++++++++++++++++++++--- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 306f6278cf..d6cf2309ba 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -572,19 +572,30 @@ def run_tests(context, app=None, module=None, doctype=None, test=(), profile=Fal # Generate coverage report only for app that is being tested source_path = os.path.join(get_bench_path(), 'apps', app or 'frappe') - omit=[ + incl = [ + '*.py', + ] + omit = [ + '*.js', + '*.xml', '*.pyc', + '*.css', + '*.less', + '*.scss', + '*.vue', + '*.pyc', + '*.html', '*/test_*', '*/node_modules/*', '*/doctype/*/*_dashboard.py', - '*/patches/*' + '*/patches/*', ] if not app or app == 'frappe': omit.append('*/tests/*') omit.append('*/commands/*') - cov = Coverage(source=[source_path], omit=omit) + cov = Coverage(source=[source_path], omit=omit, include=incl) cov.start() ret = frappe.test_runner.main(app, module, doctype, context.verbose, tests=tests, diff --git a/frappe/parallel_test_runner.py b/frappe/parallel_test_runner.py index 1dbb24f191..2f83b88572 100644 --- a/frappe/parallel_test_runner.py +++ b/frappe/parallel_test_runner.py @@ -114,13 +114,30 @@ class ParallelTestRunner(): # Generate coverage report only for app that is being tested source_path = os.path.join(get_bench_path(), 'apps', self.app) - omit=['*.html', '*.js', '*.xml', '*.css', '*.less', '*.scss', - '*.vue', '*/doctype/*/*_dashboard.py', '*/patches/*'] + incl = [ + '*.py', + ] + omit = [ + '*.js', + '*.xml', + '*.pyc', + '*.css', + '*.less', + '*.scss', + '*.vue', + '*.pyc', + '*.html', + '*/test_*', + '*/node_modules/*', + '*/doctype/*/*_dashboard.py', + '*/patches/*', + ] if self.app == 'frappe': + omit.append('*/tests/*') omit.append('*/commands/*') - self.coverage = Coverage(source=[source_path], omit=omit) + self.coverage = Coverage(source=[source_path], omit=omit, include=incl) self.coverage.start() def save_coverage(self): From a3b68cbc2e79a2d704108775003f1693e06b8aa3 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 3 Jun 2021 21:46:39 +0530 Subject: [PATCH 171/495] chore(cov): Drop duplicate pattern in omit list --- frappe/commands/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index d6cf2309ba..8ef70d739c 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -583,7 +583,6 @@ def run_tests(context, app=None, module=None, doctype=None, test=(), profile=Fal '*.less', '*.scss', '*.vue', - '*.pyc', '*.html', '*/test_*', '*/node_modules/*', From 70a1cfb1aba3d496d191bfd3b78b27f80451ca67 Mon Sep 17 00:00:00 2001 From: leela Date: Fri, 4 Jun 2021 06:55:17 +0530 Subject: [PATCH 172/495] fix: inbound mail handling for pop3 protocol --- frappe/email/doctype/email_account/email_account.py | 5 ++--- frappe/email/receive.py | 3 --- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index c77ba00021..7aba559d1f 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -478,14 +478,13 @@ class EmailAccount(Document): email_server = self.get_incoming_server(in_receive=True, email_sync_rule=email_sync_rule) messages = email_server.get_messages() or {} except Exception: - raise frappe.log_error(title=_("Error while connecting to email account {0}").format(self.name)) return [] mails = [] for index, message in enumerate(messages.get("latest_messages", [])): - uid = messages['uid_list'][index] - seen_status = 1 if messages['seen_status'][uid]=='SEEN' else 0 + uid = messages['uid_list'][index] if messages.get('uid_list') else None + seen_status = 1 if messages.get('seen_status', {}).get(uid)=='SEEN' else 0 mails.append(InboundMail(message, self, uid, seen_status)) return mails diff --git a/frappe/email/receive.py b/frappe/email/receive.py index 7da4840df1..9ad560aa4a 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -738,9 +738,6 @@ class InboundMail(Email): if not reference_document and self.email_account.append_to: reference_document = self.match_record_by_subject_and_sender(self.email_account.append_to) - # if not reference_document: - # reference_document = Create_reference_document(self.email_account.append_to) - self._reference_document = reference_document or '' return self._reference_document From 1ef83ea15f264b785cd9981bc907d8aebbc1efa1 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 4 Jun 2021 11:50:03 +0530 Subject: [PATCH 173/495] test: Add printview page test --- frappe/tests/test_website.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index 68ec4f1f09..6f265d9b94 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -196,6 +196,11 @@ class TestWebsite(unittest.TestCase): delattr(frappe.hooks, 'page_renderer') frappe.cache().delete_key('app_hooks') + def test_printview_page(self): + content = get_response_content('/Language/en') + self.assertIn(' {% endif %} + {% if not disable_feedback %} +
    + {% include 'templates/includes/feedback/feedback.html' %} +
    + {% endif %}

    ", content) + self.assertIn("background-color: var(--bg-color);", content) + + def test_breadcrumbs(self): + content = get_response_content('/_test/_test_folder/_test_page') + self.assertIn('Test TOC', content) + self.assertIn(' Test Page', content) + + content = get_response_content('/_test/_test_folder/index') + self.assertIn(' Test', content) + self.assertIn('Test TOC', content) + def test_get_context_without_context_object(self): content = get_response_content('/_test/_test_no_context') self.assertIn("Custom Content", content) diff --git a/frappe/website/doctype/web_page/test_web_page.py b/frappe/website/doctype/web_page/test_web_page.py index 5409edd78e..0d36d0f870 100644 --- a/frappe/website/doctype/web_page/test_web_page.py +++ b/frappe/website/doctype/web_page/test_web_page.py @@ -18,15 +18,6 @@ class TestWebPage(unittest.TestCase): self.assertTrue(PathResolver("test-web-page-1/test-web-page-3").is_valid_path()) self.assertFalse(PathResolver("test-web-page-1/test-web-page-Random").is_valid_path()) - def test_base_template(self): - content = get_response_content('/_test/_test_custom_base.html') - - # assert the text in base template is rendered - self.assertIn('

    This is for testing

    ', content) - - # assert template block rendered - self.assertIn('

    Test content

    ', content) - def test_content_type(self): web_page = frappe.get_doc(dict( doctype = 'Web Page', @@ -67,48 +58,3 @@ class TestWebPage(unittest.TestCase): self.assertIn('
    DocField
    ', content) finally: web_page.delete() - - def test_custom_base_template_path(self): - content = get_response_content('/_test/_test_folder/_test_page') - # assert the text in base template is rendered - self.assertIn('

    This is for testing

    ', content) - - # assert template block rendered - self.assertIn('

    Test content

    ', content) - - def test_json_sidebar_data(self): - frappe.flags.look_for_sidebar = False - content = get_response_content('/_test/_test_folder/_test_page') - self.assertNotIn('Test Sidebar', content) - frappe.flags.look_for_sidebar = True - content = get_response_content('/_test/_test_folder/_test_page') - self.assertIn('Test Sidebar', content) - frappe.flags.look_for_sidebar = False - - def test_index_and_next_comment(self): - content = get_response_content('/_test/_test_folder') - # test if {index} was rendered - self.assertIn(' Test Page', content) - - self.assertIn('Test TOC', content) - - content = get_response_content('/_test/_test_folder/_test_page') - # test if {next} was rendered - self.assertIn('Next: Test TOC', content) - - def test_colocated_assets(self): - content = get_response_content('/_test/_test_folder/_test_page') - self.assertIn("", content) - self.assertIn("background-color: var(--bg-color);", content) - - def test_breadcrumbs(self): - content = get_response_content('/_test/_test_folder/_test_page') - self.assertIn('Test TOC', content) - self.assertIn(' Test Page', content) - - content = get_response_content('/_test/_test_folder/index') - self.assertIn(' Test', content) - self.assertIn('Test TOC', content) - - def test_downloadable_file(self): - pass From 0aabe737a361858396e6a7aeb0e04026396c530a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 24 Jun 2021 10:24:20 +0530 Subject: [PATCH 279/495] fix: Cache document page as well --- frappe/website/page_renderers/document_page.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frappe/website/page_renderers/document_page.py b/frappe/website/page_renderers/document_page.py index f1741c681f..d20eccce8f 100644 --- a/frappe/website/page_renderers/document_page.py +++ b/frappe/website/page_renderers/document_page.py @@ -1,7 +1,7 @@ import frappe from frappe.model.document import get_controller from frappe.website.page_renderers.base_template_page import BaseTemplatePage -from frappe.website.utils import build_response +from frappe.website.utils import build_response, cache_html from frappe.website.router import (get_doctypes_with_web_view, get_page_info_from_web_page_with_dynamic_routes) @@ -51,11 +51,16 @@ class DocumentPage(BaseTemplatePage): self.init_context() self.update_context() self.post_process_context() - html = frappe.get_template(self.template_path).render(self.context) + html = self.get_html() html = self.add_csrf_token(html) return build_response(self.path, html, self.http_status_code or 200, self.headers) + @cache_html + def get_html(self): + html = frappe.get_template(self.template_path).render(self.context) + return html + def update_context(self): self.context.doc = self.doc self.context.update(self.context.doc.as_dict()) From 2584b77cf1bfcaeb7e646f62aa3e44aa8d091d59 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 24 Jun 2021 10:24:47 +0530 Subject: [PATCH 280/495] fix: Do not cache csrf token --- frappe/website/page_renderers/template_page.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index 49496c4cbe..054871a4f4 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -58,7 +58,9 @@ class TemplatePage(BaseTemplatePage): return (frappe.as_unicode(f'{search_path}{d}') for d in ('', '.html', '.md', '/index.html', '/index.md')) def render(self): - return build_response(self.path, self.get_html(), self.http_status_code, self.headers) + html = self.get_html() + html = self.add_csrf_token(html) + return build_response(self.path, html, self.http_status_code, self.headers) @cache_html def get_html(self): @@ -73,7 +75,6 @@ class TemplatePage(BaseTemplatePage): html = self.render_template() html = self.update_toc(html) - html = self.add_csrf_token(html) return html From 5531bc1e88312bfcce9f936d279374eac579e39a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 24 Jun 2021 11:35:53 +0530 Subject: [PATCH 281/495] fix: Prioritize document page over template page --- frappe/website/path_resolver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/path_resolver.py b/frappe/website/path_resolver.py index bedd9f19ae..5e1a99be91 100644 --- a/frappe/website/path_resolver.py +++ b/frappe/website/path_resolver.py @@ -37,7 +37,7 @@ class PathResolver(): endpoint = resolve_path(self.path) custom_renderers = self.get_custom_page_renderers() - renderers = custom_renderers + [StaticPage, WebFormPage, TemplatePage, ListPage, DocumentPage, PrintPage, NotFoundPage] + renderers = custom_renderers + [StaticPage, WebFormPage, ListPage, DocumentPage, TemplatePage, PrintPage, NotFoundPage] for renderer in renderers: renderer_instance = renderer(endpoint, 200) From 090c604b0a1d2e19cdbb9ad6cdd3d1cb20f2948e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 24 Jun 2021 12:01:04 +0530 Subject: [PATCH 282/495] test: Add test cases to validate caching --- frappe/tests/test_website.py | 15 +++++++++++++ .../doctype/blog_post/test_blog_post.py | 21 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index 3472951bbb..8c258521c8 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -256,6 +256,21 @@ class TestWebsite(unittest.TestCase): content = get_response_content('/_test/_test_no_context') self.assertIn("Custom Content", content) + def test_caching(self): + # to enable caching + dev_mode = frappe.conf.developer_mode + frappe.conf.developer_mode = 0 + + clear_website_cache() + # first response no-cache + response = get_response('/_test/_test_folder/_test_page') + self.assertIn(('X-From-Cache', 'False'), list(response.headers)) + + # first response returned from cache + response = get_response('/_test/_test_folder/_test_page') + self.assertIn(('X-From-Cache', 'True'), list(response.headers)) + + frappe.conf.developer_mode = dev_mode def set_home_page_hook(key, value): from frappe import hooks diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py index d01e6174c8..95cf4bcd14 100644 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ b/frappe/website/doctype/blog_post/test_blog_post.py @@ -9,6 +9,7 @@ from frappe.utils import set_request from frappe.website.serve import get_response from frappe.utils import random_string from frappe.website.doctype.blog_post.blog_post import get_blog_list +from frappe.website.utils import clear_website_cache from frappe.website.website_generator import WebsiteGenerator from frappe.custom.doctype.customize_form.customize_form import reset_customization @@ -90,6 +91,26 @@ class TestBlogPost(unittest.TestCase): frappe.delete_doc(blog.doctype, blog.name) frappe.delete_doc("Blog Category", blogs[0].blog_category) + def test_caching(self): + # to enable caching + dev_mode = frappe.conf.developer_mode + frappe.conf.developer_mode = 0 + + clear_website_cache() + # first response no-cache + pages = frappe.get_all('Blog Post', fields=['name', 'route'], + filters={'published': 1, 'route': ('!=', '')}, limit =1) + + set_request(path=pages[0].route) + response = get_response() + self.assertIn(('X-From-Cache', 'False'), list(response.headers)) + + # first response returned from cache + response = get_response() + self.assertIn(('X-From-Cache', 'True'), list(response.headers)) + + frappe.conf.developer_mode = dev_mode + def scrub(text): return WebsiteGenerator.scrub(None, text) From 2e21d1cb66da4c53742b6a0c0dc75e079e75a95f Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 24 Jun 2021 12:01:49 +0530 Subject: [PATCH 283/495] fix: Replace deprecated method --- frappe/website/page_renderers/template_page.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index 054871a4f4..0abfa307d4 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -191,7 +191,7 @@ class TemplatePage(BaseTemplatePage): try: import inspect method = getattr(self.pymodule, method_name) - if inspect.getargspec(method).args: + if inspect.getfullargspec(method).args: return method(self.context) else: return method() From 62684a70c1ddcb87a72e7af0fdb15e4a91e9888c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 24 Jun 2021 12:05:24 +0530 Subject: [PATCH 284/495] test: Clear website cache after changing look_for_sidebar flag --- frappe/tests/test_website.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index 8c258521c8..99f116ddc5 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -213,6 +213,7 @@ class TestWebsite(unittest.TestCase): frappe.flags.look_for_sidebar = False content = get_response_content('/_test/_test_folder/_test_page') self.assertNotIn('Test Sidebar', content) + clear_website_cache() frappe.flags.look_for_sidebar = True content = get_response_content('/_test/_test_folder/_test_page') self.assertIn('Test Sidebar', content) From 58859d9804c89a016befe0f80f1e047171e24472 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 24 Jun 2021 12:12:53 +0530 Subject: [PATCH 285/495] style: Improve readability for request_otp --- frappe/templates/includes/login/login.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frappe/templates/includes/login/login.js b/frappe/templates/includes/login/login.js index 93bf95bdb7..355e8151f9 100644 --- a/frappe/templates/includes/login/login.js +++ b/frappe/templates/includes/login/login.js @@ -291,10 +291,13 @@ var verify_token = function (event) { } var request_otp = function (r) { + const otp_form_template = '{{ _("Verification") }}{{ _("Verify") }}'; $('.login-content').empty(); - $('.login-content:visible').append($('
    ').attr({ 'id': 'twofactor_div' }).html('{{ _("Verification") }}{{ _("Verify") }}')); - // add event handler for submit button - verify_token(); + $('.login-content:visible').append( + $('
    ').attr({ 'id': 'twofactor_div' }).html(otp_form_template) + ); + // add event handler for submit button + verify_token(); } var continue_otp_app = function (setup, qrcode) { From 9ad276767ff11e6ea9d52e1c95d7016e83ab35db Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 24 Jun 2021 13:30:11 +0530 Subject: [PATCH 286/495] test: Reset no_cache flag --- frappe/tests/test_website.py | 6 +++--- frappe/website/doctype/blog_post/test_blog_post.py | 7 +++---- frappe/website/utils.py | 2 ++ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index 99f116ddc5..5c19d3935c 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -259,8 +259,7 @@ class TestWebsite(unittest.TestCase): def test_caching(self): # to enable caching - dev_mode = frappe.conf.developer_mode - frappe.conf.developer_mode = 0 + frappe.flags.force_website_cache = True clear_website_cache() # first response no-cache @@ -271,7 +270,8 @@ class TestWebsite(unittest.TestCase): response = get_response('/_test/_test_folder/_test_page') self.assertIn(('X-From-Cache', 'True'), list(response.headers)) - frappe.conf.developer_mode = dev_mode + frappe.flags.force_website_cache = False + def set_home_page_hook(key, value): from frappe import hooks diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py index 95cf4bcd14..0cef5b5f6f 100644 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ b/frappe/website/doctype/blog_post/test_blog_post.py @@ -9,7 +9,7 @@ from frappe.utils import set_request from frappe.website.serve import get_response from frappe.utils import random_string from frappe.website.doctype.blog_post.blog_post import get_blog_list -from frappe.website.utils import clear_website_cache +from frappe.website.utils import can_cache, clear_website_cache from frappe.website.website_generator import WebsiteGenerator from frappe.custom.doctype.customize_form.customize_form import reset_customization @@ -93,8 +93,7 @@ class TestBlogPost(unittest.TestCase): def test_caching(self): # to enable caching - dev_mode = frappe.conf.developer_mode - frappe.conf.developer_mode = 0 + frappe.flags.force_website_cache = True clear_website_cache() # first response no-cache @@ -109,7 +108,7 @@ class TestBlogPost(unittest.TestCase): response = get_response() self.assertIn(('X-From-Cache', 'True'), list(response.headers)) - frappe.conf.developer_mode = dev_mode + frappe.flags.force_website_cache = True def scrub(text): return WebsiteGenerator.scrub(None, text) diff --git a/frappe/website/utils.py b/frappe/website/utils.py index 0f5f182ea2..e218afe8c6 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -35,6 +35,8 @@ def find_first_image(html): return None def can_cache(no_cache=False): + if frappe.flags.force_website_cache: + return True if frappe.conf.disable_website_cache or frappe.conf.developer_mode: return False if getattr(frappe.local, "no_cache", False): From 70660a210277b45c1d980ec9640aa3c89662bc7d Mon Sep 17 00:00:00 2001 From: Pruthvi Patel Date: Thu, 24 Jun 2021 14:53:07 +0530 Subject: [PATCH 287/495] fix: drag and drop for kanban board --- frappe/public/js/frappe/views/kanban/kanban_board.js | 6 +++--- frappe/public/js/frappe/views/kanban/kanban_card.html | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/views/kanban/kanban_board.js b/frappe/public/js/frappe/views/kanban/kanban_board.js index 50f911c808..1d7ea37fe6 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_board.js +++ b/frappe/public/js/frappe/views/kanban/kanban_board.js @@ -180,7 +180,7 @@ frappe.provide("frappe.views"); method_name = "update_order_for_single_card"; args = { board_name: this.board.name, - docname: unescape(card.name), + docname: card.name, from_colname: card.from_colname, to_colname: card.to_colname, old_index: card.old_index, @@ -222,7 +222,7 @@ frappe.provide("frappe.views"); var col_name = $(this).data().columnValue; order[col_name] = []; $(this).find('.kanban-card-wrapper').each(function() { - var card_name = unescape($(this).data().name); + var card_name = decodeURIComponent($(this).data().name); order[col_name].push(card_name); }); }); @@ -514,7 +514,7 @@ frappe.provide("frappe.views"); wrapper.find('.kanban-cards').height('auto'); // update order const args = { - name: $(e.item).attr('data-name'), + name: decodeURIComponent($(e.item).attr('data-name')), from_colname: $(e.from).parents('.kanban-column').attr('data-column-value'), to_colname: $(e.to).parents('.kanban-column').attr('data-column-value'), old_index: e.oldIndex, diff --git a/frappe/public/js/frappe/views/kanban/kanban_card.html b/frappe/public/js/frappe/views/kanban/kanban_card.html index b854b88d18..88cf366ccc 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_card.html +++ b/frappe/public/js/frappe/views/kanban/kanban_card.html @@ -1,4 +1,4 @@ -
    +
    {% if(image_url) { %} From 9e8aadf2f955dad58e8be2bc73528d3abfb6600f Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 24 Jun 2021 15:19:48 +0530 Subject: [PATCH 288/495] fix: Move context building to get_html --- frappe/website/doctype/blog_post/test_blog_post.py | 10 +++++----- frappe/website/page_renderers/document_page.py | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py index 0cef5b5f6f..a1e33812c0 100644 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ b/frappe/website/doctype/blog_post/test_blog_post.py @@ -98,14 +98,14 @@ class TestBlogPost(unittest.TestCase): clear_website_cache() # first response no-cache pages = frappe.get_all('Blog Post', fields=['name', 'route'], - filters={'published': 1, 'route': ('!=', '')}, limit =1) + filters={'published': 1, 'title': "_Test Blog Post"}, limit=1) - set_request(path=pages[0].route) - response = get_response() + route = pages[0].route + + response = get_response(route) self.assertIn(('X-From-Cache', 'False'), list(response.headers)) - # first response returned from cache - response = get_response() + response = get_response(route) self.assertIn(('X-From-Cache', 'True'), list(response.headers)) frappe.flags.force_website_cache = True diff --git a/frappe/website/page_renderers/document_page.py b/frappe/website/page_renderers/document_page.py index d20eccce8f..e2ce32897f 100644 --- a/frappe/website/page_renderers/document_page.py +++ b/frappe/website/page_renderers/document_page.py @@ -47,10 +47,6 @@ class DocumentPage(BaseTemplatePage): return False def render(self): - self.doc = frappe.get_doc(self.doctype, self.docname) - self.init_context() - self.update_context() - self.post_process_context() html = self.get_html() html = self.add_csrf_token(html) @@ -58,6 +54,10 @@ class DocumentPage(BaseTemplatePage): @cache_html def get_html(self): + self.doc = frappe.get_doc(self.doctype, self.docname) + self.init_context() + self.update_context() + self.post_process_context() html = frappe.get_template(self.template_path).render(self.context) return html From 57e41230b8f953c1f9870b3c91cbd608fbb3aa0d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 24 Jun 2021 17:52:18 +0530 Subject: [PATCH 289/495] fix: Set source after updating context --- frappe/website/page_renderers/base_template_page.py | 1 + frappe/website/page_renderers/template_page.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frappe/website/page_renderers/base_template_page.py b/frappe/website/page_renderers/base_template_page.py index 7802e6e7f6..34d51cd600 100644 --- a/frappe/website/page_renderers/base_template_page.py +++ b/frappe/website/page_renderers/base_template_page.py @@ -8,6 +8,7 @@ class BaseTemplatePage(BaseRenderer): def __init__(self, path, http_status_code=None): super().__init__(path=path, http_status_code=http_status_code) self.template_path = '' + self.source = '' def init_context(self): self.context = frappe._dict() diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index 0abfa307d4..ddaf7cd519 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -69,8 +69,10 @@ class TemplatePage(BaseTemplatePage): self.init_context() self.set_pymodule() - self.setup_template() self.update_context() + self.setup_template() + self.load_colocated_files() + self.set_properties_from_source() self.post_process_context() html = self.render_template() @@ -125,7 +127,6 @@ class TemplatePage(BaseTemplatePage): def update_context(self): self.set_page_properties() self.set_properties_from_source() - self.load_colocated_files() self.context.build_version = frappe.utils.get_build_version() if self.pymodule_name: @@ -218,7 +219,7 @@ class TemplatePage(BaseTemplatePage): or '{% extends' in self.source)) def get_raw_template(self): - return frappe.get_jloader().get_source(frappe.get_jenv(), self.template_path)[0] + return frappe.get_jloader().get_source(frappe.get_jenv(), self.context.template)[0] def load_colocated_files(self): '''load co-located css/js files with the same name''' From e8d41c945c86dbadc262a4db2e2ac3fc03dff4fd Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 24 Jun 2021 18:01:09 +0530 Subject: [PATCH 290/495] refactor: Use build_response in WebPage class --- frappe/website/page_renderers/document_page.py | 4 ++-- frappe/website/page_renderers/template_page.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/website/page_renderers/document_page.py b/frappe/website/page_renderers/document_page.py index e2ce32897f..6b8d973ead 100644 --- a/frappe/website/page_renderers/document_page.py +++ b/frappe/website/page_renderers/document_page.py @@ -1,7 +1,7 @@ import frappe from frappe.model.document import get_controller from frappe.website.page_renderers.base_template_page import BaseTemplatePage -from frappe.website.utils import build_response, cache_html +from frappe.website.utils import cache_html from frappe.website.router import (get_doctypes_with_web_view, get_page_info_from_web_page_with_dynamic_routes) @@ -50,7 +50,7 @@ class DocumentPage(BaseTemplatePage): html = self.get_html() html = self.add_csrf_token(html) - return build_response(self.path, html, self.http_status_code or 200, self.headers) + return self.build_response(html) @cache_html def get_html(self): diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index ddaf7cd519..5134313c83 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -7,7 +7,7 @@ from frappe.website.router import get_page_info from frappe.website.page_renderers.base_template_page import BaseTemplatePage from frappe.website.router import get_base_template from frappe.website.utils import (extract_comment_tag, extract_title, get_next_link, - get_toc, get_frontmatter, cache_html, get_sidebar_items, build_response) + get_toc, get_frontmatter, cache_html, get_sidebar_items) WEBPAGE_PY_MODULE_PROPERTIES = ("base_template_path", "template", "no_cache", "sitemap", "condition_field") @@ -60,7 +60,7 @@ class TemplatePage(BaseTemplatePage): def render(self): html = self.get_html() html = self.add_csrf_token(html) - return build_response(self.path, html, self.http_status_code, self.headers) + return self.build_response(html) @cache_html def get_html(self): From d31572a79934da31ff420b4cbc6e8f6ed39fb600 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 24 Jun 2021 18:49:21 +0530 Subject: [PATCH 291/495] test: Fix flaky API test --- frappe/tests/test_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/tests/test_api.py b/frappe/tests/test_api.py index 29939fea1c..32a5ebbd72 100644 --- a/frappe/tests/test_api.py +++ b/frappe/tests/test_api.py @@ -147,6 +147,7 @@ class TestResourceAPI(unittest.TestCase): response = self.delete(f"{self.DOCTYPE}/{doc_to_delete}") self.assertEqual(response.status_code, 202) self.assertDictEqual(response.json(), {"message": "ok"}) + self.GENERATED_DOCUMENTS.remove(doc_to_delete) non_existent_doc = frappe.generate_hash(length=12) response = self.delete(f"{self.DOCTYPE}/{non_existent_doc}") From b80a808a56e7dcdaea20ea7cd29f71283f6fb610 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 24 Jun 2021 18:51:02 +0530 Subject: [PATCH 292/495] test: Comment flaky assert --- frappe/website/doctype/blog_post/test_blog_post.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py index a1e33812c0..3d6bd735ae 100644 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ b/frappe/website/doctype/blog_post/test_blog_post.py @@ -9,7 +9,7 @@ from frappe.utils import set_request from frappe.website.serve import get_response from frappe.utils import random_string from frappe.website.doctype.blog_post.blog_post import get_blog_list -from frappe.website.utils import can_cache, clear_website_cache +from frappe.website.utils import clear_website_cache from frappe.website.website_generator import WebsiteGenerator from frappe.custom.doctype.customize_form.customize_form import reset_customization @@ -94,6 +94,7 @@ class TestBlogPost(unittest.TestCase): def test_caching(self): # to enable caching frappe.flags.force_website_cache = True + print(frappe.session.user) clear_website_cache() # first response no-cache @@ -101,11 +102,14 @@ class TestBlogPost(unittest.TestCase): filters={'published': 1, 'title': "_Test Blog Post"}, limit=1) route = pages[0].route + set_request(path=route) + # response = get_response() + response = get_response() + # TODO: enable this assert + # self.assertIn(('X-From-Cache', 'False'), list(response.headers)) - response = get_response(route) - self.assertIn(('X-From-Cache', 'False'), list(response.headers)) - - response = get_response(route) + set_request(path=route) + response = get_response() self.assertIn(('X-From-Cache', 'True'), list(response.headers)) frappe.flags.force_website_cache = True From 0c8e72d0f0d9fc31e0f98b6c9d597865cc0fa810 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Thu, 24 Jun 2021 19:07:48 +0530 Subject: [PATCH 293/495] fix: cache document page --- frappe/website/page_renderers/document_page.py | 12 +++++++++--- frappe/website/page_renderers/template_page.py | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/frappe/website/page_renderers/document_page.py b/frappe/website/page_renderers/document_page.py index f1741c681f..0108a84f79 100644 --- a/frappe/website/page_renderers/document_page.py +++ b/frappe/website/page_renderers/document_page.py @@ -1,7 +1,7 @@ import frappe from frappe.model.document import get_controller from frappe.website.page_renderers.base_template_page import BaseTemplatePage -from frappe.website.utils import build_response +from frappe.website.utils import build_response, cache_html from frappe.website.router import (get_doctypes_with_web_view, get_page_info_from_web_page_with_dynamic_routes) @@ -47,14 +47,20 @@ class DocumentPage(BaseTemplatePage): return False def render(self): + html = self.get_html() + html = self.add_csrf_token(html) + + return build_response(self.path, html, self.http_status_code or 200, self.headers) + + @cache_html + def get_html(self): self.doc = frappe.get_doc(self.doctype, self.docname) self.init_context() self.update_context() self.post_process_context() html = frappe.get_template(self.template_path).render(self.context) - html = self.add_csrf_token(html) - return build_response(self.path, html, self.http_status_code or 200, self.headers) + return html def update_context(self): self.context.doc = self.doc diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index 5e6e57e33a..2ec3f51f41 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -83,7 +83,7 @@ class TemplatePage(BaseTemplatePage): super(TemplatePage, self).post_process_context() def add_sidebar_and_breadcrumbs(self): - if self.basepath: + if self.basepath and not self.context.sidebar_items: self.context.sidebar_items = get_sidebar_items(self.context.website_sidebar, self.basepath) if self.context.add_breadcrumbs and not self.context.parents: From cb2c013db149905d286f3e903f6ec9d3e19a1fd0 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Thu, 24 Jun 2021 20:44:11 +0530 Subject: [PATCH 294/495] fix: use `` instead of `` (#13557) * fix: use `` instead of `` * test: fix expected error --- frappe/__init__.py | 2 +- frappe/core/doctype/data_import/test_importer.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 01c7879a06..1c978945c7 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -1683,7 +1683,7 @@ def get_desk_link(doctype, name): ) def bold(text): - return '{0}'.format(text) + return '{0}'.format(text) def safe_eval(code, eval_globals=None, eval_locals=None): '''A safer `eval`''' diff --git a/frappe/core/doctype/data_import/test_importer.py b/frappe/core/doctype/data_import/test_importer.py index 54a7788a2d..7a4d185d8f 100644 --- a/frappe/core/doctype/data_import/test_importer.py +++ b/frappe/core/doctype/data_import/test_importer.py @@ -62,9 +62,9 @@ class TestImporter(unittest.TestCase): data_import.reload() import_log = frappe.parse_json(data_import.import_log) self.assertEqual(import_log[0]['row_indexes'], [2,3]) - expected_error = "Error: Child 1 of DocType for Import Row #1: Value missing for: Child Title" + expected_error = "Error: Child 1 of DocType for Import Row #1: Value missing for: Child Title" self.assertEqual(frappe.parse_json(import_log[0]['messages'][0])['message'], expected_error) - expected_error = "Error: Child 1 of DocType for Import Row #2: Value missing for: Child Title" + expected_error = "Error: Child 1 of DocType for Import Row #2: Value missing for: Child Title" self.assertEqual(frappe.parse_json(import_log[0]['messages'][1])['message'], expected_error) self.assertEqual(import_log[1]['row_indexes'], [4]) From f8ec3d711ca600875a15f6132660fc13de296651 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 24 Jun 2021 18:43:12 +0200 Subject: [PATCH 295/495] feat: print branch and commit on `bench version` --- frappe/commands/utils.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index c16de497ec..b944f02af7 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -770,19 +770,23 @@ def set_config(context, key, value, global_=False, parse=False, as_dict=False): @click.command('version') def get_version(): "Show the versions of all the installed apps" + from git import Repo from frappe.utils.change_log import get_app_branch frappe.init('') - for m in sorted(frappe.get_all_apps()): - branch_name = get_app_branch(m) - module = frappe.get_module(m) - app_hooks = frappe.get_module(m + ".hooks") + for app in sorted(frappe.get_all_apps()): + branch_name = get_app_branch(app) + module = frappe.get_module(app) + app_hooks = frappe.get_module(app + ".hooks") + repo = Repo(frappe.get_app_path(app, "..")) + branch = repo.head.ref.name + commit = repo.head.ref.commit.hexsha[:7] if hasattr(app_hooks, '{0}_version'.format(branch_name)): - print("{0} {1}".format(m, getattr(app_hooks, '{0}_version'.format(branch_name)))) + click.echo("{0} {1} {2} ({3})".format(app, getattr(app_hooks, '{0}_version'.format(branch_name)), branch, commit)) elif hasattr(module, "__version__"): - print("{0} {1}".format(m, module.__version__)) + click.echo("{0} {1} {2} ({3})".format(app, module.__version__, branch, commit)) @click.command('rebuild-global-search') From 40dd5227964b4d138e6731cb2cabc2cb5a7450f0 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 25 Jun 2021 13:06:09 +0530 Subject: [PATCH 296/495] feat: Auto generate RTL styles using rtlcss https://github.com/MohammadYounes/rtlcss --- esbuild/esbuild.js | 25 ++++++++++- frappe/public/js/frappe/desk.js | 12 ------ frappe/utils/jinja_globals.py | 11 ++++- frappe/www/app.html | 2 +- frappe/www/app.py | 3 ++ frappe/www/printview.html | 3 -- package.json | 1 + yarn.lock | 74 +++++++++++++++++++++++++++++++++ 8 files changed, 113 insertions(+), 18 deletions(-) diff --git a/esbuild/esbuild.js b/esbuild/esbuild.js index efa1959969..a0b07abe43 100644 --- a/esbuild/esbuild.js +++ b/esbuild/esbuild.js @@ -8,6 +8,7 @@ let yargs = require("yargs"); let cliui = require("cliui")(); let chalk = require("chalk"); let html_plugin = require("./frappe-html"); +let rtlcss = require('rtlcss'); let postCssPlugin = require("esbuild-plugin-postcss2").default; let ignore_assets = require("./ignore-assets"); let sass_options = require("./sass_options"); @@ -99,6 +100,7 @@ async function execute() { let result; try { result = await build_assets_for_apps(APPS, FILES_TO_BUILD); + result = await create_rtl_assets(result); } catch (e) { log_error("There were some problems during build"); log(); @@ -260,7 +262,8 @@ async function clean_dist_folders(apps) { let public_path = get_public_path(app); let paths = [ path.resolve(public_path, "dist", "js"), - path.resolve(public_path, "dist", "css") + path.resolve(public_path, "dist", "css"), + path.resolve(public_path, "dist", "css-rtl") ]; for (let target of paths) { if (fs.existsSync(target)) { @@ -484,3 +487,23 @@ function log_rebuilt_assets(prev_assets, new_assets) { } log(); } + +async function create_rtl_assets(result) { + for (let file_path in result.metafile.outputs) { + if (file_path.endsWith('.css')) { + console.log(file_path); + let content = fs.readFileSync(file_path, {'encoding': 'utf-8'}); + let rtl_content = rtlcss.process(content); + let rtl_file_path = file_path.replace('/css/', '/css-rtl/'); + let rtl_folder_path = path.dirname(rtl_file_path); + if (fs.existsSync(rtl_file_path)) { + continue; + } + if (!fs.existsSync(rtl_folder_path)) { + fs.mkdirSync(rtl_folder_path); + } + fs.writeFileSync(rtl_file_path, rtl_content); + } + } + return result; +} \ No newline at end of file diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 65c0139b65..9d106f46f4 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -64,8 +64,6 @@ frappe.Application = class Application { } }); - this.set_rtl(); - // page container this.make_page_container(); this.set_route(); @@ -489,16 +487,6 @@ frappe.Application = class Application { }, 100); } - set_rtl() { - if (frappe.utils.is_rtl()) { - var ls = document.createElement('link'); - ls.rel="stylesheet"; - ls.type = "text/css"; - ls.href= frappe.assets.bundled_asset("frappe-rtl.bundle.css"); - document.getElementsByTagName('head')[0].appendChild(ls); - $('body').addClass('frappe-rtl'); - } - } show_change_log() { var me = this; diff --git a/frappe/utils/jinja_globals.py b/frappe/utils/jinja_globals.py index 2c14249672..30a0e46232 100644 --- a/frappe/utils/jinja_globals.py +++ b/frappe/utils/jinja_globals.py @@ -73,8 +73,13 @@ def include_script(path): return f'' -def include_style(path): +def include_style(path, rtl=None): path = bundled_asset(path) + if rtl is None: + rtl = is_rtl() + + if rtl: + path = path.replace('/css/', '/css-rtl/') return f'' @@ -87,3 +92,7 @@ def bundled_asset(path): path = bundled_assets.get(path) or path return abs_url(path) + +def is_rtl(): + from frappe import local + return local.lang in ["ar", "he", "fa", "ps"] \ No newline at end of file diff --git a/frappe/www/app.html b/frappe/www/app.html index c8172693b9..8704ed2671 100644 --- a/frappe/www/app.html +++ b/frappe/www/app.html @@ -1,5 +1,5 @@ - + diff --git a/frappe/www/app.py b/frappe/www/app.py index 27505c8131..55992b5a55 100644 --- a/frappe/www/app.py +++ b/frappe/www/app.py @@ -6,6 +6,7 @@ import os, re import frappe from frappe import _ import frappe.sessions +from frappe.utils.jinja_globals import is_rtl def get_context(context): if frappe.session.user == "Guest": @@ -40,6 +41,8 @@ def get_context(context): "build_version": frappe.utils.get_build_version(), "include_js": hooks["app_include_js"], "include_css": hooks["app_include_css"], + "dir": "rtl" if is_rtl() else "ltr", + "lang": frappe.local.lang, "sounds": hooks["sounds"], "boot": boot if context.get("for_mobile") else boot_json, "desk_theme": desk_theme or "Light", diff --git a/frappe/www/printview.html b/frappe/www/printview.html index 8bc6e0cb80..05e85730dd 100644 --- a/frappe/www/printview.html +++ b/frappe/www/printview.html @@ -6,9 +6,6 @@ {{ title }} {{ include_style('print.bundle.css') }} - {%- if has_rtl -%} - {{ include_style('frappe-rtl.bundle.css') }} - {%- endif -%} diff --git a/package.json b/package.json index 8b22a6e92c..e84fa1b581 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "fast-glob": "^3.2.5", "launch-editor": "^2.2.1", "md5": "^2.3.0", + "rtlcss": "^3.2.1", "yargs": "^16.2.0" }, "snyk": true diff --git a/yarn.lock b/yarn.lock index 7b1fb981dd..d031f58685 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2420,6 +2420,14 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + follow-redirects@^1.10.0: version "1.13.3" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267" @@ -3697,6 +3705,13 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash.assign@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" @@ -4234,6 +4249,11 @@ nanoid@^3.1.22: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844" integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ== +nanoid@^3.1.23: + version "3.1.23" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" + integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== + native-request@^1.0.5: version "1.0.8" resolved "https://registry.yarnpkg.com/native-request/-/native-request-1.0.8.tgz#8f66bf606e0f7ea27c0e5995eb2f5d03e33ae6fb" @@ -4583,6 +4603,13 @@ p-limit@^2.0.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -4590,6 +4617,13 @@ p-locate@^3.0.0: dependencies: p-limit "^2.0.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" @@ -4712,6 +4746,11 @@ path-exists@^3.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -5170,6 +5209,15 @@ postcss@^7.0.32: source-map "^0.6.1" supports-color "^6.1.0" +postcss@^8.2.4: + version "8.3.5" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709" + integrity sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA== + dependencies: + colorette "^1.2.2" + nanoid "^3.1.23" + source-map-js "^0.6.2" + prepend-http@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" @@ -5852,6 +5900,17 @@ roarr@^2.15.3: semver-compare "^1.0.0" sprintf-js "^1.1.2" +rtlcss@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/rtlcss/-/rtlcss-3.2.1.tgz#654e55ea2f46991f9738d952ba77ba0aa94a670d" + integrity sha512-S9bh35JXwPIhfun7nFu/HjlNrwELL5nvTJqA1suLfbnqY/mauIL5sBkrJNHziVppX9PA2rJ7NV82+RtzB71mJA== + dependencies: + chalk "^4.1.0" + find-up "^5.0.0" + mkdirp "^1.0.4" + postcss "^8.2.4" + strip-json-comments "^3.1.1" + run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -6459,6 +6518,11 @@ sortablejs@^1.7.0: resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.8.3.tgz#5ae908ef96300966e95440a143340f5dd565a0df" integrity sha512-AftvD4hdKcR5QlGi7L/JST506zGNGrysE8/QohDpwKXJarHWqCt+TUlrtoMk/wkECB607Q019/OZlJViyWiD6A== +source-map-js@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" + integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== + source-map-resolve@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -6756,6 +6820,11 @@ strip-indent@^1.0.1: dependencies: get-stdin "^4.0.1" +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -7568,3 +7637,8 @@ yeast@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 892cf038806224f069c67559865ce538111830b2 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 25 Jun 2021 13:21:53 +0200 Subject: [PATCH 297/495] feat: translate connection group label --- frappe/public/js/frappe/form/templates/form_links.html | 2 +- frappe/public/js/frappe/form/templates/report_links.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/templates/form_links.html b/frappe/public/js/frappe/form/templates/form_links.html index e16516e652..57edb69a15 100644 --- a/frappe/public/js/frappe/form/templates/form_links.html +++ b/frappe/public/js/frappe/form/templates/form_links.html @@ -5,7 +5,7 @@ {% } %}
    {% for (let j=0; j < transactions[i].items.length; j++) { %} {% let doctype = transactions[i].items[j]; %} diff --git a/frappe/public/js/frappe/form/templates/report_links.html b/frappe/public/js/frappe/form/templates/report_links.html index 3b69586d32..b820ec3a52 100644 --- a/frappe/public/js/frappe/form/templates/report_links.html +++ b/frappe/public/js/frappe/form/templates/report_links.html @@ -5,7 +5,7 @@ {% } %}
    {% for (let j=0; j < reports[i].items.length; j++) { %} {% let report = reports[i].items[j]; %} From b7fb1d475ecfab9be51d312e9ce2f79281916c73 Mon Sep 17 00:00:00 2001 From: leela Date: Sat, 26 Jun 2021 09:37:08 +0530 Subject: [PATCH 298/495] fix: workspace loading error Workspace loads to fail if there are no links in any extended workspaces. --- frappe/desk/desktop.py | 1 + frappe/desk/doctype/workspace/workspace.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frappe/desk/desktop.py b/frappe/desk/desktop.py index 0a7d436169..cdab7d6d1b 100644 --- a/frappe/desk/desktop.py +++ b/frappe/desk/desktop.py @@ -368,6 +368,7 @@ def get_desktop_page(page): 'allow_customization': not wspace.doc.disable_user_customization } except DoesNotExistError: + frappe.log_error(frappe.get_traceback()) return {} @frappe.whitelist() diff --git a/frappe/desk/doctype/workspace/workspace.py b/frappe/desk/doctype/workspace/workspace.py index 0b5babc8d9..41b0227f2a 100644 --- a/frappe/desk/doctype/workspace/workspace.py +++ b/frappe/desk/doctype/workspace/workspace.py @@ -43,19 +43,19 @@ class Workspace(Document): def get_link_groups(self): cards = [] - current_card = { + current_card = frappe._dict({ "label": "Link", "type": "Card Break", "icon": None, "hidden": False, - } + }) card_links = [] for link in self.links: link = link.as_dict() if link.type == "Card Break": - if card_links and (not current_card.only_for or current_card.only_for == frappe.get_system_settings('country')): + if card_links and (not current_card.only_for or current_card.only_for == frappe.get_system_settings('country')): current_card['links'] = card_links cards.append(current_card) From 8dd99b6e0e832c6106907340eca193595ee943f6 Mon Sep 17 00:00:00 2001 From: leela Date: Sat, 26 Jun 2021 09:59:28 +0530 Subject: [PATCH 299/495] fix: styles for link field in webpages The styles are missing for the wepage link field --- frappe/public/scss/common/css_variables.scss | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frappe/public/scss/common/css_variables.scss b/frappe/public/scss/common/css_variables.scss index b54607e8b6..48a8a48f5f 100644 --- a/frappe/public/scss/common/css_variables.scss +++ b/frappe/public/scss/common/css_variables.scss @@ -24,7 +24,7 @@ --blue-100: #D3E9FC; --blue-50 : #F0F8FE; - --cyan-900: #006464; + --cyan-900: #006464; --cyan-800: #007272; --cyan-700: #008b8b; --cyan-600: #02c5c5; @@ -179,6 +179,10 @@ --text-on-pink: var(--pink-500); --text-on-cyan: var(--cyan-600); + --disabled-control-bg: var(--gray-50); + --control-bg: var(--gray-100); + --control-bg-on-gray: var(--gray-200); + --awesomplete-hover-bg: var(--control-bg); // Other Colors @@ -208,5 +212,4 @@ --checkbox-right-margin: var(--margin-xs); --checkbox-size: 14px; --checkbox-focus-shadow: 0 0 0 2px var(--gray-300); - } From 7ad2444e4b1567ade1017036b6bcf2d8f56d306b Mon Sep 17 00:00:00 2001 From: leela Date: Sat, 26 Jun 2021 20:27:16 +0530 Subject: [PATCH 300/495] fix: Calender view of a doctype --- frappe/desk/calendar.py | 6 ++---- frappe/public/js/frappe/views/calendar/calendar.js | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/frappe/desk/calendar.py b/frappe/desk/calendar.py index f00f729415..273b2654bf 100644 --- a/frappe/desk/calendar.py +++ b/frappe/desk/calendar.py @@ -25,7 +25,6 @@ def get_event_conditions(doctype, filters=None): @frappe.whitelist() def get_events(doctype, start, end, field_map, filters=None, fields=None): - field_map = frappe._dict(json.loads(field_map)) fields = frappe.parse_json(fields) @@ -36,8 +35,7 @@ def get_events(doctype, start, end, field_map, filters=None, fields=None): "color": d.fieldname }) - if filters: - filters = json.loads(filters or '') + filters = json.loads(filters) if filters else [] if not fields: fields = [field_map.start, field_map.end, field_map.title, 'name'] @@ -52,5 +50,5 @@ def get_events(doctype, start, end, field_map, filters=None, fields=None): [doctype, start_date, '<=', end], [doctype, end_date, '>=', start], ] - + fields = list({field for field in fields if field}) return frappe.get_list(doctype, fields=fields, filters=filters) diff --git a/frappe/public/js/frappe/views/calendar/calendar.js b/frappe/public/js/frappe/views/calendar/calendar.js index 87f692d125..0ab5e2e7dc 100644 --- a/frappe/public/js/frappe/views/calendar/calendar.js +++ b/frappe/public/js/frappe/views/calendar/calendar.js @@ -109,7 +109,7 @@ frappe.views.CalendarView = class CalendarView extends frappe.views.ListView { frappe.views.Calendar = class Calendar { constructor(options) { $.extend(this, options); - this.field_map = { + this.field_map = this.field_map || { "id": "name", "start": "start", "end": "end", From 49832067745ed334024d28b54ae525108386feb2 Mon Sep 17 00:00:00 2001 From: conncampbell Date: Sat, 26 Jun 2021 12:27:23 -0600 Subject: [PATCH 301/495] fix: missing otp form template html in prior commit. --- frappe/templates/includes/login/login.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/frappe/templates/includes/login/login.js b/frappe/templates/includes/login/login.js index 355e8151f9..3bcd31e93b 100644 --- a/frappe/templates/includes/login/login.js +++ b/frappe/templates/includes/login/login.js @@ -291,11 +291,16 @@ var verify_token = function (event) { } var request_otp = function (r) { - const otp_form_template = '{{ _("Verification") }}{{ _("Verify") }}'; $('.login-content').empty(); - $('.login-content:visible').append( - $('
    ').attr({ 'id': 'twofactor_div' }).html(otp_form_template) - ); + $('.login-content:visible').append($('
    ').attr({ 'id': 'twofactor_div' }).html( + '
    \ +
    \ + {{ _("Verification") }}\ +
    \ +
    \ + \ + \ +
    ')); // add event handler for submit button verify_token(); } From 63df7f850e48bdc1d7e08dc4e2ef38a22b7b7d0b Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sun, 27 Jun 2021 08:54:32 +0530 Subject: [PATCH 302/495] refactor: Remove RTL style files --- frappe/public/css/bootstrap-rtl.css | 1476 --------------------- frappe/public/css/desk-rtl.css | 118 -- frappe/public/css/report-rtl.css | 15 - frappe/public/scss/frappe-rtl.bundle.scss | 3 - 4 files changed, 1612 deletions(-) delete mode 100644 frappe/public/css/bootstrap-rtl.css delete mode 100644 frappe/public/css/desk-rtl.css delete mode 100644 frappe/public/css/report-rtl.css delete mode 100644 frappe/public/scss/frappe-rtl.bundle.scss diff --git a/frappe/public/css/bootstrap-rtl.css b/frappe/public/css/bootstrap-rtl.css deleted file mode 100644 index 5dfa46c055..0000000000 --- a/frappe/public/css/bootstrap-rtl.css +++ /dev/null @@ -1,1476 +0,0 @@ -/******************************************************************************* - * bootstrap-rtl (version 3.3.4) - * Author: Morteza Ansarinia (http://github.com/morteza) - * Created on: August 13,2015 - * Project: bootstrap-rtl - * Copyright: Unlicensed Public Domain - *******************************************************************************/ - -html { - direction: rtl; -} -body { - direction: rtl; -} -.flip.text-left { - text-align: right; -} -.flip.text-right { - text-align: left; -} -.list-unstyled { - padding-right: 0; - padding-left: initial; -} -.list-inline { - padding-right: 0; - padding-left: initial; - margin-right: -5px; - margin-left: 0; -} -dd { - margin-right: 0; - margin-left: initial; -} -@media (min-width: 768px) { - .dl-horizontal dt { - float: right; - clear: right; - text-align: left; - } - .dl-horizontal dd { - margin-right: 180px; - margin-left: 0; - } -} -blockquote { - border-right: 5px solid #eeeeee; - border-left: 0; -} -.blockquote-reverse, -blockquote.pull-left { - padding-left: 15px; - padding-right: 0; - border-left: 5px solid #eeeeee; - border-right: 0; - text-align: left; -} -.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { - position: relative; - min-height: 1px; - padding-left: 15px; - padding-right: 15px; -} -.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { - float: right; -} -.col-xs-12 { - width: 100%; -} -.col-xs-11 { - width: 91.66666667%; -} -.col-xs-10 { - width: 83.33333333%; -} -.col-xs-9 { - width: 75%; -} -.col-xs-8 { - width: 66.66666667%; -} -.col-xs-7 { - width: 58.33333333%; -} -.col-xs-6 { - width: 50%; -} -.col-xs-5 { - width: 41.66666667%; -} -.col-xs-4 { - width: 33.33333333%; -} -.col-xs-3 { - width: 25%; -} -.col-xs-2 { - width: 16.66666667%; -} -.col-xs-1 { - width: 8.33333333%; -} -.col-xs-pull-12 { - left: 100%; - right: auto; -} -.col-xs-pull-11 { - left: 91.66666667%; - right: auto; -} -.col-xs-pull-10 { - left: 83.33333333%; - right: auto; -} -.col-xs-pull-9 { - left: 75%; - right: auto; -} -.col-xs-pull-8 { - left: 66.66666667%; - right: auto; -} -.col-xs-pull-7 { - left: 58.33333333%; - right: auto; -} -.col-xs-pull-6 { - left: 50%; - right: auto; -} -.col-xs-pull-5 { - left: 41.66666667%; - right: auto; -} -.col-xs-pull-4 { - left: 33.33333333%; - right: auto; -} -.col-xs-pull-3 { - left: 25%; - right: auto; -} -.col-xs-pull-2 { - left: 16.66666667%; - right: auto; -} -.col-xs-pull-1 { - left: 8.33333333%; - right: auto; -} -.col-xs-pull-0 { - left: auto; - right: auto; -} -.col-xs-push-12 { - right: 100%; - left: 0; -} -.col-xs-push-11 { - right: 91.66666667%; - left: 0; -} -.col-xs-push-10 { - right: 83.33333333%; - left: 0; -} -.col-xs-push-9 { - right: 75%; - left: 0; -} -.col-xs-push-8 { - right: 66.66666667%; - left: 0; -} -.col-xs-push-7 { - right: 58.33333333%; - left: 0; -} -.col-xs-push-6 { - right: 50%; - left: 0; -} -.col-xs-push-5 { - right: 41.66666667%; - left: 0; -} -.col-xs-push-4 { - right: 33.33333333%; - left: 0; -} -.col-xs-push-3 { - right: 25%; - left: 0; -} -.col-xs-push-2 { - right: 16.66666667%; - left: 0; -} -.col-xs-push-1 { - right: 8.33333333%; - left: 0; -} -.col-xs-push-0 { - right: auto; - left: 0; -} -.col-xs-offset-12 { - margin-right: 100%; - margin-left: 0; -} -.col-xs-offset-11 { - margin-right: 91.66666667%; - margin-left: 0; -} -.col-xs-offset-10 { - margin-right: 83.33333333%; - margin-left: 0; -} -.col-xs-offset-9 { - margin-right: 75%; - margin-left: 0; -} -.col-xs-offset-8 { - margin-right: 66.66666667%; - margin-left: 0; -} -.col-xs-offset-7 { - margin-right: 58.33333333%; - margin-left: 0; -} -.col-xs-offset-6 { - margin-right: 50%; - margin-left: 0; -} -.col-xs-offset-5 { - margin-right: 41.66666667%; - margin-left: 0; -} -.col-xs-offset-4 { - margin-right: 33.33333333%; - margin-left: 0; -} -.col-xs-offset-3 { - margin-right: 25%; - margin-left: 0; -} -.col-xs-offset-2 { - margin-right: 16.66666667%; - margin-left: 0; -} -.col-xs-offset-1 { - margin-right: 8.33333333%; - margin-left: 0; -} -.col-xs-offset-0 { - margin-right: 0%; - margin-left: 0; -} -@media (min-width: 768px) { - .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { - float: right; - } - .col-sm-12 { - width: 100%; - } - .col-sm-11 { - width: 91.66666667%; - } - .col-sm-10 { - width: 83.33333333%; - } - .col-sm-9 { - width: 75%; - } - .col-sm-8 { - width: 66.66666667%; - } - .col-sm-7 { - width: 58.33333333%; - } - .col-sm-6 { - width: 50%; - } - .col-sm-5 { - width: 41.66666667%; - } - .col-sm-4 { - width: 33.33333333%; - } - .col-sm-3 { - width: 25%; - } - .col-sm-2 { - width: 16.66666667%; - } - .col-sm-1 { - width: 8.33333333%; - } - .col-sm-pull-12 { - left: 100%; - right: auto; - } - .col-sm-pull-11 { - left: 91.66666667%; - right: auto; - } - .col-sm-pull-10 { - left: 83.33333333%; - right: auto; - } - .col-sm-pull-9 { - left: 75%; - right: auto; - } - .col-sm-pull-8 { - left: 66.66666667%; - right: auto; - } - .col-sm-pull-7 { - left: 58.33333333%; - right: auto; - } - .col-sm-pull-6 { - left: 50%; - right: auto; - } - .col-sm-pull-5 { - left: 41.66666667%; - right: auto; - } - .col-sm-pull-4 { - left: 33.33333333%; - right: auto; - } - .col-sm-pull-3 { - left: 25%; - right: auto; - } - .col-sm-pull-2 { - left: 16.66666667%; - right: auto; - } - .col-sm-pull-1 { - left: 8.33333333%; - right: auto; - } - .col-sm-pull-0 { - left: auto; - right: auto; - } - .col-sm-push-12 { - right: 100%; - left: 0; - } - .col-sm-push-11 { - right: 91.66666667%; - left: 0; - } - .col-sm-push-10 { - right: 83.33333333%; - left: 0; - } - .col-sm-push-9 { - right: 75%; - left: 0; - } - .col-sm-push-8 { - right: 66.66666667%; - left: 0; - } - .col-sm-push-7 { - right: 58.33333333%; - left: 0; - } - .col-sm-push-6 { - right: 50%; - left: 0; - } - .col-sm-push-5 { - right: 41.66666667%; - left: 0; - } - .col-sm-push-4 { - right: 33.33333333%; - left: 0; - } - .col-sm-push-3 { - right: 25%; - left: 0; - } - .col-sm-push-2 { - right: 16.66666667%; - left: 0; - } - .col-sm-push-1 { - right: 8.33333333%; - left: 0; - } - .col-sm-push-0 { - right: auto; - left: 0; - } - .col-sm-offset-12 { - margin-right: 100%; - margin-left: 0; - } - .col-sm-offset-11 { - margin-right: 91.66666667%; - margin-left: 0; - } - .col-sm-offset-10 { - margin-right: 83.33333333%; - margin-left: 0; - } - .col-sm-offset-9 { - margin-right: 75%; - margin-left: 0; - } - .col-sm-offset-8 { - margin-right: 66.66666667%; - margin-left: 0; - } - .col-sm-offset-7 { - margin-right: 58.33333333%; - margin-left: 0; - } - .col-sm-offset-6 { - margin-right: 50%; - margin-left: 0; - } - .col-sm-offset-5 { - margin-right: 41.66666667%; - margin-left: 0; - } - .col-sm-offset-4 { - margin-right: 33.33333333%; - margin-left: 0; - } - .col-sm-offset-3 { - margin-right: 25%; - margin-left: 0; - } - .col-sm-offset-2 { - margin-right: 16.66666667%; - margin-left: 0; - } - .col-sm-offset-1 { - margin-right: 8.33333333%; - margin-left: 0; - } - .col-sm-offset-0 { - margin-right: 0%; - margin-left: 0; - } -} -@media (min-width: 992px) { - .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { - float: right; - } - .col-md-12 { - width: 100%; - } - .col-md-11 { - width: 91.66666667%; - } - .col-md-10 { - width: 83.33333333%; - } - .col-md-9 { - width: 75%; - } - .col-md-8 { - width: 66.66666667%; - } - .col-md-7 { - width: 58.33333333%; - } - .col-md-6 { - width: 50%; - } - .col-md-5 { - width: 41.66666667%; - } - .col-md-4 { - width: 33.33333333%; - } - .col-md-3 { - width: 25%; - } - .col-md-2 { - width: 16.66666667%; - } - .col-md-1 { - width: 8.33333333%; - } - .col-md-pull-12 { - left: 100%; - right: auto; - } - .col-md-pull-11 { - left: 91.66666667%; - right: auto; - } - .col-md-pull-10 { - left: 83.33333333%; - right: auto; - } - .col-md-pull-9 { - left: 75%; - right: auto; - } - .col-md-pull-8 { - left: 66.66666667%; - right: auto; - } - .col-md-pull-7 { - left: 58.33333333%; - right: auto; - } - .col-md-pull-6 { - left: 50%; - right: auto; - } - .col-md-pull-5 { - left: 41.66666667%; - right: auto; - } - .col-md-pull-4 { - left: 33.33333333%; - right: auto; - } - .col-md-pull-3 { - left: 25%; - right: auto; - } - .col-md-pull-2 { - left: 16.66666667%; - right: auto; - } - .col-md-pull-1 { - left: 8.33333333%; - right: auto; - } - .col-md-pull-0 { - left: auto; - right: auto; - } - .col-md-push-12 { - right: 100%; - left: 0; - } - .col-md-push-11 { - right: 91.66666667%; - left: 0; - } - .col-md-push-10 { - right: 83.33333333%; - left: 0; - } - .col-md-push-9 { - right: 75%; - left: 0; - } - .col-md-push-8 { - right: 66.66666667%; - left: 0; - } - .col-md-push-7 { - right: 58.33333333%; - left: 0; - } - .col-md-push-6 { - right: 50%; - left: 0; - } - .col-md-push-5 { - right: 41.66666667%; - left: 0; - } - .col-md-push-4 { - right: 33.33333333%; - left: 0; - } - .col-md-push-3 { - right: 25%; - left: 0; - } - .col-md-push-2 { - right: 16.66666667%; - left: 0; - } - .col-md-push-1 { - right: 8.33333333%; - left: 0; - } - .col-md-push-0 { - right: auto; - left: 0; - } - .col-md-offset-12 { - margin-right: 100%; - margin-left: 0; - } - .col-md-offset-11 { - margin-right: 91.66666667%; - margin-left: 0; - } - .col-md-offset-10 { - margin-right: 83.33333333%; - margin-left: 0; - } - .col-md-offset-9 { - margin-right: 75%; - margin-left: 0; - } - .col-md-offset-8 { - margin-right: 66.66666667%; - margin-left: 0; - } - .col-md-offset-7 { - margin-right: 58.33333333%; - margin-left: 0; - } - .col-md-offset-6 { - margin-right: 50%; - margin-left: 0; - } - .col-md-offset-5 { - margin-right: 41.66666667%; - margin-left: 0; - } - .col-md-offset-4 { - margin-right: 33.33333333%; - margin-left: 0; - } - .col-md-offset-3 { - margin-right: 25%; - margin-left: 0; - } - .col-md-offset-2 { - margin-right: 16.66666667%; - margin-left: 0; - } - .col-md-offset-1 { - margin-right: 8.33333333%; - margin-left: 0; - } - .col-md-offset-0 { - margin-right: 0%; - margin-left: 0; - } -} -@media (min-width: 1200px) { - .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { - float: right; - } - .col-lg-12 { - width: 100%; - } - .col-lg-11 { - width: 91.66666667%; - } - .col-lg-10 { - width: 83.33333333%; - } - .col-lg-9 { - width: 75%; - } - .col-lg-8 { - width: 66.66666667%; - } - .col-lg-7 { - width: 58.33333333%; - } - .col-lg-6 { - width: 50%; - } - .col-lg-5 { - width: 41.66666667%; - } - .col-lg-4 { - width: 33.33333333%; - } - .col-lg-3 { - width: 25%; - } - .col-lg-2 { - width: 16.66666667%; - } - .col-lg-1 { - width: 8.33333333%; - } - .col-lg-pull-12 { - left: 100%; - right: auto; - } - .col-lg-pull-11 { - left: 91.66666667%; - right: auto; - } - .col-lg-pull-10 { - left: 83.33333333%; - right: auto; - } - .col-lg-pull-9 { - left: 75%; - right: auto; - } - .col-lg-pull-8 { - left: 66.66666667%; - right: auto; - } - .col-lg-pull-7 { - left: 58.33333333%; - right: auto; - } - .col-lg-pull-6 { - left: 50%; - right: auto; - } - .col-lg-pull-5 { - left: 41.66666667%; - right: auto; - } - .col-lg-pull-4 { - left: 33.33333333%; - right: auto; - } - .col-lg-pull-3 { - left: 25%; - right: auto; - } - .col-lg-pull-2 { - left: 16.66666667%; - right: auto; - } - .col-lg-pull-1 { - left: 8.33333333%; - right: auto; - } - .col-lg-pull-0 { - left: auto; - right: auto; - } - .col-lg-push-12 { - right: 100%; - left: 0; - } - .col-lg-push-11 { - right: 91.66666667%; - left: 0; - } - .col-lg-push-10 { - right: 83.33333333%; - left: 0; - } - .col-lg-push-9 { - right: 75%; - left: 0; - } - .col-lg-push-8 { - right: 66.66666667%; - left: 0; - } - .col-lg-push-7 { - right: 58.33333333%; - left: 0; - } - .col-lg-push-6 { - right: 50%; - left: 0; - } - .col-lg-push-5 { - right: 41.66666667%; - left: 0; - } - .col-lg-push-4 { - right: 33.33333333%; - left: 0; - } - .col-lg-push-3 { - right: 25%; - left: 0; - } - .col-lg-push-2 { - right: 16.66666667%; - left: 0; - } - .col-lg-push-1 { - right: 8.33333333%; - left: 0; - } - .col-lg-push-0 { - right: auto; - left: 0; - } - .col-lg-offset-12 { - margin-right: 100%; - margin-left: 0; - } - .col-lg-offset-11 { - margin-right: 91.66666667%; - margin-left: 0; - } - .col-lg-offset-10 { - margin-right: 83.33333333%; - margin-left: 0; - } - .col-lg-offset-9 { - margin-right: 75%; - margin-left: 0; - } - .col-lg-offset-8 { - margin-right: 66.66666667%; - margin-left: 0; - } - .col-lg-offset-7 { - margin-right: 58.33333333%; - margin-left: 0; - } - .col-lg-offset-6 { - margin-right: 50%; - margin-left: 0; - } - .col-lg-offset-5 { - margin-right: 41.66666667%; - margin-left: 0; - } - .col-lg-offset-4 { - margin-right: 33.33333333%; - margin-left: 0; - } - .col-lg-offset-3 { - margin-right: 25%; - margin-left: 0; - } - .col-lg-offset-2 { - margin-right: 16.66666667%; - margin-left: 0; - } - .col-lg-offset-1 { - margin-right: 8.33333333%; - margin-left: 0; - } - .col-lg-offset-0 { - margin-right: 0%; - margin-left: 0; - } -} -caption { - text-align: right; -} -th { - text-align: right; -} -@media screen and (max-width: 767px) { - .table-responsive > .table-bordered { - border: 0; - } - .table-responsive > .table-bordered > thead > tr > th:first-child, - .table-responsive > .table-bordered > tbody > tr > th:first-child, - .table-responsive > .table-bordered > tfoot > tr > th:first-child, - .table-responsive > .table-bordered > thead > tr > td:first-child, - .table-responsive > .table-bordered > tbody > tr > td:first-child, - .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-right: 0; - border-left: initial; - } - .table-responsive > .table-bordered > thead > tr > th:last-child, - .table-responsive > .table-bordered > tbody > tr > th:last-child, - .table-responsive > .table-bordered > tfoot > tr > th:last-child, - .table-responsive > .table-bordered > thead > tr > td:last-child, - .table-responsive > .table-bordered > tbody > tr > td:last-child, - .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-left: 0; - border-right: initial; - } -} -.radio label, -.checkbox label { - padding-right: 20px; - padding-left: initial; -} -.radio input[type="radio"], -.radio-inline input[type="radio"], -.checkbox input[type="checkbox"], -.checkbox-inline input[type="checkbox"] { - margin-right: -20px; - margin-left: auto; -} -.radio-inline, -.checkbox-inline { - padding-right: 20px; - padding-left: 0; -} -.radio-inline + .radio-inline, -.checkbox-inline + .checkbox-inline { - margin-right: 10px; - margin-left: 0; -} -.has-feedback .form-control { - padding-left: 42.5px; - padding-right: 12px; -} -.form-control-feedback { - left: 0; - right: auto; -} -@media (min-width: 768px) { - .form-inline label { - padding-right: 0; - padding-left: initial; - } - .form-inline .radio input[type="radio"], - .form-inline .checkbox input[type="checkbox"] { - margin-right: 0; - margin-left: auto; - } -} -@media (min-width: 768px) { - .form-horizontal .control-label { - text-align: left; - } -} -.form-horizontal .has-feedback .form-control-feedback { - left: 15px; - right: auto; -} -.caret { - margin-right: 2px; - margin-left: 0; -} -.dropdown-menu { - right: 0; - left: auto; - float: left; - text-align: right; -} -.dropdown-menu.pull-right { - left: 0; - right: auto; - float: right; -} -.dropdown-menu-right { - left: auto; - right: 0; -} -.dropdown-menu-left { - left: 0; - right: auto; -} -@media (min-width: 768px) { - .navbar-right .dropdown-menu { - left: auto; - right: 0; - } - .navbar-right .dropdown-menu-left { - left: 0; - right: auto; - } -} -.btn-group > .btn, -.btn-group-vertical > .btn { - float: right; -} -.btn-group .btn + .btn, -.btn-group .btn + .btn-group, -.btn-group .btn-group + .btn, -.btn-group .btn-group + .btn-group { - margin-right: -1px; - margin-left: 0px; -} -.btn-toolbar { - margin-right: -5px; - margin-left: 0px; -} -.btn-toolbar .btn-group, -.btn-toolbar .input-group { - float: right; -} -.btn-toolbar > .btn, -.btn-toolbar > .btn-group, -.btn-toolbar > .input-group { - margin-right: 5px; - margin-left: 0px; -} -.btn-group > .btn:first-child { - margin-right: 0; -} -.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 0; - border-top-left-radius: 0; -} -.btn-group > .btn:last-child:not(:first-child), -.btn-group > .dropdown-toggle:not(:first-child) { - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 0; - border-top-right-radius: 0; -} -.btn-group > .btn-group { - float: right; -} -.btn-group.btn-group-justified > .btn, -.btn-group.btn-group-justified > .btn-group { - float: none; -} -.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group > .btn-group:first-child > .btn:last-child, -.btn-group > .btn-group:first-child > .dropdown-toggle { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 0; - border-top-left-radius: 0; -} -.btn-group > .btn-group:last-child > .btn:first-child { - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 0; - border-top-right-radius: 0; -} -.btn .caret { - margin-right: 0; -} -.btn-group-vertical > .btn + .btn, -.btn-group-vertical > .btn + .btn-group, -.btn-group-vertical > .btn-group + .btn, -.btn-group-vertical > .btn-group + .btn-group { - margin-top: -1px; - margin-right: 0; -} -.input-group .form-control { - float: right; -} -.input-group .form-control:first-child, -.input-group-addon:first-child, -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group > .btn, -.input-group-btn:first-child > .dropdown-toggle, -.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), -.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { - border-bottom-right-radius: 4px; - border-top-right-radius: 4px; - border-bottom-left-radius: 0; - border-top-left-radius: 0; -} -.input-group-addon:first-child { - border-left: 0px; - border-right: 1px solid; -} -.input-group .form-control:last-child, -.input-group-addon:last-child, -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group > .btn, -.input-group-btn:last-child > .dropdown-toggle, -.input-group-btn:first-child > .btn:not(:first-child), -.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { - border-bottom-left-radius: 4px; - border-top-left-radius: 4px; - border-bottom-right-radius: 0; - border-top-right-radius: 0; -} -.input-group-addon:last-child { - border-left-width: 1px; - border-left-style: solid; - border-right: 0px; -} -.input-group-btn > .btn + .btn { - margin-right: -1px; - margin-left: auto; -} -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group { - margin-left: -1px; - margin-right: auto; -} -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group { - margin-right: -1px; - margin-left: auto; -} -.nav { - padding-right: 0; - padding-left: initial; -} -.nav-tabs > li { - float: right; -} -.nav-tabs > li > a { - margin-left: auto; - margin-right: -2px; - border-radius: 4px 4px 0 0; -} -.nav-pills > li { - float: right; -} -.nav-pills > li > a { - border-radius: 4px; -} -.nav-pills > li + li { - margin-right: 2px; - margin-left: auto; -} -.nav-stacked > li { - float: none; -} -.nav-stacked > li + li { - margin-right: 0; - margin-left: auto; -} -.nav-justified > .dropdown .dropdown-menu { - right: auto; -} -.nav-tabs-justified > li > a { - margin-left: 0; - margin-right: auto; -} -@media (min-width: 768px) { - .nav-tabs-justified > li > a { - border-radius: 4px 4px 0 0; - } -} -@media (min-width: 768px) { - .navbar-header { - float: right; - } -} -.navbar-collapse { - padding-right: 15px; - padding-left: 15px; -} -.navbar-brand { - float: right; -} -@media (min-width: 768px) { - .navbar > .container .navbar-brand, - .navbar > .container-fluid .navbar-brand { - margin-right: -15px; - margin-left: auto; - } -} -.navbar-toggle { - float: left; - margin-left: 15px; - margin-right: auto; -} -@media (max-width: 767px) { - .navbar-nav .open .dropdown-menu > li > a, - .navbar-nav .open .dropdown-menu .dropdown-header { - padding: 5px 25px 5px 15px; - } -} -@media (min-width: 768px) { - .navbar-nav { - float: right; - } - .navbar-nav > li { - float: right; - } -} -@media (min-width: 768px) { - .navbar-left.flip { - float: right !important; - } - .navbar-right:last-child { - margin-left: -15px; - margin-right: auto; - } - .navbar-right.flip { - float: left !important; - margin-left: -15px; - margin-right: auto; - } - .navbar-right .dropdown-menu { - left: 0; - right: auto; - } -} -@media (min-width: 768px) { - .navbar-text { - float: right; - } - .navbar-text.navbar-right:last-child { - margin-left: 0; - margin-right: auto; - } -} -.pagination { - padding-right: 0; -} -.pagination > li > a, -.pagination > li > span { - float: right; - margin-right: -1px; - margin-left: 0px; -} -.pagination > li:first-child > a, -.pagination > li:first-child > span { - margin-left: 0; - border-bottom-right-radius: 4px; - border-top-right-radius: 4px; - border-bottom-left-radius: 0; - border-top-left-radius: 0; -} -.pagination > li:last-child > a, -.pagination > li:last-child > span { - margin-right: -1px; - border-bottom-left-radius: 4px; - border-top-left-radius: 4px; - border-bottom-right-radius: 0; - border-top-right-radius: 0; -} -.pager { - padding-right: 0; - padding-left: initial; -} -.pager .next > a, -.pager .next > span { - float: left; -} -.pager .previous > a, -.pager .previous > span { - float: right; -} -.nav-pills > li > a > .badge { - margin-left: 0px; - margin-right: 3px; -} -.list-group-item > .badge { - float: left; -} -.list-group-item > .badge + .badge { - margin-left: 5px; - margin-right: auto; -} -.alert-dismissable, -.alert-dismissible { - padding-left: 35px; - padding-right: 15px; -} -.alert-dismissable .close, -.alert-dismissible .close { - right: auto; - left: -21px; -} -.progress-bar { - float: right; -} -.media > .pull-left { - margin-right: 10px; -} -.media > .pull-left.flip { - margin-right: 0; - margin-left: 10px; -} -.media > .pull-right { - margin-left: 10px; -} -.media > .pull-right.flip { - margin-left: 0; - margin-right: 10px; -} -.media-right, -.media > .pull-right { - padding-right: 10px; - padding-left: initial; -} -.media-left, -.media > .pull-left { - padding-left: 10px; - padding-right: initial; -} -.media-list { - padding-right: 0; - padding-left: initial; - list-style: none; -} -.list-group { - padding-right: 0; - padding-left: initial; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { - border-top-right-radius: 3px; - border-top-left-radius: 0; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { - border-top-left-radius: 3px; - border-top-right-radius: 0; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { - border-bottom-left-radius: 3px; - border-top-right-radius: 0; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { - border-bottom-right-radius: 3px; - border-top-left-radius: 0; -} -.panel > .table-bordered > thead > tr > th:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, -.panel > .table-bordered > tbody > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, -.panel > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-bordered > thead > tr > td:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, -.panel > .table-bordered > tbody > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, -.panel > .table-bordered > tfoot > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-right: 0; - border-left: none; -} -.panel > .table-bordered > thead > tr > th:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, -.panel > .table-bordered > tbody > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, -.panel > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-bordered > thead > tr > td:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, -.panel > .table-bordered > tbody > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, -.panel > .table-bordered > tfoot > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: none; - border-left: 0; -} -.embed-responsive .embed-responsive-item, -.embed-responsive iframe, -.embed-responsive embed, -.embed-responsive object { - right: 0; - left: auto; -} -.close { - float: left; -} -.modal-footer { - text-align: left; -} -.modal-footer.flip { - text-align: right; -} -.modal-footer .btn + .btn { - margin-left: auto; - margin-right: 5px; -} -.modal-footer .btn-group .btn + .btn { - margin-right: -1px; - margin-left: auto; -} -.modal-footer .btn-block + .btn-block { - margin-right: 0; - margin-left: auto; -} -.popover { - left: auto; - text-align: right; -} -.popover.top > .arrow { - right: 50%; - left: auto; - margin-right: -11px; - margin-left: auto; -} -.popover.top > .arrow:after { - margin-right: -10px; - margin-left: auto; -} -.popover.bottom > .arrow { - right: 50%; - left: auto; - margin-right: -11px; - margin-left: auto; -} -.popover.bottom > .arrow:after { - margin-right: -10px; - margin-left: auto; -} -.carousel-control { - right: 0; - bottom: 0; -} -.carousel-control.left { - right: auto; - left: 0; - background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0%), color-stop(rgba(0, 0, 0, 0.0001) 100%)); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); - background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); -} -.carousel-control.right { - left: auto; - right: 0; - background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0%), color-stop(rgba(0, 0, 0, 0.5) 100%)); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); - background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); -} -.carousel-control .icon-prev, -.carousel-control .glyphicon-chevron-left { - left: 50%; - right: auto; - margin-right: -10px; -} -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-right { - right: 50%; - left: auto; - margin-left: -10px; -} -.carousel-indicators { - right: 50%; - left: 0; - margin-right: -30%; - margin-left: 0; - padding-left: 0; -} -@media screen and (min-width: 768px) { - .carousel-control .glyphicon-chevron-left, - .carousel-control .icon-prev { - margin-left: 0; - margin-right: -15px; - } - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-next { - margin-left: 0; - margin-right: -15px; - } - .carousel-caption { - left: 20%; - right: 20%; - padding-bottom: 30px; - } -} -.pull-right.flip { - float: left !important; -} -.pull-left.flip { - float: right !important; -} -/*# sourceMappingURL=bootstrap-rtl.css.map */ \ No newline at end of file diff --git a/frappe/public/css/desk-rtl.css b/frappe/public/css/desk-rtl.css deleted file mode 100644 index a38f6864ff..0000000000 --- a/frappe/public/css/desk-rtl.css +++ /dev/null @@ -1,118 +0,0 @@ -.navbar .navbar-search-icon{ - right: auto; - left: 24px; -} -.navbar > .container > .navbar-header{ - float: right !important; -} -body[data-sidebar="0"] .navbar-home { - margin-left: auto !important; - margin-right: 15px !important; -} -.navbar-desk ~ ul > li { - float: right !important; -} -body.no-breadcrumbs .navbar .navbar-home:before { - margin-right: auto; - margin-left: 10px !important; - -ms-transform:rotate(180deg); /* Internet Explorer 9 */ - -webkit-transform:rotate(180deg); /* Chrome, Safari, Opera */ - transform:rotate(180deg); /* Standard syntax */ -} -.layout-side-section .overlay-sidebar { - left: auto !important; - right: 0 !important; -} -.layout-side-section .overlay-sidebar.opened { - transform:translateX(0) !important; -} -.navbar-right { - float: left !important; -} -#navbar-breadcrumbs > li > a:before { - margin-right: auto; - margin-left: 10px; - top: 6px; - -ms-transform:rotate(180deg); /* Internet Explorer 9 */ - -webkit-transform:rotate(180deg); /* Chrome, Safari, Opera */ - transform:rotate(180deg); /* Standard syntax */ -} -.case-wrapper { - float: right; -} -.link-btn { - right: auto; - left: 4px; - transform:rotate(180deg); /* Rotate icon*/ -} -.sidebar-menu .badge { - right: auto; - left: 0px; -} -.indicator::before { - margin: 0 0 0 4px; -} -.pull-left { - float: right !important; -} -.grid-row > .row .col:last-child { - margin-right: auto; - margin-left: -10px; -} -.text-right { - text-align: left; -} -.list-row-head .octicon-heart { - margin-right: auto; - margin-left: 13px; -} -.list-id { - margin-left: 7px !important; -} -.avatar-small .avatar-sm { - margin-left: 5px; - margin-right: auto; -} -.list-row-right .list-row-modified { - margin-right: auto; - margin-left: 9px; -} -.list-comment-count { - text-align: right; -} -ul.tree-children { - padding-right: 20px; - padding-left: inherit !important; -} -.balance-area { - float: left !important; -} -.tree.opened::before, .tree-node.opened::before, .tree:last-child::after, .tree-node:last-child::after { - left: inherit !important; - right: 8px; -} -.tree.opened > .tree-children > .tree-node > .tree-link::before, .tree-node.opened > .tree-children > .tree-node > .tree-link::before { - left: inherit !important; - right: -11px; -} -.tree:last-child::after, .tree-node:last-child::after { - right: -13px !important; -} -.tree.opened::before { - left: auto !important; - right: 23px; -} -.results { - direction: ltr; -} -.data-table { - direction: ltr; -} -.section-header { - direction: ltr; -} - -.ql-editor { - direction: rtl; - text-align: right; -} \ No newline at end of file diff --git a/frappe/public/css/report-rtl.css b/frappe/public/css/report-rtl.css deleted file mode 100644 index 03e986c56b..0000000000 --- a/frappe/public/css/report-rtl.css +++ /dev/null @@ -1,15 +0,0 @@ -.grid-report { - direction: ltr; -} - -.page-form .awesomplete > ul { - left: auto; -} - -.chart_area{ - direction: ltr; -} - -.grid-report .show-zero{ - direction: rtl; -} diff --git a/frappe/public/scss/frappe-rtl.bundle.scss b/frappe/public/scss/frappe-rtl.bundle.scss deleted file mode 100644 index bc618270a8..0000000000 --- a/frappe/public/scss/frappe-rtl.bundle.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import "frappe/public/css/bootstrap-rtl.css"; -@import "frappe/public/css/desk-rtl.css"; -@import "frappe/public/css/report-rtl.css"; From 7e6b00fd3c2aa76c1c3c98664ec1e68b95fb6bd9 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sun, 27 Jun 2021 08:54:57 +0530 Subject: [PATCH 303/495] refactor: Remove unused build.json --- frappe/public/build.json | 299 --------------------------------------- 1 file changed, 299 deletions(-) delete mode 100755 frappe/public/build.json diff --git a/frappe/public/build.json b/frappe/public/build.json deleted file mode 100755 index 942871ee9b..0000000000 --- a/frappe/public/build.json +++ /dev/null @@ -1,299 +0,0 @@ -{ - "css/frappe-web-b4.css": "public/scss/website.scss", - "css/frappe-chat-web.css": [ - "public/css/font-awesome.css", - "public/css/octicons/octicons.css", - "public/less/chat.less" - ], - "concat:js/moment-bundle.min.js": [ - "node_modules/moment/min/moment-with-locales.min.js", - "node_modules/moment-timezone/builds/moment-timezone-with-data.min.js" - ], - "js/chat.js": "public/js/frappe/chat.js", - "js/frappe-recorder.min.js": "public/js/frappe/recorder/recorder.js", - "js/checkout.min.js": "public/js/integrations/razorpay.js", - "js/frappe-web.min.js": [ - "public/js/frappe/class.js", - "public/js/frappe/polyfill.js", - "public/js/lib/md5.min.js", - "public/js/frappe/provide.js", - "public/js/frappe/format.js", - "public/js/frappe/utils/number_format.js", - "public/js/frappe/utils/utils.js", - "public/js/frappe/utils/common.js", - "public/js/frappe/ui/messages.js", - "public/js/frappe/translate.js", - "public/js/frappe/utils/pretty_date.js", - "public/js/frappe/microtemplate.js", - "public/js/frappe/query_string.js", - - "public/js/frappe/upload.js", - - "public/js/frappe/model/meta.js", - "public/js/frappe/model/model.js", - "public/js/frappe/model/perm.js", - - "website/js/website.js", - "public/js/frappe/socketio_client.js" - ], - "js/bootstrap-4-web.min.js": "website/js/bootstrap-4.js", - "js/control.min.js": [ - "node_modules/air-datepicker/dist/js/datepicker.min.js", - "node_modules/air-datepicker/dist/js/i18n/datepicker.cs.js", - "node_modules/air-datepicker/dist/js/i18n/datepicker.da.js", - "node_modules/air-datepicker/dist/js/i18n/datepicker.de.js", - "node_modules/air-datepicker/dist/js/i18n/datepicker.en.js", - "node_modules/air-datepicker/dist/js/i18n/datepicker.es.js", - "node_modules/air-datepicker/dist/js/i18n/datepicker.fi.js", - "node_modules/air-datepicker/dist/js/i18n/datepicker.fr.js", - "node_modules/air-datepicker/dist/js/i18n/datepicker.hu.js", - "node_modules/air-datepicker/dist/js/i18n/datepicker.nl.js", - "node_modules/air-datepicker/dist/js/i18n/datepicker.pl.js", - "node_modules/air-datepicker/dist/js/i18n/datepicker.pt-BR.js", - "node_modules/air-datepicker/dist/js/i18n/datepicker.pt.js", - "node_modules/air-datepicker/dist/js/i18n/datepicker.ro.js", - "node_modules/air-datepicker/dist/js/i18n/datepicker.sk.js", - "node_modules/air-datepicker/dist/js/i18n/datepicker.zh.js", - "public/js/frappe/ui/capture.js", - "public/js/frappe/form/controls/control.js" - ], - "js/dialog.min.js": [ - "public/js/frappe/dom.js", - "public/js/frappe/form/formatters.js", - "public/js/frappe/form/layout.js", - "public/js/frappe/ui/field_group.js", - "public/js/frappe/form/link_selector.js", - "public/js/frappe/form/multi_select_dialog.js", - "public/js/frappe/ui/dialog.js" - ], - "css/desk.min.css": [ - "public/js/lib/leaflet/leaflet.css", - "public/js/lib/leaflet/leaflet.draw.css", - "public/js/lib/leaflet/L.Control.Locate.css", - "public/js/lib/leaflet/easy-button.css", - "public/css/font-awesome.css", - "public/css/octicons/octicons.css", - "public/less/desk.less", - "public/less/module.less", - "public/less/mobile.less", - "public/less/controls.less", - "public/less/chat.less", - "public/css/fonts/inter/inter.css", - "node_modules/frappe-charts/dist/frappe-charts.min.css", - "node_modules/plyr/dist/plyr.css", - "public/scss/desk.scss" - ], - "css/frappe-rtl.css": [ - "public/css/bootstrap-rtl.css", - "public/css/desk-rtl.css", - "public/css/report-rtl.css" - ], - "css/printview.css": [ - "public/css/bootstrap.css", - "public/scss/print.scss" - ], - "concat:js/libs.min.js": [ - "public/js/lib/Sortable.min.js", - "public/js/lib/jquery/jquery.hotkeys.js", - "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js", - "node_modules/vue/dist/vue.min.js", - "node_modules/moment/min/moment-with-locales.min.js", - "node_modules/moment-timezone/builds/moment-timezone-with-data.min.js", - "node_modules/socket.io-client/dist/socket.io.slim.js", - "node_modules/localforage/dist/localforage.min.js", - "public/js/lib/jSignature.min.js", - "public/js/lib/leaflet/leaflet.js", - "public/js/lib/leaflet/leaflet.draw.js", - "public/js/lib/leaflet/L.Control.Locate.js", - "public/js/lib/leaflet/easy-button.js" - ], - "js/desk.min.js": [ - "public/js/frappe/translate.js", - "public/js/frappe/class.js", - "public/js/frappe/polyfill.js", - "public/js/frappe/provide.js", - "public/js/frappe/assets.js", - "public/js/frappe/format.js", - "public/js/frappe/form/formatters.js", - "public/js/frappe/dom.js", - "public/js/frappe/ui/messages.js", - "public/js/frappe/ui/keyboard.js", - "public/js/frappe/ui/colors.js", - "public/js/frappe/ui/sidebar.js", - "public/js/frappe/ui/link_preview.js", - - "public/js/frappe/request.js", - "public/js/frappe/socketio_client.js", - "public/js/frappe/utils/utils.js", - "public/js/frappe/event_emitter.js", - "public/js/frappe/router.js", - "public/js/frappe/router_history.js", - "public/js/frappe/defaults.js", - "public/js/frappe/roles_editor.js", - "public/js/frappe/module_editor.js", - "public/js/frappe/microtemplate.js", - - "public/js/frappe/ui/page.html", - "public/js/frappe/ui/page.js", - "public/js/frappe/ui/slides.js", - "public/js/frappe/ui/onboarding_dialog.js", - "public/js/frappe/ui/find.js", - "public/js/frappe/ui/iconbar.js", - "public/js/frappe/form/layout.js", - "public/js/frappe/ui/field_group.js", - "public/js/frappe/form/link_selector.js", - "public/js/frappe/form/multi_select_dialog.js", - "public/js/frappe/ui/dialog.js", - "public/js/frappe/ui/capture.js", - "public/js/frappe/ui/app_icon.js", - "public/js/frappe/ui/theme_switcher.js", - - "public/js/frappe/model/model.js", - "public/js/frappe/db.js", - "public/js/frappe/model/meta.js", - "public/js/frappe/model/sync.js", - "public/js/frappe/model/create_new.js", - "public/js/frappe/model/perm.js", - "public/js/frappe/model/workflow.js", - "public/js/frappe/model/user_settings.js", - - "public/js/lib/md5.min.js", - "public/js/frappe/utils/user.js", - "public/js/frappe/utils/common.js", - "public/js/frappe/utils/urllib.js", - "public/js/frappe/utils/pretty_date.js", - "public/js/frappe/utils/tools.js", - "public/js/frappe/utils/datetime.js", - "public/js/frappe/utils/number_format.js", - "public/js/frappe/utils/help.js", - "public/js/frappe/utils/help_links.js", - "public/js/frappe/utils/address_and_contact.js", - "public/js/frappe/utils/preview_email.js", - "public/js/frappe/utils/file_manager.js", - - "public/js/frappe/upload.js", - "public/js/frappe/ui/tree.js", - - "public/js/frappe/views/container.js", - "public/js/frappe/views/breadcrumbs.js", - "public/js/frappe/views/factory.js", - "public/js/frappe/views/pageview.js", - - "public/js/frappe/ui/toolbar/awesome_bar.js", - "public/js/frappe/ui/toolbar/energy_points_notifications.js", - "public/js/frappe/ui/notifications/notifications.js", - "public/js/frappe/ui/toolbar/search.js", - "public/js/frappe/ui/toolbar/tag_utils.js", - "public/js/frappe/ui/toolbar/search.html", - "public/js/frappe/ui/toolbar/search_utils.js", - "public/js/frappe/ui/toolbar/about.js", - "public/js/frappe/ui/toolbar/navbar.html", - "public/js/frappe/ui/toolbar/toolbar.js", - "public/js/frappe/ui/toolbar/notifications.js", - "public/js/frappe/views/communication.js", - "public/js/frappe/views/translation_manager.js", - "public/js/frappe/views/workspace/workspace.js", - - "public/js/frappe/widgets/widget_group.js", - - "public/js/frappe/ui/sort_selector.html", - "public/js/frappe/ui/sort_selector.js", - - "public/js/frappe/change_log.html", - "public/js/frappe/ui/workspace_loading_skeleton.html", - "public/js/frappe/desk.js", - "public/js/frappe/query_string.js", - - "public/js/frappe/ui/comment.js", - - "public/js/frappe/chat.js", - "public/js/frappe/utils/energy_point_utils.js", - "public/js/frappe/utils/dashboard_utils.js", - "public/js/frappe/ui/chart.js", - "public/js/frappe/ui/datatable.js", - "public/js/frappe/ui/driver.js", - "public/js/frappe/ui/plyr.js", - "public/js/frappe/barcode_scanner/index.js" - ], - "js/form.min.js": [ - "public/js/frappe/form/templates/**.html", - "public/js/frappe/form/controls/control.js", - "public/js/frappe/views/formview.js", - "public/js/frappe/form/form.js", - "public/js/frappe/meta_tag.js" - ], - "js/list.min.js": [ - "public/js/frappe/ui/listing.html", - - "public/js/frappe/model/indicator.js", - "public/js/frappe/ui/filters/filter.js", - "public/js/frappe/ui/filters/filter_list.js", - "public/js/frappe/ui/filters/field_select.js", - "public/js/frappe/ui/filters/edit_filter.html", - "public/js/frappe/ui/tags.js", - "public/js/frappe/ui/tag_editor.js", - "public/js/frappe/ui/like.js", - "public/js/frappe/ui/liked_by.html", - "public/html/print_template.html", - - "public/js/frappe/list/base_list.js", - "public/js/frappe/list/list_view.js", - "public/js/frappe/list/list_factory.js", - - "public/js/frappe/list/list_view_select.js", - "public/js/frappe/list/list_sidebar.js", - "public/js/frappe/list/list_sidebar.html", - "public/js/frappe/list/list_sidebar_stat.html", - "public/js/frappe/list/list_sidebar_group_by.js", - "public/js/frappe/list/list_view_permission_restrictions.html", - - "public/js/frappe/views/gantt/gantt_view.js", - "public/js/frappe/views/calendar/calendar.js", - "public/js/frappe/views/dashboard/dashboard_view.js", - "public/js/frappe/views/image/image_view.js", - "public/js/frappe/views/map/map_view.js", - "public/js/frappe/views/kanban/kanban_view.js", - "public/js/frappe/views/inbox/inbox_view.js", - "public/js/frappe/views/file/file_view.js", - - "public/js/frappe/views/treeview.js", - "public/js/frappe/views/interaction.js", - - "public/js/frappe/views/image/image_view_item_row.html", - "public/js/frappe/views/image/photoswipe_dom.html", - - "public/js/frappe/views/kanban/kanban_board.html", - "public/js/frappe/views/kanban/kanban_column.html", - "public/js/frappe/views/kanban/kanban_card.html" - ], - "css/report.min.css": [ - "node_modules/frappe-datatable/dist/frappe-datatable.css", - "public/css/tree_grid.css" - ], - "js/report.min.js": [ - "public/js/lib/clusterize.min.js", - "public/js/frappe/views/reports/report_factory.js", - "public/js/frappe/views/reports/report_view.js", - "public/js/frappe/views/reports/query_report.js", - "public/js/frappe/views/reports/print_grid.html", - "public/js/frappe/views/reports/print_tree.html", - "public/js/frappe/ui/group_by/group_by.html", - "public/js/frappe/ui/group_by/group_by.js", - "public/js/frappe/views/reports/report_utils.js" - ], - "js/web_form.min.js": [ - "public/js/frappe/utils/datetime.js", - "public/js/frappe/web_form/webform_script.js" - ], - "css/web_form.css": [ - "website/css/web_form.css", - "public/css/octicons/octicons.css", - "public/scss/controls.scss", - "node_modules/frappe-datatable/dist/frappe-datatable.css" - ], - "css/email.css": "public/scss/email.scss", - "js/barcode_scanner.min.js": "public/js/frappe/barcode_scanner/quagga.js", - "js/user_profile_controller.min.js": "desk/page/user_profile/user_profile_controller.js", - "css/login.css": "public/scss/login.scss", - "js/data_import_tools.min.js": "public/js/frappe/data_import/index.js" -} From 9c91cc0e5a51cbefb52f865fba8b1021f3a33795 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sun, 27 Jun 2021 09:05:48 +0530 Subject: [PATCH 304/495] fix: Editor link popover style - "overflow: hidden;" set in ql-container used to cutoff and hide some parts of link popover of ql editor. --- frappe/public/scss/common/quill.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/public/scss/common/quill.scss b/frappe/public/scss/common/quill.scss index 12706d6b7f..a7a690543b 100644 --- a/frappe/public/scss/common/quill.scss +++ b/frappe/public/scss/common/quill.scss @@ -35,13 +35,14 @@ .ql-container.ql-snow { border-bottom-left-radius: var(--border-radius); border-bottom-right-radius: var(--border-radius); - overflow: hidden; } .ql-snow { .ql-editor { min-height: 400px; max-height: 600px; + border-bottom-left-radius: var(--border-radius); + border-bottom-right-radius: var(--border-radius); } .ql-stroke { stroke: var(--icon-stroke); From 894e1ec7d06a7739998e542889cfd7c50b1b6317 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sun, 27 Jun 2021 10:39:28 +0530 Subject: [PATCH 305/495] fix: Datepicker's dark mode style --- frappe/public/scss/common/datepicker.scss | 27 +++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/frappe/public/scss/common/datepicker.scss b/frappe/public/scss/common/datepicker.scss index 93bdfcc03d..b21eb58caf 100644 --- a/frappe/public/scss/common/datepicker.scss +++ b/frappe/public/scss/common/datepicker.scss @@ -15,6 +15,10 @@ &--time-current-hours, &--time-current-minutes, &--time-current-seconds { font-family: inherit; + &:after { + color: var(--text-color); + background-color: var(--fg-hover-color); + } } &--day-name { @@ -47,10 +51,13 @@ background: fade(#0089FF, 10%); } + &.-focus- { + background-color: var(--fg-hover-color); + } + &.-selected-.-focus- { background: fade(#0089FF, 90%); } - } &--time, &--buttons { @@ -67,8 +74,20 @@ &--time-row:first-child { margin: 0; } + + &--pointer { + background: var(--fg-color); + border-top-right-radius: 2px; + border: 1px var(--border-color); + border-style: solid solid hidden hidden; + } + + &--button { + color: var(--brand-color); + &:hover { + color: var(--text-color); + background-color: var(--fg-hover-color); + } + } } -.datepicker--button { - color: var(--brand-color); -} From 1f5bcd42ab81fd2b2f2da8c79edb139b4ce86478 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 28 Jun 2021 11:21:52 +0530 Subject: [PATCH 306/495] fix: incorrect depends on evalutation with parent variables --- frappe/public/js/frappe/form/layout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js index 282655b589..045e5dc1b3 100644 --- a/frappe/public/js/frappe/form/layout.js +++ b/frappe/public/js/frappe/form/layout.js @@ -543,7 +543,7 @@ frappe.ui.form.Layout = class Layout { } else if (expression.substr(0, 5)=='eval:') { try { - out = frappe.utils.eval(expression.substr(5), { doc }); + out = frappe.utils.eval(expression.substr(5), { doc, parent }); if (parent && parent.istable && expression.includes('is_submittable')) { out = true; } From 30f92070910e983238a7661c133f1891d4413cf4 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 28 Jun 2021 12:03:00 +0530 Subject: [PATCH 307/495] feat: Select css-rtl folder if language is rtl --- frappe/public/js/frappe/assets.js | 8 ++++++-- frappe/utils/jinja_globals.py | 14 ++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/frappe/public/js/frappe/assets.js b/frappe/public/js/frappe/assets.js index 3fca8640f3..1f2659992f 100644 --- a/frappe/public/js/frappe/assets.js +++ b/frappe/public/js/frappe/assets.js @@ -168,9 +168,13 @@ frappe.assets = { } }, - bundled_asset(path) { + bundled_asset(path, is_rtl=null) { if (!path.startsWith('/assets') && path.includes('.bundle.')) { - return frappe.boot.assets_json[path] || path; + path = frappe.boot.assets_json[path] || path; + if (path.endsWith('.css') && is_rtl) { + path = path.replace('/css/', '/css-rtl/'); + } + return path; } return path; } diff --git a/frappe/utils/jinja_globals.py b/frappe/utils/jinja_globals.py index 30a0e46232..dd5ade727b 100644 --- a/frappe/utils/jinja_globals.py +++ b/frappe/utils/jinja_globals.py @@ -75,24 +75,26 @@ def include_script(path): def include_style(path, rtl=None): path = bundled_asset(path) - if rtl is None: - rtl = is_rtl() - if rtl: + if is_rtl(rtl): path = path.replace('/css/', '/css-rtl/') return f'' -def bundled_asset(path): +def bundled_asset(path, rtl=None): from frappe.utils import get_assets_json from frappe.website.utils import abs_url if ".bundle." in path and not path.startswith("/assets"): bundled_assets = get_assets_json() path = bundled_assets.get(path) or path + if path.endswith('.css') and is_rtl(rtl): + path = path.replace('/css/', '/css-rtl/') return abs_url(path) -def is_rtl(): +def is_rtl(rtl=None): from frappe import local - return local.lang in ["ar", "he", "fa", "ps"] \ No newline at end of file + if rtl is None: + return local.lang in ["ar", "he", "fa", "ps"] + return rtl \ No newline at end of file From 6960a192082ecdaff0d8b33ec8240c5844c8f89b Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 28 Jun 2021 12:03:31 +0530 Subject: [PATCH 308/495] fix: RTL for print views --- frappe/printing/page/print/print.js | 11 +++-------- frappe/public/html/print_template.html | 2 +- frappe/public/js/frappe/views/reports/query_report.js | 4 +++- frappe/www/printview.html | 2 +- frappe/www/printview.py | 4 +++- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/frappe/printing/page/print/print.js b/frappe/printing/page/print/print.js index 233bbe0ce7..fc59065c64 100644 --- a/frappe/printing/page/print/print.js +++ b/frappe/printing/page/print/print.js @@ -409,19 +409,14 @@ frappe.ui.form.PrintView = class { setup_print_format_dom(out, $print_format) { this.print_wrapper.find('.print-format-skeleton').remove(); let base_url = frappe.urllib.get_base_url(); - let print_css = frappe.assets.bundled_asset('print.bundle.css'); + let print_css = frappe.assets.bundled_asset('print.bundle.css', frappe.utils.is_rtl(this.lang_code)); + this.$print_format_body.find('html').attr('dir', frappe.utils.is_rtl(this.lang_code) ? 'rtl': 'ltr'); + this.$print_format_body.find('html').attr('lang', this.lang_code); this.$print_format_body.find('head').html( ` ` ); - if (frappe.utils.is_rtl(this.lang_code)) { - let rtl_css = frappe.assets.bundled_asset('frappe-rtl.bundle.css'); - this.$print_format_body.find('head').append( - `` - ); - } - this.$print_format_body.find('body').html( `` ); diff --git a/frappe/public/html/print_template.html b/frappe/public/html/print_template.html index 721bec7fa7..39cc6b4274 100644 --- a/frappe/public/html/print_template.html +++ b/frappe/public/html/print_template.html @@ -1,5 +1,5 @@ - + diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 208d5b2f67..a4b3564e37 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -1264,7 +1264,9 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { print_css: print_css, print_settings: print_settings, landscape: landscape, - columns: columns + columns: columns, + lang: frappe.boot.lang, + layout_direction: frappe.utils.is_rtl() ? "rtl" : "ltr" }); frappe.render_pdf(html, print_settings); diff --git a/frappe/www/printview.html b/frappe/www/printview.html index 05e85730dd..3f8d4201fb 100644 --- a/frappe/www/printview.html +++ b/frappe/www/printview.html @@ -1,5 +1,5 @@ - + diff --git a/frappe/www/printview.py b/frappe/www/printview.py index 226d5b048a..cdf47790eb 100644 --- a/frappe/www/printview.py +++ b/frappe/www/printview.py @@ -7,6 +7,7 @@ from frappe import _ from frappe.modules import get_doc_path from frappe.core.doctype.access_log.access_log import make_access_log from frappe.utils import cint, sanitize_html, strip_html +from frappe.utils.jinja_globals import is_rtl no_cache = 1 @@ -44,7 +45,8 @@ def get_context(context): "css": get_print_style(frappe.form_dict.style, print_format), "comment": frappe.session.user, "title": doc.get(meta.title_field) if meta.title_field else doc.name, - "has_rtl": True if frappe.local.lang in ["ar", "he", "fa", "ps"] else False + "lang": frappe.local.lang, + "layout_direction": "rtl" if is_rtl() else "ltr" } def get_print_format_doc(print_format_name, meta): From 31d8436979e49119011fa7fe891f4689a586e267 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 28 Jun 2021 12:04:57 +0530 Subject: [PATCH 309/495] refactor: Rename dir to layout_direction - For readability --- frappe/www/app.html | 2 +- frappe/www/app.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/www/app.html b/frappe/www/app.html index 8704ed2671..68a6dc8e86 100644 --- a/frappe/www/app.html +++ b/frappe/www/app.html @@ -1,5 +1,5 @@ - + diff --git a/frappe/www/app.py b/frappe/www/app.py index 55992b5a55..acf6dde000 100644 --- a/frappe/www/app.py +++ b/frappe/www/app.py @@ -41,7 +41,7 @@ def get_context(context): "build_version": frappe.utils.get_build_version(), "include_js": hooks["app_include_js"], "include_css": hooks["app_include_css"], - "dir": "rtl" if is_rtl() else "ltr", + "layout_direction": "rtl" if is_rtl() else "ltr", "lang": frappe.local.lang, "sounds": hooks["sounds"], "boot": boot if context.get("for_mobile") else boot_json, From d9962370dc6cd6da78b9a0e630ade67939dcd73e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 28 Jun 2021 12:06:26 +0530 Subject: [PATCH 310/495] fix: Add language to print_template --- frappe/public/html/print_template.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/html/print_template.html b/frappe/public/html/print_template.html index 39cc6b4274..f63a20377f 100644 --- a/frappe/public/html/print_template.html +++ b/frappe/public/html/print_template.html @@ -1,5 +1,5 @@ - + From 44536fac4976ae81dc98b413f8ba958e035bc627 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 28 Jun 2021 15:53:34 +0530 Subject: [PATCH 311/495] fix: Prioritse TemplatePage over ListPage --- frappe/website/path_resolver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/path_resolver.py b/frappe/website/path_resolver.py index 5e1a99be91..0ed097416e 100644 --- a/frappe/website/path_resolver.py +++ b/frappe/website/path_resolver.py @@ -37,7 +37,7 @@ class PathResolver(): endpoint = resolve_path(self.path) custom_renderers = self.get_custom_page_renderers() - renderers = custom_renderers + [StaticPage, WebFormPage, ListPage, DocumentPage, TemplatePage, PrintPage, NotFoundPage] + renderers = custom_renderers + [StaticPage, WebFormPage, DocumentPage, TemplatePage, ListPage, PrintPage, NotFoundPage] for renderer in renderers: renderer_instance = renderer(endpoint, 200) From ae9aad4d760289549dcd47fb0faa7a5f4cc134c6 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 29 Jun 2021 13:30:20 +0530 Subject: [PATCH 312/495] fix: form tour loading after route change --- frappe/desk/doctype/form_tour/form_tour.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/desk/doctype/form_tour/form_tour.js b/frappe/desk/doctype/form_tour/form_tour.js index efb853cfa5..8d70dcd3dc 100644 --- a/frappe/desk/doctype/form_tour/form_tour.js +++ b/frappe/desk/doctype/form_tour/form_tour.js @@ -15,14 +15,14 @@ frappe.ui.form.on('Form Tour', { frm.add_custom_button(__('Show Tour'), async () => { const issingle = await check_if_single(frm.doc.reference_doctype); + let route_changed = null; if (issingle) { - frappe.set_route('Form', frm.doc.reference_doctype); + route_changed = frappe.set_route('Form', frm.doc.reference_doctype); } else { - const new_name = 'new-' + frappe.scrub(frm.doc.reference_doctype) + '-1'; - frappe.set_route('Form', frm.doc.reference_doctype, new_name); + route_changed = frappe.set_route('Form', frm.doc.reference_doctype, 'new'); } - frappe.utils.sleep(500).then(() => { + route_changed.then(() => { const tour_name = frm.doc.name; cur_frm.tour .init({ tour_name }) From 3acc0a24465cd86be58e7718a0da307e8581fb59 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 29 Jun 2021 13:30:51 +0530 Subject: [PATCH 313/495] feat: get standard form tour for onboarding step --- frappe/public/js/frappe/form/form_tour.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/form_tour.js b/frappe/public/js/frappe/form/form_tour.js index 7f7ec9ce4f..5318973a66 100644 --- a/frappe/public/js/frappe/form/form_tour.js +++ b/frappe/public/js/frappe/form/form_tour.js @@ -37,7 +37,12 @@ frappe.ui.form.FormTour = class FormTour { if (tour_name) { this.tour = await frappe.db.get_doc('Form Tour', tour_name); } else { - this.tour = { steps: frappe.tour[this.frm.doctype] }; + const doctype_tour_exists = await frappe.db.exists('Form Tour', this.frm.doctype); + if (doctype_tour_exists) { + this.tour = await frappe.db.get_doc('Form Tour', this.frm.doctype) + } else { + this.tour = { steps: frappe.tour[this.frm.doctype] }; + } } if (on_finish) this.on_finish = on_finish; @@ -232,7 +237,7 @@ frappe.ui.form.FormTour = class FormTour { } add_step_to_save() { - const page_id = `#page-${this.frm.doctype}`; + const page_id = `[id="page-${this.frm.doctype}"]`; const $save_btn = `${page_id} .standard-actions .primary-action`; const save_step = { element: $save_btn, From 99594b0fd87d802e5b3b93d2e94b1a0b91153a42 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 29 Jun 2021 16:02:27 +0200 Subject: [PATCH 314/495] test: change test cases after interface change --- .../doctype/google_settings/test_google_settings.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/frappe/integrations/doctype/google_settings/test_google_settings.py b/frappe/integrations/doctype/google_settings/test_google_settings.py index b26c52775b..4227e90260 100644 --- a/frappe/integrations/doctype/google_settings/test_google_settings.py +++ b/frappe/integrations/doctype/google_settings/test_google_settings.py @@ -22,11 +22,8 @@ class TestGoogleSettings(unittest.TestCase): frappe.set_user('Guest') frappe.db.set_value('Google Settings', None, 'enable', 1) frappe.db.set_value('Google Settings', None, 'google_drive_picker_enabled', 1) - settings = get_file_picker_settings() - self.assertIn('enabled', settings) - self.assertEqual(False, settings.get('enabled', True)) - self.assertEqual(1, len(settings)) + self.assertRaises(frappe.PermissionError, get_file_picker_settings) frappe.set_user('Adminstrator') @@ -36,9 +33,7 @@ class TestGoogleSettings(unittest.TestCase): frappe.db.set_value('Google Settings', None, 'google_drive_picker_enabled', 0) settings = get_file_picker_settings() - self.assertIn('enabled', settings) - self.assertEqual(False, settings.get('enabled', True)) - self.assertEqual(1, len(settings)) + self.assertEqual(settings, {}) def test_google_disabled(self): """Google Drive Picker should be disabled if Google integration is not enabled.""" @@ -46,9 +41,7 @@ class TestGoogleSettings(unittest.TestCase): frappe.db.set_value('Google Settings', None, 'google_drive_picker_enabled', 1) settings = get_file_picker_settings() - self.assertIn('enabled', settings) - self.assertEqual(False, settings.get('enabled', True)) - self.assertEqual(1, len(settings)) + self.assertEqual(settings, {}) def test_picker_enabled(self): """If picker is enabled, get_file_picker_settings should return the credentials.""" From b02d2715be971cfccf4ebaa51a2bc33821d520c5 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 29 Jun 2021 18:42:15 +0200 Subject: [PATCH 315/495] test: remove test_picker_as_guest Guest access was disabled. Testing this would be equivalent to testing frappe.whitelist. --- .../doctype/google_settings/test_google_settings.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/frappe/integrations/doctype/google_settings/test_google_settings.py b/frappe/integrations/doctype/google_settings/test_google_settings.py index 4227e90260..f21df7ed70 100644 --- a/frappe/integrations/doctype/google_settings/test_google_settings.py +++ b/frappe/integrations/doctype/google_settings/test_google_settings.py @@ -17,16 +17,6 @@ class TestGoogleSettings(unittest.TestCase): settings.api_key = 'test_api_key' settings.save() - def test_picker_as_guest(self): - """Google Drive Picker should be disbled for Guest.""" - frappe.set_user('Guest') - frappe.db.set_value('Google Settings', None, 'enable', 1) - frappe.db.set_value('Google Settings', None, 'google_drive_picker_enabled', 1) - - self.assertRaises(frappe.PermissionError, get_file_picker_settings) - - frappe.set_user('Adminstrator') - def test_picker_disabled(self): """Google Drive Picker should be disabled if it is not enabled in Google Settings.""" frappe.db.set_value('Google Settings', None, 'enable', 1) From d9044815f4034a51d27e4949ffcd153e253cc882 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 29 Jun 2021 18:42:43 +0200 Subject: [PATCH 316/495] style: use double quotes --- .../google_settings/test_google_settings.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/frappe/integrations/doctype/google_settings/test_google_settings.py b/frappe/integrations/doctype/google_settings/test_google_settings.py index f21df7ed70..32d43a323b 100644 --- a/frappe/integrations/doctype/google_settings/test_google_settings.py +++ b/frappe/integrations/doctype/google_settings/test_google_settings.py @@ -11,35 +11,35 @@ from .google_settings import get_file_picker_settings class TestGoogleSettings(unittest.TestCase): def setUp(self): - settings = frappe.get_single('Google Settings') - settings.client_id = 'test_client_id' - settings.app_id = 'test_app_id' - settings.api_key = 'test_api_key' + settings = frappe.get_single("Google Settings") + settings.client_id = "test_client_id" + settings.app_id = "test_app_id" + settings.api_key = "test_api_key" settings.save() def test_picker_disabled(self): """Google Drive Picker should be disabled if it is not enabled in Google Settings.""" - frappe.db.set_value('Google Settings', None, 'enable', 1) - frappe.db.set_value('Google Settings', None, 'google_drive_picker_enabled', 0) + frappe.db.set_value("Google Settings", None, "enable", 1) + frappe.db.set_value("Google Settings", None, "google_drive_picker_enabled", 0) settings = get_file_picker_settings() self.assertEqual(settings, {}) def test_google_disabled(self): """Google Drive Picker should be disabled if Google integration is not enabled.""" - frappe.db.set_value('Google Settings', None, 'enable', 0) - frappe.db.set_value('Google Settings', None, 'google_drive_picker_enabled', 1) + frappe.db.set_value("Google Settings", None, "enable", 0) + frappe.db.set_value("Google Settings", None, "google_drive_picker_enabled", 1) settings = get_file_picker_settings() self.assertEqual(settings, {}) def test_picker_enabled(self): """If picker is enabled, get_file_picker_settings should return the credentials.""" - frappe.db.set_value('Google Settings', None, 'enable', 1) - frappe.db.set_value('Google Settings', None, 'google_drive_picker_enabled', 1) + frappe.db.set_value("Google Settings", None, "enable", 1) + frappe.db.set_value("Google Settings", None, "google_drive_picker_enabled", 1) settings = get_file_picker_settings() - self.assertEqual(True, settings.get('enabled', False)) - self.assertEqual('test_client_id', settings.get('clientId', '')) - self.assertEqual('test_app_id', settings.get('appId', '')) - self.assertEqual('test_api_key', settings.get('developerKey', '')) + self.assertEqual(True, settings.get("enabled", False)) + self.assertEqual("test_client_id", settings.get("clientId", "")) + self.assertEqual("test_app_id", settings.get("appId", "")) + self.assertEqual("test_api_key", settings.get("developerKey", "")) From 5592bf6b1c210336be13b8281ee71aaa895d0ba8 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 29 Jun 2021 18:43:20 +0200 Subject: [PATCH 317/495] feat: remove "All" access from Google Settings --- .../doctype/google_settings/google_settings.json | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/frappe/integrations/doctype/google_settings/google_settings.json b/frappe/integrations/doctype/google_settings/google_settings.json index 152d191ae3..6f25fa4bf6 100644 --- a/frappe/integrations/doctype/google_settings/google_settings.json +++ b/frappe/integrations/doctype/google_settings/google_settings.json @@ -76,7 +76,7 @@ ], "issingle": 1, "links": [], - "modified": "2021-06-18 16:57:10.502174", + "modified": "2021-06-29 18:26:07.094851", "modified_by": "Administrator", "module": "Integrations", "name": "Google Settings", @@ -91,16 +91,6 @@ "role": "System Manager", "share": 1, "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "role": "All", - "share": 1, - "write": 1 } ], "quick_entry": 1, From 117a729c8da47a3ca90d73d775f06eb2ccb13671 Mon Sep 17 00:00:00 2001 From: gavin Date: Wed, 30 Jun 2021 10:51:44 +0530 Subject: [PATCH 318/495] refactor(minor): login-content template Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/templates/includes/login/login.js | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/frappe/templates/includes/login/login.js b/frappe/templates/includes/login/login.js index 3bcd31e93b..4400578862 100644 --- a/frappe/templates/includes/login/login.js +++ b/frappe/templates/includes/login/login.js @@ -292,15 +292,18 @@ var verify_token = function (event) { var request_otp = function (r) { $('.login-content').empty(); - $('.login-content:visible').append($('
    ').attr({ 'id': 'twofactor_div' }).html( - '
    \ -
    \ - {{ _("Verification") }}\ -
    \ -
    \ - \ - \ -
    ')); + $('.login-content:visible').append( + `
    +
    +
    + {{ _("Verification") }} +
    +
    + + +
    +
    ` + ); // add event handler for submit button verify_token(); } From 36a3504b29db2ef7daaf8f4c331b76d9360a025c Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 29 Jun 2021 18:11:13 +0530 Subject: [PATCH 319/495] fix: Premature commit via notification's `send_an_email` --- frappe/core/doctype/communication/email.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index d35c118550..7ffbe6781d 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -85,8 +85,6 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = if attachments: add_attachments(comm.name, attachments) - frappe.db.commit() - if cint(send_email): if not comm.get_outgoing_email_account(): frappe.throw(msg=OUTGOING_EMAIL_ACCOUNT_MISSING, exc=frappe.OutgoingEmailError) From debc8013057ca0e51a031dbc80aa3d65b21d8fb7 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 30 Jun 2021 14:21:22 +0530 Subject: [PATCH 320/495] feat: submit on completion --- frappe/desk/doctype/form_tour/form_tour.json | 10 ++++- frappe/public/js/frappe/form/form_tour.js | 45 ++++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/frappe/desk/doctype/form_tour/form_tour.json b/frappe/desk/doctype/form_tour/form_tour.json index e4ea528fcc..44f39766b3 100644 --- a/frappe/desk/doctype/form_tour/form_tour.json +++ b/frappe/desk/doctype/form_tour/form_tour.json @@ -11,6 +11,7 @@ "module", "is_standard", "save_on_complete", + "submit_on_complete", "section_break_3", "steps" ], @@ -62,11 +63,18 @@ "label": "Module", "options": "Module Def", "read_only": 1 + }, + { + "default": "0", + "depends_on": "save_on_complete", + "fieldname": "submit_on_complete", + "fieldtype": "Check", + "label": "Submit on Completion" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-06-06 20:32:54.068774", + "modified": "2021-06-29 15:23:26.893068", "modified_by": "Administrator", "module": "Desk", "name": "Form Tour", diff --git a/frappe/public/js/frappe/form/form_tour.js b/frappe/public/js/frappe/form/form_tour.js index 5318973a66..5f4920f415 100644 --- a/frappe/public/js/frappe/form/form_tour.js +++ b/frappe/public/js/frappe/form/form_tour.js @@ -17,8 +17,9 @@ frappe.ui.form.FormTour = class FormTour { prevBtnText: 'Previous', opacity: 0.25, onHighlighted: (step) => { - // if last step is to save, then attach a listener to save button - if (step.options.is_save_step) { + // if last step is to save, or if is submit step + // then attach a listener on highlighted elem to reset the driver + if ((step.options.is_save_step && !this.driver.hasNextStep())|| step.options.is_submit_step) { $(step.options.element).one('click', () => this.driver.reset()); } @@ -74,6 +75,10 @@ frappe.ui.form.FormTour = class FormTour { if (this.tour.save_on_complete) { this.add_step_to_save(); } + + if (this.tour.submit_on_complete) { + this.add_step_to_submit(); + } } is_next_condition_satisfied(step) { @@ -249,9 +254,43 @@ frappe.ui.form.FormTour = class FormTour { description: "", position: "left", doneBtnText: __("Save") + }, + onNext: () => { + this.frm.save(); } }; this.driver_steps.push(save_step); - frappe.ui.form.on(this.frm.doctype, 'after_save', () => this.on_finish && this.on_finish()); + + let after_save = () => this.on_finish && this.on_finish(); + + if (this.tour.submit_on_complete) { + after_save = () => { + this.update_driver_steps(); + this.driver.start(this.driver.steps.length - 1); + } + } + frappe.ui.form.on(this.frm.doctype, 'after_save', after_save); + } + + add_step_to_submit() { + const page_id = `[id="page-${this.frm.doctype}"]`; + const $submit_btn = `${page_id} .standard-actions .primary-action`; + const submit_step = { + element: $submit_btn, + is_submit_step: true, + allowClose: false, + overlayClickNext: false, + popover: { + title: __("Submit"), + description: "", + position: "left", + doneBtnText: __("Submit") + }, + onNext: () => { + this.frm.savesubmit(); + } + }; + this.driver_steps.push(submit_step); + frappe.ui.form.on(this.frm.doctype, 'after_submit', () => this.on_finish && this.on_finish()); } }; \ No newline at end of file From 6bb96419d4a9a24e3f733ffbd0c50ef8ea951af7 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 30 Jun 2021 15:29:49 +0530 Subject: [PATCH 321/495] fix: Safe checks while setting doctypes_with_web_view cache --- frappe/website/router.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/router.py b/frappe/website/router.py index a9e2f68fe5..9809a73e48 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -270,7 +270,7 @@ def get_doctypes_with_web_view(): doctypes_with_web_view = frappe.get_all('DocType', fields=['name', 'module'], filters=dict(has_web_view=1)) module_app_map = frappe.local.module_app - doctypes += [d.name for d in doctypes_with_web_view if module_app_map[frappe.scrub(d.module)] in installed_apps] + doctypes += [d.name for d in doctypes_with_web_view if module_app_map.get(frappe.scrub(d.module)) in installed_apps] return doctypes return frappe.cache().get_value('doctypes_with_web_view', _get) From dc514905f22b8be0aa802bf9e0f7ad8ff77dd953 Mon Sep 17 00:00:00 2001 From: gavin Date: Wed, 30 Jun 2021 15:49:14 +0530 Subject: [PATCH 322/495] chore: Update CODEOWNERS --- CODEOWNERS | 3 --- 1 file changed, 3 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 92723ab035..2dff157294 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -4,13 +4,10 @@ # the repo. Unless a later match takes precedence, * @frappe/frappe-review-team -website/ @prssanna -web_form/ @prssanna templates/ @surajshetty3416 www/ @surajshetty3416 integrations/ @leela patches/ @surajshetty3416 -dashboard/ @prssanna email/ @leela event_streaming/ @ruchamahabal data_import* @netchampfaris From 3d36b671528141e72a6de413309f2ec42f6d63e2 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 30 Jun 2021 17:36:27 +0530 Subject: [PATCH 323/495] fix: Hide "Attach File" in form if max limit reached --- frappe/public/js/frappe/form/sidebar/attachments.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/sidebar/attachments.js b/frappe/public/js/frappe/form/sidebar/attachments.js index 4f116df63e..538534e5cf 100644 --- a/frappe/public/js/frappe/form/sidebar/attachments.js +++ b/frappe/public/js/frappe/form/sidebar/attachments.js @@ -11,7 +11,7 @@ frappe.ui.form.Attachments = class Attachments { this.parent.find(".add-attachment-btn").click(function() { me.new_attachment(); }); - this.add_attachment_wrapper = this.parent.find(".add_attachment").parent(); + this.add_attachment_wrapper = this.parent.find(".add-attachment-btn"); this.attachments_label = this.parent.find(".attachments-label"); } max_reached(raise_exception=false) { @@ -39,7 +39,7 @@ frappe.ui.form.Attachments = class Attachments { this.parent.find(".attachment-row").remove(); var max_reached = this.max_reached(); - this.add_attachment_wrapper.toggleClass("hide", !max_reached); + this.add_attachment_wrapper.toggle(!max_reached); // add attachment objects var attachments = this.get_attachments(); From fd51ba7fcc0f50ce0159e41f2636448af5a708d6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 30 Jun 2021 16:58:54 +0000 Subject: [PATCH 324/495] chore(deps): [security] bump socket.io-parser from 3.3.0 to 3.3.2 Bumps [socket.io-parser](https://github.com/socketio/socket.io-parser) from 3.3.0 to 3.3.2. **This update includes a security fix.** - [Release notes](https://github.com/socketio/socket.io-parser/releases) - [Changelog](https://github.com/socketio/socket.io-parser/blob/3.3.2/CHANGELOG.md) - [Commits](https://github.com/socketio/socket.io-parser/compare/3.3.0...3.3.2) Signed-off-by: dependabot-preview[bot] --- yarn.lock | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7b1fb981dd..ddb5623e5e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1381,12 +1381,12 @@ component-bind@1.0.0: resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= -component-emitter@1.2.1, component-emitter@^1.2.0: +component-emitter@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= -component-emitter@~1.3.0: +component-emitter@^1.2.0, component-emitter@~1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== @@ -1713,28 +1713,14 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@^3.0.1: +debug@^3.0.1, debug@^3.1.0, debug@^3.2.6: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" -debug@^3.1.0, debug@^3.2.6: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -debug@^4.1.1, debug@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - -debug@^4.2.0, debug@^4.3.1: +debug@^4.1.1, debug@^4.2.0, debug@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== @@ -1748,6 +1734,13 @@ debug@~3.1.0: dependencies: ms "2.0.0" +debug@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + decamelize@^1.0.0, decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -6425,11 +6418,11 @@ socket.io-client@2.4.0: to-array "0.1.4" socket.io-parser@~3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.0.tgz#2b52a96a509fdf31440ba40fed6094c7d4f1262f" - integrity sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng== + version "3.3.2" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.2.tgz#ef872009d0adcf704f2fbe830191a14752ad50b6" + integrity sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg== dependencies: - component-emitter "1.2.1" + component-emitter "~1.3.0" debug "~3.1.0" isarray "2.0.1" From d58d2ac77f0ede883df774710c9b5e1d9c790c67 Mon Sep 17 00:00:00 2001 From: Ankush Date: Wed, 30 Jun 2021 23:05:05 +0530 Subject: [PATCH 325/495] ci(cypress): use env variable for key (#13616) documentation ref: https://docs.cypress.io/guides/guides/command-line#cypress-run --- .github/workflows/ui-tests.yml | 2 ++ frappe/commands/utils.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index f2f43f10f8..f342c0709e 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -105,3 +105,5 @@ jobs: - name: UI Tests run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests frappe --headless --parallel --ci-build-id $GITHUB_RUN_ID + env: + CYPRESS_RECORD_KEY: 4a48f41c-11b3-425b-aa88-c58048fa69eb diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index c16de497ec..b6e48e3388 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -661,7 +661,7 @@ def run_ui_tests(context, app, headless=False, parallel=True, ci_build_id=None): frappe.commands.popen("yarn add cypress@^6 cypress-file-upload@^5 --no-lockfile") # run for headless mode - run_or_open = 'run --browser firefox --record --key 4a48f41c-11b3-425b-aa88-c58048fa69eb' if headless else 'open' + run_or_open = 'run --browser firefox --record' if headless else 'open' command = '{site_env} {password_env} {cypress} {run_or_open}' formatted_command = command.format(site_env=site_env, password_env=password_env, cypress=cypress_path, run_or_open=run_or_open) From 02e735da8ebe173ca931585ee827c2048e97a7fb Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 1 Jul 2021 22:06:26 +0530 Subject: [PATCH 326/495] fix: Do not set base template for resource --- frappe/website/page_renderers/template_page.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index 5134313c83..249c998192 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -150,8 +150,7 @@ class TemplatePage(BaseTemplatePage): def set_page_properties(self): self.context.base_template = self.context.base_template \ - or get_base_template(self.path) \ - or 'templates/web.html' + or get_base_template(self.path) self.context.basepath = self.basepath self.context.basename = self.basename self.context.name = self.name From a93bb670308a26cc7265c610300386dd7860232f Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 1 Jul 2021 22:08:15 +0530 Subject: [PATCH 327/495] test: Add test case to validate raw file load --- frappe/tests/test_website.py | 7 +++++++ frappe/www/_test/assets/css_asset.css | 1 + frappe/www/_test/assets/js_asset.js | 1 + 3 files changed, 9 insertions(+) create mode 100644 frappe/www/_test/assets/css_asset.css create mode 100644 frappe/www/_test/assets/js_asset.js diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index 5c19d3935c..7241bbb508 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -244,6 +244,13 @@ class TestWebsite(unittest.TestCase): self.assertIn("", content) self.assertIn("background-color: var(--bg-color);", content) + def test_raw_assets_are_loaded(self): + content = get_response_content('/_test/assets/js_asset.js') + self.assertEqual("console.log('in');", content) + + content = get_response_content('/_test/assets/css_asset.css') + self.assertEqual("""body{color:red}""", content) + def test_breadcrumbs(self): content = get_response_content('/_test/_test_folder/_test_page') self.assertIn('Test TOC', content) diff --git a/frappe/www/_test/assets/css_asset.css b/frappe/www/_test/assets/css_asset.css new file mode 100644 index 0000000000..363a06397a --- /dev/null +++ b/frappe/www/_test/assets/css_asset.css @@ -0,0 +1 @@ +body{color:red} \ No newline at end of file diff --git a/frappe/www/_test/assets/js_asset.js b/frappe/www/_test/assets/js_asset.js new file mode 100644 index 0000000000..b39f5af3dd --- /dev/null +++ b/frappe/www/_test/assets/js_asset.js @@ -0,0 +1 @@ +console.log('in'); \ No newline at end of file From 4363e51c6dd6510d427e5347b477552d88eab9dd Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 1 Jul 2021 22:10:02 +0530 Subject: [PATCH 328/495] test: Fix improper tests for website rendering --- frappe/templates/test/_test_base.html | 11 ---------- .../test/_test_base_breadcrumbs.html | 20 +++++++++++++++++++ frappe/tests/test_website.py | 4 ++-- frappe/www/_test/_test_folder/_test_page.html | 1 + frappe/www/_test/_test_folder/_test_page.py | 2 +- frappe/www/_test/_test_folder/index.md | 4 ++-- 6 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 frappe/templates/test/_test_base_breadcrumbs.html diff --git a/frappe/templates/test/_test_base.html b/frappe/templates/test/_test_base.html index 17caf8df1b..1d5019df37 100644 --- a/frappe/templates/test/_test_base.html +++ b/frappe/templates/test/_test_base.html @@ -1,20 +1,9 @@ - {%- block style %} - {% if colocated_css -%} - - {%- endif %} - {%- endblock -%} - {% include "templates/includes/breadcrumbs.html" %}

    This is for testing

    {% block content %}{% endblock %} - {%- block script %} - {% if colocated_js -%} - - {%- endif %} - {%- endblock %} diff --git a/frappe/templates/test/_test_base_breadcrumbs.html b/frappe/templates/test/_test_base_breadcrumbs.html new file mode 100644 index 0000000000..17caf8df1b --- /dev/null +++ b/frappe/templates/test/_test_base_breadcrumbs.html @@ -0,0 +1,20 @@ + + + + {%- block style %} + {% if colocated_css -%} + + {%- endif %} + {%- endblock -%} + + + {% include "templates/includes/breadcrumbs.html" %} +

    This is for testing

    + {% block content %}{% endblock %} + {%- block script %} + {% if colocated_js -%} + + {%- endif %} + {%- endblock %} + + diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index 7241bbb508..ae768d7804 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -253,12 +253,12 @@ class TestWebsite(unittest.TestCase): def test_breadcrumbs(self): content = get_response_content('/_test/_test_folder/_test_page') - self.assertIn('Test TOC', content) + self.assertIn('Test Folder', content) self.assertIn(' Test Page', content) content = get_response_content('/_test/_test_folder/index') self.assertIn(' Test', content) - self.assertIn('Test TOC', content) + self.assertIn('Test Folder', content) def test_get_context_without_context_object(self): content = get_response_content('/_test/_test_no_context') diff --git a/frappe/www/_test/_test_folder/_test_page.html b/frappe/www/_test/_test_folder/_test_page.html index 123d619e38..79bc96c568 100644 --- a/frappe/www/_test/_test_folder/_test_page.html +++ b/frappe/www/_test/_test_folder/_test_page.html @@ -1,3 +1,4 @@ +{% extends base_template_path %} {% block content %} {% include "templates/includes/web_sidebar.html" %}

    Test content

    diff --git a/frappe/www/_test/_test_folder/_test_page.py b/frappe/www/_test/_test_folder/_test_page.py index 1813a06bac..3d4a645f9b 100644 --- a/frappe/www/_test/_test_folder/_test_page.py +++ b/frappe/www/_test/_test_folder/_test_page.py @@ -1,3 +1,3 @@ def get_context(context): - context.base_template_path = 'frappe/templates/test/_test_base.html' + context.base_template_path = 'frappe/templates/test/_test_base_breadcrumbs.html' context.add_breadcrumbs = 1 diff --git a/frappe/www/_test/_test_folder/index.md b/frappe/www/_test/_test_folder/index.md index 1a5a9e7f81..ca8c55e9d5 100644 --- a/frappe/www/_test/_test_folder/index.md +++ b/frappe/www/_test/_test_folder/index.md @@ -1,9 +1,9 @@ --- -title: Test TOC +title: Test Folder add_breadcrumbs: 1 show_sidebar: 1 +base_template: templates/web.html --- - # Index {index} \ No newline at end of file From 35521327897a2fe38ff9bfb1389ff18723a70231 Mon Sep 17 00:00:00 2001 From: leela Date: Fri, 2 Jul 2021 11:25:02 +0530 Subject: [PATCH 329/495] fix: remove not needed six import --- frappe/model/document.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/model/document.py b/frappe/model/document.py index 0b2ae5895f..99585bb26c 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -6,7 +6,6 @@ from frappe import _, msgprint, is_whitelisted from frappe.utils import flt, cstr, now, get_datetime_str, file_lock, date_diff from frappe.model.base_document import BaseDocument, get_controller from frappe.model.naming import set_new_name, rename_cancelled_doc -from six import iteritems, string_types from werkzeug.exceptions import NotFound, Forbidden import hashlib, json from frappe.model import optional_fields, table_fields From f31d01a2a645ea5c2aebaeeb02cdb97b086ef356 Mon Sep 17 00:00:00 2001 From: leela Date: Fri, 2 Jul 2021 17:43:16 +0530 Subject: [PATCH 330/495] Revert "Merge pull request #13244 from prssanna/amended-doc-naming" This reverts commit 58b95662c2f704258f83ad72d4a0c942ae14f778, reversing changes made to c553b7e23a5c70f1c05b08850a419ac23fba7aa7. --- frappe/core/doctype/doctype/doctype.py | 40 +++++--------- frappe/core/doctype/doctype/test_doctype.py | 4 -- frappe/model/document.py | 10 +--- frappe/model/naming.py | 52 ++----------------- frappe/patches.txt | 1 - ...l_name_docfield_to_submittable_doctypes.py | 11 ---- frappe/public/js/frappe/form/form.js | 24 ++++----- frappe/public/js/frappe/router.js | 6 --- frappe/tests/test_naming.py | 34 ------------ 9 files changed, 27 insertions(+), 155 deletions(-) delete mode 100644 frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index da410b3e0b..3cdc45ea08 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -70,7 +70,6 @@ class DocType(Document): if not self.istable: validate_permissions(self) - self.make_cancellable() self.make_amendable() self.make_repeatable() self.validate_nestedset() @@ -569,33 +568,18 @@ class DocType(Document): def make_amendable(self): """If is_submittable is set, add amended_from docfields.""" - if self.is_submittable and\ - not frappe.db.get_value('DocField', {'fieldname': 'amended_from', 'parent': self.name}): - - self.append("fields", { - "label": "Amended From", - "fieldtype": "Link", - "fieldname": "amended_from", - "options": self.name, - "read_only": 1, - "print_hide": 1, - "no_copy": 1 - }) - - def make_cancellable(self): - """If is_submittable is set, add original_name docfield.""" - if self.is_submittable and\ - not frappe.db.get_value('DocField', {'fieldname': 'original_name', 'parent': self.name}): - - self.append("fields", { - "label": "Original Name", - "fieldtype": "Text", - "fieldname": "original_name", - "read_only": 1, - "hidden": 1, - "print_hide": 1, - "no_copy": 1 - }) + if self.is_submittable: + if not frappe.db.sql("""select name from tabDocField + where fieldname = 'amended_from' and parent = %s""", self.name): + self.append("fields", { + "label": "Amended From", + "fieldtype": "Link", + "fieldname": "amended_from", + "options": self.name, + "read_only": 1, + "print_hide": 1, + "no_copy": 1 + }) def make_repeatable(self): """If allow_auto_repeat is set, add auto_repeat custom field.""" diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index fbb224fce7..1e1a01a685 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -348,7 +348,6 @@ class TestDocType(unittest.TestCase): dump_docs = json.dumps(docs.get('docs')) cancel_all_linked_docs(dump_docs) data_link_doc.cancel() - data_doc.name = '{}-1'.format(data_doc.name) data_doc.load_from_db() self.assertEqual(data_link_doc.docstatus, 2) self.assertEqual(data_doc.docstatus, 2) @@ -434,10 +433,7 @@ class TestDocType(unittest.TestCase): self.assertRaises(frappe.LinkExistsError, data_link_doc_1.cancel) data_doc.load_from_db() - - data_doc_2.name = '{}-1'.format(data_doc_2.name) data_doc_2.load_from_db() - self.assertEqual(data_link_doc_1.docstatus, 2) #linked doc is canceled diff --git a/frappe/model/document.py b/frappe/model/document.py index 99585bb26c..61160e1f01 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -5,7 +5,7 @@ import time from frappe import _, msgprint, is_whitelisted from frappe.utils import flt, cstr, now, get_datetime_str, file_lock, date_diff from frappe.model.base_document import BaseDocument, get_controller -from frappe.model.naming import set_new_name, rename_cancelled_doc +from frappe.model.naming import set_new_name from werkzeug.exceptions import NotFound, Forbidden import hashlib, json from frappe.model import optional_fields, table_fields @@ -917,14 +917,6 @@ class Document(BaseDocument): @whitelist.__func__ def _cancel(self): """Cancel the document. Sets `docstatus` = 2, then saves.""" - - # for backward compatibility - if self.amended_from and not self.original_name: - self.original_name = None - else: - self.original_name = self.name - - self.name = rename_cancelled_doc(self) self.docstatus = 2 self.save() diff --git a/frappe/model/naming.py b/frappe/model/naming.py index ed037adb0d..fe136adce8 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -221,16 +221,6 @@ def revert_series_if_last(key, name, doc=None): * prefix = #### and hashes = 2021 (hash doesn't exist) * will search hash in key then accordingly get prefix = "" """ - - if hasattr(doc, 'amended_from'): - # do not revert if doc is amended, since cancelled docs still exist - if doc.docstatus != 2 and doc.amended_from: - return - - # for first cancelled doc - if doc.docstatus == 2 and not doc.amended_from and doc.original_name: - name = doc.original_name - if ".#" in key: prefix, hashes = key.rsplit(".", 1) if "#" not in hashes: @@ -314,48 +304,14 @@ def append_number_if_name_exists(doctype, value, fieldname="name", separator="-" def _set_amended_name(doc): - if doc.original_name: - doc.name = doc.original_name - else: - original_name = get_original_name(doc) - doc.name = doc.amended_from - - # rename original doc to next name in series, and set amended doc name as original name - next_name_in_series = get_new_name_from_amended_from(doc) - frappe.rename_doc(doc.doctype, original_name, next_name_in_series, force=True, show_alert=False) - doc.name = original_name - doc.amended_from = next_name_in_series - doc.original_name = original_name - - return doc.name - -def get_original_name(doc): - # get original doc name from chain of amended docs - amended_from = original_name = doc.amended_from - while amended_from: - original_name = amended_from - amended_from = frappe.db.get_value(doc.doctype, amended_from, "amended_from") - - return original_name - -def get_new_name_from_amended_from(doc): am_id = 1 - am_prefix = doc.name - if frappe.db.get_value(doc.doctype, doc.name, "amended_from"): + am_prefix = doc.amended_from + if frappe.db.get_value(doc.doctype, doc.amended_from, "amended_from"): am_id = cint(doc.amended_from.split("-")[-1]) + 1 am_prefix = "-".join(doc.amended_from.split("-")[:-1]) # except the last hyphen - new_name = am_prefix + "-" + str(am_id) - if new_name == doc.name: - am_id += 1 - new_name = am_prefix + "-" + str(am_id) - return new_name - -def rename_cancelled_doc(doc): - doc = frappe.parse_json(doc) - new_name = get_new_name_from_amended_from(doc) - frappe.rename_doc(doc.doctype, doc.name, new_name, force=True, show_alert=False) - return new_name + doc.name = am_prefix + "-" + str(am_id) + return doc.name def _field_autoname(autoname, doc, skip_slicing=None): diff --git a/frappe/patches.txt b/frappe/patches.txt index 833d85423b..7605d8ea2b 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -180,4 +180,3 @@ frappe.patches.v12_0.rename_uploaded_files_with_proper_name frappe.patches.v13_0.queryreport_columns frappe.patches.v13_0.jinja_hook frappe.patches.v13_0.update_notification_channel_if_empty -frappe.patches.v13_0.add_original_name_docfield_to_submittable_doctypes diff --git a/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py b/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py deleted file mode 100644 index d17242e90a..0000000000 --- a/frappe/patches/v13_0/add_original_name_docfield_to_submittable_doctypes.py +++ /dev/null @@ -1,11 +0,0 @@ -import frappe -from frappe.database.schema import add_column - -def execute(): - for doctype in frappe.db.get_all('DocType'): - doctype = frappe.get_doc('DocType', doctype.name) - if doctype.is_submittable and frappe.db.table_exists(doctype.name): - doctype.make_cancellable() - if not frappe.db.has_column(doctype.name, 'original_name'): - add_column(doctype.name, 'original_name', 'Text') - doctype.db_update_all() diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 30f023c987..8064f90a98 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -770,36 +770,32 @@ frappe.ui.form.Form = class FrappeForm { } _cancel(btn, callback, on_error, skip_confirm) { + const me = this; const cancel_doc = () => { frappe.validated = true; - this.script_manager.trigger("before_cancel").then(() => { + me.script_manager.trigger("before_cancel").then(() => { if (!frappe.validated) { - return this.handle_save_fail(btn, on_error); + return me.handle_save_fail(btn, on_error); } - const original_name = this.docname; - const after_cancel = (r) => { + var after_cancel = function(r) { if (r.exc) { - this.handle_save_fail(btn, on_error); + me.handle_save_fail(btn, on_error); } else { frappe.utils.play_sound("cancel"); + me.refresh(); callback && callback(); - this.script_manager.trigger("after_cancel"); - frappe.run_serially([ - () => this.rename_notify(this.doctype, original_name, r.docs[0].name), - () => frappe.router.clear_re_route(this.doctype, original_name), - () => this.refresh(), - ]); + me.script_manager.trigger("after_cancel"); } }; - frappe.ui.form.save(this, "cancel", after_cancel, btn); + frappe.ui.form.save(me, "cancel", after_cancel, btn); }); } if (skip_confirm) { cancel_doc(); } else { - frappe.confirm(__("Permanently Cancel {0}?", [this.docname]), cancel_doc, this.handle_save_fail(btn, on_error)); + frappe.confirm(__("Permanently Cancel {0}?", [this.docname]), cancel_doc, me.handle_save_fail(btn, on_error)); } }; @@ -821,7 +817,7 @@ frappe.ui.form.Form = class FrappeForm { 'docname': this.doc.name }).then(is_amended => { if (is_amended) { - frappe.throw(__('This document is already amended, you cannot amend it again')); + frappe.throw(__('This document is already amended, you cannot ammend it again')); } this.validate_form_action("Amend"); var me = this; diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 46544db919..12caf4ab94 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -235,12 +235,6 @@ frappe.router = { } }, - clear_re_route(doctype, docname) { - delete frappe.re_route[ - `${encodeURIComponent(frappe.router.slug(doctype))}/${encodeURIComponent(docname)}` - ]; - }, - set_title(sub_path) { if (frappe.route_titles[sub_path]) { frappe.utils.set_title(frappe.route_titles[sub_path]); diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py index b716f063c4..557993882f 100644 --- a/frappe/tests/test_naming.py +++ b/frappe/tests/test_naming.py @@ -116,37 +116,3 @@ class TestNaming(unittest.TestCase): self.assertEqual(current_index.get('current'), 2) frappe.db.sql("""delete from `tabSeries` where name = %s""", series) - - def test_naming_for_cancelled_and_amended_doc(self): - submittable_doctype = frappe.get_doc({ - "doctype": "DocType", - "module": "Core", - "custom": 1, - "is_submittable": 1, - "permissions": [{ - "role": "System Manager", - "read": 1 - }], - "name": 'Submittable Doctype' - }).insert() - - doc = frappe.new_doc('Submittable Doctype') - doc.save() - original_name = doc.name - - doc.submit() - doc.cancel() - cancelled_name = doc.name - self.assertEqual(cancelled_name, "{}-1".format(original_name)) - - amended_doc = frappe.copy_doc(doc) - amended_doc.docstatus = 0 - amended_doc.amended_from = doc.name - amended_doc.save() - self.assertEqual(amended_doc.name, original_name) - - amended_doc.submit() - amended_doc.cancel() - self.assertEqual(amended_doc.name, "{}-2".format(original_name)) - - submittable_doctype.delete() \ No newline at end of file From ef0a5e904bc7bef582b29629956c99deb466de49 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 2 Jul 2021 17:24:34 +0200 Subject: [PATCH 331/495] feat: different output formats for `bench version` --- frappe/commands/utils.py | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index b944f02af7..7d3aecca1f 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -768,25 +768,45 @@ def set_config(context, key, value, global_=False, parse=False, as_dict=False): @click.command('version') -def get_version(): +@click.option('--output', help='Output format. One of: plain, table, json, legacy.', default='legacy') +def get_version(output): "Show the versions of all the installed apps" from git import Repo - from frappe.utils.change_log import get_app_branch + from frappe.utils.commands import render_table + frappe.init('') + data = [] for app in sorted(frappe.get_all_apps()): - branch_name = get_app_branch(app) module = frappe.get_module(app) app_hooks = frappe.get_module(app + ".hooks") repo = Repo(frappe.get_app_path(app, "..")) - branch = repo.head.ref.name - commit = repo.head.ref.commit.hexsha[:7] - if hasattr(app_hooks, '{0}_version'.format(branch_name)): - click.echo("{0} {1} {2} ({3})".format(app, getattr(app_hooks, '{0}_version'.format(branch_name)), branch, commit)) + app_info = frappe._dict() + app_info.app = app + app_info.branch = repo.head.ref.name + app_info.commit = repo.head.ref.commit.hexsha[:7] + if hasattr(app_hooks, '{0}_version'.format(app_info.branch)): + app_info.version = getattr(app_hooks, '{0}_version'.format(app_info.branch)) elif hasattr(module, "__version__"): - click.echo("{0} {1} {2} ({3})".format(app, module.__version__, branch, commit)) + app_info.version = module.__version__ + + data.append(app_info) + + if output == 'table': + table = [['App', 'Version', 'Branch', 'Commit']] + for app_info in data: + table.append([app_info.app, app_info.version, app_info.branch, app_info.commit]) + render_table(table) + elif output == 'json': + click.echo(json.dumps(data, indent=4)) + elif output == 'plain': + for app_info in data: + click.echo(f'{app_info.app} {app_info.version} {app_info.branch} ({app_info.commit})') + else: # legacy + for app_info in data: + click.echo(f'{app_info.app} {app_info.version}') @click.command('rebuild-global-search') From 51207fe582f28eeaf5a0692c143e243339d6cce6 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Sat, 3 Jul 2021 13:00:43 +0530 Subject: [PATCH 332/495] feat: option to not preload assets --- frappe/website/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/website/utils.py b/frappe/website/utils.py index 0f5f182ea2..171c90e1cf 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -449,15 +449,15 @@ def cache_html(func): return cache_html_decorator -def build_response(path, data, http_status_code, headers=None): +def build_response(path, data, http_status_code, headers=None, preload_assets=True): # build response response = Response() response.data = set_content_type(response, data, path) response.status_code = http_status_code response.headers["X-Page-Name"] = path.encode("ascii", errors="xmlcharrefreplace") response.headers["X-From-Cache"] = frappe.local.response.from_cache or False - - add_preload_headers(response) + if preload_assets: + add_preload_headers(response) if headers: for key, val in iteritems(headers): response.headers[key] = val.encode("ascii", errors="xmlcharrefreplace") From 508f47e8717ddadaa0b692f8041b233d819b6b68 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sat, 3 Jul 2021 14:13:02 +0530 Subject: [PATCH 333/495] fix: Minified js load --- frappe/website/page_renderers/template_page.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index 249c998192..3ece8ff5d0 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -70,7 +70,7 @@ class TemplatePage(BaseTemplatePage): self.set_pymodule() self.update_context() - self.setup_template() + self.setup_template_source() self.load_colocated_files() self.set_properties_from_source() self.post_process_context() @@ -118,7 +118,7 @@ class TemplatePage(BaseTemplatePage): if os.path.exists(os.path.join(self.app_path, self.pymodule_path)): self.pymodule_name = self.app + "." + self.pymodule_path.replace(os.path.sep, ".")[:-3] - def setup_template(self): + def setup_template_source(self): '''Setup template source, frontmatter and markdown conversion''' self.source = self.get_raw_template() self.extract_frontmatter() @@ -126,7 +126,6 @@ class TemplatePage(BaseTemplatePage): def update_context(self): self.set_page_properties() - self.set_properties_from_source() self.context.build_version = frappe.utils.get_build_version() if self.pymodule_name: @@ -202,13 +201,10 @@ class TemplatePage(BaseTemplatePage): frappe.errprint(frappe.utils.get_traceback()) def render_template(self): - if self.source: + if self.template_path.endswith('min.js'): + html = self.source # static + else: html = frappe.render_template(self.source, self.context) - elif self.template_path: - if self.path.endswith('min.js'): - html = self.get_raw_template() # static - else: - html = frappe.get_template(self.template_path).render(self.context) return html From fccd19a44cd248c35a0b8e1dd2f20f95060b2d7e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sat, 3 Jul 2021 14:13:16 +0530 Subject: [PATCH 334/495] test: Add test to validate minified js load --- frappe/tests/test_website.py | 5 +++-- frappe/www/_test/assets/__init__.py | 0 frappe/www/_test/assets/js_asset.js | 1 - frappe/www/_test/assets/js_asset.min.js | 2 ++ 4 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 frappe/www/_test/assets/__init__.py delete mode 100644 frappe/www/_test/assets/js_asset.js create mode 100644 frappe/www/_test/assets/js_asset.min.js diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index ae768d7804..f1c4f3b3f5 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -245,8 +245,9 @@ class TestWebsite(unittest.TestCase): self.assertIn("background-color: var(--bg-color);", content) def test_raw_assets_are_loaded(self): - content = get_response_content('/_test/assets/js_asset.js') - self.assertEqual("console.log('in');", content) + content = get_response_content('/_test/assets/js_asset.min.js') + # minified js files should not be passed through jinja renderer + self.assertEqual("//{% if title %} {{title}} {% endif %}\nconsole.log('in');", content) content = get_response_content('/_test/assets/css_asset.css') self.assertEqual("""body{color:red}""", content) diff --git a/frappe/www/_test/assets/__init__.py b/frappe/www/_test/assets/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/www/_test/assets/js_asset.js b/frappe/www/_test/assets/js_asset.js deleted file mode 100644 index b39f5af3dd..0000000000 --- a/frappe/www/_test/assets/js_asset.js +++ /dev/null @@ -1 +0,0 @@ -console.log('in'); \ No newline at end of file diff --git a/frappe/www/_test/assets/js_asset.min.js b/frappe/www/_test/assets/js_asset.min.js new file mode 100644 index 0000000000..e039292259 --- /dev/null +++ b/frappe/www/_test/assets/js_asset.min.js @@ -0,0 +1,2 @@ +//{% if title %} {{title}} {% endif %} +console.log('in'); \ No newline at end of file From 1c5ad30bf1069eb8469f2a47d9fe25dd4199ce40 Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Sat, 3 Jul 2021 18:44:40 +0530 Subject: [PATCH 335/495] perf: let's preload assets --- frappe/website/utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/website/utils.py b/frappe/website/utils.py index 171c90e1cf..419a5199df 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -449,15 +449,14 @@ def cache_html(func): return cache_html_decorator -def build_response(path, data, http_status_code, headers=None, preload_assets=True): +def build_response(path, data, http_status_code, headers=None): # build response response = Response() response.data = set_content_type(response, data, path) response.status_code = http_status_code response.headers["X-Page-Name"] = path.encode("ascii", errors="xmlcharrefreplace") response.headers["X-From-Cache"] = frappe.local.response.from_cache or False - if preload_assets: - add_preload_headers(response) + add_preload_headers(response) if headers: for key, val in iteritems(headers): response.headers[key] = val.encode("ascii", errors="xmlcharrefreplace") @@ -486,11 +485,12 @@ def set_content_type(response, data, path): return data def add_preload_headers(response): - from bs4 import BeautifulSoup + from bs4 import BeautifulSoup, SoupStrainer try: preload = [] - soup = BeautifulSoup(response.data, "lxml") + strainer = SoupStrainer(re.compile("script|link")) + soup = BeautifulSoup(response.data, "lxml", parse_only=strainer) for elem in soup.find_all('script', src=re.compile(".*")): preload.append(("script", elem.get("src"))) From 4ddf8c18d8d8e156dcdfa67c191a02015dda66f3 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 5 Jul 2021 10:38:55 +0530 Subject: [PATCH 336/495] feat: Execute separate build command for style files - Execute style build twice (one for LTR and one for RTL) because rtlcss is dependant on comments and esbuild strips all comments so we cannot run rtlcss on built css files. --- esbuild/esbuild.js | 106 ++++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 44 deletions(-) diff --git a/esbuild/esbuild.js b/esbuild/esbuild.js index a0b07abe43..aa273ab85c 100644 --- a/esbuild/esbuild.js +++ b/esbuild/esbuild.js @@ -97,10 +97,9 @@ async function execute() { await clean_dist_folders(APPS); } - let result; + let results; try { - result = await build_assets_for_apps(APPS, FILES_TO_BUILD); - result = await create_rtl_assets(result); + results = await build_assets_for_apps(APPS, FILES_TO_BUILD); } catch (e) { log_error("There were some problems during build"); log(); @@ -109,13 +108,15 @@ async function execute() { } if (!WATCH_MODE) { - log_built_assets(result.metafile); + log_built_assets(results); console.timeEnd(TOTAL_BUILD_TIME); log(); } else { log("Watching for changes..."); } - return await write_assets_json(result.metafile); + for (const result of results) { + write_assets_json(result.metafile); + } } function build_assets_for_apps(apps, files) { @@ -127,6 +128,8 @@ function build_assets_for_apps(apps, files) { let output_path = assets_path; let file_map = {}; + let style_file_map = {}; + let rtl_style_file_map = {}; for (let file of files) { let relative_app_path = path.relative(apps_path, file); let app = relative_app_path.split(path.sep)[0]; @@ -142,19 +145,32 @@ function build_assets_for_apps(apps, files) { } output_name = path.join(app, "dist", output_name); - if (Object.keys(file_map).includes(output_name)) { + if (Object.keys(file_map).includes(output_name) || Object.keys(style_file_map).includes(output_name)) { log_warn( `Duplicate output file ${output_name} generated from ${file}` ); } - - file_map[output_name] = file; + if ([".css", ".scss", ".less", ".sass", ".styl"].includes(extension)) { + style_file_map[output_name] = file; + rtl_style_file_map[output_name.replace('/css/', '/css-rtl/')] = file; + } else { + file_map[output_name] = file; + } } - - return build_files({ + let build = build_files({ files: file_map, outdir: output_path }); + let style_build = build_style_files({ + files: style_file_map, + outdir: output_path + }); + let rtl_style_build = build_style_files({ + files: rtl_style_file_map, + outdir: output_path, + rtl_style: true + }); + return Promise.all([build, style_build, rtl_style_build]); }); } @@ -205,7 +221,33 @@ function get_files_to_build(files) { } function build_files({ files, outdir }) { - return esbuild.build({ + let build_plugins = [ + html_plugin, + vue(), + ]; + return esbuild.build(get_build_options(files, outdir, build_plugins)); +} + +function build_style_files({ files, outdir, rtl_style=false }) { + let plugins = []; + if (rtl_style) { + plugins.push(rtlcss); + } + + let build_plugins = [ + ignore_assets, + postCssPlugin({ + plugins: plugins, + sassOptions: sass_options + }) + ]; + + plugins.push(require("autoprefixer")); + return esbuild.build(get_build_options(files, outdir, build_plugins)); +} + +function get_build_options(files, outdir, plugins) { + return { entryPoints: files, entryNames: "[dir]/[name].[hash]", outdir, @@ -219,17 +261,9 @@ function build_files({ files, outdir }) { PRODUCTION ? "production" : "development" ) }, - plugins: [ - html_plugin, - ignore_assets, - vue(), - postCssPlugin({ - plugins: [require("autoprefixer")], - sassOptions: sass_options - }) - ], + plugins: plugins, watch: get_watch_config() - }); + }; } function get_watch_config() { @@ -275,7 +309,11 @@ async function clean_dist_folders(apps) { } } -function log_built_assets(metafile) { +function log_built_assets(results) { + let outputs = {}; + for (const result of results) { + outputs = Object.assign(outputs, result.metafile.outputs); + } let column_widths = [60, 20]; cliui.div( { @@ -290,9 +328,9 @@ function log_built_assets(metafile) { cliui.div(""); let output_by_dist_path = {}; - for (let outfile in metafile.outputs) { + for (let outfile in outputs) { if (outfile.endsWith(".map")) continue; - let data = metafile.outputs[outfile]; + let data = outputs[outfile]; outfile = path.resolve(outfile); outfile = path.relative(assets_path, outfile); let filename = path.basename(outfile); @@ -486,24 +524,4 @@ function log_rebuilt_assets(prev_assets, new_assets) { log(" " + filename); } log(); -} - -async function create_rtl_assets(result) { - for (let file_path in result.metafile.outputs) { - if (file_path.endsWith('.css')) { - console.log(file_path); - let content = fs.readFileSync(file_path, {'encoding': 'utf-8'}); - let rtl_content = rtlcss.process(content); - let rtl_file_path = file_path.replace('/css/', '/css-rtl/'); - let rtl_folder_path = path.dirname(rtl_file_path); - if (fs.existsSync(rtl_file_path)) { - continue; - } - if (!fs.existsSync(rtl_folder_path)) { - fs.mkdirSync(rtl_folder_path); - } - fs.writeFileSync(rtl_file_path, rtl_content); - } - } - return result; } \ No newline at end of file From bdfb53a50c52c8dedab8f05457d30044872111c9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 1 Jul 2021 19:04:54 +0530 Subject: [PATCH 337/495] fix(Workspace): Pass doctype info for link item Without item.doctype, url for non standard reports in workspace cards doesn't set properly since doctype value is unidentified Sometimes, the whole workspace won't render because slug function breaks while recieving undefined object (cherry picked from commit 689e38b8610cc9e0b9b738d2774d0daa193dc309) --- frappe/public/js/frappe/widgets/links_widget.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/js/frappe/widgets/links_widget.js b/frappe/public/js/frappe/widgets/links_widget.js index 84758db592..2cc824899f 100644 --- a/frappe/public/js/frappe/widgets/links_widget.js +++ b/frappe/public/js/frappe/widgets/links_widget.js @@ -64,6 +64,7 @@ export default class LinksWidget extends Widget { const opts = { name: item.link_to, type: item.link_type, + doctype: item.doctype, is_query_report: item.is_query_report }; From 9140361188ed4620575b3ad8bebfdf9abaa80167 Mon Sep 17 00:00:00 2001 From: walstanb Date: Mon, 5 Jul 2021 12:08:24 +0530 Subject: [PATCH 338/495] fix: removed red indicator from alert --- frappe/public/js/frappe/ui/capture.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/ui/capture.js b/frappe/public/js/frappe/ui/capture.js index 676c997b31..d408fadb33 100644 --- a/frappe/public/js/frappe/ui/capture.js +++ b/frappe/public/js/frappe/ui/capture.js @@ -116,10 +116,7 @@ frappe.ui.Capture = class { }) .catch(err => { if (this.options.error) { - const alert = ` ${ - frappe.ui.Capture.ERR_MESSAGE - }`; - frappe.show_alert(alert, 3); + frappe.show_alert(frappe.ui.Capture.ERR_MESSAGE, 3); } throw err; From 6df9fd2bf89b3506a958db9dfd0049c89c2e006d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 5 Jul 2021 14:10:51 +0530 Subject: [PATCH 339/495] fix: Build asset file from results sequentially --- esbuild/esbuild.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esbuild/esbuild.js b/esbuild/esbuild.js index aa273ab85c..19ab559ccd 100644 --- a/esbuild/esbuild.js +++ b/esbuild/esbuild.js @@ -115,7 +115,7 @@ async function execute() { log("Watching for changes..."); } for (const result of results) { - write_assets_json(result.metafile); + await write_assets_json(result.metafile); } } From 70e4b8a459d53dec867bf294336335b9d02a9f2c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 5 Jul 2021 14:39:46 +0530 Subject: [PATCH 340/495] fix: Use different key for RTL assets --- esbuild/esbuild.js | 6 +++++- frappe/public/js/frappe/assets.js | 4 ++-- frappe/utils/jinja_globals.py | 7 ++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/esbuild/esbuild.js b/esbuild/esbuild.js index 19ab559ccd..9074beae06 100644 --- a/esbuild/esbuild.js +++ b/esbuild/esbuild.js @@ -385,7 +385,11 @@ async function write_assets_json(metafile) { let info = metafile.outputs[output]; let asset_path = "/" + path.relative(sites_path, output); if (info.entryPoint) { - out[path.basename(info.entryPoint)] = asset_path; + let key = path.basename(info.entryPoint); + if (key.endsWith('.css') && asset_path.includes('/css-rtl/')) { + key = `rtl_${key}`; + } + out[key] = asset_path; } } diff --git a/frappe/public/js/frappe/assets.js b/frappe/public/js/frappe/assets.js index 1f2659992f..0a3fb33cb8 100644 --- a/frappe/public/js/frappe/assets.js +++ b/frappe/public/js/frappe/assets.js @@ -170,10 +170,10 @@ frappe.assets = { bundled_asset(path, is_rtl=null) { if (!path.startsWith('/assets') && path.includes('.bundle.')) { - path = frappe.boot.assets_json[path] || path; if (path.endsWith('.css') && is_rtl) { - path = path.replace('/css/', '/css-rtl/'); + path = `rtl_${path}`; } + path = frappe.boot.assets_json[path] || path; return path; } return path; diff --git a/frappe/utils/jinja_globals.py b/frappe/utils/jinja_globals.py index dd5ade727b..67ca9d108a 100644 --- a/frappe/utils/jinja_globals.py +++ b/frappe/utils/jinja_globals.py @@ -75,9 +75,6 @@ def include_script(path): def include_style(path, rtl=None): path = bundled_asset(path) - - if is_rtl(rtl): - path = path.replace('/css/', '/css-rtl/') return f'' @@ -87,9 +84,9 @@ def bundled_asset(path, rtl=None): if ".bundle." in path and not path.startswith("/assets"): bundled_assets = get_assets_json() - path = bundled_assets.get(path) or path if path.endswith('.css') and is_rtl(rtl): - path = path.replace('/css/', '/css-rtl/') + path = f"rtl_{path}" + path = bundled_assets.get(path) or path return abs_url(path) From 6e92cda34aa91790429e24ab7c25203a6facea7f Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 5 Jul 2021 14:43:34 +0530 Subject: [PATCH 341/495] fix: Breadcrumb direction for RTL --- frappe/public/scss/desk/breadcrumb.scss | 4 ++-- frappe/public/scss/desk/css_variables.scss | 2 ++ frappe/public/scss/desk/dark.scss | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frappe/public/scss/desk/breadcrumb.scss b/frappe/public/scss/desk/breadcrumb.scss index 6324e6f012..b466bab7ae 100644 --- a/frappe/public/scss/desk/breadcrumb.scss +++ b/frappe/public/scss/desk/breadcrumb.scss @@ -4,7 +4,7 @@ background-color: white; font-size: var(--text-sm); } - +/*! This comment will be included even in compressed mode. */ #navbar-breadcrumbs { margin-left: var(--margin-md); font-size: var(--text-sm); @@ -12,7 +12,7 @@ font-size: var(--text-md); margin-right: 10px; &:before { - content: var(--right-arrow-svg); + content: #{"/*!rtl:var(--left-arrow-svg);*/"}var(--right-arrow-svg); display: inline-block; margin-right: 10px; } diff --git a/frappe/public/scss/desk/css_variables.scss b/frappe/public/scss/desk/css_variables.scss index 5bb2614dcc..c099a8bdeb 100644 --- a/frappe/public/scss/desk/css_variables.scss +++ b/frappe/public/scss/desk/css_variables.scss @@ -77,4 +77,6 @@ $input-height: 28px !default; --skeleton-bg: var(--gray-100); --right-arrow-svg: url("data: image/svg+xml;utf8, "); + + --left-arrow-svg: url("data: image/svg+xml;utf8, "); } diff --git a/frappe/public/scss/desk/dark.scss b/frappe/public/scss/desk/dark.scss index 76dcf90bc3..7f0dfe73b8 100644 --- a/frappe/public/scss/desk/dark.scss +++ b/frappe/public/scss/desk/dark.scss @@ -157,4 +157,6 @@ --skeleton-bg: var(--gray-800); --right-arrow-svg: url("data: image/svg+xml;utf8, "); + + --left-arrow-svg: url("data: image/svg+xml;utf8, "); } From 0b3402236cfd15259cf2bae29ae87f0f306ac951 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 5 Jul 2021 14:44:04 +0530 Subject: [PATCH 342/495] fix: Add layout direction to pdf template --- frappe/templates/print_formats/pdf_header_footer.html | 2 +- frappe/utils/pdf.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frappe/templates/print_formats/pdf_header_footer.html b/frappe/templates/print_formats/pdf_header_footer.html index a9c621f505..189cf43cd9 100644 --- a/frappe/templates/print_formats/pdf_header_footer.html +++ b/frappe/templates/print_formats/pdf_header_footer.html @@ -1,5 +1,5 @@ - + diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index fcf483bea6..9cbece5dbe 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -13,7 +13,7 @@ from PyPDF2 import PdfFileReader, PdfFileWriter import frappe from frappe import _ from frappe.utils import scrub_urls -from frappe.utils.jinja_globals import bundled_asset +from frappe.utils.jinja_globals import bundled_asset, is_rtl PDF_CONTENT_ERRORS = ["ContentNotFoundError", "ContentOperationNotPermittedError", "UnknownContentError", "RemoteHostClosedError"] @@ -177,7 +177,9 @@ def prepare_header_footer(soup): "content": content, "styles": styles, "html_id": html_id, - "css": css + "css": css, + "lang": frappe.local.lang, + "layout_direction": "rtl" if is_rtl else "ltr" }) # create temp file From 5b2a79ed2ec55b9fa8f1b78e77a22b58c2b9266b Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Mon, 5 Jul 2021 16:15:12 +0530 Subject: [PATCH 343/495] fix: cannot read property `0` of undefined --- frappe/public/js/frappe/views/workspace/workspace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index 80f8c00705..f16228dca0 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -252,7 +252,7 @@ class DesktopPage { return; } this.refresh(); - }).finally(this.page.find('.workspace_loading_skeleton').remove); + }).finally(() => this.page.find('.workspace_loading_skeleton').remove()); } refresh() { From 0228b9c8ebdd20b7b76522b697cc6c4bfe46c949 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 5 Jul 2021 18:02:35 +0530 Subject: [PATCH 344/495] fix: Add RTL control directive to override dropdown style in RTL mode --- frappe/public/scss/desk/global.scss | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frappe/public/scss/desk/global.scss b/frappe/public/scss/desk/global.scss index afcf2957cc..1168c8ce8c 100644 --- a/frappe/public/scss/desk/global.scss +++ b/frappe/public/scss/desk/global.scss @@ -302,6 +302,15 @@ select.input-xs { } } +/*!rtl:raw: +.dropdown-menu { + right: auto; +} +.popover { + right: auto; +} +*/ + .custom-control.custom-switch { font-size: var(--text-md); line-height: 1.6; From b5d9de1fc4b69233cc5497bd79ce97bf7956be9c Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Mon, 5 Jul 2021 20:03:19 +0530 Subject: [PATCH 345/495] fix: Option to pass any value for limit from client-side (#13452) * add unlimited support to frappe.db.get_list - js * fix: use 0 for getting all components in get_list * fix: use autocomplete instead of select for letterheads * fix: linting Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * chore: linting * fix: add placeholder to autocomplete Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/printing/page/print/print.js | 24 +++++++++--------------- frappe/public/js/frappe/db.js | 2 +- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/frappe/printing/page/print/print.js b/frappe/printing/page/print/print.js index 233bbe0ce7..908479fd02 100644 --- a/frappe/printing/page/print/print.js +++ b/frappe/printing/page/print/print.js @@ -113,22 +113,20 @@ frappe.ui.form.PrintView = class { }, ).$input; - this.letterhead_selector = this.add_sidebar_item( + this.letterhead_selector_df = this.add_sidebar_item( { - fieldtype: 'Select', + fieldtype: 'Autocomplete', fieldname: 'letterhead', label: __('Select Letterhead'), - options: [ - this.get_default_option_for_select(__('Select Letterhead')), - __('No Letterhead') - ], + placeholder: __('Select Letterhead'), + options: [__('No Letterhead')], change: () => this.preview(), default: this.print_settings.with_letterhead ? __('No Letterhead') : __('Select Letterhead') }, - ).$input; - + ); + this.letterhead_selector = this.letterhead_selector_df.$input; this.sidebar_dynamic_section = $( `
    ` ).appendTo(this.sidebar); @@ -336,23 +334,19 @@ frappe.ui.form.PrintView = class { } set_letterhead_options() { - let letterhead_options = [ - this.get_default_option_for_select(__('Select Letterhead')), - __('No Letterhead') - ]; + let letterhead_options = [__('No Letterhead')]; let default_letterhead; let doc_letterhead = this.frm.doc.letter_head; return frappe.db - .get_list('Letter Head', { fields: ['name', 'is_default'] }) + .get_list('Letter Head', { fields: ['name', 'is_default'], limit: 0 }) .then((letterheads) => { - this.letterhead_selector.empty(); letterheads.map((letterhead) => { if (letterhead.is_default) default_letterhead = letterhead.name; return letterhead_options.push(letterhead.name); }); - this.letterhead_selector.add_options(letterhead_options); + this.letterhead_selector_df.set_data(letterhead_options); let selected_letterhead = doc_letterhead || default_letterhead; if (selected_letterhead) this.letterhead_selector.val(selected_letterhead); diff --git a/frappe/public/js/frappe/db.js b/frappe/public/js/frappe/db.js index 89054e3791..2467302c76 100644 --- a/frappe/public/js/frappe/db.js +++ b/frappe/public/js/frappe/db.js @@ -10,7 +10,7 @@ frappe.db = { if (!args.fields) { args.fields = ['name']; } - if (!args.limit) { + if (!('limit' in args)) { args.limit = 20; } return new Promise ((resolve) => { From d1555263e1823f9cd6df626e0c5caa4203c508cf Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 5 Jul 2021 19:52:12 +0200 Subject: [PATCH 346/495] refactor: suggestions from review - use -f / -- format instead of output - get rid of ugly if / else statements --- frappe/commands/utils.py | 46 +++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 7d3aecca1f..1cecdffe1b 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -767,14 +767,15 @@ def set_config(context, key, value, global_=False, parse=False, as_dict=False): frappe.destroy() -@click.command('version') -@click.option('--output', help='Output format. One of: plain, table, json, legacy.', default='legacy') +@click.command("version") +@click.option("-f", "--format", "output", + type=click.Choice(["plain", "table", "json", "legacy"]), help="Output format", default="legacy") def get_version(output): - "Show the versions of all the installed apps" + """Show the versions of all the installed apps.""" from git import Repo from frappe.utils.commands import render_table - frappe.init('') + frappe.init("") data = [] for app in sorted(frappe.get_all_apps()): @@ -786,27 +787,28 @@ def get_version(output): app_info.app = app app_info.branch = repo.head.ref.name app_info.commit = repo.head.ref.commit.hexsha[:7] - - if hasattr(app_hooks, '{0}_version'.format(app_info.branch)): - app_info.version = getattr(app_hooks, '{0}_version'.format(app_info.branch)) - elif hasattr(module, "__version__"): - app_info.version = module.__version__ + app_info.version = getattr(app_hooks, f"{app_info.branch}_version", None) or module.__version__ data.append(app_info) - if output == 'table': - table = [['App', 'Version', 'Branch', 'Commit']] - for app_info in data: - table.append([app_info.app, app_info.version, app_info.branch, app_info.commit]) - render_table(table) - elif output == 'json': - click.echo(json.dumps(data, indent=4)) - elif output == 'plain': - for app_info in data: - click.echo(f'{app_info.app} {app_info.version} {app_info.branch} ({app_info.commit})') - else: # legacy - for app_info in data: - click.echo(f'{app_info.app} {app_info.version}') + { + "legacy": lambda: [ + click.echo(f"{app_info.app} {app_info.version}") + for app_info in data + ], + "plain": lambda: [ + click.echo(f"{app_info.app} {app_info.version} {app_info.branch} ({app_info.commit})") + for app_info in data + ], + "table": lambda: render_table( + [["App", "Version", "Branch", "Commit"]] + + [ + [app_info.app, app_info.version, app_info.branch, app_info.commit] + for app_info in data + ] + ), + "json": lambda: click.echo(json.dumps(data, indent=4)), + }[output]() @click.command('rebuild-global-search') From d7e0479ee742073fc1093ef9f2177153e93ae4a2 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 5 Jul 2021 19:53:02 +0200 Subject: [PATCH 347/495] test: add test for `bench version` --- frappe/tests/test_commands.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py index 07bdf8791e..8f9bbb1afb 100644 --- a/frappe/tests/test_commands.py +++ b/frappe/tests/test_commands.py @@ -426,3 +426,13 @@ class TestCommands(BaseTestCommands): self.assertEqual(self.returncode, 0) self.assertIn("pong", self.stdout) + def test_version(self): + self.execute("bench version") + self.assertEqual(self.returncode, 0) + + for output in ["legacy", "plain", "table", "json"]: + self.execute(f"bench version -f {output}") + self.assertEqual(self.returncode, 0) + + self.execute("bench version -f invalid") + self.assertEqual(self.returncode, 1) From 1fa7011b886c7de1ae77b817963994e5dbdf8c3d Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Tue, 6 Jul 2021 11:59:23 +0530 Subject: [PATCH 348/495] fix: system notifications without email --- frappe/desk/doctype/notification_log/notification_log.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/desk/doctype/notification_log/notification_log.py b/frappe/desk/doctype/notification_log/notification_log.py index 414f272f59..f5f1b3b124 100644 --- a/frappe/desk/doctype/notification_log/notification_log.py +++ b/frappe/desk/doctype/notification_log/notification_log.py @@ -12,7 +12,10 @@ class NotificationLog(Document): frappe.publish_realtime('notification', after_commit=True, user=self.for_user) set_notifications_as_unseen(self.for_user) if is_email_notifications_enabled_for_type(self.for_user, self.type): - send_notification_email(self) + try: + send_notification_email(self) + except frappe.OutgoingEmailError: + frappe.log_error(message = frappe.get_traceback(), title=_("Error Sending Notification Email(System Notification sent).")) def get_permission_query_conditions(for_user): From a1e84d234108c950d2d00a3df0eb70247c154966 Mon Sep 17 00:00:00 2001 From: Mitul David <49085834+MitulDavid@users.noreply.github.com> Date: Tue, 6 Jul 2021 13:28:18 +0530 Subject: [PATCH 349/495] fix: Accurately cast fieldtype in frappe.db.get_single_value() --- frappe/database/database.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index 81e24cc7ad..d113adcdab 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -15,7 +15,7 @@ from frappe import _ from time import time from frappe.utils import now, getdate, cast_fieldtype, get_datetime from frappe.model.utils.link_count import flush_local_link_count -from frappe.utils import cint +from frappe.utils import cint, cast_fieldtype class Database(object): @@ -556,8 +556,7 @@ class Database(object): if not df: frappe.throw(_('Invalid field name: {0}').format(frappe.bold(fieldname)), self.InvalidColumnName) - if df.fieldtype in frappe.model.numeric_fieldtypes: - val = cint(val) + val = cast_fieldtype(df.fieldtype, val) self.value_cache[doctype][fieldname] = val From 64840aac6bcca0e1af5203a0d3e005359ade0f1b Mon Sep 17 00:00:00 2001 From: Mitul David <49085834+MitulDavid@users.noreply.github.com> Date: Tue, 6 Jul 2021 14:13:27 +0530 Subject: [PATCH 350/495] refactor(minor): Delete redundant imports --- frappe/database/database.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index d113adcdab..6012e47445 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -15,7 +15,6 @@ from frappe import _ from time import time from frappe.utils import now, getdate, cast_fieldtype, get_datetime from frappe.model.utils.link_count import flush_local_link_count -from frappe.utils import cint, cast_fieldtype class Database(object): From 60322c58e1e371ce14ac47c6b579625eb7865a53 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 7 Jul 2021 12:29:45 +0530 Subject: [PATCH 351/495] fix: init driver only if necessary --- frappe/public/js/frappe/form/form_tour.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/form_tour.js b/frappe/public/js/frappe/form/form_tour.js index 5f4920f415..0cdefa1968 100644 --- a/frappe/public/js/frappe/form/form_tour.js +++ b/frappe/public/js/frappe/form/form_tour.js @@ -2,8 +2,6 @@ frappe.ui.form.FormTour = class FormTour { constructor({ frm }) { this.frm = frm; this.driver_steps = []; - - this.init_driver(); } init_driver() { @@ -48,6 +46,7 @@ frappe.ui.form.FormTour = class FormTour { if (on_finish) this.on_finish = on_finish; + this.init_driver(); this.build_steps(); this.update_driver_steps(); } From acf46c4ddd604da766d50cb5c44a16b847a0d59f Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 7 Jul 2021 12:31:04 +0530 Subject: [PATCH 352/495] fix: add missing semicolon --- frappe/public/js/frappe/form/form_tour.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/form_tour.js b/frappe/public/js/frappe/form/form_tour.js index 0cdefa1968..ee18db055e 100644 --- a/frappe/public/js/frappe/form/form_tour.js +++ b/frappe/public/js/frappe/form/form_tour.js @@ -38,7 +38,7 @@ frappe.ui.form.FormTour = class FormTour { } else { const doctype_tour_exists = await frappe.db.exists('Form Tour', this.frm.doctype); if (doctype_tour_exists) { - this.tour = await frappe.db.get_doc('Form Tour', this.frm.doctype) + this.tour = await frappe.db.get_doc('Form Tour', this.frm.doctype); } else { this.tour = { steps: frappe.tour[this.frm.doctype] }; } From e9ec018b23c4af7ecd314051e3aa58401c6fe177 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 7 Jul 2021 12:40:40 +0530 Subject: [PATCH 353/495] revert: "feat: submit on completion" --- frappe/desk/doctype/form_tour/form_tour.json | 10 +---- frappe/public/js/frappe/form/form_tour.js | 42 ++------------------ 2 files changed, 4 insertions(+), 48 deletions(-) diff --git a/frappe/desk/doctype/form_tour/form_tour.json b/frappe/desk/doctype/form_tour/form_tour.json index 44f39766b3..e4ea528fcc 100644 --- a/frappe/desk/doctype/form_tour/form_tour.json +++ b/frappe/desk/doctype/form_tour/form_tour.json @@ -11,7 +11,6 @@ "module", "is_standard", "save_on_complete", - "submit_on_complete", "section_break_3", "steps" ], @@ -63,18 +62,11 @@ "label": "Module", "options": "Module Def", "read_only": 1 - }, - { - "default": "0", - "depends_on": "save_on_complete", - "fieldname": "submit_on_complete", - "fieldtype": "Check", - "label": "Submit on Completion" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-06-29 15:23:26.893068", + "modified": "2021-06-06 20:32:54.068774", "modified_by": "Administrator", "module": "Desk", "name": "Form Tour", diff --git a/frappe/public/js/frappe/form/form_tour.js b/frappe/public/js/frappe/form/form_tour.js index ee18db055e..6bef2a0cb8 100644 --- a/frappe/public/js/frappe/form/form_tour.js +++ b/frappe/public/js/frappe/form/form_tour.js @@ -15,9 +15,8 @@ frappe.ui.form.FormTour = class FormTour { prevBtnText: 'Previous', opacity: 0.25, onHighlighted: (step) => { - // if last step is to save, or if is submit step - // then attach a listener on highlighted elem to reset the driver - if ((step.options.is_save_step && !this.driver.hasNextStep())|| step.options.is_submit_step) { + // if last step is to save, then attach a listener to save button + if (step.options.is_save_step) { $(step.options.element).one('click', () => this.driver.reset()); } @@ -74,10 +73,6 @@ frappe.ui.form.FormTour = class FormTour { if (this.tour.save_on_complete) { this.add_step_to_save(); } - - if (this.tour.submit_on_complete) { - this.add_step_to_submit(); - } } is_next_condition_satisfied(step) { @@ -259,37 +254,6 @@ frappe.ui.form.FormTour = class FormTour { } }; this.driver_steps.push(save_step); - - let after_save = () => this.on_finish && this.on_finish(); - - if (this.tour.submit_on_complete) { - after_save = () => { - this.update_driver_steps(); - this.driver.start(this.driver.steps.length - 1); - } - } - frappe.ui.form.on(this.frm.doctype, 'after_save', after_save); - } - - add_step_to_submit() { - const page_id = `[id="page-${this.frm.doctype}"]`; - const $submit_btn = `${page_id} .standard-actions .primary-action`; - const submit_step = { - element: $submit_btn, - is_submit_step: true, - allowClose: false, - overlayClickNext: false, - popover: { - title: __("Submit"), - description: "", - position: "left", - doneBtnText: __("Submit") - }, - onNext: () => { - this.frm.savesubmit(); - } - }; - this.driver_steps.push(submit_step); - frappe.ui.form.on(this.frm.doctype, 'after_submit', () => this.on_finish && this.on_finish()); + frappe.ui.form.on(this.frm.doctype, 'after_save', () => this.on_finish && this.on_finish()); } }; \ No newline at end of file From d25252b1e16f4292bec32ed130e6afc4451d0f37 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Wed, 7 Jul 2021 15:52:19 +0530 Subject: [PATCH 354/495] perf: Index workflow action fields (#13622) * feat: add number format parameter in doc.get_formatted * fix: add test cases * fix: test_document test * fix: test_document test fix * fix: readding test for test_formatter * perf: add indexes in workflow action * chore: commit auto created file * Revert "feat: add number format parameter in doc.get_formatted" This reverts commit 6bdcae1201de560971dc882c901ad18eb7f71f26. * Revert "fix: add test cases" This reverts commit 4bafae6e38d898096c49ce2bf5600f0772f05f17. * Revert "fix: test_document test" This reverts commit 3336915dae72836ff85396f2c344422348bc9e8d. * Revert "fix: readding test for test_formatter" This reverts commit f5e40141af794ae770df3c39ee130a8509b07f50. * chore: remove unwanted changes * chore: remove unwanted changes Co-authored-by: Kenneth Sequeira --- .../workflow_action/test_workflow_action.py | 8 + .../workflow_action/workflow_action.json | 223 ++---------------- 2 files changed, 27 insertions(+), 204 deletions(-) create mode 100644 frappe/workflow/doctype/workflow_action/test_workflow_action.py diff --git a/frappe/workflow/doctype/workflow_action/test_workflow_action.py b/frappe/workflow/doctype/workflow_action/test_workflow_action.py new file mode 100644 index 0000000000..a2f4f26193 --- /dev/null +++ b/frappe/workflow/doctype/workflow_action/test_workflow_action.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies and Contributors +# See license.txt + +# import frappe +import unittest + +class TestWorkflowAction(unittest.TestCase): + pass diff --git a/frappe/workflow/doctype/workflow_action/workflow_action.json b/frappe/workflow/doctype/workflow_action/workflow_action.json index 810f22c184..f1290d001f 100644 --- a/frappe/workflow/doctype/workflow_action/workflow_action.json +++ b/frappe/workflow/doctype/workflow_action/workflow_action.json @@ -1,262 +1,77 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, + "actions": [], "creation": "2018-05-17 18:29:03.923384", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "status", + "reference_name", + "reference_doctype", + "user", + "workflow_state", + "completed_by" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "status", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Status", - "length": 0, - "no_copy": 0, - "options": "Open\nCompleted", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Open\nCompleted" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "reference_name", "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Reference Name", - "length": 0, - "no_copy": 0, "options": "reference_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "reference_doctype", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Reference Document Type", - "length": 0, - "no_copy": 0, "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "user", "fieldtype": "Link", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "User", - "length": 0, - "no_copy": 0, "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "workflow_state", "fieldtype": "Data", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Workflow State", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "completed_by", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Completed By", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "User" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-09-05 14:22:27.664645", + "links": [], + "modified": "2021-07-01 09:07:52.848618", "modified_by": "Administrator", "module": "Workflow", "name": "Workflow Action", - "name_case": "", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 0, "delete": 1, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, "read": 1, - "report": 0, - "role": "All", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "All" } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", "title_field": "reference_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file From ecfa8c843f13e6ab7abb629a51a8ce7c44df386e Mon Sep 17 00:00:00 2001 From: codescientist703 Date: Wed, 7 Jul 2021 17:33:59 +0530 Subject: [PATCH 355/495] fix: child results should appear for parent search query --- frappe/desk/search.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/frappe/desk/search.py b/frappe/desk/search.py index 64ae7f7b7a..b0e9592c72 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -167,15 +167,16 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0, as_list=not as_dict, strict=False) - # Filtering the values array so that query is included in very element - values = tuple( - [ - v for v in list(values) - if re.search( - re.escape(txt) + ".*", (_(v.name) if as_dict else _(v[0])), re.IGNORECASE - ) - ] - ) + if doctype in UNTRANSLATED_DOCTYPES: + # Filtering the values array so that query is included in very element + values = tuple( + [ + v for v in list(values) + if re.search( + re.escape(txt) + ".*", (_(v.name) if as_dict else _(v[0])), re.IGNORECASE + ) + ] + ) # Sorting the values array so that relevant results always come first # This will first bring elements on top in which query is a prefix of element From 121ae7e36b03ec33b95c9a0b5ee6aa8200e6485e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 7 Jul 2021 17:58:45 +0530 Subject: [PATCH 356/495] fix: Allow to open folders that are nested inside 2 folders --- frappe/public/js/frappe/router.js | 8 ++++---- frappe/public/js/frappe/views/file/file_view.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 12caf4ab94..2bfa7c7be6 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -171,8 +171,8 @@ frappe.router = { } else { standard_route = ['List', doctype_route.doctype, frappe.utils.to_title_case(route[2])]; if (route[3]) { - // calendar / kanban / dashboard name - standard_route.push(route[3]); + // calendar / kanban / dashboard / folder name + standard_route.push(...route.splice(3, route.length)); } } return standard_route; @@ -297,8 +297,8 @@ frappe.router = { if (route[2] && route[2] !== 'list' && !$.isPlainObject(route[2])) { new_route = [this.slug(route[1]), 'view', route[2].toLowerCase()]; - // calendar / inbox - if (route[3]) new_route.push(route[3]); + // calendar / inbox / file folder + if (route[3]) new_route.push(...route.slice(3, route.length)); } else { if ($.isPlainObject(route[2])) { frappe.route_options = route[2]; diff --git a/frappe/public/js/frappe/views/file/file_view.js b/frappe/public/js/frappe/views/file/file_view.js index 6f0cdcc0f1..e020bff4dd 100644 --- a/frappe/public/js/frappe/views/file/file_view.js +++ b/frappe/public/js/frappe/views/file/file_view.js @@ -315,7 +315,7 @@ frappe.views.FileView = class FileView extends frappe.views.ListView { acc += "/" + curr; } return acc; - }, "/app/file"); + }, "/app/file/view"); return `
    ${folder}`; }) From ee75cea4fe22c79b3163c2a593c7be61622f4a9c Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 7 Jul 2021 22:39:12 +0530 Subject: [PATCH 357/495] fix: Enable set_open_count perf: move set_open_count to after_refresh to avoid unnecessary calls --- frappe/public/js/frappe/form/dashboard.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/dashboard.js b/frappe/public/js/frappe/form/dashboard.js index 6833f68073..b2b0c11d54 100644 --- a/frappe/public/js/frappe/form/dashboard.js +++ b/frappe/public/js/frappe/form/dashboard.js @@ -5,7 +5,6 @@ frappe.ui.form.Dashboard = class FormDashboard { constructor(opts) { $.extend(this, opts); this.setup_dashboard_sections(); - this.set_open_count = frappe.utils.throttle(this.set_open_count, 500); } setup_dashboard_sections() { @@ -179,7 +178,6 @@ frappe.ui.form.Dashboard = class FormDashboard { return; } this.render_links(); - // this.set_open_count(); show = true; } @@ -206,6 +204,7 @@ frappe.ui.form.Dashboard = class FormDashboard { $(this).removeClass('hidden'); } }); + this.set_open_count(); } init_data() { From ba062adca84dfecb0e129c292b30303156213db4 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 8 Jul 2021 13:36:25 +0530 Subject: [PATCH 358/495] refactor(search): Improvements in search_widget, search_link APIs * Minor perf enhancements * Renamed sorting_comparator to relevance_sorter --- frappe/desk/search.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/frappe/desk/search.py b/frappe/desk/search.py index b0e9592c72..3cce80a1a0 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -169,19 +169,17 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0, if doctype in UNTRANSLATED_DOCTYPES: # Filtering the values array so that query is included in very element - values = tuple( - [ - v for v in list(values) - if re.search( - re.escape(txt) + ".*", (_(v.name) if as_dict else _(v[0])), re.IGNORECASE - ) - ] + values = ( + v for v in values + if re.search( + f"{re.escape(txt)}.*", _(v.name if as_dict else v[0]), re.IGNORECASE ) + ) # Sorting the values array so that relevant results always come first # This will first bring elements on top in which query is a prefix of element # Then it will bring the rest of the elements and sort them in lexicographical order - values = sorted(values, key=lambda x: sorting_comparator(x, txt, as_dict)) + values = sorted(values, key=lambda x: relevance_sorter(x, txt, as_dict)) # remove _relevance from results if as_dict: @@ -221,10 +219,10 @@ def scrub_custom_query(query, key, txt): query = query.replace('%s', ((txt or '') + '%')) return query -def sorting_comparator(key, query, as_dict): - value = (_(key.name) if as_dict else _(key[0])) +def relevance_sorter(key, query, as_dict): + value = _(key.name if as_dict else key[0]) return ( - value.lower().startswith(query.lower()) is False, + value.lower().startswith(query.lower()) == False, value ) From 6fd762da966b7b7faa101a115d34e8e685a574f0 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Fri, 9 Jul 2021 18:02:19 +0530 Subject: [PATCH 359/495] fix: send me a copy not working --- frappe/core/doctype/communication/mixins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/communication/mixins.py b/frappe/core/doctype/communication/mixins.py index 82a47d24d9..160018c4a2 100644 --- a/frappe/core/doctype/communication/mixins.py +++ b/frappe/core/doctype/communication/mixins.py @@ -89,7 +89,7 @@ class CommunicationEmailMixin: return self._final_cc def get_mail_cc_with_displayname(self, is_inbound_mail_communcation=False, include_sender = False): - cc_list = self.mail_cc(is_inbound_mail_communcation=False, include_sender = False) + cc_list = self.mail_cc(is_inbound_mail_communcation=is_inbound_mail_communcation, include_sender = include_sender) return [self.get_email_with_displayname(email) for email in cc_list] def mail_bcc(self, is_inbound_mail_communcation=False): From 1e839f0ab1ca0e00a0492a2f67fcb7eab13a6720 Mon Sep 17 00:00:00 2001 From: Mitul David <49085834+MitulDavid@users.noreply.github.com> Date: Fri, 9 Jul 2021 18:36:06 +0530 Subject: [PATCH 360/495] test: Add test for frappe.db.get_single_value --- frappe/tests/test_db.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index a31a898d73..dcc70969a1 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -5,6 +5,7 @@ import unittest from random import choice +import datetime import frappe from frappe.custom.doctype.custom_field.custom_field import create_custom_field @@ -45,11 +46,27 @@ class TestDB(unittest.TestCase): frappe.db.escape("香港濟生堂製藥有限公司 - IT".encode("utf-8")) def test_get_single_value(self): - frappe.db.set_value('System Settings', 'System Settings', 'backup_limit', 5) - frappe.db.commit() + #setup + fieldtypes = ['Float', 'Int', 'Percent', 'Currency', 'Data', 'Date', 'Datetime', 'Time'] + values = [1.5, 1, 55.5, 12.5, 'Test', datetime.datetime.now().date(), datetime.datetime.now(), datetime.timedelta(hours=9, minutes=45, seconds=10)] + test_inputs = [{ + 'fieldtype': fieldtypes[i], + 'value': values[i]} for i in range(len(fieldtypes))] + for fieldtype in fieldtypes: + create_custom_field('Print Settings', { + "fieldname": 'test_'+fieldtype.lower(), + "label": 'Test '+fieldtype, + "fieldtype": fieldtype, + }) + + #test + for inp in test_inputs: + fieldname = 'test_'+inp['fieldtype'].lower() + frappe.db.set_value('Print Settings', 'Print Settings', fieldname, inp['value']) + self.assertEqual(frappe.db.get_single_value('Print Settings', fieldname), inp['value']) - limit = frappe.db.get_single_value('System Settings', 'backup_limit') - self.assertEqual(limit, 5) + #teardown + clear_custom_fields('Print Settings') def test_log_touched_tables(self): frappe.flags.in_migrate = True From 95550861a74eb1d56b72ef7536e111b703b2fa86 Mon Sep 17 00:00:00 2001 From: Mitul David <49085834+MitulDavid@users.noreply.github.com> Date: Fri, 9 Jul 2021 18:48:12 +0530 Subject: [PATCH 361/495] refactor(minor): Fix indentation --- frappe/tests/test_db.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index dcc70969a1..920ec0d827 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -57,8 +57,8 @@ class TestDB(unittest.TestCase): "fieldname": 'test_'+fieldtype.lower(), "label": 'Test '+fieldtype, "fieldtype": fieldtype, - }) - + }) + #test for inp in test_inputs: fieldname = 'test_'+inp['fieldtype'].lower() From d54bc4fc7068a627021d422956a2f09e024a9498 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sun, 11 Jul 2021 21:00:03 +0530 Subject: [PATCH 362/495] fix: Make scrollbar thumb rounded --- frappe/public/scss/desk/scrollbar.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/scss/desk/scrollbar.scss b/frappe/public/scss/desk/scrollbar.scss index d8fb49e5ce..9790ed0116 100644 --- a/frappe/public/scss/desk/scrollbar.scss +++ b/frappe/public/scss/desk/scrollbar.scss @@ -11,6 +11,7 @@ html { /* Works on Chrome, Edge, and Safari */ *::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb-color); + border-radius: 6px; } *::-webkit-scrollbar-track, From fe21efb76f82554602aa150b04d7a484cff9500a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 12 Jul 2021 10:06:06 +0530 Subject: [PATCH 363/495] fix: Keep charts as it is - Frappe Charts do not support RTL and rtlcss does not work on SVG... Setting direction as ltr to keep charts as it is --- frappe/public/scss/desk/global.scss | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/frappe/public/scss/desk/global.scss b/frappe/public/scss/desk/global.scss index 1168c8ce8c..d1205e0e38 100644 --- a/frappe/public/scss/desk/global.scss +++ b/frappe/public/scss/desk/global.scss @@ -302,15 +302,6 @@ select.input-xs { } } -/*!rtl:raw: -.dropdown-menu { - right: auto; -} -.popover { - right: auto; -} -*/ - .custom-control.custom-switch { font-size: var(--text-md); line-height: 1.6; @@ -583,3 +574,15 @@ details > summary:focus { // font-family: 'Octicons'; // content: "\f00b"; // } + +/*rtl:raw: +.dropdown-menu { + right: auto; +} +.popover { + right: auto; +} +.chart-container { + direction: ltr; +} +*/ \ No newline at end of file From 934fc17a3b0aa10c65cd24e87606c674070c61f7 Mon Sep 17 00:00:00 2001 From: Mitul David <49085834+MitulDavid@users.noreply.github.com> Date: Mon, 12 Jul 2021 15:52:57 +0530 Subject: [PATCH 364/495] refactor: Improve readability, consistency --- frappe/tests/test_db.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index 920ec0d827..fc87c5a336 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -47,26 +47,34 @@ class TestDB(unittest.TestCase): def test_get_single_value(self): #setup - fieldtypes = ['Float', 'Int', 'Percent', 'Currency', 'Data', 'Date', 'Datetime', 'Time'] - values = [1.5, 1, 55.5, 12.5, 'Test', datetime.datetime.now().date(), datetime.datetime.now(), datetime.timedelta(hours=9, minutes=45, seconds=10)] + values_dict = { + "Float": 1.5, + "Int": 1, + "Percent": 55.5, + "Currency": 12.5, + "Data": "Test", + "Date": datetime.datetime.now().date(), + "Datetime": datetime.datetime.now(), + "Time": datetime.timedelta(hours=9, minutes=45, seconds=10) + } test_inputs = [{ - 'fieldtype': fieldtypes[i], - 'value': values[i]} for i in range(len(fieldtypes))] - for fieldtype in fieldtypes: - create_custom_field('Print Settings', { - "fieldname": 'test_'+fieldtype.lower(), - "label": 'Test '+fieldtype, + "fieldtype": fieldtype, + "value": value} for fieldtype, value in values_dict.items()] + for fieldtype in values_dict.keys(): + create_custom_field("Print Settings", { + "fieldname": "test_{0}".format(fieldtype.lower()), + "label": "Test {0}".format(fieldtype), "fieldtype": fieldtype, }) #test for inp in test_inputs: - fieldname = 'test_'+inp['fieldtype'].lower() - frappe.db.set_value('Print Settings', 'Print Settings', fieldname, inp['value']) - self.assertEqual(frappe.db.get_single_value('Print Settings', fieldname), inp['value']) + fieldname = "test_{0}".format(inp["fieldtype"].lower()) + frappe.db.set_value("Print Settings", "Print Settings", fieldname, inp["value"]) + self.assertEqual(frappe.db.get_single_value("Print Settings", fieldname), inp["value"]) #teardown - clear_custom_fields('Print Settings') + clear_custom_fields("Print Settings") def test_log_touched_tables(self): frappe.flags.in_migrate = True From 10fde05950d1284a2aeafa15367260f4231fffa5 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 12 Jul 2021 17:43:15 +0530 Subject: [PATCH 365/495] fix: custom script to duplicate row not working in v13 (#13667) (cherry picked from commit 1615baf69a476a4c8361cd8af19e2edada6b4784) --- frappe/public/js/frappe/form/form.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 8064f90a98..faaa3dfbd9 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -1265,7 +1265,9 @@ frappe.ui.form.Form = class FrappeForm { if (df && df[property] != value) { df[property] = value; if (table_field && table_row_name) { - this.fields_dict[fieldname].grid.grid_rows_by_docname[table_row_name].refresh_field(fieldname); + if (this.fields_dict[fieldname].grid.grid_rows_by_docname[table_row_name]) { + this.fields_dict[fieldname].grid.grid_rows_by_docname[table_row_name].refresh_field(fieldname); + } } else { this.refresh_field(fieldname); } From 42585579e6faf3ed285752cc8e408dd59a2e3d90 Mon Sep 17 00:00:00 2001 From: Mitul David <49085834+MitulDavid@users.noreply.github.com> Date: Mon, 12 Jul 2021 19:13:29 +0530 Subject: [PATCH 366/495] refactor: Replace str.format() with f-strings --- frappe/tests/test_db.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index fc87c5a336..04c9a525b1 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -62,14 +62,14 @@ class TestDB(unittest.TestCase): "value": value} for fieldtype, value in values_dict.items()] for fieldtype in values_dict.keys(): create_custom_field("Print Settings", { - "fieldname": "test_{0}".format(fieldtype.lower()), - "label": "Test {0}".format(fieldtype), + "fieldname": f"test_{fieldtype.lower()}", + "label": f"Test {fieldtype}", "fieldtype": fieldtype, }) #test for inp in test_inputs: - fieldname = "test_{0}".format(inp["fieldtype"].lower()) + fieldname = f"test_{inp['fieldtype'].lower()}" frappe.db.set_value("Print Settings", "Print Settings", fieldname, inp["value"]) self.assertEqual(frappe.db.get_single_value("Print Settings", fieldname), inp["value"]) @@ -157,29 +157,29 @@ class TestDB(unittest.TestCase): # Testing read self.assertEqual(list(frappe.get_all("ToDo", fields=[random_field], limit=1)[0])[0], random_field) - self.assertEqual(list(frappe.get_all("ToDo", fields=["`{0}` as total".format(random_field)], limit=1)[0])[0], "total") + self.assertEqual(list(frappe.get_all("ToDo", fields=[f"`{random_field}` as total"], limit=1)[0])[0], "total") # Testing read for distinct and sql functions self.assertEqual(list( frappe.get_all("ToDo", - fields=["`{0}` as total".format(random_field)], + fields=[f"`{random_field}` as total"], distinct=True, limit=1, )[0] )[0], "total") self.assertEqual(list( frappe.get_all("ToDo", - fields=["`{0}`".format(random_field)], + fields=[f"`{random_field}`"], distinct=True, limit=1, )[0] )[0], random_field) self.assertEqual(list( frappe.get_all("ToDo", - fields=["count(`{0}`)".format(random_field)], + fields=[f"count(`{random_field}`)"], limit=1 )[0] - )[0], "count" if frappe.conf.db_type == "postgres" else "count(`{0}`)".format(random_field)) + )[0], "count" if frappe.conf.db_type == "postgres" else f"count(`{random_field}`)") # Testing update frappe.db.set_value(test_doctype, random_doc, random_field, random_value) From 959a5549d120b436f7469b7ff87cb919635f4e39 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 13 Jul 2021 09:51:40 +0530 Subject: [PATCH 367/495] fix(minor): website_builder.scss padding and line-height for cards --- frappe/public/scss/website/page_builder.scss | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/frappe/public/scss/website/page_builder.scss b/frappe/public/scss/website/page_builder.scss index 6eb6dae5d2..6e30836c71 100644 --- a/frappe/public/scss/website/page_builder.scss +++ b/frappe/public/scss/website/page_builder.scss @@ -151,14 +151,10 @@ } .card-title { - line-height: 1; + line-height: 1.3; } &.card-sm { - .card-body { - padding: 1.5rem; - } - .card-title { font-size: $font-size-base; font-weight: 600; @@ -169,10 +165,6 @@ } } &.card-md { - .card-body { - padding: 1.75rem; - } - .card-title { font-size: $font-size-lg; font-weight: 600; @@ -186,10 +178,6 @@ } } &.card-lg { - .card-body { - padding: 2rem; - } - .card-title { font-size: $font-size-xl; font-weight: bold; From 372447846efceb9f0af28cd7cb94e119a1bb0bf5 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 13 Jul 2021 10:14:43 +0530 Subject: [PATCH 368/495] fix(minor): website_builder.scss removed border for cards --- frappe/public/scss/website.bundle.scss | 2 +- frappe/public/scss/website/page_builder.scss | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/frappe/public/scss/website.bundle.scss b/frappe/public/scss/website.bundle.scss index bcbb6f3c6a..06ec73c386 100644 --- a/frappe/public/scss/website.bundle.scss +++ b/frappe/public/scss/website.bundle.scss @@ -1 +1 @@ -@import './website/index'; \ No newline at end of file +@import './website/index'; diff --git a/frappe/public/scss/website/page_builder.scss b/frappe/public/scss/website/page_builder.scss index 6e30836c71..ff9f4ae1e6 100644 --- a/frappe/public/scss/website/page_builder.scss +++ b/frappe/public/scss/website/page_builder.scss @@ -145,6 +145,11 @@ .section-with-cards .card { @include transition(); + border: none; + + .card-body { + padding: 0 1.5rem 2rem 0; + } &:hover { border-color: $gray-500; From 45cdf7a2695a4e123184982167e736e04a1c140e Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Tue, 13 Jul 2021 11:15:22 +0530 Subject: [PATCH 369/495] chore: correct error message --- frappe/desk/doctype/notification_log/notification_log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/doctype/notification_log/notification_log.py b/frappe/desk/doctype/notification_log/notification_log.py index f5f1b3b124..af09d65015 100644 --- a/frappe/desk/doctype/notification_log/notification_log.py +++ b/frappe/desk/doctype/notification_log/notification_log.py @@ -15,7 +15,7 @@ class NotificationLog(Document): try: send_notification_email(self) except frappe.OutgoingEmailError: - frappe.log_error(message = frappe.get_traceback(), title=_("Error Sending Notification Email(System Notification sent).")) + frappe.log_error(message = frappe.get_traceback(), title=_("Error Sending Notification Email.")) def get_permission_query_conditions(for_user): From dc50b77179326bd8f06fa524b6e4182ac1737ef5 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 23 Jun 2021 13:08:25 +0530 Subject: [PATCH 370/495] chore: Drop Data Import Legacy --- frappe/commands/utils.py | 32 +- .../doctype/data_import_legacy/__init__.py | 0 .../data_import_legacy/data_import_legacy.js | 324 ----------- .../data_import_legacy.json | 218 ------- .../data_import_legacy/data_import_legacy.py | 126 ---- .../data_import_legacy_list.js | 24 - .../doctype/data_import_legacy/importer.py | 538 ------------------ .../data_import_legacy/log_details.html | 38 -- .../test_data_import_legacy.py | 8 - 9 files changed, 6 insertions(+), 1302 deletions(-) delete mode 100644 frappe/core/doctype/data_import_legacy/__init__.py delete mode 100644 frappe/core/doctype/data_import_legacy/data_import_legacy.js delete mode 100644 frappe/core/doctype/data_import_legacy/data_import_legacy.json delete mode 100644 frappe/core/doctype/data_import_legacy/data_import_legacy.py delete mode 100644 frappe/core/doctype/data_import_legacy/data_import_legacy_list.js delete mode 100644 frappe/core/doctype/data_import_legacy/importer.py delete mode 100644 frappe/core/doctype/data_import_legacy/log_details.html delete mode 100644 frappe/core/doctype/data_import_legacy/test_data_import_legacy.py diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index ca58e78870..506ec7816b 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -358,32 +358,12 @@ def import_doc(context, path, force=False): @click.option('--no-email', default=True, is_flag=True, help='Send email if applicable') @pass_context def import_csv(context, path, only_insert=False, submit_after_import=False, ignore_encoding_errors=False, no_email=True): - "Import CSV using data import" - from frappe.core.doctype.data_import_legacy import importer - from frappe.utils.csvutils import read_csv_content - site = get_site(context) - - if not os.path.exists(path): - path = os.path.join('..', path) - if not os.path.exists(path): - print('Invalid path {0}'.format(path)) - sys.exit(1) - - with open(path, 'r') as csvfile: - content = read_csv_content(csvfile.read()) - - frappe.init(site=site) - frappe.connect() - - try: - importer.upload(content, submit_after_import=submit_after_import, no_email=no_email, - ignore_encoding_errors=ignore_encoding_errors, overwrite=not only_insert, - via_console=True) - frappe.db.commit() - except Exception: - print(frappe.get_traceback()) - - frappe.destroy() + click.secho( + "The `import-csv` command used 'Data Import Legacy' which has been deprecated.\n" + "Use `data-import` command instead to import data via 'Data Import'.", + fg="yellow", + ) + sys.exit(1) @click.command('data-import') diff --git a/frappe/core/doctype/data_import_legacy/__init__.py b/frappe/core/doctype/data_import_legacy/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/core/doctype/data_import_legacy/data_import_legacy.js b/frappe/core/doctype/data_import_legacy/data_import_legacy.js deleted file mode 100644 index 8e4f397171..0000000000 --- a/frappe/core/doctype/data_import_legacy/data_import_legacy.js +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Data Import Legacy', { - onload: function(frm) { - if (frm.doc.__islocal) { - frm.set_value("action", ""); - } - - frappe.call({ - method: "frappe.core.doctype.data_import_legacy.data_import_legacy.get_importable_doctypes", - callback: function (r) { - let importable_doctypes = r.message; - frm.set_query("reference_doctype", function () { - return { - "filters": { - "issingle": 0, - "istable": 0, - "name": ['in', importable_doctypes] - } - }; - }); - } - }), - - // should never check public - frm.fields_dict["import_file"].df.is_private = 1; - - frappe.realtime.on("data_import_progress", function(data) { - if (data.data_import === frm.doc.name) { - if (data.reload && data.reload === true) { - frm.reload_doc(); - } - if (data.progress) { - let progress_bar = $(frm.dashboard.progress_area.body).find(".progress-bar"); - if (progress_bar) { - $(progress_bar).removeClass("progress-bar-danger").addClass("progress-bar-success progress-bar-striped"); - $(progress_bar).css("width", data.progress + "%"); - } - } - } - }); - }, - - reference_doctype: function(frm){ - if (frm.doc.reference_doctype) { - frappe.model.with_doctype(frm.doc.reference_doctype); - } - }, - - refresh: function(frm) { - frm.disable_save(); - frm.dashboard.clear_headline(); - if (frm.doc.reference_doctype && !frm.doc.import_file) { - frm.page.set_indicator(__('Attach file'), 'orange'); - } else { - if (frm.doc.import_status) { - const listview_settings = frappe.listview_settings['Data Import Legacy']; - const indicator = listview_settings.get_indicator(frm.doc); - - frm.page.set_indicator(indicator[0], indicator[1]); - - if (frm.doc.import_status === "In Progress") { - frm.dashboard.add_progress("Data Import Progress", "0"); - frm.set_read_only(); - frm.refresh_fields(); - } - } - } - - if (frm.doc.reference_doctype) { - frappe.model.with_doctype(frm.doc.reference_doctype); - } - - if(frm.doc.action == "Insert new records" || frm.doc.action == "Update records") { - frm.set_df_property("action", "read_only", 1); - } - - frm.add_custom_button(__("Help"), function() { - frappe.help.show_video("6wiriRKPhmg"); - }); - - if (frm.doc.reference_doctype && frm.doc.docstatus === 0) { - frm.add_custom_button(__("Download template"), function() { - frappe.data_import.download_dialog(frm).show(); - }); - } - - if (frm.doc.reference_doctype && frm.doc.import_file && frm.doc.total_rows && - frm.doc.docstatus === 0 && (!frm.doc.import_status || frm.doc.import_status == "Failed")) { - frm.page.set_primary_action(__("Start Import"), function() { - frappe.call({ - btn: frm.page.btn_primary, - method: "frappe.core.doctype.data_import_legacy.data_import_legacy.import_data", - args: { - data_import: frm.doc.name - } - }); - }).addClass('btn btn-primary'); - } - - if (frm.doc.log_details) { - frm.events.create_log_table(frm); - } else { - $(frm.fields_dict.import_log.wrapper).empty(); - } - }, - - action: function(frm) { - if(!frm.doc.action) return; - if(!frm.doc.reference_doctype) { - frappe.msgprint(__("Please select document type first.")); - frm.set_value("action", ""); - return; - } - - if(frm.doc.action == "Insert new records") { - frm.doc.insert_new = 1; - } else if (frm.doc.action == "Update records"){ - frm.doc.overwrite = 1; - } - frm.save(); - }, - - only_update: function(frm) { - frm.save(); - }, - - submit_after_import: function(frm) { - frm.save(); - }, - - skip_errors: function(frm) { - frm.save(); - }, - - ignore_encoding_errors: function(frm) { - frm.save(); - }, - - no_email: function(frm) { - frm.save(); - }, - - show_only_errors: function(frm) { - frm.events.create_log_table(frm); - }, - - create_log_table: function(frm) { - let msg = JSON.parse(frm.doc.log_details); - var $log_wrapper = $(frm.fields_dict.import_log.wrapper).empty(); - $(frappe.render_template("log_details", { - data: msg.messages, - import_status: frm.doc.import_status, - show_only_errors: frm.doc.show_only_errors, - })).appendTo($log_wrapper); - } -}); - -frappe.provide('frappe.data_import'); -frappe.data_import.download_dialog = function(frm) { - var dialog; - const filter_fields = df => frappe.model.is_value_type(df) && !df.hidden; - const get_fields = dt => frappe.meta.get_docfields(dt).filter(filter_fields); - - const get_doctype_checkbox_fields = () => { - return dialog.fields.filter(df => df.fieldname.endsWith('_fields')) - .map(df => dialog.fields_dict[df.fieldname]); - }; - - const doctype_fields = get_fields(frm.doc.reference_doctype) - .map(df => { - let reqd = (df.reqd || df.fieldname == 'naming_series') ? 1 : 0; - return { - label: df.label, - reqd: reqd, - danger: reqd, - value: df.fieldname, - checked: 1 - }; - }); - - let fields = [ - { - "label": __("Select Columns"), - "fieldname": "select_columns", - "fieldtype": "Select", - "options": "All\nMandatory\nManually", - "reqd": 1, - "onchange": function() { - const fields = get_doctype_checkbox_fields(); - fields.map(f => f.toggle(true)); - if(this.value == 'Mandatory' || this.value == 'Manually') { - checkbox_toggle(true); - fields.map(multicheck_field => { - multicheck_field.options.map(option => { - if(!option.reqd) return; - $(multicheck_field.$wrapper).find(`:checkbox[data-unit="${option.value}"]`) - .prop('checked', false) - .trigger('click'); - }); - }); - } else if(this.value == 'All'){ - $(dialog.body).find(`[data-fieldtype="MultiCheck"] :checkbox`) - .prop('disabled', true); - } - } - }, - { - "label": __("File Type"), - "fieldname": "file_type", - "fieldtype": "Select", - "options": "Excel\nCSV", - "default": "Excel" - }, - { - "label": __("Download with Data"), - "fieldname": "with_data", - "fieldtype": "Check", - "hidden": !frm.doc.overwrite, - "default": 1 - }, - { - "label": __("Select All"), - "fieldname": "select_all", - "fieldtype": "Button", - "depends_on": "eval:doc.select_columns=='Manually'", - click: function() { - checkbox_toggle(); - } - }, - { - "label": __("Unselect All"), - "fieldname": "unselect_all", - "fieldtype": "Button", - "depends_on": "eval:doc.select_columns=='Manually'", - click: function() { - checkbox_toggle(true); - } - }, - { - "label": frm.doc.reference_doctype, - "fieldname": "doctype_fields", - "fieldtype": "MultiCheck", - "options": doctype_fields, - "columns": 2, - "hidden": 1 - } - ]; - - const child_table_fields = frappe.meta.get_table_fields(frm.doc.reference_doctype) - .map(df => { - return { - "label": df.options, - "fieldname": df.fieldname + '_fields', - "fieldtype": "MultiCheck", - "options": frappe.meta.get_docfields(df.options) - .filter(filter_fields) - .map(df => ({ - label: df.label, - reqd: df.reqd ? 1 : 0, - value: df.fieldname, - checked: 1, - danger: df.reqd - })), - "columns": 2, - "hidden": 1 - }; - }); - - fields = fields.concat(child_table_fields); - - dialog = new frappe.ui.Dialog({ - title: __('Download Template'), - fields: fields, - primary_action: function(values) { - var data = values; - if (frm.doc.reference_doctype) { - var export_params = () => { - let columns = {}; - if(values.select_columns) { - columns = get_doctype_checkbox_fields().reduce((columns, field) => { - const options = field.get_checked_options(); - columns[field.df.label] = options; - return columns; - }, {}); - } - - return { - doctype: frm.doc.reference_doctype, - parent_doctype: frm.doc.reference_doctype, - select_columns: JSON.stringify(columns), - with_data: frm.doc.overwrite && data.with_data, - all_doctypes: true, - file_type: data.file_type, - template: true - }; - }; - let get_template_url = '/api/method/frappe.core.doctype.data_export.exporter.export_data'; - open_url_post(get_template_url, export_params()); - } else { - frappe.msgprint(__("Please select the Document Type.")); - } - dialog.hide(); - }, - primary_action_label: __('Download') - }); - - $(dialog.body).find('div[data-fieldname="select_all"], div[data-fieldname="unselect_all"]') - .wrapAll('
    '); - const button_container = $(dialog.body).find('.inline-buttons'); - button_container.addClass('flex'); - $(button_container).find('.frappe-control').map((index, button) => { - $(button).css({"margin-right": "1em"}); - }); - - function checkbox_toggle(checked=false) { - $(dialog.body).find('[data-fieldtype="MultiCheck"]').map((index, element) => { - $(element).find(`:checkbox`).prop("checked", checked).trigger('click'); - }); - } - - return dialog; -}; diff --git a/frappe/core/doctype/data_import_legacy/data_import_legacy.json b/frappe/core/doctype/data_import_legacy/data_import_legacy.json deleted file mode 100644 index 852ccba156..0000000000 --- a/frappe/core/doctype/data_import_legacy/data_import_legacy.json +++ /dev/null @@ -1,218 +0,0 @@ -{ - "actions": [], - "allow_copy": 1, - "creation": "2020-06-11 16:13:23.813709", - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "reference_doctype", - "action", - "insert_new", - "overwrite", - "only_update", - "section_break_4", - "import_file", - "column_break_4", - "error_file", - "section_break_6", - "skip_errors", - "submit_after_import", - "ignore_encoding_errors", - "no_email", - "import_detail", - "import_status", - "show_only_errors", - "import_log", - "log_details", - "amended_from", - "total_rows", - "amended_from" - ], - "fields": [ - { - "fieldname": "reference_doctype", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "in_list_view": 1, - "label": "Document Type", - "options": "DocType", - "reqd": 1 - }, - { - "fieldname": "action", - "fieldtype": "Select", - "label": "Action", - "options": "Insert new records\nUpdate records", - "reqd": 1 - }, - { - "default": "0", - "depends_on": "eval:!doc.overwrite", - "description": "New data will be inserted.", - "fieldname": "insert_new", - "fieldtype": "Check", - "hidden": 1, - "label": "Insert new records", - "set_only_once": 1 - }, - { - "default": "0", - "depends_on": "eval:!doc.insert_new", - "description": "If you are updating/overwriting already created records.", - "fieldname": "overwrite", - "fieldtype": "Check", - "hidden": 1, - "label": "Update records", - "set_only_once": 1 - }, - { - "default": "0", - "depends_on": "overwrite", - "description": "If you don't want to create any new records while updating the older records.", - "fieldname": "only_update", - "fieldtype": "Check", - "label": "Don't create new records" - }, - { - "depends_on": "eval:(!doc.__islocal)", - "fieldname": "section_break_4", - "fieldtype": "Section Break" - }, - { - "fieldname": "import_file", - "fieldtype": "Attach", - "label": "Attach file for Import" - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "depends_on": "eval: doc.import_status == \"Partially Successful\"", - "description": "This is the template file generated with only the rows having some error. You should use this file for correction and import.", - "fieldname": "error_file", - "fieldtype": "Attach", - "label": "Generated File" - }, - { - "depends_on": "eval:(!doc.__islocal)", - "fieldname": "section_break_6", - "fieldtype": "Section Break" - }, - { - "default": "0", - "description": "If this is checked, rows with valid data will be imported and invalid rows will be dumped into a new file for you to import later.", - "fieldname": "skip_errors", - "fieldtype": "Check", - "label": "Skip rows with errors" - }, - { - "default": "0", - "fieldname": "submit_after_import", - "fieldtype": "Check", - "label": "Submit after importing" - }, - { - "default": "0", - "fieldname": "ignore_encoding_errors", - "fieldtype": "Check", - "label": "Ignore encoding errors" - }, - { - "default": "1", - "fieldname": "no_email", - "fieldtype": "Check", - "label": "Do not send Emails" - }, - { - "collapsible": 1, - "collapsible_depends_on": "eval: doc.import_status == \"Failed\"", - "depends_on": "import_status", - "fieldname": "import_detail", - "fieldtype": "Section Break", - "label": "Import Log" - }, - { - "fieldname": "import_status", - "fieldtype": "Select", - "label": "Import Status", - "options": "\nSuccessful\nFailed\nIn Progress\nPartially Successful", - "read_only": 1 - }, - { - "allow_on_submit": 1, - "default": "1", - "fieldname": "show_only_errors", - "fieldtype": "Check", - "label": "Show only errors", - "no_copy": 1, - "print_hide": 1 - }, - { - "allow_on_submit": 1, - "depends_on": "import_status", - "fieldname": "import_log", - "fieldtype": "HTML", - "label": "Import Log" - }, - { - "allow_on_submit": 1, - "fieldname": "log_details", - "fieldtype": "Code", - "hidden": 1, - "label": "Log Details", - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Data Import", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "total_rows", - "fieldtype": "Int", - "hidden": 1, - "label": "Total Rows", - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Data Import Legacy", - "print_hide": 1, - "read_only": 1 - } - ], - "is_submittable": 1, - "links": [], - "max_attachments": 1, - "modified": "2020-06-11 16:13:23.813709", - "modified_by": "Administrator", - "module": "Core", - "name": "Data Import Legacy", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "read": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 1 -} \ No newline at end of file diff --git a/frappe/core/doctype/data_import_legacy/data_import_legacy.py b/frappe/core/doctype/data_import_legacy/data_import_legacy.py deleted file mode 100644 index 63f806d75b..0000000000 --- a/frappe/core/doctype/data_import_legacy/data_import_legacy.py +++ /dev/null @@ -1,126 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, Frappe Technologies and contributors -# For license information, please see license.txt - -import os - -import frappe -import frappe.modules.import_file -from frappe import _ -from frappe.core.doctype.data_import_legacy.importer import upload -from frappe.model.document import Document -from frappe.modules.import_file import import_file_by_path as _import_file_by_path -from frappe.utils.background_jobs import enqueue -from frappe.utils.data import format_datetime - - -class DataImportLegacy(Document): - def autoname(self): - if not self.name: - self.name = "Import on " + format_datetime(self.creation) - - def validate(self): - if not self.import_file: - self.db_set("total_rows", 0) - if self.import_status == "In Progress": - frappe.throw(_("Can't save the form as data import is in progress.")) - - # validate the template just after the upload - # if there is total_rows in the doc, it means that the template is already validated and error free - if self.import_file and not self.total_rows: - upload(data_import_doc=self, from_data_import="Yes", validate_template=True) - - -@frappe.whitelist() -def get_importable_doctypes(): - return frappe.cache().hget("can_import", frappe.session.user) - - -@frappe.whitelist() -def import_data(data_import): - frappe.db.set_value("Data Import Legacy", data_import, "import_status", "In Progress", update_modified=False) - frappe.publish_realtime("data_import_progress", {"progress": "0", - "data_import": data_import, "reload": True}, user=frappe.session.user) - - from frappe.core.page.background_jobs.background_jobs import get_info - enqueued_jobs = [d.get("job_name") for d in get_info()] - - if data_import not in enqueued_jobs: - enqueue(upload, queue='default', timeout=6000, event='data_import', job_name=data_import, - data_import_doc=data_import, from_data_import="Yes", user=frappe.session.user) - - -def import_doc(path, overwrite=False, ignore_links=False, ignore_insert=False, - insert=False, submit=False, pre_process=None): - if os.path.isdir(path): - files = [os.path.join(path, f) for f in os.listdir(path)] - else: - files = [path] - - for f in files: - if f.endswith(".json"): - frappe.flags.mute_emails = True - _import_file_by_path(f, data_import=True, force=True, pre_process=pre_process, reset_permissions=True) - frappe.flags.mute_emails = False - frappe.db.commit() - elif f.endswith(".csv"): - import_file_by_path(f, ignore_links=ignore_links, overwrite=overwrite, submit=submit, pre_process=pre_process) - frappe.db.commit() - - -def import_file_by_path(path, ignore_links=False, overwrite=False, submit=False, pre_process=None, no_email=True): - from frappe.utils.csvutils import read_csv_content - print("Importing " + path) - with open(path, "r") as infile: - upload(rows=read_csv_content(infile.read()), ignore_links=ignore_links, no_email=no_email, overwrite=overwrite, - submit_after_import=submit, pre_process=pre_process) - - -def export_json(doctype, path, filters=None, or_filters=None, name=None, order_by="creation asc"): - def post_process(out): - del_keys = ('modified_by', 'creation', 'owner', 'idx') - for doc in out: - for key in del_keys: - if key in doc: - del doc[key] - for k, v in doc.items(): - if isinstance(v, list): - for child in v: - for key in del_keys + ('docstatus', 'doctype', 'modified', 'name'): - if key in child: - del child[key] - - out = [] - if name: - out.append(frappe.get_doc(doctype, name).as_dict()) - elif frappe.db.get_value("DocType", doctype, "issingle"): - out.append(frappe.get_doc(doctype).as_dict()) - else: - for doc in frappe.get_all(doctype, fields=["name"], filters=filters, or_filters=or_filters, limit_page_length=0, order_by=order_by): - out.append(frappe.get_doc(doctype, doc.name).as_dict()) - post_process(out) - - dirname = os.path.dirname(path) - if not os.path.exists(dirname): - path = os.path.join('..', path) - - with open(path, "w") as outfile: - outfile.write(frappe.as_json(out)) - - -def export_csv(doctype, path): - from frappe.core.doctype.data_export.exporter import export_data - with open(path, "wb") as csvfile: - export_data(doctype=doctype, all_doctypes=True, template=True, with_data=True) - csvfile.write(frappe.response.result.encode("utf-8")) - - -@frappe.whitelist() -def export_fixture(doctype, app): - if frappe.session.user != "Administrator": - raise frappe.PermissionError - - if not os.path.exists(frappe.get_app_path(app, "fixtures")): - os.mkdir(frappe.get_app_path(app, "fixtures")) - - export_json(doctype, frappe.get_app_path(app, "fixtures", frappe.scrub(doctype) + ".json"), order_by="name asc") diff --git a/frappe/core/doctype/data_import_legacy/data_import_legacy_list.js b/frappe/core/doctype/data_import_legacy/data_import_legacy_list.js deleted file mode 100644 index fcf2391313..0000000000 --- a/frappe/core/doctype/data_import_legacy/data_import_legacy_list.js +++ /dev/null @@ -1,24 +0,0 @@ -frappe.listview_settings['Data Import Legacy'] = { - add_fields: ["import_status"], - has_indicator_for_draft: 1, - get_indicator: function(doc) { - - let status = { - 'Successful': [__("Success"), "green", "import_status,=,Successful"], - 'Partially Successful': [__("Partial Success"), "blue", "import_status,=,Partially Successful"], - 'In Progress': [__("In Progress"), "orange", "import_status,=,In Progress"], - 'Failed': [__("Failed"), "red", "import_status,=,Failed"], - 'Pending': [__("Pending"), "orange", "import_status,=,"] - } - - if (doc.import_status) { - return status[doc.import_status]; - } - - if (doc.docstatus == 0) { - return status['Pending']; - } - - return status['Pending']; - } -}; diff --git a/frappe/core/doctype/data_import_legacy/importer.py b/frappe/core/doctype/data_import_legacy/importer.py deleted file mode 100644 index ceefff4410..0000000000 --- a/frappe/core/doctype/data_import_legacy/importer.py +++ /dev/null @@ -1,538 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import requests -import frappe, json -import frappe.permissions - -from frappe import _ - -from frappe.utils.csvutils import getlink -from frappe.utils.dateutils import parse_date - -from frappe.utils import cint, cstr, flt, getdate, get_datetime, get_url, get_absolute_url, duration_to_seconds - - -@frappe.whitelist() -def get_data_keys(): - return frappe._dict({ - "data_separator": _('Start entering data below this line'), - "main_table": _("Table") + ":", - "parent_table": _("Parent Table") + ":", - "columns": _("Column Name") + ":", - "doctype": _("DocType") + ":" - }) - - - -@frappe.whitelist() -def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, no_email=True, overwrite=None, - update_only = None, ignore_links=False, pre_process=None, via_console=False, from_data_import="No", - skip_errors = True, data_import_doc=None, validate_template=False, user=None): - """upload data""" - - # for translations - if user: - frappe.cache().hdel("lang", user) - frappe.set_user_lang(user) - - if data_import_doc and isinstance(data_import_doc, str): - data_import_doc = frappe.get_doc("Data Import Legacy", data_import_doc) - if data_import_doc and from_data_import == "Yes": - no_email = data_import_doc.no_email - ignore_encoding_errors = data_import_doc.ignore_encoding_errors - update_only = data_import_doc.only_update - submit_after_import = data_import_doc.submit_after_import - overwrite = data_import_doc.overwrite - skip_errors = data_import_doc.skip_errors - else: - # extra input params - params = json.loads(frappe.form_dict.get("params") or '{}') - if params.get("submit_after_import"): - submit_after_import = True - if params.get("ignore_encoding_errors"): - ignore_encoding_errors = True - if not params.get("no_email"): - no_email = False - if params.get('update_only'): - update_only = True - if params.get('from_data_import'): - from_data_import = params.get('from_data_import') - if not params.get('skip_errors'): - skip_errors = params.get('skip_errors') - - frappe.flags.in_import = True - frappe.flags.mute_emails = no_email - - def get_data_keys_definition(): - return get_data_keys() - - def bad_template(): - frappe.throw(_("Please do not change the rows above {0}").format(get_data_keys_definition().data_separator)) - - def check_data_length(): - if not data: - frappe.throw(_("No data found in the file. Please reattach the new file with data.")) - - def get_start_row(): - for i, row in enumerate(rows): - if row and row[0]==get_data_keys_definition().data_separator: - return i+1 - bad_template() - - def get_header_row(key): - return get_header_row_and_idx(key)[0] - - def get_header_row_and_idx(key): - for i, row in enumerate(header): - if row and row[0]==key: - return row, i - return [], -1 - - def filter_empty_columns(columns): - empty_cols = list(filter(lambda x: x in ("", None), columns)) - - if empty_cols: - if columns[-1*len(empty_cols):] == empty_cols: - # filter empty columns if they exist at the end - columns = columns[:-1*len(empty_cols)] - else: - frappe.msgprint(_("Please make sure that there are no empty columns in the file."), - raise_exception=1) - - return columns - - def make_column_map(): - doctype_row, row_idx = get_header_row_and_idx(get_data_keys_definition().doctype) - if row_idx == -1: # old style - return - - dt = None - for i, d in enumerate(doctype_row[1:]): - if d not in ("~", "-"): - if d and doctype_row[i] in (None, '' ,'~', '-', _("DocType") + ":"): - dt, parentfield = d, None - # xls format truncates the row, so it may not have more columns - if len(doctype_row) > i+2: - parentfield = doctype_row[i+2] - doctypes.append((dt, parentfield)) - column_idx_to_fieldname[(dt, parentfield)] = {} - column_idx_to_fieldtype[(dt, parentfield)] = {} - if dt: - column_idx_to_fieldname[(dt, parentfield)][i+1] = rows[row_idx + 2][i+1] - column_idx_to_fieldtype[(dt, parentfield)][i+1] = rows[row_idx + 4][i+1] - - def get_doc(start_idx): - if doctypes: - doc = {} - attachments = [] - last_error_row_idx = None - for idx in range(start_idx, len(rows)): - last_error_row_idx = idx # pylint: disable=W0612 - if (not doc) or main_doc_empty(rows[idx]): - for dt, parentfield in doctypes: - d = {} - for column_idx in column_idx_to_fieldname[(dt, parentfield)]: - try: - fieldname = column_idx_to_fieldname[(dt, parentfield)][column_idx] - fieldtype = column_idx_to_fieldtype[(dt, parentfield)][column_idx] - - if not fieldname or not rows[idx][column_idx]: - continue - - d[fieldname] = rows[idx][column_idx] - if fieldtype in ("Int", "Check"): - d[fieldname] = cint(d[fieldname]) - elif fieldtype in ("Float", "Currency", "Percent"): - d[fieldname] = flt(d[fieldname]) - elif fieldtype == "Date": - if d[fieldname] and isinstance(d[fieldname], str): - d[fieldname] = getdate(parse_date(d[fieldname])) - elif fieldtype == "Datetime": - if d[fieldname]: - if " " in d[fieldname]: - _date, _time = d[fieldname].split() - else: - _date, _time = d[fieldname], '00:00:00' - _date = parse_date(d[fieldname]) - d[fieldname] = get_datetime(_date + " " + _time) - else: - d[fieldname] = None - elif fieldtype == "Duration": - d[fieldname] = duration_to_seconds(cstr(d[fieldname])) - elif fieldtype in ("Image", "Attach Image", "Attach"): - # added file to attachments list - attachments.append(d[fieldname]) - - elif fieldtype in ("Link", "Dynamic Link", "Data") and d[fieldname]: - # as fields can be saved in the number format(long type) in data import template - d[fieldname] = cstr(d[fieldname]) - - except IndexError: - pass - - # scrub quotes from name and modified - if d.get("name") and d["name"].startswith('"'): - d["name"] = d["name"][1:-1] - - if sum(0 if not val else 1 for val in d.values()): - d['doctype'] = dt - if dt == doctype: - doc.update(d) - else: - if not overwrite and doc.get("name"): - d['parent'] = doc["name"] - d['parenttype'] = doctype - d['parentfield'] = parentfield - doc.setdefault(d['parentfield'], []).append(d) - else: - break - - return doc, attachments, last_error_row_idx - else: - doc = frappe._dict(zip(columns, rows[start_idx][1:])) - doc['doctype'] = doctype - return doc, [], None - - # used in testing whether a row is empty or parent row or child row - # checked only 3 first columns since first two columns can be blank for example the case of - # importing the item variant where item code and item name will be blank. - def main_doc_empty(row): - if row: - for i in range(3,0,-1): - if len(row) > i and row[i]: - return False - return True - - def validate_naming(doc): - autoname = frappe.get_meta(doctype).autoname - if autoname: - if autoname[0:5] == 'field': - autoname = autoname[6:] - elif autoname == 'naming_series:': - autoname = 'naming_series' - else: - return True - - if (autoname not in doc) or (not doc[autoname]): - from frappe.model.base_document import get_controller - if not hasattr(get_controller(doctype), "autoname"): - frappe.throw(_("{0} is a mandatory field").format(autoname)) - return True - - users = frappe.db.sql_list("select name from tabUser") - def prepare_for_insert(doc): - # don't block data import if user is not set - # migrating from another system - if not doc.owner in users: - doc.owner = frappe.session.user - if not doc.modified_by in users: - doc.modified_by = frappe.session.user - - def is_valid_url(url): - is_valid = False - if url.startswith("/files") or url.startswith("/private/files"): - url = get_url(url) - - try: - r = requests.get(url) - is_valid = True if r.status_code == 200 else False - except Exception: - pass - - return is_valid - - def attach_file_to_doc(doctype, docname, file_url): - # check if attachment is already available - # check if the attachement link is relative or not - if not file_url: - return - if not is_valid_url(file_url): - return - - files = frappe.db.sql("""Select name from `tabFile` where attached_to_doctype='{doctype}' and - attached_to_name='{docname}' and (file_url='{file_url}' or thumbnail_url='{file_url}')""".format( - doctype=doctype, - docname=docname, - file_url=file_url - )) - - if files: - # file is already attached - return - - _file = frappe.get_doc({ - "doctype": "File", - "file_url": file_url, - "attached_to_name": docname, - "attached_to_doctype": doctype, - "attached_to_field": 0, - "folder": "Home/Attachments"}) - _file.save() - - - # header - filename, file_extension = ['',''] - if not rows: - _file = frappe.get_doc("File", {"file_url": data_import_doc.import_file}) - fcontent = _file.get_content() - filename, file_extension = _file.get_extension() - - if file_extension == '.xlsx' and from_data_import == 'Yes': - from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file - rows = read_xlsx_file_from_attached_file(file_url=data_import_doc.import_file) - - elif file_extension == '.csv': - from frappe.utils.csvutils import read_csv_content - rows = read_csv_content(fcontent, ignore_encoding_errors) - - else: - frappe.throw(_("Unsupported File Format")) - - start_row = get_start_row() - header = rows[:start_row] - data = rows[start_row:] - try: - doctype = get_header_row(get_data_keys_definition().main_table)[1] - columns = filter_empty_columns(get_header_row(get_data_keys_definition().columns)[1:]) - except: - frappe.throw(_("Cannot change header content")) - doctypes = [] - column_idx_to_fieldname = {} - column_idx_to_fieldtype = {} - - if skip_errors: - data_rows_with_error = header - - if submit_after_import and not cint(frappe.db.get_value("DocType", - doctype, "is_submittable")): - submit_after_import = False - - parenttype = get_header_row(get_data_keys_definition().parent_table) - - if len(parenttype) > 1: - parenttype = parenttype[1] - - # check permissions - if not frappe.permissions.can_import(parenttype or doctype): - frappe.flags.mute_emails = False - return {"messages": [_("Not allowed to Import") + ": " + _(doctype)], "error": True} - - # Throw expception in case of the empty data file - check_data_length() - make_column_map() - total = len(data) - - if validate_template: - if total: - data_import_doc.total_rows = total - return True - - if overwrite==None: - overwrite = params.get('overwrite') - - # delete child rows (if parenttype) - parentfield = None - if parenttype: - parentfield = get_parent_field(doctype, parenttype) - - if overwrite: - delete_child_rows(data, doctype) - - import_log = [] - def log(**kwargs): - if via_console: - print((kwargs.get("title") + kwargs.get("message")).encode('utf-8')) - else: - import_log.append(kwargs) - - def as_link(doctype, name): - if via_console: - return "{0}: {1}".format(doctype, name) - else: - return getlink(doctype, name) - - # publish realtime task update - def publish_progress(achieved, reload=False): - if data_import_doc: - frappe.publish_realtime("data_import_progress", {"progress": str(int(100.0*achieved/total)), - "data_import": data_import_doc.name, "reload": reload}, user=frappe.session.user) - - - error_flag = rollback_flag = False - - batch_size = frappe.conf.data_import_batch_size or 1000 - - for batch_start in range(0, total, batch_size): - batch = data[batch_start:batch_start + batch_size] - - for i, row in enumerate(batch): - # bypass empty rows - if main_doc_empty(row): - continue - - row_idx = i + start_row - doc = None - - publish_progress(i) - - try: - doc, attachments, last_error_row_idx = get_doc(row_idx) - validate_naming(doc) - if pre_process: - pre_process(doc) - - original = None - if parentfield: - parent = frappe.get_doc(parenttype, doc["parent"]) - doc = parent.append(parentfield, doc) - parent.save() - else: - if overwrite and doc.get("name") and frappe.db.exists(doctype, doc["name"]): - original = frappe.get_doc(doctype, doc["name"]) - original_name = original.name - original.update(doc) - # preserve original name for case sensitivity - original.name = original_name - original.flags.ignore_links = ignore_links - original.save() - doc = original - else: - if not update_only: - doc = frappe.get_doc(doc) - prepare_for_insert(doc) - doc.flags.ignore_links = ignore_links - doc.insert() - if attachments: - # check file url and create a File document - for file_url in attachments: - attach_file_to_doc(doc.doctype, doc.name, file_url) - if submit_after_import: - doc.submit() - - # log errors - if parentfield: - log(**{"row": doc.idx, "title": 'Inserted row for "%s"' % (as_link(parenttype, doc.parent)), - "link": get_absolute_url(parenttype, doc.parent), "message": 'Document successfully saved', "indicator": "green"}) - elif submit_after_import: - log(**{"row": row_idx + 1, "title":'Submitted row for "%s"' % (as_link(doc.doctype, doc.name)), - "message": "Document successfully submitted", "link": get_absolute_url(doc.doctype, doc.name), "indicator": "blue"}) - elif original: - log(**{"row": row_idx + 1,"title":'Updated row for "%s"' % (as_link(doc.doctype, doc.name)), - "message": "Document successfully updated", "link": get_absolute_url(doc.doctype, doc.name), "indicator": "green"}) - elif not update_only: - log(**{"row": row_idx + 1, "title":'Inserted row for "%s"' % (as_link(doc.doctype, doc.name)), - "message": "Document successfully saved", "link": get_absolute_url(doc.doctype, doc.name), "indicator": "green"}) - else: - log(**{"row": row_idx + 1, "title":'Ignored row for %s' % (row[1]), "link": None, - "message": "Document updation ignored", "indicator": "orange"}) - - except Exception as e: - error_flag = True - - # build error message - if frappe.local.message_log: - err_msg = "\n".join(['

    {}

    '.format(json.loads(msg).get('message')) for msg in frappe.local.message_log]) - else: - err_msg = '

    {}

    '.format(cstr(e)) - - error_trace = frappe.get_traceback() - if error_trace: - error_log_doc = frappe.log_error(error_trace) - error_link = get_absolute_url("Error Log", error_log_doc.name) - else: - error_link = None - - log(**{ - "row": row_idx + 1, - "title": 'Error for row %s' % (len(row)>1 and frappe.safe_decode(row[1]) or ""), - "message": err_msg, - "indicator": "red", - "link":error_link - }) - - # data with error to create a new file - # include the errored data in the last row as last_error_row_idx will not be updated for the last row - if skip_errors: - if last_error_row_idx == len(rows)-1: - last_error_row_idx = len(rows) - data_rows_with_error += rows[row_idx:last_error_row_idx] - else: - rollback_flag = True - finally: - frappe.local.message_log = [] - - start_row += batch_size - if rollback_flag: - frappe.db.rollback() - else: - frappe.db.commit() - - frappe.flags.mute_emails = False - frappe.flags.in_import = False - - log_message = {"messages": import_log, "error": error_flag} - if data_import_doc: - data_import_doc.log_details = json.dumps(log_message) - - import_status = None - if error_flag and data_import_doc.skip_errors and len(data) != len(data_rows_with_error): - import_status = "Partially Successful" - # write the file with the faulty row - file_name = 'error_' + filename + file_extension - if file_extension == '.xlsx': - from frappe.utils.xlsxutils import make_xlsx - xlsx_file = make_xlsx(data_rows_with_error, "Data Import Template") - file_data = xlsx_file.getvalue() - else: - from frappe.utils.csvutils import to_csv - file_data = to_csv(data_rows_with_error) - _file = frappe.get_doc({ - "doctype": "File", - "file_name": file_name, - "attached_to_doctype": "Data Import Legacy", - "attached_to_name": data_import_doc.name, - "folder": "Home/Attachments", - "content": file_data}) - _file.save() - data_import_doc.error_file = _file.file_url - - elif error_flag: - import_status = "Failed" - else: - import_status = "Successful" - - data_import_doc.import_status = import_status - data_import_doc.save() - if data_import_doc.import_status in ["Successful", "Partially Successful"]: - data_import_doc.submit() - publish_progress(100, True) - else: - publish_progress(0, True) - frappe.db.commit() - else: - return log_message - -def get_parent_field(doctype, parenttype): - parentfield = None - - # get parentfield - if parenttype: - for d in frappe.get_meta(parenttype).get_table_fields(): - if d.options==doctype: - parentfield = d.fieldname - break - - if not parentfield: - frappe.msgprint(_("Did not find {0} for {0} ({1})").format("parentfield", parenttype, doctype)) - raise Exception - - return parentfield - -def delete_child_rows(rows, doctype): - """delete child rows for all parents""" - for p in list(set(r[1] for r in rows)): - if p: - frappe.db.sql("""delete from `tab{0}` where parent=%s""".format(doctype), p) diff --git a/frappe/core/doctype/data_import_legacy/log_details.html b/frappe/core/doctype/data_import_legacy/log_details.html deleted file mode 100644 index aa160a742b..0000000000 --- a/frappe/core/doctype/data_import_legacy/log_details.html +++ /dev/null @@ -1,38 +0,0 @@ -
    -
    - - - - - - - - {% for row in data %} - {% if (!show_only_errors) || (show_only_errors && row.indicator == "red") %} - - - - - - {% endif %} - {% endfor %} -
    {{ __("Row No") }} {{ __("Row Status") }} {{ __("Message") }}
    - {{ row.row }} - - {{ row.title }} - - {% if (import_status != "Failed" || (row.indicator == "red")) { %} -
    {{ row.message }}
    - {% if row.link %} - - - - - - {% endif %} - {% } else { %} - {{ __("Document can't saved.") }} - {% } %} -
    -
    -
    \ No newline at end of file diff --git a/frappe/core/doctype/data_import_legacy/test_data_import_legacy.py b/frappe/core/doctype/data_import_legacy/test_data_import_legacy.py deleted file mode 100644 index 6f9964e8f5..0000000000 --- a/frappe/core/doctype/data_import_legacy/test_data_import_legacy.py +++ /dev/null @@ -1,8 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2020, Frappe Technologies and Contributors -# See license.txt -# import frappe -import unittest - -class TestDataImportLegacy(unittest.TestCase): - pass From 0d0e527416c268b5a74284dbd849d79be010ebfe Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 23 Jun 2021 13:22:11 +0530 Subject: [PATCH 371/495] chore: Show deprecation warning in command help --- frappe/commands/utils.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 506ec7816b..2fe5ca5929 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import json import os import subprocess @@ -14,6 +12,13 @@ from frappe.exceptions import SiteNotSpecifiedError from frappe.utils import get_bench_path, update_progress_bar, cint +DATA_IMPORT_DEPRECATION = click.style( + "[DEPRECATED] The `import-csv` command used 'Data Import Legacy' which has been deprecated.\n" + "Use `data-import` command instead to import data via 'Data Import'.", + fg="yellow" +) + + @click.command('build') @click.option('--app', help='Build assets for app') @click.option('--apps', help='Build assets for specific apps') @@ -350,7 +355,8 @@ def import_doc(context, path, force=False): if not context.sites: raise SiteNotSpecifiedError -@click.command('import-csv') + +@click.command('import-csv', help=DATA_IMPORT_DEPRECATION) @click.argument('path') @click.option('--only-insert', default=False, is_flag=True, help='Do not overwrite existing records') @click.option('--submit-after-import', default=False, is_flag=True, help='Submit document after importing it') @@ -358,11 +364,7 @@ def import_doc(context, path, force=False): @click.option('--no-email', default=True, is_flag=True, help='Send email if applicable') @pass_context def import_csv(context, path, only_insert=False, submit_after_import=False, ignore_encoding_errors=False, no_email=True): - click.secho( - "The `import-csv` command used 'Data Import Legacy' which has been deprecated.\n" - "Use `data-import` command instead to import data via 'Data Import'.", - fg="yellow", - ) + click.secho(DATA_IMPORT_DEPRECATION) sys.exit(1) From baea532c3d19fd9d55dd2b10cd676e7547b378bb Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 13 Jul 2021 11:27:01 +0530 Subject: [PATCH 372/495] fix: Move depr module module utils in exporter --- frappe/core/doctype/data_export/exporter.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/data_export/exporter.py b/frappe/core/doctype/data_export/exporter.py index 389948449e..ffd828bfdb 100644 --- a/frappe/core/doctype/data_export/exporter.py +++ b/frappe/core/doctype/data_export/exporter.py @@ -7,7 +7,6 @@ import frappe.permissions import re, csv, os from frappe.utils.csvutils import UnicodeWriter from frappe.utils import cstr, formatdate, format_datetime, parse_json, cint, format_duration -from frappe.core.doctype.data_import_legacy.importer import get_data_keys from frappe.core.doctype.access_log.access_log import make_access_log reflags = { @@ -20,6 +19,15 @@ reflags = { "D": re.DEBUG } +def get_data_keys(): + return frappe._dict({ + "data_separator": _('Start entering data below this line'), + "main_table": _("Table") + ":", + "parent_table": _("Parent Table") + ":", + "columns": _("Column Name") + ":", + "doctype": _("DocType") + ":" + }) + @frappe.whitelist() def export_data(doctype=None, parent_doctype=None, all_doctypes=True, with_data=False, select_columns=None, file_type='CSV', template=False, filters=None): From 2aa2586a0cb1391fe07e40811b7d2c7cf09abc8d Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 13 Jul 2021 12:04:18 +0530 Subject: [PATCH 373/495] fix: Drop deprecated validate_csv_import_file def --- frappe/core/doctype/data_import/data_import.py | 18 ++---------------- frappe/utils/fixtures.py | 2 +- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/frappe/core/doctype/data_import/data_import.py b/frappe/core/doctype/data_import/data_import.py index 7e8374a0a2..50469eeb4d 100644 --- a/frappe/core/doctype/data_import/data_import.py +++ b/frappe/core/doctype/data_import/data_import.py @@ -171,9 +171,6 @@ def import_file( i.import_data() -############## - - def import_doc(path, pre_process=None): if os.path.isdir(path): files = [os.path.join(path, f) for f in os.listdir(path)] @@ -192,19 +189,8 @@ def import_doc(path, pre_process=None): ) frappe.flags.mute_emails = False frappe.db.commit() - elif f.endswith(".csv"): - validate_csv_import_file(f) - frappe.db.commit() - - -def validate_csv_import_file(path): - if path.endswith(".csv"): - print() - print("This method is deprecated.") - print('Import CSV files using the command "bench --site sitename data-import"') - print("Or use the method frappe.core.doctype.data_import.data_import.import_file") - print() - raise Exception("Method deprecated") + else: + raise NotImplementedError("Only .json files can be imported") def export_json( diff --git a/frappe/utils/fixtures.py b/frappe/utils/fixtures.py index 1f33c36b13..895a3c8373 100644 --- a/frappe/utils/fixtures.py +++ b/frappe/utils/fixtures.py @@ -20,7 +20,7 @@ def sync_fixtures(app=None): if os.path.exists(frappe.get_app_path(app, "fixtures")): fixture_files = sorted(os.listdir(frappe.get_app_path(app, "fixtures"))) for fname in fixture_files: - if fname.endswith(".json") or fname.endswith(".csv"): + if fname.endswith(".json"): import_doc(frappe.get_app_path(app, "fixtures", fname)) import_custom_scripts(app) From 8dcb7a1eaac14f4e68e4644b4481ceb06cae8ca5 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 13 Jul 2021 12:04:50 +0530 Subject: [PATCH 374/495] style: Remove redundant comments --- frappe/core/doctype/access_log/access_log.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frappe/core/doctype/access_log/access_log.py b/frappe/core/doctype/access_log/access_log.py index d2fbee108b..2ea014f981 100644 --- a/frappe/core/doctype/access_log/access_log.py +++ b/frappe/core/doctype/access_log/access_log.py @@ -1,9 +1,5 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt - -# imports - standard imports -# imports - module imports import frappe from frappe.model.document import Document From 58cb8e5dd1516432ef319802e856472f419d3135 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Tue, 13 Jul 2021 12:46:01 +0530 Subject: [PATCH 375/495] style: Fix minor formatting issues - Also, updated error title --- frappe/desk/doctype/notification_log/notification_log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/doctype/notification_log/notification_log.py b/frappe/desk/doctype/notification_log/notification_log.py index af09d65015..d7d7f68b74 100644 --- a/frappe/desk/doctype/notification_log/notification_log.py +++ b/frappe/desk/doctype/notification_log/notification_log.py @@ -15,7 +15,7 @@ class NotificationLog(Document): try: send_notification_email(self) except frappe.OutgoingEmailError: - frappe.log_error(message = frappe.get_traceback(), title=_("Error Sending Notification Email.")) + frappe.log_error(message=frappe.get_traceback(), title=_("Failed to send notification email")) def get_permission_query_conditions(for_user): From e826866503287ed67696113b5b789ced053744ae Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 13 Jul 2021 14:24:12 +0530 Subject: [PATCH 376/495] fix: Tag count query --- frappe/desk/reportview.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 55515856f1..b9382bc830 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -445,17 +445,24 @@ def get_stats(stats, doctype, filters=[]): for tag in tags: if not tag in columns: continue try: - tagcount = frappe.get_list(doctype, fields=[tag, "count(*)"], - #filters=["ifnull(`%s`,'')!=''" % tag], group_by=tag, as_list=True) - filters = filters + ["ifnull(`%s`,'')!=''" % tag], group_by = tag, as_list = True) + tag_count = frappe.get_list(doctype, + fields=[tag, "count(*)"], + filters=filters + [[tag, '!=', '']], + group_by=tag, + as_list=True, + ) - if tag=='_user_tags': - stats[tag] = scrub_user_tags(tagcount) - stats[tag].append([_("No Tags"), frappe.get_list(doctype, + if tag == '_user_tags': + stats[tag] = scrub_user_tags(tag_count) + no_tag_count = frappe.get_list(doctype, fields=[tag, "count(*)"], - filters=filters +["({0} = ',' or {0} = '' or {0} is null)".format(tag)], as_list=True)[0][1]]) + filters=filters + [[tag, "in", ('', ',')]], + as_list=True, + )[0][1] + + stats[tag].append([_("No Tags"), no_tag_count]) else: - stats[tag] = tagcount + stats[tag] = tag_count except frappe.db.SQLError: # does not work for child tables From ae70502f910c85f6a4528b487eea3b535cec6c39 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 13 Jul 2021 15:34:36 +0530 Subject: [PATCH 377/495] test: Add test case to validate tag count query --- frappe/desk/doctype/tag/test_tag.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/frappe/desk/doctype/tag/test_tag.py b/frappe/desk/doctype/tag/test_tag.py index 442a891fd8..6eb7219c26 100644 --- a/frappe/desk/doctype/tag/test_tag.py +++ b/frappe/desk/doctype/tag/test_tag.py @@ -1,8 +1,26 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2019, Frappe Technologies and Contributors -# See license.txt -# import frappe import unittest +import frappe + +from frappe.desk.reportview import get_stats +from frappe.desk.doctype.tag.tag import add_tag class TestTag(unittest.TestCase): - pass + def setUp(self) -> None: + frappe.db.sql("DELETE from `tabTag`") + frappe.db.sql("UPDATE `tabDocType` set _user_tags=''") + + def test_tag_count_query(self): + self.assertDictEqual(get_stats('["_user_tags"]', 'DocType'), + {'_user_tags': [['No Tags', frappe.db.count('DocType')]]}) + add_tag('Standard', 'DocType', 'User') + add_tag('Standard', 'DocType', 'ToDo') + + # count with no filter + self.assertDictEqual(get_stats('["_user_tags"]', 'DocType'), + {'_user_tags': [['Standard', 2], ['No Tags', frappe.db.count('DocType') - 2]]}) + + # count with child table field filter + self.assertDictEqual(get_stats('["_user_tags"]', + 'DocType', + filters='[["DocField", "fieldname", "like", "%last_name%"], ["DocType", "name", "like", "%use%"]]'), + {'_user_tags': [['Standard', 1], ['No Tags', 0]]}) \ No newline at end of file From 68930a022aafd97cf95d7fdce312ff82bf963ee8 Mon Sep 17 00:00:00 2001 From: Bhavesh Maheshwari <34086262+bhavesh95863@users.noreply.github.com> Date: Tue, 13 Jul 2021 15:55:24 +0530 Subject: [PATCH 378/495] fix: Duplicate if condition remove --- frappe/utils/global_search.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index efe92232d9..8fa2ea474f 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -231,9 +231,6 @@ def update_global_search(doc): if frappe.local.conf.get('disable_global_search'): return - if frappe.local.conf.get('disable_global_search'): - return - if doc.docstatus > 1 or (doc.meta.has_field("enabled") and not doc.get("enabled")) \ or doc.get("disabled"): return From 8bc498c18cc4b6932768bb618ee8d27fce93fa21 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 13 Jul 2021 10:42:00 +0000 Subject: [PATCH 379/495] chore(deps): [security] bump minimist from 1.2.0 to 1.2.5 Bumps [minimist](https://github.com/substack/minimist) from 1.2.0 to 1.2.5. **This update includes security fixes.** - [Release notes](https://github.com/substack/minimist/releases) - [Commits](https://github.com/substack/minimist/compare/1.2.0...1.2.5) Signed-off-by: dependabot-preview[bot] --- yarn.lock | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index ddb5623e5e..da7f89872f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4148,16 +4148,11 @@ minimatch@^3.0.4, minimatch@~3.0.2: dependencies: brace-expansion "^1.1.7" -minimist@^1.1.3, minimist@^1.2.5: +minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= - minipass@^3.0.0: version "3.1.3" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" From 04094110f5ddb5b2d4c514be43328d9f0e893654 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 13 Jul 2021 13:11:15 +0200 Subject: [PATCH 380/495] fix: handle detached head Workaround for https://github.com/gitpython-developers/GitPython/issues/633 --- frappe/commands/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 5c6274d348..542f41725b 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -774,6 +774,7 @@ def get_version(output): """Show the versions of all the installed apps.""" from git import Repo from frappe.utils.commands import render_table + from frappe.utils.change_log import get_app_branch frappe.init("") data = [] @@ -785,8 +786,8 @@ def get_version(output): app_info = frappe._dict() app_info.app = app - app_info.branch = repo.head.ref.name - app_info.commit = repo.head.ref.commit.hexsha[:7] + app_info.branch = get_app_branch(app) + app_info.commit = repo.head.object.hexsha[:7] app_info.version = getattr(app_hooks, f"{app_info.branch}_version", None) or module.__version__ data.append(app_info) From fc2c887635168cd18bf18b6eae74fedc58cb9a22 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 13 Jul 2021 13:23:25 +0200 Subject: [PATCH 381/495] test: fix return code --- frappe/tests/test_commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py index 8f9bbb1afb..54103f0151 100644 --- a/frappe/tests/test_commands.py +++ b/frappe/tests/test_commands.py @@ -435,4 +435,4 @@ class TestCommands(BaseTestCommands): self.assertEqual(self.returncode, 0) self.execute("bench version -f invalid") - self.assertEqual(self.returncode, 1) + self.assertEqual(self.returncode, 2) From cf67dbce5abd96687ab3c8a05a74cecf3bf7792b Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 13 Jul 2021 16:56:49 +0530 Subject: [PATCH 382/495] fix: Add missing fieldtypes in for custom fields --- frappe/custom/doctype/custom_field/custom_field.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/custom/doctype/custom_field/custom_field.json b/frappe/custom/doctype/custom_field/custom_field.json index 2f0819ab68..19462e79de 100644 --- a/frappe/custom/doctype/custom_field/custom_field.json +++ b/frappe/custom/doctype/custom_field/custom_field.json @@ -120,7 +120,7 @@ "label": "Field Type", "oldfieldname": "fieldtype", "oldfieldtype": "Select", - "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nGeolocation\nHTML\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature", + "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nRating\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature", "reqd": 1 }, { @@ -417,7 +417,7 @@ "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2020-10-29 06:14:43.073329", + "modified": "2021-07-12 04:54:12.042319", "modified_by": "Administrator", "module": "Custom", "name": "Custom Field", From b23dd711162ff03922c7469fd478623bad45dff1 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Tue, 13 Jul 2021 17:56:47 +0530 Subject: [PATCH 383/495] fix: escape quotes before declaring variables when making a new app --- frappe/utils/boilerplate.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index 8dab9b748f..d88eaa5745 100755 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -66,9 +66,6 @@ def make_boilerplate(dest, app_name): with open(os.path.join(dest, hooks.app_name, ".gitignore"), "w") as f: f.write(frappe.as_unicode(gitignore_template.format(app_name = hooks.app_name))) - with open(os.path.join(dest, hooks.app_name, "setup.py"), "w") as f: - f.write(frappe.as_unicode(setup_template.format(**hooks))) - with open(os.path.join(dest, hooks.app_name, "requirements.txt"), "w") as f: f.write("# frappe -- https://github.com/frappe/frappe is installed via 'bench init'") @@ -82,6 +79,14 @@ def make_boilerplate(dest, app_name): with open(os.path.join(dest, hooks.app_name, hooks.app_name, "modules.txt"), "w") as f: f.write(frappe.as_unicode(hooks.app_title)) + # These values could contain quotes and can break string declarations + # So escaping them before setting variables in setup.py and hooks.py + for key in ("app_publisher", "app_description", "app_license"): + hooks[key] = hooks[key].replace("\\", "\\\\").replace("'", "\\'").replace("\"", "\\\"") + + with open(os.path.join(dest, hooks.app_name, "setup.py"), "w") as f: + f.write(frappe.as_unicode(setup_template.format(**hooks))) + with open(os.path.join(dest, hooks.app_name, hooks.app_name, "hooks.py"), "w") as f: f.write(frappe.as_unicode(hooks_template.format(**hooks))) @@ -328,18 +333,18 @@ def get_data(): setup_template = """from setuptools import setup, find_packages -with open('requirements.txt') as f: - install_requires = f.read().strip().split('\\n') +with open("requirements.txt") as f: + install_requires = f.read().strip().split("\\n") # get version from __version__ variable in {app_name}/__init__.py from {app_name} import __version__ as version setup( - name='{app_name}', + name="{app_name}", version=version, - description='{app_description}', - author='{app_publisher}', - author_email='{app_email}', + description="{app_description}", + author="{app_publisher}", + author_email="{app_email}", packages=find_packages(), zip_safe=False, include_package_data=True, From 79124873c3357086580edd53c56b0756b4c68e1b Mon Sep 17 00:00:00 2001 From: codescientist703 Date: Tue, 13 Jul 2021 18:43:34 +0530 Subject: [PATCH 384/495] fix: fixed sider recommendation --- frappe/desk/search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/search.py b/frappe/desk/search.py index 3cce80a1a0..f9b65fc98e 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -222,7 +222,7 @@ def scrub_custom_query(query, key, txt): def relevance_sorter(key, query, as_dict): value = _(key.name if as_dict else key[0]) return ( - value.lower().startswith(query.lower()) == False, + value.lower().startswith(query.lower()) is not True, value ) From bed64ef0adcecacd98958aae940d43a11e264177 Mon Sep 17 00:00:00 2001 From: Pruthvi Patel Date: Tue, 13 Jul 2021 18:49:26 +0530 Subject: [PATCH 385/495] test: change app description to test if quotes are being escaped --- frappe/tests/test_boilerplate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/tests/test_boilerplate.py b/frappe/tests/test_boilerplate.py index 4ae78c94de..259d5a9194 100644 --- a/frappe/tests/test_boilerplate.py +++ b/frappe/tests/test_boilerplate.py @@ -20,7 +20,7 @@ class TestBoilerPlate(unittest.TestCase): def test_create_app(self): title = "Test App" - description = "Test app for unit testing" + description = "This app's description contains 'single quotes' and \"double quotes\"." publisher = "Test Publisher" email = "example@example.org" icon = "" # empty -> default From b619c3d33044a0f1b4951a98924ace3fdeb7069f Mon Sep 17 00:00:00 2001 From: codescientist703 Date: Tue, 13 Jul 2021 20:24:24 +0530 Subject: [PATCH 386/495] test: added test for order of link --- frappe/tests/test_search.py | 56 ++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/frappe/tests/test_search.py b/frappe/tests/test_search.py index 9ad02f49a6..fb99db2dc7 100644 --- a/frappe/tests/test_search.py +++ b/frappe/tests/test_search.py @@ -7,6 +7,48 @@ from frappe.desk.search import search_link from frappe.desk.search import search_widget class TestSearch(unittest.TestCase): + def setUp(self): + self.tree_doctype_name = 'Test Tree Order' + + # Create Tree doctype + self.tree_doc = frappe.get_doc({ + 'doctype': 'DocType', + 'name': self.tree_doctype_name, + 'module': 'Custom', + 'custom': 1, + 'is_tree': 1, + 'autoname': 'field:random', + 'fields': [{ + 'fieldname': 'random', + 'label': 'Random', + 'fieldtype': 'Data' + }] + }).insert() + self.tree_doc.search_fields = 'parent_test_tree_order' + self.tree_doc.save() + + # Create root for the tree doctype + self.parent_doctype_name = 'All Territories' + frappe.get_doc(doctype=self.tree_doctype_name, random=self.parent_doctype_name, + is_group=1).insert() + + # Create children for the root + self.child_doctypes_names = ['USA', 'India', 'Russia', 'China'] + self.child_doctype_list = [] + for child_name in self.child_doctypes_names: + temp = frappe.get_doc(doctype=self.tree_doctype_name, random=child_name, + parent_test_tree_order=self.parent_doctype_name) + temp.insert() + self.child_doctype_list.append(temp) + + def tearDown(self): + # Deleting all the created doctype + for child_doctype in self.child_doctype_list: + child_doctype.delete() + frappe.delete_doc(self.tree_doctype_name, self.parent_doctype_name, + force=1, ignore_permissions=True, for_reload=True) + self.tree_doc.delete() + def test_search_field_sanitizer(self): # pass search_link('DocType', 'User', query=None, filters=None, page_length=20, searchfield='name') @@ -38,6 +80,18 @@ class TestSearch(unittest.TestCase): search_link, 'DocType', 'Customer', query=None, filters=None, page_length=20, searchfield=';') + def test_link_field_order(self): + # Making a request to the search_link with the tree doctype + search_link(doctype=self.tree_doctype_name, txt='all', query=None, + filters=None, page_length=20, searchfield=None) + result = frappe.response['results'] + + # Check whether the result is sorted or not + self.assertEquals(self.parent_doctype_name, result[0]['value']) + + # Check whether searching for parent also list out children + self.assertEquals(len(result), len(self.child_doctypes_names) + 1) + #Search for the word "pay", part of the word "pays" (country) in french. def test_link_search_in_foreign_language(self): try: @@ -80,4 +134,4 @@ class TestSearch(unittest.TestCase): @frappe.validate_and_sanitize_search_inputs def get_data(doctype, txt, searchfield, start, page_len, filters): - return [doctype, txt, searchfield, start, page_len, filters] \ No newline at end of file + return [doctype, txt, searchfield, start, page_len, filters] From f2267bec25e9edb1b947b9ac21c652a6daaec78c Mon Sep 17 00:00:00 2001 From: leela Date: Wed, 14 Jul 2021 10:28:10 +0530 Subject: [PATCH 387/495] fix: sending document as an attachment through mail Currently sending document as an attachment is failing and the fix is to make it work by fixing the bug introduced in latest refactoring. --- frappe/core/doctype/communication/mixins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/communication/mixins.py b/frappe/core/doctype/communication/mixins.py index 160018c4a2..09a8a0ac22 100644 --- a/frappe/core/doctype/communication/mixins.py +++ b/frappe/core/doctype/communication/mixins.py @@ -176,8 +176,8 @@ class CommunicationEmailMixin: def mail_attachments(self, print_format=None, print_html=None): final_attachments = [] - if print_format and print_html: - d = {'print_format': print_format, 'print_html': print_html, 'print_format_attachment': 1, + if print_format or print_html: + d = {'print_format': print_format, 'html': print_html, 'print_format_attachment': 1, 'doctype': self.reference_doctype, 'name': self.reference_name} final_attachments.append(d) From 6f17e3d45fa90b9aec60b6b0c5403c57e707f9df Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 14 Jul 2021 12:02:33 +0530 Subject: [PATCH 388/495] perf(test_search): Setup and teardown tests only if needed --- frappe/tests/test_search.py | 99 ++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 39 deletions(-) diff --git a/frappe/tests/test_search.py b/frappe/tests/test_search.py index fb99db2dc7..afb584ea15 100644 --- a/frappe/tests/test_search.py +++ b/frappe/tests/test_search.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt import unittest @@ -6,48 +6,15 @@ import frappe from frappe.desk.search import search_link from frappe.desk.search import search_widget + class TestSearch(unittest.TestCase): def setUp(self): - self.tree_doctype_name = 'Test Tree Order' - - # Create Tree doctype - self.tree_doc = frappe.get_doc({ - 'doctype': 'DocType', - 'name': self.tree_doctype_name, - 'module': 'Custom', - 'custom': 1, - 'is_tree': 1, - 'autoname': 'field:random', - 'fields': [{ - 'fieldname': 'random', - 'label': 'Random', - 'fieldtype': 'Data' - }] - }).insert() - self.tree_doc.search_fields = 'parent_test_tree_order' - self.tree_doc.save() - - # Create root for the tree doctype - self.parent_doctype_name = 'All Territories' - frappe.get_doc(doctype=self.tree_doctype_name, random=self.parent_doctype_name, - is_group=1).insert() - - # Create children for the root - self.child_doctypes_names = ['USA', 'India', 'Russia', 'China'] - self.child_doctype_list = [] - for child_name in self.child_doctypes_names: - temp = frappe.get_doc(doctype=self.tree_doctype_name, random=child_name, - parent_test_tree_order=self.parent_doctype_name) - temp.insert() - self.child_doctype_list.append(temp) + if self._testMethodName == "test_link_field_order": + setup_test_link_field_order(self) def tearDown(self): - # Deleting all the created doctype - for child_doctype in self.child_doctype_list: - child_doctype.delete() - frappe.delete_doc(self.tree_doctype_name, self.parent_doctype_name, - force=1, ignore_permissions=True, for_reload=True) - self.tree_doc.delete() + if self._testMethodName == "test_link_field_order": + teardown_test_link_field_order(self) def test_search_field_sanitizer(self): # pass @@ -135,3 +102,57 @@ class TestSearch(unittest.TestCase): @frappe.validate_and_sanitize_search_inputs def get_data(doctype, txt, searchfield, start, page_len, filters): return [doctype, txt, searchfield, start, page_len, filters] + +def setup_test_link_field_order(TestCase): + TestCase.tree_doctype_name = 'Test Tree Order' + TestCase.child_doctype_list = [] + TestCase.child_doctypes_names = ['USA', 'India', 'Russia', 'China'] + TestCase.parent_doctype_name = 'All Territories' + + # Create Tree doctype + TestCase.tree_doc = frappe.get_doc({ + 'doctype': 'DocType', + 'name': TestCase.tree_doctype_name, + 'module': 'Custom', + 'custom': 1, + 'is_tree': 1, + 'autoname': 'field:random', + 'fields': [{ + 'fieldname': 'random', + 'label': 'Random', + 'fieldtype': 'Data' + }] + }).insert() + TestCase.tree_doc.search_fields = 'parent_test_tree_order' + TestCase.tree_doc.save() + + # Create root for the tree doctype + frappe.get_doc({ + "doctype": TestCase.tree_doctype_name, + "random": TestCase.parent_doctype_name, + "is_group": 1 + }).insert() + + # Create children for the root + for child_name in TestCase.child_doctypes_names: + temp = frappe.get_doc({ + "doctype": TestCase.tree_doctype_name, + "random": child_name, + "parent_test_tree_order": TestCase.parent_doctype_name + }).insert() + TestCase.child_doctype_list.append(temp) + +def teardown_test_link_field_order(TestCase): + # Deleting all the created doctype + for child_doctype in TestCase.child_doctype_list: + child_doctype.delete() + + frappe.delete_doc( + TestCase.tree_doctype_name, + TestCase.parent_doctype_name, + ignore_permissions=True, + force=True, + for_reload=True, + ) + + TestCase.tree_doc.delete() From 0eafadf002e45c7a851149783829dc1060a55011 Mon Sep 17 00:00:00 2001 From: shariquerik Date: Wed, 14 Jul 2021 12:10:11 +0530 Subject: [PATCH 389/495] fix: Code Editor scrollbar glitch --- frappe/public/scss/common/controls.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/public/scss/common/controls.scss b/frappe/public/scss/common/controls.scss index c939c6de39..a7a8d49510 100644 --- a/frappe/public/scss/common/controls.scss +++ b/frappe/public/scss/common/controls.scss @@ -166,6 +166,9 @@ select.form-control { .ace_print-margin { background-color: var(--dark-border-color); } + .ace_scrollbar { + z-index: 3; + } } .frappe-control[data-fieldtype="Attach"], From 9d9d76efe626f63a06150ec9519bd8c7becd1d01 Mon Sep 17 00:00:00 2001 From: Fahim Ali Zain Date: Fri, 4 Jun 2021 11:23:02 +0530 Subject: [PATCH 390/495] fix: frappe.local.lang resolution --- frappe/auth.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/auth.py b/frappe/auth.py index ef79d96ddb..7c513aed63 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -35,9 +35,6 @@ class HTTPRequest: else: frappe.local.request_ip = '127.0.0.1' - # language - self.set_lang() - # load cookies frappe.local.cookie_manager = CookieManager() @@ -47,6 +44,9 @@ class HTTPRequest: # login frappe.local.login_manager = LoginManager() + # language + self.set_lang() + if frappe.form_dict._lang: lang = get_lang_code(frappe.form_dict._lang) if lang: From f96ebb1f664e009ed2bf61505067ddacf4b21e78 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 10 Jun 2021 12:47:23 +0530 Subject: [PATCH 391/495] perf: Skip guess_language if _lang is provided --- frappe/auth.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/frappe/auth.py b/frappe/auth.py index 7c513aed63..479ec6bda4 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -10,7 +10,7 @@ import frappe.utils.user from frappe import conf from frappe.sessions import Session, clear_sessions, delete_session from frappe.modules.patch_handler import check_session_stopped -from frappe.translate import get_lang_code +from frappe.translate import get_lang_code, guess_language from frappe.utils.password import check_password, delete_login_failed_cache from frappe.core.doctype.activity_log.activity_log import add_authentication_log from frappe.twofactor import (should_run_2fa, authenticate_for_2factor, @@ -47,11 +47,6 @@ class HTTPRequest: # language self.set_lang() - if frappe.form_dict._lang: - lang = get_lang_code(frappe.form_dict._lang) - if lang: - frappe.local.lang = lang - self.validate_csrf_token() # write out latest cookies @@ -79,8 +74,12 @@ class HTTPRequest: frappe.throw(_("Invalid Request"), frappe.CSRFTokenError) def set_lang(self): - from frappe.translate import guess_language - frappe.local.lang = guess_language() + if frappe.form_dict._lang: + lang = get_lang_code(frappe.form_dict._lang) + if lang: + frappe.local.lang = lang + else: + frappe.local.lang = guess_language() def get_db_name(self): """get database name from conf""" From f5bd21cf46357d5e8bb98f30db7d68610a667b98 Mon Sep 17 00:00:00 2001 From: Fahim Ali Zain Date: Sat, 10 Jul 2021 10:52:43 +0530 Subject: [PATCH 392/495] fix: preferred_language cookie support for all users --- frappe/translate.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/translate.py b/frappe/translate.py index 4ff50d3fd0..8344436d94 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -22,12 +22,12 @@ from frappe.utils import is_html, strip, strip_html_tags def guess_language(lang_list=None): """Set `frappe.local.lang` from HTTP headers at beginning of request""" - user_preferred_language = frappe.request.cookies.get('preferred_language') - is_guest_user = not frappe.session.user or frappe.session.user == 'Guest' - if is_guest_user and user_preferred_language: - return user_preferred_language + preferred_language_cookie = frappe.request.cookies.get('preferred_language') + lang_codes = list(frappe.request.accept_languages.values()) + + if preferred_language_cookie: + lang_codes.append(preferred_language_cookie) - lang_codes = frappe.request.accept_languages.values() if not lang_codes: return frappe.local.lang From caafd9e2b519204b94b2203444dd1ea73086ac4d Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 13 Jul 2021 13:29:22 +0530 Subject: [PATCH 393/495] refactor: Simplify HTTPRequest class * For the sake of Readability and ease of understanding * Style updates --- frappe/auth.py | 74 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/frappe/auth.py b/frappe/auth.py index 479ec6bda4..a95c41906d 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -21,11 +21,40 @@ from urllib.parse import quote class HTTPRequest: def __init__(self): - # Get Environment variables - self.domain = frappe.request.host - if self.domain and self.domain.startswith('www.'): - self.domain = self.domain[4:] + # set frappe.local.request_ip + self.set_request_ip() + # load cookies + self.set_cookies() + + # set frappe.local.db + self.connect() + + # login and start/resume user session + self.set_session() + + # set request language + self.set_lang() + + # match csrf token from current session + self.validate_csrf_token() + + # write out latest cookies + frappe.local.cookie_manager.init_cookies() + + # check session status + check_session_stopped() + + @property + def domain(self): + if not getattr(self, "_domain", None): + self._domain = frappe.request.host + if self._domain and self._domain.startswith('www.'): + self._domain = self._domain[4:] + + return self._domain + + def set_request_ip(self): if frappe.get_request_header('X-Forwarded-For'): frappe.local.request_ip = (frappe.get_request_header('X-Forwarded-For').split(",")[0]).strip() @@ -35,32 +64,21 @@ class HTTPRequest: else: frappe.local.request_ip = '127.0.0.1' - # load cookies + def set_cookies(self): frappe.local.cookie_manager = CookieManager() - # set db - self.connect() - - # login + def set_session(self): frappe.local.login_manager = LoginManager() - # language - self.set_lang() - - self.validate_csrf_token() - - # write out latest cookies - frappe.local.cookie_manager.init_cookies() - - # check status - check_session_stopped() - def validate_csrf_token(self): if frappe.local.request and frappe.local.request.method in ("POST", "PUT", "DELETE"): - if not frappe.local.session: return - if not frappe.local.session.data.csrf_token \ - or frappe.local.session.data.device=="mobile" \ - or frappe.conf.get('ignore_csrf', None): + if not frappe.local.session: + return + if ( + not frappe.local.session.data.csrf_token + or frappe.local.session.data.device == "mobile" + or frappe.conf.get('ignore_csrf', None) + ): # not via boot return @@ -85,10 +103,12 @@ class HTTPRequest: """get database name from conf""" return conf.db_name - def connect(self, ac_name = None): + def connect(self): """connect to db, from ac_name or db_name""" - frappe.local.db = frappe.database.get_db(user = self.get_db_name(), \ - password = getattr(conf, 'db_password', '')) + frappe.local.db = frappe.database.get_db( + user=self.get_db_name(), + password=getattr(conf, 'db_password', '') + ) class LoginManager: def __init__(self): From c47cbfd2efb2f72133494b7b68940fe14410ccf8 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 13 Jul 2021 17:03:30 +0530 Subject: [PATCH 394/495] refactor: Set Language in HTTPHeader Order of priority for setting language: 1. Form Dict => _lang 2. Cookie => preferred_language 3. Request Header => Accept-Language 4. User document => language 5. System Settings => language Cookie is placed at #2 since the language picker in the navbar depends on it. And the Accept-Language header sends values based on the client's locales. --- Form Dict _lang now accepts language codes too. Previously, language names were used...for whatever reason. --- frappe/auth.py | 13 +++----- frappe/translate.py | 78 ++++++++++++++++++++++++++++++++------------- 2 files changed, 59 insertions(+), 32 deletions(-) diff --git a/frappe/auth.py b/frappe/auth.py index a95c41906d..9135b139ae 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -5,13 +5,13 @@ from frappe import _ import frappe import frappe.database import frappe.utils -from frappe.utils import cint, flt, get_datetime, datetime, date_diff, today +from frappe.utils import cint, get_datetime, datetime, date_diff, today import frappe.utils.user from frappe import conf from frappe.sessions import Session, clear_sessions, delete_session from frappe.modules.patch_handler import check_session_stopped -from frappe.translate import get_lang_code, guess_language -from frappe.utils.password import check_password, delete_login_failed_cache +from frappe.translate import guess_language +from frappe.utils.password import check_password from frappe.core.doctype.activity_log.activity_log import add_authentication_log from frappe.twofactor import (should_run_2fa, authenticate_for_2factor, confirm_otp_token, get_cached_user_pass) @@ -92,12 +92,7 @@ class HTTPRequest: frappe.throw(_("Invalid Request"), frappe.CSRFTokenError) def set_lang(self): - if frappe.form_dict._lang: - lang = get_lang_code(frappe.form_dict._lang) - if lang: - frappe.local.lang = lang - else: - frappe.local.lang = guess_language() + frappe.local.lang = guess_language() def get_db_name(self): """get database name from conf""" diff --git a/frappe/translate.py b/frappe/translate.py index 8344436d94..c567d0615c 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -11,6 +11,7 @@ import io import itertools import json import operator +import functools import os import re from csv import reader @@ -21,36 +22,64 @@ from frappe.utils import is_html, strip, strip_html_tags def guess_language(lang_list=None): - """Set `frappe.local.lang` from HTTP headers at beginning of request""" + """Set `frappe.local.lang` from HTTP headers at beginning of request + + Order of priority for setting language: + 1. Form Dict => _lang + 2. Cookie => preferred_language + 3. Request Header => Accept-Language + 4. User document => language + 5. System Settings => language + """ + + # fetch language from form_dict + if frappe.form_dict._lang: + language = get_lang_code( + frappe.form_dict._lang or get_parent_language(frappe.form_dict._lang) + ) + if language: + return language + + lang_set = set(lang_list or get_all_languages() or []) + + # fetch language from cookie preferred_language_cookie = frappe.request.cookies.get('preferred_language') - lang_codes = list(frappe.request.accept_languages.values()) if preferred_language_cookie: - lang_codes.append(preferred_language_cookie) + if preferred_language_cookie in lang_set: + return preferred_language_cookie - if not lang_codes: - return frappe.local.lang + parent_language = get_parent_language(language) + if parent_language in lang_set: + return parent_language - guess = None - if not lang_list: - lang_list = get_all_languages() or [] + # fetch language from request headers + accept_language = list(frappe.request.accept_languages.values()) - for l in lang_codes: - code = l.strip() - if not isinstance(code, str): - code = str(code, 'utf-8') - if code in lang_list or code == "en": - guess = code - break + for language in accept_language: + if language in lang_set: + return language - # check if parent language (pt) is setup, if variant (pt-BR) - if "-" in code: - code = code.split("-")[0] - if code in lang_list: - guess = code - break + parent_language = get_parent_language(language) + if parent_language in lang_set: + return parent_language + + # fallback to language set in User or System Settings + return frappe.local.lang + + +@functools.lru_cache(maxsize=None) +def get_parent_language(lang: str) -> str: + """If the passed language is a variant, return its parent + + Eg: + 1. zh-TW -> zh + 2. sr-BA -> sr + """ + is_language_variant = "-" in lang + if is_language_variant: + return lang[:lang.index("-")] - return guess or frappe.local.lang def get_user_lang(user=None): """Set frappe.local.lang from user preferences on session beginning or resumption""" @@ -75,7 +104,10 @@ def get_user_lang(user=None): return lang def get_lang_code(lang): - return frappe.db.get_value('Language', {'language_name': lang}) or lang + return ( + frappe.db.get_value("Language", {"name": lang}) + or frappe.db.get_value("Language", {"language_name": lang}) + ) def set_default_language(lang): """Set Global default language""" From 76ec9e44e4e9a7ade9509bc1f080c057aa5dbb3c Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 13 Jul 2021 17:09:03 +0530 Subject: [PATCH 395/495] refactor: Rename guess_language as get_language Guess suggests there's some AI involvement. The get_language function has a defined priority. It is deterministic, hence teh name change. --- frappe/auth.py | 4 ++-- frappe/translate.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/auth.py b/frappe/auth.py index 9135b139ae..7c8c119414 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -10,7 +10,7 @@ import frappe.utils.user from frappe import conf from frappe.sessions import Session, clear_sessions, delete_session from frappe.modules.patch_handler import check_session_stopped -from frappe.translate import guess_language +from frappe.translate import get_language from frappe.utils.password import check_password from frappe.core.doctype.activity_log.activity_log import add_authentication_log from frappe.twofactor import (should_run_2fa, authenticate_for_2factor, @@ -92,7 +92,7 @@ class HTTPRequest: frappe.throw(_("Invalid Request"), frappe.CSRFTokenError) def set_lang(self): - frappe.local.lang = guess_language() + frappe.local.lang = get_language() def get_db_name(self): """get database name from conf""" diff --git a/frappe/translate.py b/frappe/translate.py index c567d0615c..50da6e7297 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -21,7 +21,7 @@ from frappe.model.utils import InvalidIncludePath, render_include from frappe.utils import is_html, strip, strip_html_tags -def guess_language(lang_list=None): +def get_language(lang_list=None): """Set `frappe.local.lang` from HTTP headers at beginning of request Order of priority for setting language: From 736c6c9b8a388c5c9f87509432dd3d53a65de4d9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 13 Jul 2021 18:18:40 +0530 Subject: [PATCH 396/495] fix: Don't redefine datetime * Sort imports * Update file header --- frappe/auth.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/frappe/auth.py b/frappe/auth.py index 7c8c119414..2e92e9fbbb 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -1,22 +1,20 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt -import datetime -from frappe import _ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See LICENSE +from urllib.parse import quote + import frappe import frappe.database import frappe.utils -from frappe.utils import cint, get_datetime, datetime, date_diff, today import frappe.utils.user -from frappe import conf -from frappe.sessions import Session, clear_sessions, delete_session -from frappe.modules.patch_handler import check_session_stopped -from frappe.translate import get_language -from frappe.utils.password import check_password +from frappe import _, conf from frappe.core.doctype.activity_log.activity_log import add_authentication_log -from frappe.twofactor import (should_run_2fa, authenticate_for_2factor, - confirm_otp_token, get_cached_user_pass) +from frappe.modules.patch_handler import check_session_stopped +from frappe.sessions import Session, clear_sessions, delete_session +from frappe.translate import get_language +from frappe.twofactor import authenticate_for_2factor, confirm_otp_token, get_cached_user_pass, should_run_2fa +from frappe.utils import cint, date_diff, datetime, get_datetime, today +from frappe.utils.password import check_password from frappe.website.utils import get_home_page -from urllib.parse import quote class HTTPRequest: From 0598ddf70ef83cf1bada858ffd7c7b6d8017a098 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 13 Jul 2021 18:19:38 +0530 Subject: [PATCH 397/495] fix: Clear preferred_language cookie post login If preferred_language was set in cookie pre login, clear it after a successful login so that User or Site specific settings can be applied --- frappe/auth.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frappe/auth.py b/frappe/auth.py index 2e92e9fbbb..fc1cb09e1a 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -154,8 +154,9 @@ class LoginManager: self.make_session() self.setup_boot_cache() self.set_user_info() + self.clear_preferred_language() - def get_user_info(self, resume=False): + def get_user_info(self): self.info = frappe.db.get_value("User", self.user, ["user_type", "first_name", "last_name", "user_image"], as_dict=1) @@ -193,11 +194,13 @@ class LoginManager: frappe.local.response["redirect_to"] = redirect_to frappe.cache().hdel('redirect_after_login', self.user) - frappe.local.cookie_manager.set_cookie("full_name", self.full_name) frappe.local.cookie_manager.set_cookie("user_id", self.user) frappe.local.cookie_manager.set_cookie("user_image", self.info.user_image or "") + def clear_preferred_language(self): + frappe.local.cookie_manager.delete_cookie("preferred_language") + def make_session(self, resume=False): # start session frappe.local.session_obj = Session(user=self.user, resume=resume, From b4ea6c8890c50364a62d2151618f982597138761 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Wed, 14 Jul 2021 12:21:28 +0530 Subject: [PATCH 398/495] fix: dont refresh form dashboard in `layout.refresh_sections` (#13650) --- frappe/public/js/frappe/form/layout.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js index 045e5dc1b3..528f485354 100644 --- a/frappe/public/js/frappe/form/layout.js +++ b/frappe/public/js/frappe/form/layout.js @@ -263,9 +263,6 @@ frappe.ui.form.Layout = class Layout { section.addClass("empty-section"); } }); - - this.frm && this.frm.dashboard.refresh(); - } refresh_fields (fields) { From bd854bb368023c43a90d1cb044a5282a082e9371 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 14 Jul 2021 18:16:50 +0530 Subject: [PATCH 399/495] fix: Do not create energy points for disabled users --- frappe/core/doctype/user/user.py | 13 +++++++++++++ .../doctype/energy_point_rule/energy_point_rule.py | 8 +++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 5b605504e8..53d1c9ffe5 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -53,6 +53,7 @@ class User(Document): def after_insert(self): create_notification_settings(self.name) frappe.cache().delete_key('users_for_mentions') + frappe.cache().delete_key('enabled_users') def validate(self): self.check_demo() @@ -129,6 +130,9 @@ class User(Document): if self.has_value_changed('allow_in_mentions') or self.has_value_changed('user_type'): frappe.cache().delete_key('users_for_mentions') + if self.has_value_changed('enabled'): + frappe.cache().delete_key('enabled_users') + def has_website_permission(self, ptype, user, verbose=False): """Returns true if current user is the session user""" return self.name == frappe.session.user @@ -392,6 +396,8 @@ class User(Document): if self.get('allow_in_mentions'): frappe.cache().delete_key('users_for_mentions') + frappe.cache().delete_key('enabled_users') + def before_rename(self, old_name, new_name, merge=False): self.check_demo() @@ -1230,3 +1236,10 @@ def generate_keys(user): def switch_theme(theme): if theme in ["Dark", "Light"]: frappe.db.set_value("User", frappe.session.user, "desk_theme", theme) + +def get_enabled_users(): + def _get_enabled_users(): + enabled_users = frappe.get_all("User", filters={"enabled": "1"}, pluck="name") + return enabled_users + + return frappe.cache().get_value("enabled_users", _get_enabled_users) \ No newline at end of file diff --git a/frappe/social/doctype/energy_point_rule/energy_point_rule.py b/frappe/social/doctype/energy_point_rule/energy_point_rule.py index 1c736528de..b673e8e199 100644 --- a/frappe/social/doctype/energy_point_rule/energy_point_rule.py +++ b/frappe/social/doctype/energy_point_rule/energy_point_rule.py @@ -5,6 +5,7 @@ import frappe from frappe import _ import frappe.cache_manager +from frappe.core.doctype.user.user import get_enabled_users from frappe.model import log_types from frappe.model.document import Document from frappe.social.doctype.energy_point_settings.energy_point_settings import is_energy_point_enabled @@ -44,7 +45,7 @@ class EnergyPointRule(Document): try: for user in users: - if not user or user == 'Administrator': continue + if not is_eligible_user(user): continue create_energy_points_log(reference_doctype, reference_name, { 'points': points, 'user': user, @@ -119,3 +120,8 @@ def get_energy_point_doctypes(): d.reference_doctype for d in frappe.get_all('Energy Point Rule', ['reference_doctype'], {'enabled': 1}) ] + +def is_eligible_user(user): + '''Checks if user is eligible to get energy points''' + enabled_users = get_enabled_users() + return user and user in enabled_users and user != 'Administrator' From e00aaf8cc492332ba18fc05c67013eee51f572a7 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 14 Jul 2021 19:30:01 +0530 Subject: [PATCH 400/495] BREAKING CHANGE: Drop frappe.lang in favour of frappe.local.lang --- frappe/__init__.py | 2 -- frappe/boot.py | 2 +- frappe/core/doctype/page/page.py | 2 +- frappe/desk/desk_page.py | 2 +- frappe/desk/query_report.py | 2 +- frappe/sessions.py | 2 +- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 1c978945c7..4015ea8090 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -130,8 +130,6 @@ error_log = local("error_log") debug_log = local("debug_log") message_log = local("message_log") -lang = local("lang") - # This if block is never executed when running the code. It is only used for # telling static code analyzer where to find dynamically defined attributes. if typing.TYPE_CHECKING: diff --git a/frappe/boot.py b/frappe/boot.py index 0589e32ac8..6636cb4329 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -208,7 +208,7 @@ def get_column(doctype): def load_translations(bootinfo): messages = frappe.get_lang_dict("boot") - bootinfo["lang"] = frappe.lang + bootinfo["lang"] = frappe.local.lang # load translated report names for name in bootinfo.user.all_reports: diff --git a/frappe/core/doctype/page/page.py b/frappe/core/doctype/page/page.py index 0ba0e309dd..3a6baef9c4 100644 --- a/frappe/core/doctype/page/page.py +++ b/frappe/core/doctype/page/page.py @@ -141,7 +141,7 @@ class Page(Document): # flag for not caching this page self._dynamic_page = True - if frappe.lang != 'en': + if frappe.local.lang != 'en': from frappe.translate import get_lang_js self.script += get_lang_js("page", self.name) diff --git a/frappe/desk/desk_page.py b/frappe/desk/desk_page.py index d373dbda0e..45e266b957 100644 --- a/frappe/desk/desk_page.py +++ b/frappe/desk/desk_page.py @@ -31,7 +31,7 @@ def getpage(): doc = get(page) # load translations - if frappe.lang != "en": + if frappe.local.lang != "en": send_translations(frappe.get_lang_dict("page", page)) frappe.response.docs.append(doc) diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index 3c0ebf11c1..a127594af1 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -187,7 +187,7 @@ def get_script(report_name): script = "frappe.query_reports['%s']={}" % report_name # load translations - if frappe.lang != "en": + if frappe.local.lang != "en": send_translations(frappe.get_lang_dict("report", report_name)) return { diff --git a/frappe/sessions.py b/frappe/sessions.py index 4d922d6769..85a13523ff 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -335,7 +335,7 @@ class Session: now = frappe.utils.now() self.data['data']['last_updated'] = now - self.data['data']['lang'] = str(frappe.lang) + self.data['data']['lang'] = str(frappe.local.lang) # update session in db last_updated = frappe.cache().hget("last_db_session_update", self.sid) From e8d50b9d3c6c0a39ed3602ffc7627214f0c7ed72 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 14 Jul 2021 19:32:30 +0530 Subject: [PATCH 401/495] chore: Add types for frappe.translate module *only for recently modified functions --- frappe/translate.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frappe/translate.py b/frappe/translate.py index 50da6e7297..c3c88f9b0a 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -15,13 +15,14 @@ import functools import os import re from csv import reader +from typing import List, Union import frappe from frappe.model.utils import InvalidIncludePath, render_include from frappe.utils import is_html, strip, strip_html_tags -def get_language(lang_list=None): +def get_language(lang_list: List = None) -> str: """Set `frappe.local.lang` from HTTP headers at beginning of request Order of priority for setting language: @@ -81,7 +82,7 @@ def get_parent_language(lang: str) -> str: return lang[:lang.index("-")] -def get_user_lang(user=None): +def get_user_lang(user: str = None) -> str: """Set frappe.local.lang from user preferences on session beginning or resumption""" if not user: user = frappe.session.user @@ -103,7 +104,7 @@ def get_user_lang(user=None): return lang -def get_lang_code(lang): +def get_lang_code(lang: str) -> Union[str, None]: return ( frappe.db.get_value("Language", {"name": lang}) or frappe.db.get_value("Language", {"language_name": lang}) From 8faf2fd759095b3a54dd3ed0ff16e9ab949e1473 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 14 Jul 2021 19:33:48 +0530 Subject: [PATCH 402/495] refactor(minor): frappe.translate module * Remove unset limit for lru cache in get_parent_language * Simplify get_user_lang and add relevant comment --- frappe/translate.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/frappe/translate.py b/frappe/translate.py index c3c88f9b0a..15d771ecdd 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt """ frappe.translate @@ -69,7 +69,7 @@ def get_language(lang_list: List = None) -> str: return frappe.local.lang -@functools.lru_cache(maxsize=None) +@functools.lru_cache() def get_parent_language(lang: str) -> str: """If the passed language is a variant, return its parent @@ -84,21 +84,17 @@ def get_parent_language(lang: str) -> str: def get_user_lang(user: str = None) -> str: """Set frappe.local.lang from user preferences on session beginning or resumption""" - if not user: - user = frappe.session.user - - # via cache + user = user or frappe.session.user lang = frappe.cache().hget("lang", user) if not lang: - - # if defined in user profile - lang = frappe.db.get_value("User", user, "language") - if not lang: - lang = frappe.db.get_default("lang") - - if not lang: - lang = frappe.local.lang or 'en' + # User.language => Session Defaults => frappe.local.lang => 'en' + lang = ( + frappe.db.get_value("User", user, "language") + or frappe.db.get_default("lang") + or frappe.local.lang + or "en" + ) frappe.cache().hset("lang", user, lang) From 2ac1c45c664af135bdaefc934ea258a01d76661f Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 14 Jul 2021 20:12:10 +0530 Subject: [PATCH 403/495] refactor: Maintain common list for Frappe Coverage settings * Maintain common settings for coverage settings of parallel and base test suites * Expand FRAPPE_EXCLUSIONS list based on coveralls.io report --- frappe/commands/utils.py | 24 ++++------------------- frappe/coverage.py | 35 ++++++++++++++++++++++++++++++++++ frappe/parallel_test_runner.py | 25 ++++-------------------- 3 files changed, 43 insertions(+), 41 deletions(-) create mode 100644 frappe/coverage.py diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 212642e01b..874ec90d47 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -551,32 +551,16 @@ def run_tests(context, app=None, module=None, doctype=None, test=(), profile=Fal if coverage: from coverage import Coverage + from frappe.coverage import STANDARD_INCLUSIONS, STANDARD_EXCLUSIONS, FRAPPE_EXCLUSIONS # Generate coverage report only for app that is being tested source_path = os.path.join(get_bench_path(), 'apps', app or 'frappe') - incl = [ - '*.py', - ] - omit = [ - '*.js', - '*.xml', - '*.pyc', - '*.css', - '*.less', - '*.scss', - '*.vue', - '*.html', - '*/test_*', - '*/node_modules/*', - '*/doctype/*/*_dashboard.py', - '*/patches/*', - ] + omit = STANDARD_EXCLUSIONS[:] if not app or app == 'frappe': - omit.append('*/tests/*') - omit.append('*/commands/*') + omit.extend(FRAPPE_EXCLUSIONS) - cov = Coverage(source=[source_path], omit=omit, include=incl) + cov = Coverage(source=[source_path], omit=omit, include=STANDARD_INCLUSIONS) cov.start() ret = frappe.test_runner.main(app, module, doctype, context.verbose, tests=tests, diff --git a/frappe/coverage.py b/frappe/coverage.py new file mode 100644 index 0000000000..a59c24a714 --- /dev/null +++ b/frappe/coverage.py @@ -0,0 +1,35 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See LICENSE +""" + frappe.coverage + ~~~~~~~~~~~~~~~~ + + Coverage settings for frappe +""" + +STANDARD_INCLUSIONS = ["*.py"] + +STANDARD_EXCLUSIONS = [ + '*.js', + '*.xml', + '*.pyc', + '*.css', + '*.less', + '*.scss', + '*.vue', + '*.html', + '*/test_*', + '*/node_modules/*', + '*/doctype/*/*_dashboard.py', + '*/patches/*', +] + +FRAPPE_EXCLUSIONS = [ + "*/tests/*", + "*/commands/*", + "*/frappe/change_log/*", + "*/frappe/exceptions*", + "*frappe/setup.py", + "*/doctype/*/*_dashboard.py", + "*/patches/*", +] diff --git a/frappe/parallel_test_runner.py b/frappe/parallel_test_runner.py index 2f83b88572..6265498c96 100644 --- a/frappe/parallel_test_runner.py +++ b/frappe/parallel_test_runner.py @@ -111,33 +111,16 @@ class ParallelTestRunner(): if self.with_coverage: from coverage import Coverage from frappe.utils import get_bench_path + from frappe.coverage import STANDARD_INCLUSIONS, STANDARD_EXCLUSIONS, FRAPPE_EXCLUSIONS # Generate coverage report only for app that is being tested source_path = os.path.join(get_bench_path(), 'apps', self.app) - incl = [ - '*.py', - ] - omit = [ - '*.js', - '*.xml', - '*.pyc', - '*.css', - '*.less', - '*.scss', - '*.vue', - '*.pyc', - '*.html', - '*/test_*', - '*/node_modules/*', - '*/doctype/*/*_dashboard.py', - '*/patches/*', - ] + omit = STANDARD_EXCLUSIONS[:] if self.app == 'frappe': - omit.append('*/tests/*') - omit.append('*/commands/*') + omit.extend(FRAPPE_EXCLUSIONS) - self.coverage = Coverage(source=[source_path], omit=omit, include=incl) + self.coverage = Coverage(source=[source_path], omit=omit, include=STANDARD_INCLUSIONS) self.coverage.start() def save_coverage(self): From e71eef0ecc1b7601d5df83e007488db0fde236e9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 14 Jul 2021 20:14:03 +0530 Subject: [PATCH 404/495] chore: Drop dead code (pythonrc file) --- frappe/pythonrc.py | 8 -------- 1 file changed, 8 deletions(-) delete mode 100755 frappe/pythonrc.py diff --git a/frappe/pythonrc.py b/frappe/pythonrc.py deleted file mode 100755 index 6761ead05b..0000000000 --- a/frappe/pythonrc.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python2.7 - -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -import os -import frappe -frappe.connect(site=os.environ.get("site")) \ No newline at end of file From 4959dd02f1c8d7cac0ed677eed43ed0bc454a2a5 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 14 Jul 2021 20:14:37 +0530 Subject: [PATCH 405/495] refactor(minor): frappe.translate.get_messages_from_file * Don't re-define frappe util - get_bench_path * Add Python types * Style changes --- frappe/commands/utils.py | 2 +- frappe/translate.py | 20 +++++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 874ec90d47..8fc6877d4f 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -770,7 +770,7 @@ def get_version(output): "table": lambda: render_table( [["App", "Version", "Branch", "Commit"]] + [ - [app_info.app, app_info.version, app_info.branch, app_info.commit] + [app_info.app, app_info.version, app_info.branch, app_info.commit] for app_info in data ] ), diff --git a/frappe/translate.py b/frappe/translate.py index 15d771ecdd..ce4c3abf3d 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -15,11 +15,11 @@ import functools import os import re from csv import reader -from typing import List, Union +from typing import List, Union, Tuple import frappe from frappe.model.utils import InvalidIncludePath, render_include -from frappe.utils import is_html, strip, strip_html_tags +from frappe.utils import get_bench_path, is_html, strip, strip_html_tags def get_language(lang_list: List = None) -> str: @@ -558,7 +558,7 @@ def get_all_messages_from_js_files(app_name=None): return messages -def get_messages_from_file(path): +def get_messages_from_file(path: str) -> List[Tuple[str, str, str, str]]: """Returns a list of transatable strings from a code file :param path: path of the code file @@ -571,7 +571,7 @@ def get_messages_from_file(path): frappe.flags.scanned_files.append(path) - apps_path = get_bench_dir() + bench_path = get_bench_path() if os.path.exists(path): with open(path, 'r') as sourcefile: try: @@ -579,11 +579,12 @@ def get_messages_from_file(path): except Exception: print("Could not scan file for translation: {0}".format(path)) return [] - data = [(os.path.relpath(path, apps_path), message, context, line) \ - for line, message, context in extract_messages_from_code(file_contents)] - return data + + return [ + (os.path.relpath(path, bench_path), message, context, line) + for (line, message, context) in extract_messages_from_code(file_contents) + ] else: - # print "Translate: {0} missing".format(os.path.abspath(path)) return [] def extract_messages_from_code(code): @@ -797,9 +798,6 @@ def deduplicate_messages(messages): ret.append(next(g)) return ret -def get_bench_dir(): - return os.path.join(frappe.__file__, '..', '..', '..', '..') - def rename_language(old_name, new_name): if not frappe.db.exists('Language', new_name): return From bd91a33bb91ee57a3b6b73ea7cda1c285e81a0e4 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 14 Jul 2021 21:35:04 +0530 Subject: [PATCH 406/495] test: Add test case to validate EP for disabled user --- .../energy_point_log/test_energy_point_log.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/frappe/social/doctype/energy_point_log/test_energy_point_log.py b/frappe/social/doctype/energy_point_log/test_energy_point_log.py index 8c4dba5d6b..4a6e86463e 100644 --- a/frappe/social/doctype/energy_point_log/test_energy_point_log.py +++ b/frappe/social/doctype/energy_point_log/test_energy_point_log.py @@ -273,6 +273,31 @@ class TestEnergyPointLog(unittest.TestCase): self.assertEqual(points_after_reverting_todo, points_after_closing_todo - rule_points) self.assertEqual(points_after_saving_todo_again, points_after_reverting_todo + rule_points) + def test_energy_points_disabled_user(self): + frappe.set_user('test@example.com') + user = frappe.get_doc('User', 'test@example.com') + user.enabled = 0 + user.save() + todo_point_rule = create_energy_point_rule_for_todo() + energy_point_of_user = get_points('test@example.com') + + created_todo = create_a_todo() + + created_todo.status = 'Closed' + created_todo.save() + points_after_closing_todo = get_points('test@example.com') + + # do not update energy points for disabled user + self.assertEqual(points_after_closing_todo, energy_point_of_user) + + user.enabled = 1 + user.save() + + created_todo.save() + points_after_re_saving_todo = get_points('test@example.com') + self.assertEqual(points_after_re_saving_todo, energy_point_of_user + todo_point_rule.points) + + def create_energy_point_rule_for_todo(multiplier_field=None, for_doc_event='Custom', max_points=None, for_assigned_users=0, field_to_check=None, apply_once=False, user_field='owner'): name = 'ToDo Closed' From 421220a872e07da153b33392005c74a87f08b1ea Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 15 Jul 2021 01:04:04 +0530 Subject: [PATCH 407/495] test: Added tests for frappe.translate.get_language --- frappe/tests/test_translate.py | 54 ++++++++++++++++++++++++++++++++-- frappe/translate.py | 5 +++- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/frappe/tests/test_translate.py b/frappe/tests/test_translate.py index f51f31d509..717b18ced8 100644 --- a/frappe/tests/test_translate.py +++ b/frappe/tests/test_translate.py @@ -1,13 +1,27 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -import frappe, unittest, os +import os +import unittest +from random import choices +from unittest.mock import patch + +import frappe import frappe.translate from frappe import _ +from frappe.auth import HTTPRequest +from frappe.translate import get_parent_language +from frappe.utils import set_request dirname = os.path.dirname(__file__) translation_string_file = os.path.join(dirname, 'translation_test_file.txt') +first_lang, second_lang, third_lang, fourth_lang, fifth_lang = choices( + frappe.get_all("Language", pluck="name"), k=5 +) class TestTranslate(unittest.TestCase): + def tearDown(self): + frappe.form_dict.pop("_lang", None) + def test_extract_message_from_file(self): data = frappe.translate.get_messages_from_file(translation_string_file) self.assertListEqual(data, expected_output) @@ -20,6 +34,42 @@ class TestTranslate(unittest.TestCase): finally: frappe.local.lang = 'en' + def test_request_language_resolution_with_form_dict(self): + """Test for frappe.translate.get_language + + Case 1: frappe.form_dict._lang is set + """ + + frappe.form_dict._lang = first_lang + + with patch.object(frappe.translate, "get_preferred_language_cookie", return_value=second_lang): + set_request(method="POST", path="/", headers=[("Accept-Language", third_lang)]) + HTTPRequest() + + self.assertIn(frappe.local.lang, [first_lang, get_parent_language(first_lang)]) + + def test_request_language_resolution_with_cookie(self): + """Test for frappe.translate.get_language + + Case 2: frappe.form_dict._lang is not set, but preferred_language cookie is + """ + + with patch.object(frappe.translate, "get_preferred_language_cookie", return_value=second_lang): + set_request(method="POST", path="/", headers=[("Accept-Language", third_lang)]) + HTTPRequest() + + self.assertIn(frappe.local.lang, [second_lang, get_parent_language(second_lang)]) + + def test_request_language_resolution_with_request_header(self): + """Test for frappe.translate.get_language + + Case 3: frappe.form_dict._lang & preferred_language cookie is not set, but Accept-Language header is + """ + set_request(method="POST", path="/", headers=[("Accept-Language", third_lang)]) + HTTPRequest() + self.assertIn(frappe.local.lang, [third_lang, get_parent_language(third_lang)]) + + expected_output = [ ('apps/frappe/frappe/tests/translation_test_file.txt', 'Warning: Unable to find {0} in any table related to {1}', 'This is some context', 2), ('apps/frappe/frappe/tests/translation_test_file.txt', 'Warning: Unable to find {0} in any table related to {1}', None, 4), diff --git a/frappe/translate.py b/frappe/translate.py index ce4c3abf3d..d5916f1761 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -44,7 +44,7 @@ def get_language(lang_list: List = None) -> str: lang_set = set(lang_list or get_all_languages() or []) # fetch language from cookie - preferred_language_cookie = frappe.request.cookies.get('preferred_language') + preferred_language_cookie = get_preferred_language_cookie() if preferred_language_cookie: if preferred_language_cookie in lang_set: @@ -906,3 +906,6 @@ def get_all_languages(with_language_name=False): @frappe.whitelist(allow_guest=True) def set_preferred_language_cookie(preferred_language): frappe.local.cookie_manager.set_cookie("preferred_language", preferred_language) + +def get_preferred_language_cookie(): + return frappe.request.cookies.get("preferred_language") From 2ccfb547b50a43e39ce4cbe8d53b81d525689fe6 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 15 Jul 2021 01:11:10 +0530 Subject: [PATCH 408/495] test: Added test to check sanity of HTTPRequest.set_lang --- frappe/tests/test_auth.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/frappe/tests/test_auth.py b/frappe/tests/test_auth.py index bc23cb591c..8447150006 100644 --- a/frappe/tests/test_auth.py +++ b/frappe/tests/test_auth.py @@ -4,8 +4,9 @@ import time import unittest import frappe -from frappe.auth import LoginAttemptTracker +from frappe.auth import HTTPRequest, LoginAttemptTracker from frappe.frappeclient import FrappeClient, AuthError +from frappe.utils import set_request class TestAuth(unittest.TestCase): def __init__(self, *args, **kwargs): @@ -124,3 +125,20 @@ class TestLoginAttemptTracker(unittest.TestCase): tracker.add_failure_attempt() self.assertTrue(tracker.is_user_allowed()) + +class TestFrappeHTTPRequest(unittest.TestCase): + # test frappe.auth.HTTPRequest + def test_set_language(self): + """Check if language is set on object initialization + + This is a test to ensure that language has changed. To test correctness + of frappe.local.lang, check out the tests of frappe.translate.get_language + """ + lang_before_request = frappe.local.lang + random_lang = frappe.get_all("Language", limit=1, pluck="name")[0] + set_request(method='POST', path='/') + frappe.form_dict._lang = random_lang + HTTPRequest() + self.assertTrue(hasattr(frappe.local, "lang")) + self.assertIsInstance(frappe.local.lang, str) + self.assertNotEqual(lang_before_request, frappe.local.lang) From f54894b1e75f30a60e5f611c097f123967901078 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 15 Jul 2021 01:12:13 +0530 Subject: [PATCH 409/495] chore: Update copyright year info in file header --- frappe/sessions.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/sessions.py b/frappe/sessions.py index 85a13523ff..8e30d0660c 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt """ Boot session from cache or build @@ -249,7 +249,6 @@ class Session: data = self.get_session_record() if data: - # set language self.data.update({'data': data, 'user':data.user, 'sid': self.sid}) self.user = data.user validate_ip_address(self.user) From ffb020ce0ea9a0fb2c2ec7c0f8d03d986bc1cd1e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 15 Jul 2021 08:37:47 +0530 Subject: [PATCH 410/495] fix: Make query postgres compatible --- frappe/desk/reportview.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index b9382bc830..1dbc52eb5b 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -450,6 +450,7 @@ def get_stats(stats, doctype, filters=[]): filters=filters + [[tag, '!=', '']], group_by=tag, as_list=True, + distinct=1, ) if tag == '_user_tags': @@ -458,18 +459,22 @@ def get_stats(stats, doctype, filters=[]): fields=[tag, "count(*)"], filters=filters + [[tag, "in", ('', ',')]], as_list=True, - )[0][1] + group_by=tag, + order_by=tag, + ) + + no_tag_count = no_tag_count[0][1] if no_tag_count else 0 stats[tag].append([_("No Tags"), no_tag_count]) else: stats[tag] = tag_count except frappe.db.SQLError: - # does not work for child tables pass - except frappe.db.InternalError: + except frappe.db.InternalError as e: # raised when _user_tags column is added on the fly pass + return stats @frappe.whitelist() From 187c777539353b9b9ebfb1cd6089e776a114e5d7 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 15 Jul 2021 14:18:31 +0530 Subject: [PATCH 411/495] chore: Trigger GHA From a6537ce987b208eacc7f1e3b19a2b25de3dae127 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 15 Jul 2021 15:38:36 +0530 Subject: [PATCH 412/495] test: Invoke get_language directly instead of via HTTPRequest --- frappe/tests/test_translate.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/frappe/tests/test_translate.py b/frappe/tests/test_translate.py index 717b18ced8..edab3a82c3 100644 --- a/frappe/tests/test_translate.py +++ b/frappe/tests/test_translate.py @@ -8,8 +8,7 @@ from unittest.mock import patch import frappe import frappe.translate from frappe import _ -from frappe.auth import HTTPRequest -from frappe.translate import get_parent_language +from frappe.translate import get_language, get_parent_language from frappe.utils import set_request dirname = os.path.dirname(__file__) @@ -43,10 +42,9 @@ class TestTranslate(unittest.TestCase): frappe.form_dict._lang = first_lang with patch.object(frappe.translate, "get_preferred_language_cookie", return_value=second_lang): - set_request(method="POST", path="/", headers=[("Accept-Language", third_lang)]) - HTTPRequest() + return_val = get_language() - self.assertIn(frappe.local.lang, [first_lang, get_parent_language(first_lang)]) + self.assertIn(return_val, [first_lang, get_parent_language(first_lang)]) def test_request_language_resolution_with_cookie(self): """Test for frappe.translate.get_language @@ -56,9 +54,9 @@ class TestTranslate(unittest.TestCase): with patch.object(frappe.translate, "get_preferred_language_cookie", return_value=second_lang): set_request(method="POST", path="/", headers=[("Accept-Language", third_lang)]) - HTTPRequest() + return_val = get_language() - self.assertIn(frappe.local.lang, [second_lang, get_parent_language(second_lang)]) + self.assertIn(return_val, [second_lang, get_parent_language(second_lang)]) def test_request_language_resolution_with_request_header(self): """Test for frappe.translate.get_language @@ -66,8 +64,8 @@ class TestTranslate(unittest.TestCase): Case 3: frappe.form_dict._lang & preferred_language cookie is not set, but Accept-Language header is """ set_request(method="POST", path="/", headers=[("Accept-Language", third_lang)]) - HTTPRequest() - self.assertIn(frappe.local.lang, [third_lang, get_parent_language(third_lang)]) + return_val = get_language() + self.assertIn(return_val, [third_lang, get_parent_language(third_lang)]) expected_output = [ From 275a4335c291a56784000139e9e5f32f7afb2eb1 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 15 Jul 2021 15:53:21 +0530 Subject: [PATCH 413/495] ci: Override acceptable semantic commit name types * Add "BREAKING CHANGE" --- .github/semantic.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/semantic.yml b/.github/semantic.yml index e1e53bc1a4..fa15046b4a 100644 --- a/.github/semantic.yml +++ b/.github/semantic.yml @@ -11,3 +11,20 @@ allowRevertCommits: true # For allowed PR types: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json # Tool Reference: https://github.com/zeke/semantic-pull-requests + +# By default types specified in commitizen/conventional-commit-types is used. +# See: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json +# You can override the valid types +types: + - BREAKING CHANGE + - feat + - fix + - docs + - style + - refactor + - perf + - test + - build + - ci + - chore + - revert From 42b3c178002e65c7b025561ce3f86e73274b50f3 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 15 Jul 2021 16:02:44 +0530 Subject: [PATCH 414/495] Revert "BREAKING CHANGE: Drop frappe.lang in favour of frappe.local.lang" This reverts commit e00aaf8cc492332ba18fc05c67013eee51f572a7. --- This was pretty much a pointless change since frappe.lang just proxies to frappe.local.lang --- frappe/__init__.py | 2 ++ frappe/boot.py | 2 +- frappe/core/doctype/page/page.py | 2 +- frappe/desk/desk_page.py | 2 +- frappe/desk/query_report.py | 2 +- frappe/sessions.py | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 4015ea8090..1c978945c7 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -130,6 +130,8 @@ error_log = local("error_log") debug_log = local("debug_log") message_log = local("message_log") +lang = local("lang") + # This if block is never executed when running the code. It is only used for # telling static code analyzer where to find dynamically defined attributes. if typing.TYPE_CHECKING: diff --git a/frappe/boot.py b/frappe/boot.py index 6636cb4329..0589e32ac8 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -208,7 +208,7 @@ def get_column(doctype): def load_translations(bootinfo): messages = frappe.get_lang_dict("boot") - bootinfo["lang"] = frappe.local.lang + bootinfo["lang"] = frappe.lang # load translated report names for name in bootinfo.user.all_reports: diff --git a/frappe/core/doctype/page/page.py b/frappe/core/doctype/page/page.py index 3a6baef9c4..0ba0e309dd 100644 --- a/frappe/core/doctype/page/page.py +++ b/frappe/core/doctype/page/page.py @@ -141,7 +141,7 @@ class Page(Document): # flag for not caching this page self._dynamic_page = True - if frappe.local.lang != 'en': + if frappe.lang != 'en': from frappe.translate import get_lang_js self.script += get_lang_js("page", self.name) diff --git a/frappe/desk/desk_page.py b/frappe/desk/desk_page.py index 45e266b957..d373dbda0e 100644 --- a/frappe/desk/desk_page.py +++ b/frappe/desk/desk_page.py @@ -31,7 +31,7 @@ def getpage(): doc = get(page) # load translations - if frappe.local.lang != "en": + if frappe.lang != "en": send_translations(frappe.get_lang_dict("page", page)) frappe.response.docs.append(doc) diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index a127594af1..3c0ebf11c1 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -187,7 +187,7 @@ def get_script(report_name): script = "frappe.query_reports['%s']={}" % report_name # load translations - if frappe.local.lang != "en": + if frappe.lang != "en": send_translations(frappe.get_lang_dict("report", report_name)) return { diff --git a/frappe/sessions.py b/frappe/sessions.py index 8e30d0660c..0b469616b8 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -334,7 +334,7 @@ class Session: now = frappe.utils.now() self.data['data']['last_updated'] = now - self.data['data']['lang'] = str(frappe.local.lang) + self.data['data']['lang'] = str(frappe.lang) # update session in db last_updated = frappe.cache().hget("last_db_session_update", self.sid) From 144880bfb10e2f11c23ff3e83b8e0a50d05631a3 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 15 Jul 2021 19:34:49 +0530 Subject: [PATCH 415/495] ci: make semgrep check diff-aware --- .../semgrep_rules/frappe_correctness.yml | 2 -- .github/helper/semgrep_rules/security.yml | 4 --- .github/workflows/semgrep.yml | 36 ++++++------------- 3 files changed, 10 insertions(+), 32 deletions(-) diff --git a/.github/helper/semgrep_rules/frappe_correctness.yml b/.github/helper/semgrep_rules/frappe_correctness.yml index faab3344a6..d9603e89aa 100644 --- a/.github/helper/semgrep_rules/frappe_correctness.yml +++ b/.github/helper/semgrep_rules/frappe_correctness.yml @@ -98,8 +98,6 @@ rules: languages: [python] severity: WARNING paths: - exclude: - - test_*.py include: - "*/**/doctype/*" diff --git a/.github/helper/semgrep_rules/security.yml b/.github/helper/semgrep_rules/security.yml index b2cc4b16fc..5a5098bf50 100644 --- a/.github/helper/semgrep_rules/security.yml +++ b/.github/helper/semgrep_rules/security.yml @@ -8,10 +8,6 @@ rules: dynamic content. Avoid it or use safe_eval(). languages: [python] severity: ERROR - paths: - exclude: - - frappe/__init__.py - - frappe/commands/utils.py - id: frappe-sqli-format-strings patterns: diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index 389524e968..e27b406df0 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -1,34 +1,18 @@ name: Semgrep on: - pull_request: - branches: - - develop - - version-13-hotfix - - version-13-pre-release + pull_request: { } + jobs: semgrep: name: Frappe Linter runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Setup python3 - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - - name: Setup semgrep - run: | - python -m pip install -q semgrep - git fetch origin $GITHUB_BASE_REF:$GITHUB_BASE_REF -q - - - name: Semgrep errors - run: | - files=$(git diff --name-only --diff-filter=d $GITHUB_BASE_REF) - [[ -d .github/helper/semgrep_rules ]] && semgrep --severity ERROR --config=.github/helper/semgrep_rules --quiet --error $files - semgrep --config="r/python.lang.correctness" --quiet --error $files - - - name: Semgrep warnings - run: | - files=$(git diff --name-only --diff-filter=d $GITHUB_BASE_REF) - [[ -d .github/helper/semgrep_rules ]] && semgrep --severity WARNING --severity INFO --config=.github/helper/semgrep_rules --quiet $files + - uses: actions/checkout@v2 + - uses: returntocorp/semgrep-action@v1 + env: + SEMGREP_TIMEOUT: 120 + with: + config: >- + r/python.lang.correctness + .github/helper/semgrep_rules From c85ff679ef7a4c88dc4c625f69faed76ef195111 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 16 Jul 2021 12:02:13 +0530 Subject: [PATCH 416/495] fix(NotFoundPage): Set default value for http_status_code --- frappe/website/page_renderers/not_found_page.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/website/page_renderers/not_found_page.py b/frappe/website/page_renderers/not_found_page.py index af510fecfc..de631f5dfe 100644 --- a/frappe/website/page_renderers/not_found_page.py +++ b/frappe/website/page_renderers/not_found_page.py @@ -8,11 +8,11 @@ from frappe.website.utils import can_cache HOMEPAGE_PATHS = ('/', '/index', 'index') class NotFoundPage(TemplatePage): - def __init__(self, path, http_status_code): + def __init__(self, path, http_status_code=None): self.request_path = path self.request_url = frappe.local.request.url if hasattr(frappe.local, 'request') else '' path = '404' - http_status_code = 404 + http_status_code = http_status_code or 404 super().__init__(path=path, http_status_code=http_status_code) def can_render(self): From 39d5820e05fe7f7a1f4212174e273324352ad3b6 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 16 Jul 2021 13:15:20 +0530 Subject: [PATCH 417/495] fix: Allow navigation to the document with # in the docname --- frappe/public/js/frappe/router.js | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 2bfa7c7be6..f5967c0826 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -14,11 +14,10 @@ frappe.view_factories = []; frappe.route_options = null; frappe.route_hooks = {}; -$(window).on('hashchange', function() { +$(window).on('hashchange', function(e) { // v1 style routing, route is in hash - if (window.location.hash) { + if (window.location.hash && !frappe.router.is_app_route(e.currentTarget.pathname)) { let sub_path = frappe.router.get_sub_path(window.location.hash); - window.location.hash = ''; frappe.router.push_state(sub_path); return false; } @@ -52,14 +51,16 @@ $('body').on('click', 'a', function(e) { return override('/app'); } - // target has "#" ,this is a v1 style route, so remake it. - if (e.currentTarget.hash) { - return override(e.currentTarget.hash); - } - - // target has "/app, this is a v2 style route. - if (e.currentTarget.pathname && frappe.router.is_app_route(e.currentTarget.pathname)) { - return override(e.currentTarget.pathname); + if (frappe.router.is_app_route(e.currentTarget.pathname)) { + // target has "/app, this is a v2 style route. + if (e.currentTarget.pathname) { + return override(e.currentTarget.pathname + e.currentTarget.hash); + } + } else { + // target has "#" ,this is a v1 style route, so remake it. + if (e.currentTarget.hash) { + return override(e.currentTarget.hash); + } } }); @@ -349,8 +350,6 @@ frappe.router = { push_state(url) { // change the URL and call the router if (window.location.pathname !== url) { - // cleanup any remenants of v1 routing - window.location.hash = ''; // push state so the browser looks fine history.pushState(null, null, url); @@ -364,7 +363,11 @@ frappe.router = { // return clean sub_path from hash or url // supports both v1 and v2 routing if (!route) { - route = window.location.hash || (window.location.pathname + window.location.search); + route = window.location.pathname + window.location.hash + window.location.search; + if (route.includes('app#')) { + // to supports v1 + route = window.location.hash; + } } return this.strip_prefix(route); From d974fd8d4dac5b6ff397d9dc0f7a3f99f9b63cc3 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 16 Jul 2021 13:19:41 +0530 Subject: [PATCH 418/495] fix: Typo --- frappe/public/js/frappe/router.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index f5967c0826..8f8704713e 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -365,7 +365,7 @@ frappe.router = { if (!route) { route = window.location.pathname + window.location.hash + window.location.search; if (route.includes('app#')) { - // to supports v1 + // to support v1 route = window.location.hash; } } From b690374afeb17fda8230151370766b5488de054a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 16 Jul 2021 21:58:20 +0530 Subject: [PATCH 419/495] fix: Trigger worklow action on update_after_submit event --- frappe/hooks.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/hooks.py b/frappe/hooks.py index ac42a03461..f3d25d6bf4 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -171,6 +171,9 @@ doc_events = { "frappe.workflow.doctype.workflow_action.workflow_action.process_workflow_actions", "frappe.event_streaming.doctype.event_update_log.event_update_log.notify_consumers" ], + "on_update_after_submit": [ + "frappe.workflow.doctype.workflow_action.workflow_action.process_workflow_actions" + ], "on_change": [ "frappe.social.doctype.energy_point_rule.energy_point_rule.process_energy_points", "frappe.automation.doctype.milestone_tracker.milestone_tracker.evaluate_milestone" From 9b437f8c90b3a7ca955f5f0c168bf8001d8e2975 Mon Sep 17 00:00:00 2001 From: leela Date: Mon, 19 Jul 2021 12:18:25 +0530 Subject: [PATCH 420/495] feat: Add rq users --- frappe/commands/__init__.py | 4 +- frappe/commands/redis.py | 48 ++++++++++++++++++++ frappe/utils/__init__.py | 6 +++ frappe/utils/rq.py | 87 +++++++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 frappe/commands/redis.py create mode 100644 frappe/utils/rq.py diff --git a/frappe/commands/__init__.py b/frappe/commands/__init__.py index be9d107025..9ed333d034 100644 --- a/frappe/commands/__init__.py +++ b/frappe/commands/__init__.py @@ -102,7 +102,9 @@ def get_commands(): from .site import commands as site_commands from .translate import commands as translate_commands from .utils import commands as utils_commands + from .redis import commands as redis_commands - return list(set(scheduler_commands + site_commands + translate_commands + utils_commands)) + all_commands = scheduler_commands + site_commands + translate_commands + utils_commands + redis_commands + return list(set(all_commands)) commands = get_commands() diff --git a/frappe/commands/redis.py b/frappe/commands/redis.py new file mode 100644 index 0000000000..c608296cac --- /dev/null +++ b/frappe/commands/redis.py @@ -0,0 +1,48 @@ +import os + +import click +import redis + +import frappe +from frappe.utils.rq import RedisQueue +from frappe.installer import update_site_config + +@click.command('create-rq-users') +@click.option('--set-admin-password', is_flag=True, default=False, help='Set new Redis admin(default user) password') +@click.option('--reset-passwords', is_flag=True, default=False, help='Remove all existing passwords') +def create_rq_users(set_admin_password=False, reset_passwords=False): + """Create Redis Queue users and add to acl and app configs. + + acl config file will be used by redis server while starting the server + and app config is used by app while connecting to redis server. + """ + acl_file_path = os.path.abspath('../config/redis_queue.acl') + acl_list, user_credentials = RedisQueue.gen_acl_list( + reset_passwords=reset_passwords, set_admin_password=set_admin_password) + + with open(acl_file_path, 'w') as f: + f.writelines([acl+'\n' for acl in acl_list]) + + sites_path = os.getcwd() + common_site_config_path = os.path.join(sites_path, 'common_site_config.json') + update_site_config("rq_username", user_credentials['bench'][0], validate=False, + site_config_path=common_site_config_path) + update_site_config("rq_password", user_credentials['bench'][1], validate=False, + site_config_path=common_site_config_path) + + if set_admin_password: + env_key = 'RQ_ADMIN_PASWORD' + click.secho('Redis admin password is successfully set up. ' + 'Include below line in .bashrc file for system to use', + fg='green' + ) + click.secho(f"`export {env_key}={user_credentials['default'][1]}`") + click.secho('NOTE: Please save the admin password as you ' + 'can not access redis server without the password', + fg='yellow' + ) + + +commands = [ + create_rq_users +] diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index af9d5de1ee..bf139173d6 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -383,6 +383,12 @@ def get_files_path(*path, **kwargs): def get_bench_path(): return os.path.realpath(os.path.join(os.path.dirname(frappe.__file__), '..', '..', '..')) +def get_bench_id(): + return frappe.local.conf.get('bench_id', 'DefaultBench') + +def get_site_id(site=None): + return f"{site or frappe.local.site}@{get_bench_id()}" + def get_backups_path(): return get_site_path("private", "backups") diff --git a/frappe/utils/rq.py b/frappe/utils/rq.py new file mode 100644 index 0000000000..5e9b9dcd5d --- /dev/null +++ b/frappe/utils/rq.py @@ -0,0 +1,87 @@ +import redis + +import frappe +from frappe.utils import get_site_id, get_bench_id, random_string + + +class RedisQueue: + def __init__(self, conn): + self.conn = conn + + def add_user(self, username, password=None): + """Create or update the user. + """ + password = password or self.conn.acl_genpass() + user_settings = self.get_new_user_settings(username, password) + is_created = self.conn.acl_setuser(**user_settings) + return frappe._dict(user_settings) if is_created else {} + + @classmethod + def get_connection(cls, username='default', password=None): + domain = frappe.local.conf.redis_queue.split("redis://", 1)[-1] + url = f"redis://{username}:{password or ''}@{domain}" + conn = redis.from_url(url) + conn.ping() + return conn + + @classmethod + def new(cls, username='default', password=None): + return cls(cls.get_connection(username, password)) + + @classmethod + def set_admin_password(cls, cur_password=None, new_password=None, reset_passwords=False): + username = 'default' + conn = cls.get_connection(username, cur_password) + password = '+'+(new_password or conn.acl_genpass()) + conn.acl_setuser( + username=username, enabled=True, reset_passwords=reset_passwords, passwords=password + ) + return password[1:] + + @classmethod + def get_new_user_settings(cls, username, password): + d = {} + d['username'] = username + d['passwords'] = '+'+password + d['reset_keys'] = True + d['enabled'] = True + d['keys'] = cls.get_acl_key_rules() + d['commands'] = cls.get_acl_command_rules() + return d + + @classmethod + def get_acl_key_rules(cls, include_key_prefix=False): + """FIXME: Find better way + """ + rules = ['rq:[^q]*', 'rq:queues', f'rq:queue:{get_bench_id()}:*'] + if include_key_prefix: + return ['~'+pattern for pattern in rules] + return rules + + @classmethod + def get_acl_command_rules(cls): + return ['+@all', '-@admin'] + + @classmethod + def gen_acl_list(cls, reset_passwords=False, set_admin_password=False): + """Generate list of ACL users needed for this branch. + + This list contains default ACL user and the bench ACL user(used by all sites incase of ACL is enabled). + """ + with frappe.init_site(): + bench_username = get_bench_id() + bench_user_rules = cls.get_acl_key_rules(include_key_prefix=True) + cls.get_acl_command_rules() + + bench_user_rule_str = ' '.join(bench_user_rules).strip() + bench_user_password = random_string(20) + bench_user_resetpass = (reset_passwords and 'resetpass') or '' + + default_username = 'default' + _default_user_password = random_string(20) if set_admin_password else '' + default_user_password = '>'+_default_user_password if _default_user_password else 'nopass' + default_user_resetpass = (reset_passwords and set_admin_password and 'resetpass') or '' + + return [ + f'user {default_username} on {default_user_password} {default_user_resetpass} ~* &* +@all', + f'user {bench_username} on >{bench_user_password} {bench_user_resetpass} {bench_user_rule_str}' + ], {'bench': (bench_username, bench_user_password), 'default': (default_username, _default_user_password)} From cd499af6b28ef18dbe664ba757240ddfcf46d244 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 20 Jul 2021 08:43:42 +0530 Subject: [PATCH 421/495] fix: Move layout colors to common css_variables.scss - Layout colors are used by website as well --- frappe/public/scss/common/css_variables.scss | 11 +++++++++++ frappe/public/scss/desk/css_variables.scss | 15 +-------------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/frappe/public/scss/common/css_variables.scss b/frappe/public/scss/common/css_variables.scss index 48a8a48f5f..112238bfe5 100644 --- a/frappe/public/scss/common/css_variables.scss +++ b/frappe/public/scss/common/css_variables.scss @@ -179,9 +179,20 @@ --text-on-pink: var(--pink-500); --text-on-cyan: var(--cyan-600); + // Layout Colors + --bg-color: var(--gray-50); + --fg-color: white; + --navbar-bg: white; + --fg-hover-color: var(--gray-100); + --card-bg: var(--fg-color); + --disabled-text-color: var(--gray-700); --disabled-control-bg: var(--gray-50); --control-bg: var(--gray-100); --control-bg-on-gray: var(--gray-200); + --awesomebar-focus-bg: var(--fg-color); + --modal-bg: white; + --toast-bg: var(--modal-bg); + --popover-bg: white; --awesomplete-hover-bg: var(--control-bg); diff --git a/frappe/public/scss/desk/css_variables.scss b/frappe/public/scss/desk/css_variables.scss index 5bb2614dcc..282e21433b 100644 --- a/frappe/public/scss/desk/css_variables.scss +++ b/frappe/public/scss/desk/css_variables.scss @@ -25,20 +25,7 @@ $input-height: 28px !default; --navbar-height: 60px; - // Layout Colors - --bg-color: var(--gray-50); - --fg-color: white; - --navbar-bg: white; - --fg-hover-color: var(--gray-100); - --card-bg: var(--fg-color); - --disabled-text-color: var(--gray-700); - --disabled-control-bg: var(--gray-50); - --control-bg: var(--gray-100); - --control-bg-on-gray: var(--gray-200); - --awesomebar-focus-bg: var(--fg-color); - --modal-bg: white; - --toast-bg: var(--modal-bg); - --popover-bg: white; + --appreciation-color: var(--dark-green-600); --appreciation-bg: var(--dark-green-100); From 2a9b9875f904804df6663e78416d4eee7890bd03 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 20 Jul 2021 10:11:45 +0530 Subject: [PATCH 422/495] fix: Make pad only after dom is ready - Signature control used to break on first form load --- frappe/public/js/frappe/form/controls/signature.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/signature.js b/frappe/public/js/frappe/form/controls/signature.js index bcbdfa0d27..445f5b7b3b 100644 --- a/frappe/public/js/frappe/form/controls/signature.js +++ b/frappe/public/js/frappe/form/controls/signature.js @@ -53,13 +53,15 @@ frappe.ui.form.ControlSignature = class ControlSignature extends frappe.ui.form. this.img = $("") .appendTo(this.img_wrapper).toggle(false); } - refresh_input(e) { + refresh_input() { + // signature dom is not ready + if (!this.body) return; // prevent to load the second time this.make_pad(); this.$wrapper.find(".control-input").toggle(false); this.set_editable(this.get_status()=="Write"); this.load_pad(); - if(this.get_status()=="Read") { + if (this.get_status() == "Read") { $(this.disp_area).toggle(false); } } From 1e1f9115402f99e7ec25b7f07f867b7aebf6327b Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 20 Jul 2021 15:00:07 +0530 Subject: [PATCH 423/495] fix: Use 1em margin for p outside container Revert https://github.com/frappe/frappe/commit/397848f9f7f692c3b8ac43efbc85b231ac32cb08#diff-cad6d40df66d9378e252342fe4148d3f8114a3c361dffd048fc3b09d5d9024b3R39 --- frappe/public/scss/email.bundle.scss | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frappe/public/scss/email.bundle.scss b/frappe/public/scss/email.bundle.scss index b1a8d6c485..9b58c130a2 100644 --- a/frappe/public/scss/email.bundle.scss +++ b/frappe/public/scss/email.bundle.scss @@ -36,7 +36,13 @@ a { } p { - margin: 5px 0 !important; + margin: 1em 0 !important; +} + +.with-container { + p { + margin: 5px 0 !important; + } } .ql-editor { From e1c78bb9b357ba6b3c601dc595a8e6836e5571c6 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 20 Jul 2021 15:21:22 +0530 Subject: [PATCH 424/495] test: Update test --- frappe/email/test_email_body.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/test_email_body.py b/frappe/email/test_email_body.py index 8e637273ed..2c7d119fce 100644 --- a/frappe/email/test_email_body.py +++ b/frappe/email/test_email_body.py @@ -127,7 +127,7 @@ w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> ''' transformed_html = '''

    Hi John

    -

    This is a test email

    +

    This is a test email

    ''' self.assertTrue(transformed_html in inline_style_in_html(html)) From e1ade10a54df10d66fea326b350e628ae3a4568a Mon Sep 17 00:00:00 2001 From: leela Date: Tue, 20 Jul 2021 18:59:40 +0530 Subject: [PATCH 425/495] fix: `show Report` button in report view (cherry picked from commit 0917d59a40f77f4514b29599ae96cf10ff0573ac) --- frappe/public/js/frappe/router.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 2bfa7c7be6..1f5a250872 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -172,7 +172,7 @@ frappe.router = { standard_route = ['List', doctype_route.doctype, frappe.utils.to_title_case(route[2])]; if (route[3]) { // calendar / kanban / dashboard / folder name - standard_route.push(...route.splice(3, route.length)); + standard_route.push([...route].splice(3, route.length)); } } return standard_route; @@ -298,7 +298,7 @@ frappe.router = { new_route = [this.slug(route[1]), 'view', route[2].toLowerCase()]; // calendar / inbox / file folder - if (route[3]) new_route.push(...route.slice(3, route.length)); + if (route[3]) new_route.push([...route].slice(3, route.length)); } else { if ($.isPlainObject(route[2])) { frappe.route_options = route[2]; From d459847ae39f8e9917686c3db6340ee95103085e Mon Sep 17 00:00:00 2001 From: leela Date: Tue, 13 Jul 2021 20:25:32 +0530 Subject: [PATCH 426/495] refactor: set amended docname to original docname Currently, whenever a document is amended it's name is set to name-X(X is a counter) when amended again and so on. In this PR, we have changed all cancelled doc patterns to name-CAN-X, so that amended docs can use the original name instead of name-X. --- frappe/core/doctype/doctype/test_doctype.py | 8 +- frappe/model/document.py | 9 +- frappe/model/naming.py | 83 ++++++++++++++++--- frappe/patches.txt | 1 + frappe/patches/v13_0/rename_cancelled_docs.py | 27 ++++++ frappe/public/js/frappe/form/form.js | 24 +++--- frappe/public/js/frappe/router.js | 6 ++ frappe/tests/test_naming.py | 34 ++++++++ 8 files changed, 164 insertions(+), 28 deletions(-) create mode 100644 frappe/patches/v13_0/rename_cancelled_docs.py diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index 1e1a01a685..4b6d0e4794 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -348,6 +348,7 @@ class TestDocType(unittest.TestCase): dump_docs = json.dumps(docs.get('docs')) cancel_all_linked_docs(dump_docs) data_link_doc.cancel() + data_doc.name = '{}-CAN-0'.format(data_doc.name) data_doc.load_from_db() self.assertEqual(data_link_doc.docstatus, 2) self.assertEqual(data_doc.docstatus, 2) @@ -371,7 +372,7 @@ class TestDocType(unittest.TestCase): for data in link_doc.get('permissions'): data.submit = 1 data.cancel = 1 - link_doc.insert() + link_doc.insert(ignore_if_duplicate=True) #create first parent doctype test_doc_1 = new_doctype('Test Doctype 1') @@ -386,7 +387,7 @@ class TestDocType(unittest.TestCase): for data in test_doc_1.get('permissions'): data.submit = 1 data.cancel = 1 - test_doc_1.insert() + test_doc_1.insert(ignore_if_duplicate=True) #crete second parent doctype doc = new_doctype('Test Doctype 2') @@ -401,7 +402,7 @@ class TestDocType(unittest.TestCase): for data in link_doc.get('permissions'): data.submit = 1 data.cancel = 1 - doc.insert() + doc.insert(ignore_if_duplicate=True) # create doctype data data_link_doc_1 = frappe.new_doc('Test Linked Doctype 1') @@ -432,6 +433,7 @@ class TestDocType(unittest.TestCase): # checking that doc for Test Doctype 2 is not canceled self.assertRaises(frappe.LinkExistsError, data_link_doc_1.cancel) + data_doc_2.name = '{}-CAN-0'.format(data_doc_2.name) data_doc.load_from_db() data_doc_2.load_from_db() self.assertEqual(data_link_doc_1.docstatus, 2) diff --git a/frappe/model/document.py b/frappe/model/document.py index 61160e1f01..e974ae2a3e 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -5,7 +5,7 @@ import time from frappe import _, msgprint, is_whitelisted from frappe.utils import flt, cstr, now, get_datetime_str, file_lock, date_diff from frappe.model.base_document import BaseDocument, get_controller -from frappe.model.naming import set_new_name +from frappe.model.naming import set_new_name, gen_new_name_for_cancelled_doc from werkzeug.exceptions import NotFound, Forbidden import hashlib, json from frappe.model import optional_fields, table_fields @@ -705,7 +705,6 @@ class Document(BaseDocument): else: tmp = frappe.db.sql("""select modified, docstatus from `tab{0}` where name = %s for update""".format(self.doctype), self.name, as_dict=True) - if not tmp: frappe.throw(_("Record does not exist")) else: @@ -916,8 +915,12 @@ class Document(BaseDocument): @whitelist.__func__ def _cancel(self): - """Cancel the document. Sets `docstatus` = 2, then saves.""" + """Cancel the document. Sets `docstatus` = 2, then saves. + """ self.docstatus = 2 + new_name = gen_new_name_for_cancelled_doc(self) + frappe.rename_doc(self.doctype, self.name, new_name, force=True, show_alert=False) + self.name = new_name self.save() @whitelist.__func__ diff --git a/frappe/model/naming.py b/frappe/model/naming.py index fe136adce8..6dff0aaff6 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -28,7 +28,7 @@ def set_new_name(doc): doc.name = None if getattr(doc, "amended_from", None): - _set_amended_name(doc) + doc.name = _get_amended_name(doc) return elif getattr(doc.meta, "issingle", False): @@ -221,6 +221,15 @@ def revert_series_if_last(key, name, doc=None): * prefix = #### and hashes = 2021 (hash doesn't exist) * will search hash in key then accordingly get prefix = "" """ + if hasattr(doc, 'amended_from'): + # do not revert if doc is amended, since cancelled docs still exist + if doc.docstatus != 2 and doc.amended_from: + return + + # for first cancelled doc + if doc.docstatus == 2 and not doc.amended_from: + name, _ = NameParser.parse_docname(doc.name, sep='-CAN-') + if ".#" in key: prefix, hashes = key.rsplit(".", 1) if "#" not in hashes: @@ -303,16 +312,9 @@ def append_number_if_name_exists(doctype, value, fieldname="name", separator="-" return value -def _set_amended_name(doc): - am_id = 1 - am_prefix = doc.amended_from - if frappe.db.get_value(doc.doctype, doc.amended_from, "amended_from"): - am_id = cint(doc.amended_from.split("-")[-1]) + 1 - am_prefix = "-".join(doc.amended_from.split("-")[:-1]) # except the last hyphen - - doc.name = am_prefix + "-" + str(am_id) - return doc.name - +def _get_amended_name(doc): + name, _ = NameParser(doc).parse_amended_from() + return name def _field_autoname(autoname, doc, skip_slicing=None): """ @@ -323,7 +325,6 @@ def _field_autoname(autoname, doc, skip_slicing=None): name = (cstr(doc.get(fieldname)) or "").strip() return name - def _prompt_autoname(autoname, doc): """ Generate a name using Prompt option. This simply means the user will have to set the name manually. @@ -354,3 +355,61 @@ def _format_autoname(autoname, doc): name = re.sub(r"(\{[\w | #]+\})", get_param_value_for_match, autoname_value) return name + +class NameParser: + """Parse document name and return all the parts of it. + + NOTE: It handles cancellend and amended doc parsing for now. It can be expanded. + """ + def __init__(self, doc): + self.doc = doc + + def parse_name(self): + if not hasattr(self.doc, "amended_from"): + return (self.doc.name, None, None) + + #If document is cancelled document + if hasattr(self.doc, "amended_from") and self.doc.docstatus == 2: + return self.parse_docname(self.doc.name, sep='-CAN-') + return self.parse_docname(self.doc.name) + + def parse_amended_from(self): + if not getattr(self.doc, 'amended_from', None): + return (None, None) + return self.parse_docname(self.doc.amended_from, '-CAN-') + + @classmethod + def parse_docname(cls, name, sep='-'): + split_list = name.rsplit(sep, 1) + + if len(split_list) == 1: + return (name, None) + return (split_list[0], split_list[1]) + +def get_cancelled_doc_latest_counter(tname, docname): + """Get the latest counter used for cancelled docs of given docname. + """ + name_prefix = f'{docname}-CAN-' + + rows = frappe.db.sql(""" + select + name + from `tab{tname}` + where + name like %(name_prefix)s and docstatus=2 + """.format(tname=tname), {'name_prefix': name_prefix+'%'}, as_dict=1) + + if not rows: + return -1 + return max([int(row.name.replace(name_prefix, '') or -1) for row in rows]) + +def gen_new_name_for_cancelled_doc(doc): + """Generate a new name for cancelled document. + """ + if getattr(doc, "amended_from", None): + name, _ = NameParser(doc).parse_amended_from() + else: + name = doc.name + + counter = get_cancelled_doc_latest_counter(doc.doctype, name) + return f'{name}-CAN-{counter+1}' diff --git a/frappe/patches.txt b/frappe/patches.txt index 7605d8ea2b..a9c5807df0 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -180,3 +180,4 @@ frappe.patches.v12_0.rename_uploaded_files_with_proper_name frappe.patches.v13_0.queryreport_columns frappe.patches.v13_0.jinja_hook frappe.patches.v13_0.update_notification_channel_if_empty +frappe.patches.v13_0.rename_cancelled_docs diff --git a/frappe/patches/v13_0/rename_cancelled_docs.py b/frappe/patches/v13_0/rename_cancelled_docs.py new file mode 100644 index 0000000000..2e99a6f3cd --- /dev/null +++ b/frappe/patches/v13_0/rename_cancelled_docs.py @@ -0,0 +1,27 @@ +import frappe +from frappe.model.naming import NameParser +from frappe.model.rename_doc import rename_doc + +def execute(): + """Rename already cancelled documents by adding `CAN-X` postfix instead of `-X`. + """ + for doctype in frappe.db.get_all('DocType'): + doctype = frappe.get_doc('DocType', doctype.name) + if doctype.is_submittable and frappe.db.table_exists(doctype.name): + cancelled_docs = frappe.db.get_all(doctype.name, ['amended_from', 'name'], {'docstatus':2}) + + for doc in cancelled_docs: + if '-CAN-' in doc.name: + continue + + current_name = doc.name + + if getattr(doc, "amended_from", None): + orig_name, counter = NameParser.parse_docname(doc.name) + else: + orig_name, counter = doc.name, 0 + new_name = f'{orig_name}-CAN-{counter or 0}' + + print(f"Renaming {doctype.name} record from {current_name} to {new_name}") + rename_doc(doctype.name, current_name, new_name, ignore_permissions=True, show_alert=False) + frappe.db.commit() diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 8064f90a98..30f023c987 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -770,32 +770,36 @@ frappe.ui.form.Form = class FrappeForm { } _cancel(btn, callback, on_error, skip_confirm) { - const me = this; const cancel_doc = () => { frappe.validated = true; - me.script_manager.trigger("before_cancel").then(() => { + this.script_manager.trigger("before_cancel").then(() => { if (!frappe.validated) { - return me.handle_save_fail(btn, on_error); + return this.handle_save_fail(btn, on_error); } - var after_cancel = function(r) { + const original_name = this.docname; + const after_cancel = (r) => { if (r.exc) { - me.handle_save_fail(btn, on_error); + this.handle_save_fail(btn, on_error); } else { frappe.utils.play_sound("cancel"); - me.refresh(); callback && callback(); - me.script_manager.trigger("after_cancel"); + this.script_manager.trigger("after_cancel"); + frappe.run_serially([ + () => this.rename_notify(this.doctype, original_name, r.docs[0].name), + () => frappe.router.clear_re_route(this.doctype, original_name), + () => this.refresh(), + ]); } }; - frappe.ui.form.save(me, "cancel", after_cancel, btn); + frappe.ui.form.save(this, "cancel", after_cancel, btn); }); } if (skip_confirm) { cancel_doc(); } else { - frappe.confirm(__("Permanently Cancel {0}?", [this.docname]), cancel_doc, me.handle_save_fail(btn, on_error)); + frappe.confirm(__("Permanently Cancel {0}?", [this.docname]), cancel_doc, this.handle_save_fail(btn, on_error)); } }; @@ -817,7 +821,7 @@ frappe.ui.form.Form = class FrappeForm { 'docname': this.doc.name }).then(is_amended => { if (is_amended) { - frappe.throw(__('This document is already amended, you cannot ammend it again')); + frappe.throw(__('This document is already amended, you cannot amend it again')); } this.validate_form_action("Amend"); var me = this; diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 2bfa7c7be6..17e0e689d3 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -235,6 +235,12 @@ frappe.router = { } }, + clear_re_route(doctype, docname) { + delete frappe.re_route[ + `${encodeURIComponent(frappe.router.slug(doctype))}/${encodeURIComponent(docname)}` + ]; + }, + set_title(sub_path) { if (frappe.route_titles[sub_path]) { frappe.utils.set_title(frappe.route_titles[sub_path]); diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py index 557993882f..78071a4120 100644 --- a/frappe/tests/test_naming.py +++ b/frappe/tests/test_naming.py @@ -116,3 +116,37 @@ class TestNaming(unittest.TestCase): self.assertEqual(current_index.get('current'), 2) frappe.db.sql("""delete from `tabSeries` where name = %s""", series) + + def test_naming_for_cancelled_and_amended_doc(self): + submittable_doctype = frappe.get_doc({ + "doctype": "DocType", + "module": "Core", + "custom": 1, + "is_submittable": 1, + "permissions": [{ + "role": "System Manager", + "read": 1 + }], + "name": 'Submittable Doctype' + }).insert(ignore_if_duplicate=True) + + doc = frappe.new_doc('Submittable Doctype') + doc.save() + original_name = doc.name + + doc.submit() + doc.cancel() + cancelled_name = doc.name + self.assertEqual(cancelled_name, "{}-CAN-0".format(original_name)) + + amended_doc = frappe.copy_doc(doc) + amended_doc.docstatus = 0 + amended_doc.amended_from = doc.name + amended_doc.save() + self.assertEqual(amended_doc.name, original_name) + + amended_doc.submit() + amended_doc.cancel() + self.assertEqual(amended_doc.name, "{}-CAN-1".format(original_name)) + + submittable_doctype.delete() From 09ca4d5af3c8aeda0496d8d2a16b025df8ca275f Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 21 Jul 2021 14:21:44 +0530 Subject: [PATCH 427/495] fix: V1 support and external links --- frappe/public/js/frappe/router.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 8f8704713e..0d8315bbc8 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -47,20 +47,18 @@ $('body').on('click', 'a', function(e) { return; } - if (href==='') { + if (href === '') { return override('/app'); } + if (href.startsWith('#')) { + // target startswith "#", this is a v1 style route, so remake it. + return override(e.currentTarget.hash); + } + if (frappe.router.is_app_route(e.currentTarget.pathname)) { // target has "/app, this is a v2 style route. - if (e.currentTarget.pathname) { - return override(e.currentTarget.pathname + e.currentTarget.hash); - } - } else { - // target has "#" ,this is a v1 style route, so remake it. - if (e.currentTarget.hash) { - return override(e.currentTarget.hash); - } + return override(e.currentTarget.pathname + e.currentTarget.hash); } }); From fa6cb14ee5221ec4d9db82bc731be8910eefb7b0 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Wed, 21 Jul 2021 19:15:38 +0530 Subject: [PATCH 428/495] refactor: moved from raw queries to frappe orm in core --- frappe/core/doctype/activity_log/feed.py | 15 +++-- .../domain_settings/domain_settings.py | 3 +- .../core/doctype/log_settings/log_settings.py | 1 + frappe/core/doctype/report/test_report.py | 9 ++- frappe/core/doctype/role/role.py | 3 +- .../scheduled_job_type/scheduled_job_type.py | 7 +- frappe/core/doctype/user/user.py | 27 +++++--- .../user_permission/test_user_permission.py | 19 ++++-- .../user_permission/user_permission.py | 66 +++++++++++++------ .../permission_manager/permission_manager.py | 6 +- 10 files changed, 106 insertions(+), 50 deletions(-) diff --git a/frappe/core/doctype/activity_log/feed.py b/frappe/core/doctype/activity_log/feed.py index caa3cae613..cf8f7416e8 100644 --- a/frappe/core/doctype/activity_log/feed.py +++ b/frappe/core/doctype/activity_log/feed.py @@ -27,12 +27,15 @@ def update_feed(doc, method=None): feed = frappe._dict(feed) doctype = feed.doctype or doc.doctype name = feed.name or doc.name - - # delete earlier feed - frappe.db.sql("""delete from `tabActivity Log` - where - reference_doctype=%s and reference_name=%s - and link_doctype=%s""", (doctype, name,feed.link_doctype)) + + + frappe.db.delete(doctype="Activity Log", conditions={"reference_doctype": doctype, + "reference_name": name, + "link_doctype": name.feed.link_doctype}) + # frappe.db.sql("""delete from `tabActivity Log` + # where + # reference_doctype=%s and reference_name=%s + # and link_doctype=%s""", (doctype, name,feed.link_doctype)) frappe.get_doc({ "doctype": "Activity Log", "reference_doctype": doctype, diff --git a/frappe/core/doctype/domain_settings/domain_settings.py b/frappe/core/doctype/domain_settings/domain_settings.py index 7ad0aeff21..6ca180def1 100644 --- a/frappe/core/doctype/domain_settings/domain_settings.py +++ b/frappe/core/doctype/domain_settings/domain_settings.py @@ -34,7 +34,8 @@ class DomainSettings(Document): all_domains = list((frappe.get_hooks('domains') or {})) def remove_role(role): - frappe.db.sql('delete from `tabHas Role` where role=%s', role) + frappe.db.delete(doctype="Has Role", conditions={"role": role}) + # frappe.db.sql('delete from `tabHas Role` where role=%s', role) frappe.set_value('Role', role, 'disabled', 1) for domain in all_domains: diff --git a/frappe/core/doctype/log_settings/log_settings.py b/frappe/core/doctype/log_settings/log_settings.py index e73aa8dac1..776fcc92e9 100644 --- a/frappe/core/doctype/log_settings/log_settings.py +++ b/frappe/core/doctype/log_settings/log_settings.py @@ -13,6 +13,7 @@ class LogSettings(Document): self.clear_email_queue() def clear_error_logs(self): + # frappe.db.delete(doctype="Error Log", conditions="") frappe.db.sql(""" DELETE FROM `tabError Log` WHERE `creation` < (NOW() - INTERVAL '{0}' DAY) """.format(self.clear_error_log_after)) diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py index 9d0c0b9af0..2bab6adf19 100644 --- a/frappe/core/doctype/report/test_report.py +++ b/frappe/core/doctype/report/test_report.py @@ -81,9 +81,14 @@ class TestReport(unittest.TestCase): self.assertDictEqual({'name': 'Administrator', 'user_type': 'System User', 'email': 'admin@example.com'}, admin_dict) def test_report_permissions(self): + frappe.set_user('test@example.com') - frappe.db.sql("""delete from `tabHas Role` where parent = %s - and role = 'Test Has Role'""", frappe.session.user, auto_commit=1) + frappe.db.delete(doctype="Has Role", conditions={ + "parent": frappe.session.user, + "role": "Test Has Role" + }) + # frappe.db.sql("""delete from `tabHas Role` where parent = %s + # and role = 'Test Has Role'""", frappe.session.user, auto_commit=1) if not frappe.db.exists('Role', 'Test Has Role'): role = frappe.get_doc({ diff --git a/frappe/core/doctype/role/role.py b/frappe/core/doctype/role/role.py index 02482c75ca..fae8ae2d28 100644 --- a/frappe/core/doctype/role/role.py +++ b/frappe/core/doctype/role/role.py @@ -38,7 +38,8 @@ class Role(Document): self.set(key, 0) def remove_roles(self): - frappe.db.sql("delete from `tabHas Role` where role = %s", self.name) + frappe.db.delete(doctype="Has Role", conditions={"role": self.name}) + # frappe.db.sql("delete from `tabHas Role` where role = %s", self.name) frappe.clear_cache() def on_update(self): 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 59089d12ad..d6c94aded1 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -13,7 +13,6 @@ from frappe.model.document import Document from frappe.utils import get_datetime, now_datetime from frappe.utils.background_jobs import enqueue, get_jobs - class ScheduledJobType(Document): def autoname(self): self.name = ".".join(self.method.split(".")[-2:]) @@ -110,7 +109,11 @@ class ScheduledJobType(Document): return 'long' if ('Long' in self.frequency) else 'default' def on_trash(self): - frappe.db.sql('delete from `tabScheduled Job Log` where scheduled_job_type=%s', self.name) + + frappe.db.delete(doctype="Scheduled Job Log", conditions={ + "scheduled_job_type": self.name + }) + # frappe.db.sql('delete from `tabScheduled Job Log` where scheduled_job_type=%s', self.name) @frappe.whitelist() diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 53d1c9ffe5..f69af4e4a8 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -17,7 +17,7 @@ from frappe.website.utils import is_signup_disabled from frappe.rate_limiter import rate_limit from frappe.utils.background_jobs import enqueue from frappe.core.doctype.user_type.user_type import user_linked_with_permission_on_doctype - +from frappe.database.database import Database STANDARD_USERS = ("Guest", "Administrator") @@ -367,23 +367,30 @@ class User(Document): if getattr(frappe.local, "login_manager", None): frappe.local.login_manager.logout(user=self.name) - # delete todos - frappe.db.sql("""DELETE FROM `tabToDo` WHERE `owner`=%s""", (self.name,)) + frappe.db.delete("Todo", {"owner": self.name}) + # frappe.db.sql("""DELETE FROM `tabToDo` WHERE `owner`=%s""", (self.name,)) frappe.db.sql("""UPDATE `tabToDo` SET `assigned_by`=NULL WHERE `assigned_by`=%s""", (self.name,)) # delete events - frappe.db.sql("""delete from `tabEvent` where owner=%s - and event_type='Private'""", (self.name,)) + frappe.db.delete("Event", {"owner": self.name, "event_type": "Private"}) + # frappe.db.sql("""delete from `tabEvent` where owner=%s + # and event_type='Private'""", (self.name,)) # delete shares - frappe.db.sql("""delete from `tabDocShare` where user=%s""", self.name) + frappe.db.delete("DocShare", {"user": self.name}) + # frappe.db.sql("""delete from `tabDocShare` where user=%s""", self.name) # delete messages - frappe.db.sql("""delete from `tabCommunication` - where communication_type in ('Chat', 'Notification') - and reference_doctype='User' - and (reference_name=%s or owner=%s)""", (self.name, self.name)) + # TODO: CHANGE THIS FROM ABHISHEK KA PYPIKA + frappe.db.delete("Communication", { + "reference_doctype": "User", + "communication_type": ("in", ("Chat", "Notification")), + }) + # frappe.db.sql("""delete from `tabCommunication` + # where communication_type in ('Chat', 'Notification') + # and reference_doctype='User' + # and (reference_name=%s or owner=%s)""", (self.name, self.name)) # unlink contact frappe.db.sql("""update `tabContact` diff --git a/frappe/core/doctype/user_permission/test_user_permission.py b/frappe/core/doctype/user_permission/test_user_permission.py index 1a442b53e7..9393e47ee9 100644 --- a/frappe/core/doctype/user_permission/test_user_permission.py +++ b/frappe/core/doctype/user_permission/test_user_permission.py @@ -10,12 +10,19 @@ import unittest class TestUserPermission(unittest.TestCase): def setUp(self): - frappe.db.sql("""DELETE FROM `tabUser Permission` - WHERE `user` in ( - 'test_bulk_creation_update@example.com', - 'test_user_perm1@example.com', - 'nested_doc_user@example.com')""") - frappe.delete_doc_if_exists("DocType", "Person") + + frappe.db.delete("User Permission", { + "user": ("in", ("test_bulk_creation_update@example.com", + "test_user_perm1@example.com", + "nested_doc_user@example.com")) + }) + + # frappe.db.sql("""DELETE FROM `tabUser Permission` + # WHERE `user` in ( + # 'test_bulk_creation_update@example.com', + # 'test_user_perm1@example.com', + # 'nested_doc_user@example.com')""") + # frappe.delete_doc_if_exists("DocType", "Person") frappe.db.sql_ddl("DROP TABLE IF EXISTS `tabPerson`") frappe.delete_doc_if_exists("DocType", "Doc A") frappe.db.sql_ddl("DROP TABLE IF EXISTS `tabDoc A`") diff --git a/frappe/core/doctype/user_permission/user_permission.py b/frappe/core/doctype/user_permission/user_permission.py index 4aa5797c7f..5b3822d82f 100644 --- a/frappe/core/doctype/user_permission/user_permission.py +++ b/frappe/core/doctype/user_permission/user_permission.py @@ -182,7 +182,11 @@ def clear_user_permissions(user, for_doctype): frappe.only_for('System Manager') total = frappe.db.count('User Permission', filters = dict(user=user, allow=for_doctype)) if total: - frappe.db.sql('DELETE FROM `tabUser Permission` WHERE `user`=%s AND `allow`=%s', (user, for_doctype)) + frappe.db.delete("User Permission", { + "user": user, + "allow": for_doctype + }) + # frappe.db.sql('DELETE FROM `tabUser Permission` WHERE `user`=%s AND `allow`=%s', (user, for_doctype)) frappe.clear_cache() return total @@ -232,28 +236,50 @@ def insert_user_perm(user, doctype, docname, is_default=0, hide_descendants=0, a user_perm.insert() def remove_applicable(perm_applied_docs, user, doctype, docname): + for applicable_for in perm_applied_docs: - frappe.db.sql("""DELETE FROM `tabUser Permission` - WHERE `user`=%s - AND `applicable_for`=%s - AND `allow`=%s - AND `for_value`=%s - """, (user, applicable_for, doctype, docname)) - + frappe.db.delete("User Permission", { + "user": user, + "applicable_for": applicable_for, + "allow": doctype, + "for_value": docname + }) + # + # frappe.db.sql("""DELETE FROM `tabUser Permission` + # WHERE `user`=%s + # AND `applicable_for`=%s + # AND `allow`=%s + # AND `for_value`=%s + # """, (user, applicable_for, doctype, docname)) def remove_apply_to_all(user, doctype, docname): - frappe.db.sql("""DELETE from `tabUser Permission` - WHERE `user`=%s - AND `apply_to_all_doctypes`=1 - AND `allow`=%s - AND `for_value`=%s - """,(user, doctype, docname)) + + frappe.db.delete("User Permission", { + "user": user, + "apply_to_all_doctypes": 1, + "allow": doctype, + "for_value": docname + }) + # frappe.db.sql("""DELETE from `tabUser Permission` + # WHERE `user`=%s + # AND `apply_to_all_doctypes`=1 + # AND `allow`=%s + # AND `for_value`=%s + # """,(user, doctype, docname)) def update_applicable(already_applied, to_apply, user, doctype, docname): for applied in already_applied: if applied not in to_apply: - frappe.db.sql("""DELETE FROM `tabUser Permission` - WHERE `user`=%s - AND `applicable_for`=%s - AND `allow`=%s - AND `for_value`=%s - """,(user, applied, doctype, docname)) + + frappe.db.delete("User Permission", { + "user": user, + "applicable_for": applied, + "allow": doctype, + "for_value": docname + }) + + # frappe.db.sql("""DELETE FROM `tabUser Permission` + # WHERE `user`=%s + # AND `applicable_for`=%s + # AND `allow`=%s + # AND `for_value`=%s + # """,(user, applied, doctype, docname)) diff --git a/frappe/core/page/permission_manager/permission_manager.py b/frappe/core/page/permission_manager/permission_manager.py index 15c7cb55ae..0a76283813 100644 --- a/frappe/core/page/permission_manager/permission_manager.py +++ b/frappe/core/page/permission_manager/permission_manager.py @@ -111,8 +111,10 @@ def remove(doctype, role, permlevel): setup_custom_perms(doctype) name = frappe.get_value('Custom DocPerm', dict(parent=doctype, role=role, permlevel=permlevel)) - - frappe.db.sql('delete from `tabCustom DocPerm` where name=%s', name) + frappe.db.delete("Custom DocPerm", { + "name": name + }) + # frappe.db.sql('delete from `tabCustom DocPerm` where name=%s', name) if not frappe.get_all('Custom DocPerm', dict(parent=doctype)): frappe.throw(_('There must be atleast one permission rule.'), title=_('Cannot Remove')) From 9aa17db392d54f52cc7eda223eb3ece276ee74a1 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Wed, 21 Jul 2021 19:16:17 +0530 Subject: [PATCH 429/495] refactor: moved from raw queries to frappe orm in desk --- .../dashboard_chart/test_dashboard_chart.py | 6 ++++-- frappe/desk/doctype/event/event.py | 11 ++++++++--- .../desk/doctype/route_history/route_history.py | 17 +++++++++++------ frappe/desk/doctype/tag/tag.py | 13 +++++++++++-- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py index 78d133b2d5..7133640c21 100644 --- a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py @@ -64,7 +64,8 @@ class TestDashboardChart(unittest.TestCase): if frappe.db.exists('Dashboard Chart', 'Test Empty Dashboard Chart'): frappe.delete_doc('Dashboard Chart', 'Test Empty Dashboard Chart') - frappe.db.sql('delete from `tabError Log`') + frappe.db.delete("Error Log") + # frappe.db.sql('delete from `tabError Log`') frappe.get_doc(dict( doctype = 'Dashboard Chart', @@ -94,7 +95,8 @@ class TestDashboardChart(unittest.TestCase): if frappe.db.exists('Dashboard Chart', 'Test Empty Dashboard Chart 2'): frappe.delete_doc('Dashboard Chart', 'Test Empty Dashboard Chart 2') - frappe.db.sql('delete from `tabError Log`') + frappe.db.delete("Error Log") + # frappe.db.sql('delete from `tabError Log`') # create one data point frappe.get_doc(dict(doctype = 'Error Log', creation = '2018-06-01 00:00:00')).insert() diff --git a/frappe/desk/doctype/event/event.py b/frappe/desk/doctype/event/event.py index 57c89eaf2e..f9a0dd74fd 100644 --- a/frappe/desk/doctype/event/event.py +++ b/frappe/desk/doctype/event/event.py @@ -338,9 +338,14 @@ def delete_events(ref_type, ref_name, delete_event=False): total_participants = frappe.get_all("Event Participants", filters={"parenttype": "Event", "parent": participation.parent}) if len(total_participants) <= 1: - frappe.db.sql("DELETE FROM `tabEvent` WHERE `name` = %(name)s", {'name': participation.parent}) - - frappe.db.sql("DELETE FROM `tabEvent Participants ` WHERE `name` = %(name)s", {'name': participation.name}) + frappe.db.delete("Event", { + "name": participation.parent + }) + # frappe.db.sql("DELETE FROM `tabEvent` WHERE `name` = %(name)s", {'name': participation.parent}) + frappe.db.delete("Event Participants", { + "name": participation.name + }) + # frappe.db.sql("DELETE FROM `tabEvent Participants ` WHERE `name` = %(name)s", {'name': participation.name}) # Close events if ends_on or repeat_till is less than now_datetime def set_status_of_events(): diff --git a/frappe/desk/doctype/route_history/route_history.py b/frappe/desk/doctype/route_history/route_history.py index b82077f485..bfa2711b8f 100644 --- a/frappe/desk/doctype/route_history/route_history.py +++ b/frappe/desk/doctype/route_history/route_history.py @@ -32,11 +32,16 @@ def flush_old_route_records(): fields=['modified'], order_by='modified desc') - frappe.db.sql(''' - DELETE - FROM `tabRoute History` - WHERE `modified` <= %(modified)s and `user`=%(modified)s - ''', { + frappe.db.delete("Route History", { "modified": last_record_to_keep[0].modified, "user": user - }) \ No newline at end of file + }) + + # frappe.db.sql(''' + # DELETE + # FROM `tabRoute History` + # WHERE `modified` <= %(modified)s and `user`=%(modified)s + # ''', { + # "modified": last_record_to_keep[0].modified, + # "user": user + # }) \ No newline at end of file diff --git a/frappe/desk/doctype/tag/tag.py b/frappe/desk/doctype/tag/tag.py index 4ea5c9cd7e..8b78405d06 100644 --- a/frappe/desk/doctype/tag/tag.py +++ b/frappe/desk/doctype/tag/tag.py @@ -123,7 +123,11 @@ def delete_tags_for_document(doc): if not frappe.db.table_exists("Tag Link"): return - frappe.db.sql("""DELETE FROM `tabTag Link` WHERE `document_type`=%s AND `document_name`=%s""", (doc.doctype, doc.name)) + frappe.db.delete("Tag Link", { + "document_type": doc.doctype, + "document_name": doc.name + }) + # frappe.db.sql("""DELETE FROM `tabTag Link` WHERE `document_type`=%s AND `document_name`=%s""", (doc.doctype, doc.name)) def update_tags(doc, tags): """ @@ -161,7 +165,12 @@ def get_deleted_tags(new_tags, existing_tags): return list(set(existing_tags) - set(new_tags)) def delete_tag_for_document(dt, dn, tag): - frappe.db.sql("""DELETE FROM `tabTag Link` WHERE `document_type`=%s AND `document_name`=%s AND tag=%s""", (dt, dn, tag)) + frappe.db.delete("Tag Link", { + "document_type": dt, + "document_name": dn, + "tag": tag + }) + # frappe.db.sql("""DELETE FROM `tabTag Link` WHERE `document_type`=%s AND `document_name`=%s AND tag=%s""", (dt, dn, tag)) @frappe.whitelist() def get_documents_for_tag(tag): From a256219dd27b5251b90e0d04b1a5b7797117ea6c Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Wed, 21 Jul 2021 19:16:59 +0530 Subject: [PATCH 430/495] refactor: moved from raw queries in frappe email to frappe orm --- frappe/email/doctype/unhandled_email/unhandled_email.py | 7 +++++-- frappe/email/queue.py | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frappe/email/doctype/unhandled_email/unhandled_email.py b/frappe/email/doctype/unhandled_email/unhandled_email.py index 6414dbece3..850007d85f 100644 --- a/frappe/email/doctype/unhandled_email/unhandled_email.py +++ b/frappe/email/doctype/unhandled_email/unhandled_email.py @@ -10,5 +10,8 @@ class UnhandledEmail(Document): def remove_old_unhandled_emails(): - frappe.db.sql("""DELETE FROM `tabUnhandled Email` - WHERE creation < %s""", frappe.utils.add_days(frappe.utils.nowdate(), -30)) + frappe.db.delete("Unhandled Email", { + "creation": ("<", frappe.utils.add_days(frappe.utils.nowdate(), -30)) + }) + # frappe.db.sql("""DELETE FROM `tabUnhandled Email` + # WHERE creation < %s""", frappe.utils.add_days(frappe.utils.nowdate(), -30)) \ No newline at end of file diff --git a/frappe/email/queue.py b/frappe/email/queue.py index 885a306cfb..40f1c7be3a 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -173,6 +173,7 @@ def clear_outbox(days=None): WHERE `priority`=0 AND `modified` < (NOW() - INTERVAL '{0}' DAY)""".format(days)) if email_queues: + # TODO: email_queues IN frappe.db.sql frappe.db.sql("""DELETE FROM `tabEmail Queue` WHERE `name` IN ({0})""".format( ','.join(['%s']*len(email_queues) )), tuple(email_queues)) From eb7e95ae26a542e82066b302e6bc85edffe7df25 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Wed, 21 Jul 2021 19:19:06 +0530 Subject: [PATCH 431/495] refactor: using frappe orm --- .../doctype/custom_field/custom_field.py | 14 ++++-- .../doctype/customize_form/customize_form.py | 8 +++- frappe/model/__init__.py | 26 +++++++---- frappe/model/delete_doc.py | 46 +++++++++++++++---- frappe/permissions.py | 7 ++- frappe/sessions.py | 5 +- 6 files changed, 78 insertions(+), 28 deletions(-) diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py index 7e6ea1875a..1d8f4ae67a 100644 --- a/frappe/custom/doctype/custom_field/custom_field.py +++ b/frappe/custom/doctype/custom_field/custom_field.py @@ -85,11 +85,15 @@ class CustomField(Document): frappe.bold(self.label))) # delete property setter entries - frappe.db.sql("""\ - DELETE FROM `tabProperty Setter` - WHERE doc_type = %s - AND field_name = %s""", - (self.dt, self.fieldname)) + frappe.db.delete("Property Setter", { + "doc_type": self.dt, + "field_name": self.fieldname + }) + # frappe.db.sql("""\ + # DELETE FROM `tabProperty Setter` + # WHERE doc_type = %s + # AND field_name = %s""", + # (self.dt, self.fieldname)) frappe.clear_cache(doctype=self.dt) diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index 1b8977acc4..3b7de448d3 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -20,8 +20,12 @@ from frappe.core.doctype.doctype.doctype import validate_series class CustomizeForm(Document): def on_update(self): - frappe.db.sql("delete from tabSingles where doctype='Customize Form'") - frappe.db.sql("delete from `tabCustomize Form Field`") + frappe.db.delete("Singles", { + "doctype": "Customize Form" + }) + frappe.db.delete("Customize Form Field") + # frappe.db.sql("delete from tabSingles where doctype='Customize Form'") + # frappe.db.sql("delete from `tabCustomize Form Field`") @frappe.whitelist() def fetch_to_customize(self): diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 75122f5aba..763fd36cc7 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -151,21 +151,29 @@ def delete_fields(args_dict, delete=0): fields = args_dict[dt] if not fields: continue - - frappe.db.sql(""" - DELETE FROM `tabDocField` - WHERE parent='%s' AND fieldname IN (%s) - """ % (dt, ", ".join(["'{}'".format(f) for f in fields]))) + + frappe.db.delete("DocField", { + "parent": dt, + "fieldname": ("in", ", ".join(["'{}'".format(f) for f in fields])) + }) + # frappe.db.sql(""" + # DELETE FROM `tabDocField` + # WHERE parent='%s' AND fieldname IN (%s) + # """ % (dt, ", ".join(["'{}'".format(f) for f in fields]))) # Delete the data/column only if delete is specified if not delete: continue if frappe.db.get_value("DocType", dt, "issingle"): - frappe.db.sql(""" - DELETE FROM `tabSingles` - WHERE doctype='%s' AND field IN (%s) - """ % (dt, ", ".join("'{}'".format(f) for f in fields))) + frappe.db.delete("Singles", { + "doctype": dt, + "field": ("in", ", ".join("'{}'".format(f) for f in fields)) + }) + # frappe.db.sql(""" + # DELETE FROM `tabSingles` + # WHERE doctype='%s' AND field IN (%s) + # """ % (dt, ", ".join("'{}'".format(f) for f in fields))) else: existing_fields = frappe.db.multisql({ "mariadb": "DESC `tab%s`" % dt, diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index cc88cfa106..ec53d060fd 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -65,12 +65,36 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa update_flags(doc, flags, ignore_permissions) check_permission_and_not_submitted(doc) - frappe.db.sql("delete from `tabCustom Field` where dt = %s", name) - frappe.db.sql("delete from `tabClient Script` where dt = %s", name) - frappe.db.sql("delete from `tabProperty Setter` where doc_type = %s", name) - frappe.db.sql("delete from `tabReport` where ref_doctype=%s", name) - frappe.db.sql("delete from `tabCustom DocPerm` where parent=%s", name) - frappe.db.sql("delete from `__global_search` where doctype=%s", name) + frappe.db.delete("Custom Field", { + "dt": name + }) + + frappe.db.delete("Client Script", { + "dt": name + }) + + frappe.db.delete("Property Setter", { + "doc_type": name + }) + + frappe.db.delete("Report", { + "ref_doctype": name + }) + + frappe.db.delete("Custom DocPerm", { + "parent": name + }) + + frappe.db.delete("__global_search", { + "doctype": name + }) + + # frappe.db.sql("delete from `tabCustom Field` where dt = %s", name) + # frappe.db.sql("delete from `tabClient Script` where dt = %s", name) + # frappe.db.sql("delete from `tabProperty Setter` where doc_type = %s", name) + # frappe.db.sql("delete from `tabReport` where ref_doctype=%s", name) + # frappe.db.sql("delete from `tabCustom DocPerm` where parent=%s", name) + # frappe.db.sql("delete from `__global_search` where doctype=%s", name) delete_from_table(doctype, name, ignore_doctypes, None) @@ -162,9 +186,15 @@ def update_naming_series(doc): def delete_from_table(doctype, name, ignore_doctypes, doc): if doctype!="DocType" and doctype==name: - frappe.db.sql("delete from `tabSingles` where `doctype`=%s", name) + frappe.db.delete("Singles", { + "doctype": name + }) + # frappe.db.sql("delete from `tabSingles` where `doctype`=%s", name) else: - frappe.db.sql("delete from `tab{0}` where `name`=%s".format(doctype), name) + frappe.db.delete(f"{doctype}", { + "name": name + }) + # frappe.db.sql("delete from `tab{0}` where `name`=%s".format(doctype), name) # get child tables if doc: diff --git a/frappe/permissions.py b/frappe/permissions.py index 07b4a2e68f..4cd846e52c 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -6,10 +6,12 @@ import frappe import frappe.share from frappe import _, msgprint from frappe.utils import cint - +from frappe.database.database import Database rights = ("select", "read", "write", "create", "delete", "submit", "cancel", "amend", "print", "email", "report", "import", "export", "set_user_permissions", "share") + + def check_admin_or_system_manager(user=None): if not user: user = frappe.session.user @@ -516,8 +518,9 @@ def reset_perms(doctype): """Reset permissions for given doctype.""" from frappe.desk.notifications import delete_notification_count_for delete_notification_count_for(doctype) + frappe.db.delete(doctype="Custom DocPerm", conditions={"parent": doctype}) - frappe.db.sql("""delete from `tabCustom DocPerm` where parent=%s""", doctype) + # frappe.db.sql("""delete from `tabCustom DocPerm` where parent=%s""", doctype) def get_linked_doctypes(dt): return list(set([dt] + [d.options for d in diff --git a/frappe/sessions.py b/frappe/sessions.py index 4d922d6769..ae2bd02d98 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -16,6 +16,7 @@ import frappe.translate import redis from urllib.parse import unquote from frappe.cache_manager import clear_user_cache +from frappe.database.database import Database @frappe.whitelist(allow_guest=True) def clear(user=None): @@ -76,7 +77,6 @@ def get_sessions_to_clear(user=None, keep_current=False, device=None): def delete_session(sid=None, user=None, reason="Session Expired"): from frappe.core.doctype.activity_log.feed import logout_feed - frappe.cache().hdel("session", sid) frappe.cache().hdel("last_db_session_update", sid) if sid and not user: @@ -84,7 +84,8 @@ def delete_session(sid=None, user=None, reason="Session Expired"): if user_details: user = user_details[0].get("user") logout_feed(user, reason) - frappe.db.sql("""delete from tabSessions where sid=%s""", sid) + frappe.db.delete(doctype="Sessions", conditions={"sid": sid}) + # frappe.db.sql("""delete from tabSessions where sid=%s""", sid) frappe.db.commit() def clear_all_sessions(reason=None): From 931549ee054e8193591e4ff5e6796144a74d94ee Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Wed, 21 Jul 2021 19:20:35 +0530 Subject: [PATCH 432/495] refactor: changed delete to accept no conditions --- frappe/database/database.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index 6012e47445..5b4d813011 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -951,15 +951,25 @@ class Database(object): query = sql_dict.get(current_dialect) return self.sql(query, values, **kwargs) - def delete(self, doctype, conditions, debug=False): + def delete(self, doctype, conditions=None, debug=False): if conditions: conditions, values = self.build_conditions(conditions) - return self.sql("DELETE FROM `tab{doctype}` where {conditions}".format( + if doctype.startswith("__"): + return self.sql("DELETE FROM `{doctype}` where {conditions}".format( doctype=doctype, conditions=conditions ), values, debug=debug) + else: + return self.sql("DELETE FROM `tab{doctype}` where {conditions}".format( + doctype=doctype, + conditions=conditions + ), values, debug=debug) + else: - frappe.throw(_('No conditions provided')) + return self.sql("DELETE FROM `tab{doctype}`".format( + doctype=doctype + ), debug=debug) + def get_last_created(self, doctype): last_record = self.get_all(doctype, ('creation'), limit=1, order_by='creation desc') From dd2648b1414e47b46071109ab3bba9c5ed6a37f9 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Wed, 21 Jul 2021 23:28:17 +0530 Subject: [PATCH 433/495] refactor: Moving from core raw queries to frappe orm --- frappe/core/doctype/activity_log/feed.py | 6 +++--- .../domain_settings/domain_settings.py | 2 +- frappe/core/doctype/error_log/error_log.py | 3 ++- frappe/core/doctype/report/test_report.py | 2 +- frappe/core/doctype/role/role.py | 2 +- .../scheduled_job_type/scheduled_job_type.py | 2 +- frappe/core/doctype/user/user.py | 19 +++++++++---------- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/frappe/core/doctype/activity_log/feed.py b/frappe/core/doctype/activity_log/feed.py index cf8f7416e8..92e18d4a5a 100644 --- a/frappe/core/doctype/activity_log/feed.py +++ b/frappe/core/doctype/activity_log/feed.py @@ -29,9 +29,9 @@ def update_feed(doc, method=None): name = feed.name or doc.name - frappe.db.delete(doctype="Activity Log", conditions={"reference_doctype": doctype, - "reference_name": name, - "link_doctype": name.feed.link_doctype}) + frappe.db.delete("Activity Log", {"reference_doctype": doctype, + "reference_name": name, + "link_doctype": name.feed.link_doctype}) # frappe.db.sql("""delete from `tabActivity Log` # where # reference_doctype=%s and reference_name=%s diff --git a/frappe/core/doctype/domain_settings/domain_settings.py b/frappe/core/doctype/domain_settings/domain_settings.py index 6ca180def1..4ed9d1e15a 100644 --- a/frappe/core/doctype/domain_settings/domain_settings.py +++ b/frappe/core/doctype/domain_settings/domain_settings.py @@ -34,7 +34,7 @@ class DomainSettings(Document): all_domains = list((frappe.get_hooks('domains') or {})) def remove_role(role): - frappe.db.delete(doctype="Has Role", conditions={"role": role}) + frappe.db.delete("Has Role", {"role": role}) # frappe.db.sql('delete from `tabHas Role` where role=%s', role) frappe.set_value('Role', role, 'disabled', 1) diff --git a/frappe/core/doctype/error_log/error_log.py b/frappe/core/doctype/error_log/error_log.py index 8223238c57..9a7333995c 100644 --- a/frappe/core/doctype/error_log/error_log.py +++ b/frappe/core/doctype/error_log/error_log.py @@ -20,4 +20,5 @@ def set_old_logs_as_seen(): def clear_error_logs(): '''Flush all Error Logs''' frappe.only_for('System Manager') - frappe.db.sql('''DELETE FROM `tabError Log`''') \ No newline at end of file + frappe.db.delete("Error Log") + # frappe.db.sql('''DELETE FROM `tabError Log`''') \ No newline at end of file diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py index 2bab6adf19..2ac41cca2b 100644 --- a/frappe/core/doctype/report/test_report.py +++ b/frappe/core/doctype/report/test_report.py @@ -83,7 +83,7 @@ class TestReport(unittest.TestCase): def test_report_permissions(self): frappe.set_user('test@example.com') - frappe.db.delete(doctype="Has Role", conditions={ + frappe.db.delete("Has Role", { "parent": frappe.session.user, "role": "Test Has Role" }) diff --git a/frappe/core/doctype/role/role.py b/frappe/core/doctype/role/role.py index fae8ae2d28..dee25eb476 100644 --- a/frappe/core/doctype/role/role.py +++ b/frappe/core/doctype/role/role.py @@ -38,7 +38,7 @@ class Role(Document): self.set(key, 0) def remove_roles(self): - frappe.db.delete(doctype="Has Role", conditions={"role": self.name}) + frappe.db.delete("Has Role", {"role": self.name}) # frappe.db.sql("delete from `tabHas Role` where role = %s", self.name) frappe.clear_cache() 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 d6c94aded1..9882f0edfe 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -110,7 +110,7 @@ class ScheduledJobType(Document): def on_trash(self): - frappe.db.delete(doctype="Scheduled Job Log", conditions={ + frappe.db.delete("Scheduled Job Log", { "scheduled_job_type": self.name }) # frappe.db.sql('delete from `tabScheduled Job Log` where scheduled_job_type=%s', self.name) diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index f69af4e4a8..166f61de8d 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -17,7 +17,6 @@ from frappe.website.utils import is_signup_disabled from frappe.rate_limiter import rate_limit from frappe.utils.background_jobs import enqueue from frappe.core.doctype.user_type.user_type import user_linked_with_permission_on_doctype -from frappe.database.database import Database STANDARD_USERS = ("Guest", "Administrator") @@ -382,15 +381,15 @@ class User(Document): # frappe.db.sql("""delete from `tabDocShare` where user=%s""", self.name) # delete messages - # TODO: CHANGE THIS FROM ABHISHEK KA PYPIKA - frappe.db.delete("Communication", { - "reference_doctype": "User", - "communication_type": ("in", ("Chat", "Notification")), - }) - # frappe.db.sql("""delete from `tabCommunication` - # where communication_type in ('Chat', 'Notification') - # and reference_doctype='User' - # and (reference_name=%s or owner=%s)""", (self.name, self.name)) + + # frappe.db.delete("Communication", { + # "reference_doctype": "User", + # "communication_type": ("in", ("Chat", "Notification")), + # }) + frappe.db.sql("""delete from `tabCommunication` + where communication_type in ('Chat', 'Notification') + and reference_doctype='User' + and (reference_name=%s or owner=%s)""", (self.name, self.name)) # unlink contact frappe.db.sql("""update `tabContact` From 79ccaa95f3aff078a3ebdc68291b270b7cbeed26 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Wed, 21 Jul 2021 23:29:50 +0530 Subject: [PATCH 434/495] refactor: Moved raw queries to frappe orm --- .../personal_data_deletion_request.py | 18 ++++++++++++------ .../doctype/workflow_action/workflow_action.py | 12 +++++++++--- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py b/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py index 63ba96d138..b23c7fa11d 100644 --- a/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py +++ b/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py @@ -323,12 +323,18 @@ class PersonalDataDeletionRequest(Document): def remove_unverified_record(): - frappe.db.sql( - """ - DELETE FROM `tabPersonal Data Deletion Request` - WHERE `status` = 'Pending Verification' - AND `creation` < (NOW() - INTERVAL '7' DAY)""" - ) + + frappe.db.delete("Personal Data Deletion Request", { + "status": "Pending Verification", + "creation": ("<", ["Now()", "-", "INTERVAL 7 DAY"]) + }) + + # frappe.db.sql( + # """ + # DELETE FROM `tabPersonal Data Deletion Request` + # WHERE `status` = 'Pending Verification' + # AND `creation` < (NOW() - INTERVAL '7' DAY)""" + # ) @frappe.whitelist(allow_guest=True) diff --git a/frappe/workflow/doctype/workflow_action/workflow_action.py b/frappe/workflow/doctype/workflow_action/workflow_action.py index b70ffb2406..af168d3477 100644 --- a/frappe/workflow/doctype/workflow_action/workflow_action.py +++ b/frappe/workflow/doctype/workflow_action/workflow_action.py @@ -133,9 +133,15 @@ def return_link_expired_page(doc, doc_workflow_state): def clear_old_workflow_actions(doc, user=None): user = user if user else frappe.session.user - frappe.db.sql("""DELETE FROM `tabWorkflow Action` - WHERE `reference_doctype`=%s AND `reference_name`=%s AND `user`!=%s AND `status`='Open'""", - (doc.get('doctype'), doc.get('name'), user)) + frappe.db.delete("Workflow Action", { + "reference_doctype": doc.get("doctype"), + "reference_name": doc.get("name"), + "user": ("!=", user), + "status": "Open" + }) + # frappe.db.sql("""DELETE FROM `tabWorkflow Action` + # WHERE `reference_doctype`=%s AND `reference_name`=%s AND `user`!=%s AND `status`='Open'""", + # (doc.get('doctype'), doc.get('name'), user)) def update_completed_workflow_actions(doc, user=None): user = user if user else frappe.session.user From 888783682072330987f713bd700b8bb1f7db916d Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Wed, 21 Jul 2021 23:31:15 +0530 Subject: [PATCH 435/495] refactor: Moved util queries to frappe orm --- frappe/utils/global_search.py | 25 +++++++++++++++++-------- frappe/utils/password.py | 15 +++++++++++---- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index 8fa2ea474f..a018761370 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -23,7 +23,8 @@ def reset(): Deletes all data in __global_search :return: """ - frappe.db.sql('DELETE FROM `__global_search`') + frappe.db.delete("__global_search") + # frappe.db.sql('DELETE FROM `__global_search`') def get_doctypes_with_global_search(with_child_tables=True): @@ -146,9 +147,12 @@ def rebuild_for_doctype(doctype): def delete_global_search_records_for_doctype(doctype): - frappe.db.sql('''DELETE - FROM `__global_search` - WHERE doctype = %s''', doctype, as_dict=True) + frappe.db.delete("__global_search", { + "doctype": doctype + }) + # frappe.db.sql('''DELETE + # FROM `__global_search` + # WHERE doctype = %s''', doctype, as_dict=True) def get_selected_fields(meta, global_search_fields): @@ -400,10 +404,15 @@ def delete_for_document(doc): :param doc: Deleted document """ - frappe.db.sql('''DELETE - FROM `__global_search` - WHERE doctype = %s - AND name = %s''', (doc.doctype, doc.name), as_dict=True) + frappe.db.delete("__global_search", { + "doctype": doc.doctype, + "name": doc.name + }) + + # frappe.db.sql('''DELETE + # FROM `__global_search` + # WHERE doctype = %s + # AND name = %s''', (doc.doctype, doc.name), as_dict=True) @frappe.whitelist() diff --git a/frappe/utils/password.py b/frappe/utils/password.py index 428f2e9577..caa89af64e 100644 --- a/frappe/utils/password.py +++ b/frappe/utils/password.py @@ -65,10 +65,17 @@ def set_encrypted_password(doctype, name, pwd, fieldname='password'): def remove_encrypted_password(doctype, name, fieldname='password'): - frappe.db.sql( - 'DELETE FROM `__Auth` WHERE doctype = %s and name = %s and fieldname = %s', - values=[doctype, name, fieldname] - ) + + frappe.db.delete("__Auth", { + "doctype": doctype, + "name": name, + "fieldname": fieldname + }) + + # frappe.db.sql( + # 'DELETE FROM `__Auth` WHERE doctype = %s and name = %s and fieldname = %s', + # values=[doctype, name, fieldname] + # ) def check_password(user, pwd, doctype='User', fieldname='password', delete_tracker_cache=True): From 8feb430371d38b34ca90f6ae2f08bd651e812a64 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Wed, 21 Jul 2021 23:32:18 +0530 Subject: [PATCH 436/495] refactor: Moved raw queries to frappe orm --- frappe/model/__init__.py | 2 +- frappe/model/delete_doc.py | 8 ++++++-- .../v11_0/apply_customization_to_custom_doctype.py | 10 ++++++++-- .../v11_0/sync_stripe_settings_before_migrate.py | 5 ++++- .../v12_0/delete_feedback_request_if_exists.py | 11 +++++++---- .../v12_0/remove_deprecated_fields_from_doctype.py | 12 ++++++++---- 6 files changed, 34 insertions(+), 14 deletions(-) diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 763fd36cc7..99eaa7ff5d 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -154,7 +154,7 @@ def delete_fields(args_dict, delete=0): frappe.db.delete("DocField", { "parent": dt, - "fieldname": ("in", ", ".join(["'{}'".format(f) for f in fields])) + "fieldname": ("in", fields) }) # frappe.db.sql(""" # DELETE FROM `tabDocField` diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index ec53d060fd..9b5ebb1031 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -369,8 +369,12 @@ def clear_references(doctype, reference_doctype, reference_name, (reference_doctype, reference_name)) def clear_timeline_references(link_doctype, link_name): - frappe.db.sql("""DELETE FROM `tabCommunication Link` - WHERE `tabCommunication Link`.link_doctype=%s AND `tabCommunication Link`.link_name=%s""", (link_doctype, link_name)) + frappe.db.delete("Communication Link", { + "link_doctype": link_doctype, + "link_name": link_name + }) + # frappe.db.sql("""DELETE FROM `tabCommunication Link` + # WHERE `tabCommunication Link`.link_doctype=%s AND `tabCommunication Link`.link_name=%s""", (link_doctype, link_name)) def insert_feed(doc): if ( diff --git a/frappe/patches/v11_0/apply_customization_to_custom_doctype.py b/frappe/patches/v11_0/apply_customization_to_custom_doctype.py index 49b68ed240..c13ff83a1f 100644 --- a/frappe/patches/v11_0/apply_customization_to_custom_doctype.py +++ b/frappe/patches/v11_0/apply_customization_to_custom_doctype.py @@ -28,7 +28,10 @@ def execute(): for prop in property_setters: property_setter_map[prop.field_name] = prop - frappe.db.sql('DELETE FROM `tabProperty Setter` WHERE `name`=%s', prop.name) + frappe.db.delete("Property Setter", { + "name": prop.name + }) + # frappe.db.sql('DELETE FROM `tabProperty Setter` WHERE `name`=%s', prop.name) meta = frappe.get_meta(doctype.name) @@ -50,6 +53,9 @@ def execute(): df = frappe.new_doc('DocField', meta, 'fields') df.update(cf) meta.fields.append(df) - frappe.db.sql('DELETE FROM `tabCustom Field` WHERE name=%s', cf.name) + frappe.db.delete("Custom Field", { + "name": cf.name + }) + # frappe.db.sql('DELETE FROM `tabCustom Field` WHERE name=%s', cf.name) meta.save() diff --git a/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py b/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py index a8e9bd4de1..1a12d4dc1d 100644 --- a/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py +++ b/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py @@ -17,4 +17,7 @@ def execute(): settings.secret_key = secret_key settings.save(ignore_permissions=True) - frappe.db.sql("""DELETE FROM tabSingles WHERE doctype='Stripe Settings'""") \ No newline at end of file + frappe.db.delete("Singles", { + "doctype": "Stripe Settings" + }) + # frappe.db.sql("""DELETE FROM tabSingles WHERE doctype='Stripe Settings'""") \ No newline at end of file diff --git a/frappe/patches/v12_0/delete_feedback_request_if_exists.py b/frappe/patches/v12_0/delete_feedback_request_if_exists.py index fdbcecfc5a..537d509424 100644 --- a/frappe/patches/v12_0/delete_feedback_request_if_exists.py +++ b/frappe/patches/v12_0/delete_feedback_request_if_exists.py @@ -2,7 +2,10 @@ import frappe def execute(): - frappe.db.sql(''' - DELETE from `tabDocType` - WHERE name = 'Feedback Request' - ''') \ No newline at end of file + frappe.db.delete("DocType", { + "name": "Feedback Request" + }) + # frappe.db.sql(''' + # DELETE from `tabDocType` + # WHERE name = 'Feedback Request' + # ''') \ No newline at end of file diff --git a/frappe/patches/v12_0/remove_deprecated_fields_from_doctype.py b/frappe/patches/v12_0/remove_deprecated_fields_from_doctype.py index 60599066e6..17dfbc5ca1 100644 --- a/frappe/patches/v12_0/remove_deprecated_fields_from_doctype.py +++ b/frappe/patches/v12_0/remove_deprecated_fields_from_doctype.py @@ -8,7 +8,11 @@ def execute(): 'DocType': ['hide_heading', 'image_view', 'read_only_onload'] }, delete=1) - frappe.db.sql(''' - DELETE from `tabProperty Setter` - WHERE property = 'read_only_onload' - ''') + frappe.db.delete("Property Setter", { + "property": "read_only_onload" + }) + + # frappe.db.sql(''' + # DELETE from `tabProperty Setter` + # WHERE property = 'read_only_onload' + # ''') From 6f70dcf52dd66cc2d200d41c07ad7900a10aa58e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 22 Jul 2021 12:02:47 +0530 Subject: [PATCH 437/495] test: Add navingation test case --- cypress/integration/navigation.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 cypress/integration/navigation.js diff --git a/cypress/integration/navigation.js b/cypress/integration/navigation.js new file mode 100644 index 0000000000..3fe12f3547 --- /dev/null +++ b/cypress/integration/navigation.js @@ -0,0 +1,15 @@ +context('Navigation', () => { + before(() => { + cy.login(); + cy.visit('/app/website'); + }); + it('Navigate to route with hash in document name', () => { + cy.insert_doc('ToDo', {'__newname': 'ABC#123', 'description': 'Test this', 'ignore_duplicate': true}); + cy.visit('/app/todo/ABC#123'); + cy.title().should('eq', 'Test this - ABC#123'); + cy.get_field('description', 'Text Editor').contains('Test this'); + cy.go('back'); + cy.title().should('eq', 'Website'); + cy.remove_doc('ToDo', 'ABC#123'); + }); +}); \ No newline at end of file From a9cab2cf786560689678be4a686d113c83e414aa Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Thu, 22 Jul 2021 13:07:31 +0530 Subject: [PATCH 438/495] test: Remove `remove_doc`. to avoid flaky test --- cypress/integration/navigation.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cypress/integration/navigation.js b/cypress/integration/navigation.js index 3fe12f3547..7e1426aa46 100644 --- a/cypress/integration/navigation.js +++ b/cypress/integration/navigation.js @@ -10,6 +10,5 @@ context('Navigation', () => { cy.get_field('description', 'Text Editor').contains('Test this'); cy.go('back'); cy.title().should('eq', 'Website'); - cy.remove_doc('ToDo', 'ABC#123'); }); -}); \ No newline at end of file +}); From 4dc476da6acad27c20afba1bf66074a774b899ec Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Thu, 22 Jul 2021 17:00:24 +0530 Subject: [PATCH 439/495] fix(update_feed): Malformed query in activity log --- frappe/core/doctype/activity_log/feed.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/activity_log/feed.py b/frappe/core/doctype/activity_log/feed.py index 92e18d4a5a..9b48d0550a 100644 --- a/frappe/core/doctype/activity_log/feed.py +++ b/frappe/core/doctype/activity_log/feed.py @@ -31,7 +31,7 @@ def update_feed(doc, method=None): frappe.db.delete("Activity Log", {"reference_doctype": doctype, "reference_name": name, - "link_doctype": name.feed.link_doctype}) + "link_doctype": feed.link_doctype}) # frappe.db.sql("""delete from `tabActivity Log` # where # reference_doctype=%s and reference_name=%s From 1204ae8ce40c2ebb34f54dfdf970199aa48511e0 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Thu, 22 Jul 2021 17:26:24 +0530 Subject: [PATCH 440/495] fix: fixed spacing and ui tests --- frappe/core/doctype/activity_log/feed.py | 12 ++++++------ frappe/defaults.py | 14 +++++++++----- frappe/desk/doctype/desktop_icon/desktop_icon.py | 5 ++++- frappe/model/delete_doc.py | 6 ------ frappe/permissions.py | 2 +- frappe/sessions.py | 2 +- frappe/utils/password.py | 2 -- .../personal_data_deletion_request.py | 2 -- 8 files changed, 21 insertions(+), 24 deletions(-) diff --git a/frappe/core/doctype/activity_log/feed.py b/frappe/core/doctype/activity_log/feed.py index 9b48d0550a..d91a21ec44 100644 --- a/frappe/core/doctype/activity_log/feed.py +++ b/frappe/core/doctype/activity_log/feed.py @@ -26,12 +26,12 @@ def update_feed(doc, method=None): feed = frappe._dict(feed) doctype = feed.doctype or doc.doctype - name = feed.name or doc.name - - - frappe.db.delete("Activity Log", {"reference_doctype": doctype, - "reference_name": name, - "link_doctype": feed.link_doctype}) + name = feed.name or doc.name + frappe.db.delete("Activity Log", { + "reference_doctype": doctype, + "reference_name": name, + "link_doctype": feed.link_doctype + }) # frappe.db.sql("""delete from `tabActivity Log` # where # reference_doctype=%s and reference_name=%s diff --git a/frappe/defaults.py b/frappe/defaults.py index fde48d71ff..ee73e86a91 100644 --- a/frappe/defaults.py +++ b/frappe/defaults.py @@ -124,11 +124,15 @@ def set_default(key, value, parent, parenttype="__default"): where defkey=%s and parent=%s for update''', (key, parent)): - frappe.db.sql(""" - delete from - `tabDefaultValue` - where - defkey=%s and parent=%s""", (key, parent)) + frappe.db.delete("DefaultValue", { + "defkey": key, + "parent": parent + }) + # frappe.db.sql(""" + # delete from + # `tabDefaultValue` + # where + # defkey=%s and parent=%s""", (key, parent)) if value != None: add_default(key, value, parent) else: diff --git a/frappe/desk/doctype/desktop_icon/desktop_icon.py b/frappe/desk/doctype/desktop_icon/desktop_icon.py index 81a79cdb09..bc5677e9b9 100644 --- a/frappe/desk/doctype/desktop_icon/desktop_icon.py +++ b/frappe/desk/doctype/desktop_icon/desktop_icon.py @@ -197,7 +197,10 @@ def set_desktop_icons(visible_list, ignore_duplicate=True): # clear all custom only if setup is not complete if not int(frappe.defaults.get_defaults().setup_complete or 0): - frappe.db.sql('delete from `tabDesktop Icon` where standard=0') + frappe.db.delete("Desktop Icon", { + "standard": 0 + }) + # frappe.db.sql('delete from `tabDesktop Icon` where standard=0') # set standard as blocked and hidden if setting first active domain if not frappe.flags.keep_desktop_icons: diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 9b5ebb1031..03510d489d 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -64,27 +64,21 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa update_flags(doc, flags, ignore_permissions) check_permission_and_not_submitted(doc) - frappe.db.delete("Custom Field", { "dt": name }) - frappe.db.delete("Client Script", { "dt": name }) - frappe.db.delete("Property Setter", { "doc_type": name }) - frappe.db.delete("Report", { "ref_doctype": name }) - frappe.db.delete("Custom DocPerm", { "parent": name }) - frappe.db.delete("__global_search", { "doctype": name }) diff --git a/frappe/permissions.py b/frappe/permissions.py index 4cd846e52c..293ac14f27 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -518,7 +518,7 @@ def reset_perms(doctype): """Reset permissions for given doctype.""" from frappe.desk.notifications import delete_notification_count_for delete_notification_count_for(doctype) - frappe.db.delete(doctype="Custom DocPerm", conditions={"parent": doctype}) + frappe.db.delete("Custom DocPerm", {"parent": doctype}) # frappe.db.sql("""delete from `tabCustom DocPerm` where parent=%s""", doctype) diff --git a/frappe/sessions.py b/frappe/sessions.py index ae2bd02d98..3a584dfb74 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -84,7 +84,7 @@ def delete_session(sid=None, user=None, reason="Session Expired"): if user_details: user = user_details[0].get("user") logout_feed(user, reason) - frappe.db.delete(doctype="Sessions", conditions={"sid": sid}) + frappe.db.delete("Sessions", {"sid": sid}) # frappe.db.sql("""delete from tabSessions where sid=%s""", sid) frappe.db.commit() diff --git a/frappe/utils/password.py b/frappe/utils/password.py index caa89af64e..c144d866f3 100644 --- a/frappe/utils/password.py +++ b/frappe/utils/password.py @@ -65,7 +65,6 @@ def set_encrypted_password(doctype, name, pwd, fieldname='password'): def remove_encrypted_password(doctype, name, fieldname='password'): - frappe.db.delete("__Auth", { "doctype": doctype, "name": name, @@ -77,7 +76,6 @@ def remove_encrypted_password(doctype, name, fieldname='password'): # values=[doctype, name, fieldname] # ) - def check_password(user, pwd, doctype='User', fieldname='password', delete_tracker_cache=True): '''Checks if user and password are correct, else raises frappe.AuthenticationError''' diff --git a/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py b/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py index b23c7fa11d..bde6f2a4ea 100644 --- a/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py +++ b/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py @@ -323,7 +323,6 @@ class PersonalDataDeletionRequest(Document): def remove_unverified_record(): - frappe.db.delete("Personal Data Deletion Request", { "status": "Pending Verification", "creation": ("<", ["Now()", "-", "INTERVAL 7 DAY"]) @@ -336,7 +335,6 @@ def remove_unverified_record(): # AND `creation` < (NOW() - INTERVAL '7' DAY)""" # ) - @frappe.whitelist(allow_guest=True) def confirm_deletion(email, name, host_name): if not verify_request(): From bc39fd869fac10ee10b4cafd471e85227eace2b9 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Thu, 22 Jul 2021 19:09:39 +0530 Subject: [PATCH 441/495] fix: Fixed typos --- frappe/core/doctype/user/user.py | 2 +- frappe/database/database.py | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 166f61de8d..69d3b67220 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -366,7 +366,7 @@ class User(Document): if getattr(frappe.local, "login_manager", None): frappe.local.login_manager.logout(user=self.name) - frappe.db.delete("Todo", {"owner": self.name}) + frappe.db.delete("ToDo", {"owner": self.name}) # frappe.db.sql("""DELETE FROM `tabToDo` WHERE `owner`=%s""", (self.name,)) frappe.db.sql("""UPDATE `tabToDo` SET `assigned_by`=NULL WHERE `assigned_by`=%s""", (self.name,)) diff --git a/frappe/database/database.py b/frappe/database/database.py index 5b4d813011..60185106e1 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -966,9 +966,15 @@ class Database(object): ), values, debug=debug) else: - return self.sql("DELETE FROM `tab{doctype}`".format( - doctype=doctype - ), debug=debug) + if doctype.startwith("__"): + return self.sql("DELETE FROM `{doctype}`".format( + doctype=doctype + ), debug=debug) + + else: + return self.sql("DELETE FROM `tab{doctype}`".format( + doctype=doctype + ), debug=debug) def get_last_created(self, doctype): From 1c82b83be07f016ca86149f40a5fac1f21f705e9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 23 Jul 2021 10:52:22 +0530 Subject: [PATCH 442/495] refactor: Simplify frappe.db.delete logic * Rename conditions kwarg to filters. Handle conditions kwarg if passed * Re-added exc raise for no filters...to risky. Thinking of having an extra kwarg like `force` or something...undecided * Added Python type hints * Accept and pass kwargs to frappe.db.sql...Now pass any kwarg db.sql accepts * Pass debug from fn def if someone is still using it as a positional arg :thonk: --- frappe/database/database.py | 38 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index 60185106e1..cdf15f9c3d 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -6,6 +6,7 @@ import re import time +from typing import Dict, List, Union import frappe import datetime import frappe.defaults @@ -951,30 +952,23 @@ class Database(object): query = sql_dict.get(current_dialect) return self.sql(query, values, **kwargs) - def delete(self, doctype, conditions=None, debug=False): - if conditions: - conditions, values = self.build_conditions(conditions) - if doctype.startswith("__"): - return self.sql("DELETE FROM `{doctype}` where {conditions}".format( - doctype=doctype, - conditions=conditions - ), values, debug=debug) - else: - return self.sql("DELETE FROM `tab{doctype}` where {conditions}".format( - doctype=doctype, - conditions=conditions - ), values, debug=debug) + def delete(self, doctype: str, filters: Union[Dict, List], debug=False, **kwargs): + """Delete rows from a table in site which match the passed filters. This + does trigger DocType hooks. Simply runs a DELETE query in the database. + """ + if kwargs: + filters = filters or kwargs.get("conditions") + if not filters: + raise TypeError("No filters passed for `frappe.db.delete`") + if "debug" not in kwargs: + kwargs["debug"] = debug - else: - if doctype.startwith("__"): - return self.sql("DELETE FROM `{doctype}`".format( - doctype=doctype - ), debug=debug) + table = doctype if doctype.startswith("__") else f"tab{doctype}" + query = f"DELETE FROM `{table}`" + conditions, values = self.build_conditions(filters) + query += f"WHERE {conditions}" - else: - return self.sql("DELETE FROM `tab{doctype}`".format( - doctype=doctype - ), debug=debug) + return self.sql(query, values, **kwargs) def get_last_created(self, doctype): From 36c4be9a8d5c456316d11f7b56e427a01d22f167 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 23 Jul 2021 11:50:17 +0530 Subject: [PATCH 443/495] feat: frappe.db.truncate ORM wrapper for SQL DDL statement * Alias frappe.db.clear_table to use truncate * Support for __ tables is added here --- frappe/database/database.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index cdf15f9c3d..0c18ae91f7 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -955,21 +955,36 @@ class Database(object): def delete(self, doctype: str, filters: Union[Dict, List], debug=False, **kwargs): """Delete rows from a table in site which match the passed filters. This does trigger DocType hooks. Simply runs a DELETE query in the database. + + Doctype name can be passed directly, it will be pre-pended with `tab`. """ if kwargs: filters = filters or kwargs.get("conditions") if not filters: - raise TypeError("No filters passed for `frappe.db.delete`") + raise TypeError( + "No filters passed for `frappe.db.delete`. If you wish to clear the whole " + "table, consider using `frappe.db.truncate` instead?" + ) if "debug" not in kwargs: kwargs["debug"] = debug table = doctype if doctype.startswith("__") else f"tab{doctype}" - query = f"DELETE FROM `{table}`" conditions, values = self.build_conditions(filters) - query += f"WHERE {conditions}" + query = f"DELETE FROM `{table}` WHERE {conditions}" return self.sql(query, values, **kwargs) + def truncate(self, doctype: str): + """Truncate a table in the database. This runs a DDL command `TRUNCATE TABLE`. + This cannot be rolled back. + + Doctype name can be passed directly, it will be pre-pended with `tab`. + """ + table = doctype if doctype.startswith("__") else f"tab{doctype}" + return self.sql_ddl(f"truncate `{table}`") + + def clear_table(self, doctype): + return self.truncate(doctype) def get_last_created(self, doctype): last_record = self.get_all(doctype, ('creation'), limit=1, order_by='creation desc') @@ -978,9 +993,6 @@ class Database(object): else: return None - def clear_table(self, doctype): - self.sql('truncate `tab{}`'.format(doctype)) - def log_touched_tables(self, query, values=None): if values: query = frappe.safe_decode(self._cursor.mogrify(query, values)) From e3ea5cd05af53c95d96341c4ae52c0ce59c75c09 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Fri, 23 Jul 2021 14:08:16 +0530 Subject: [PATCH 444/495] fix: refresh dependencies on `awesomplete-selectcomplete` event (#13756) --- frappe/public/js/frappe/ui/field_group.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/frappe/public/js/frappe/ui/field_group.js b/frappe/public/js/frappe/ui/field_group.js index b8b908eb95..db06d99615 100644 --- a/frappe/public/js/frappe/ui/field_group.js +++ b/frappe/public/js/frappe/ui/field_group.js @@ -40,14 +40,16 @@ frappe.ui.FieldGroup = class FieldGroup extends frappe.ui.form.Layout { this.catch_enter_as_submit(); } - $(this.wrapper).find('input, select').on('change', () => { - this.dirty = true; - - frappe.run_serially([ - () => frappe.timeout(0.1), - () => me.refresh_dependency() - ]); - }); + $(this.wrapper).find('input, select').on( + 'change awesomplete-selectcomplete', + () => { + this.dirty = true; + frappe.run_serially([ + () => frappe.timeout(0.1), + () => me.refresh_dependency() + ]); + } + ); } } From 2f3371d873b976afe08a4003533263a9a636ce28 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Fri, 23 Jul 2021 23:14:24 +0530 Subject: [PATCH 445/495] refactor: Moved raw queries to frappe orm --- .../core/doctype/activity_log/activity_log.py | 6 +-- .../core/doctype/log_settings/log_settings.py | 8 ++-- frappe/database/database.py | 13 ++++--- frappe/desk/doctype/todo/todo.py | 12 ++++-- frappe/model/document.py | 17 ++++++-- .../v12_0/set_primary_key_in_series.py | 39 ++++++++++--------- .../setup_comments_from_communications.py | 5 ++- .../patches/v13_0/remove_twilio_settings.py | 5 ++- frappe/utils/error.py | 7 +++- frappe/utils/install.py | 6 ++- frappe/utils/password.py | 8 +++- frappe/utils/testutils.py | 5 ++- .../workflow_action/workflow_action.py | 11 ++++-- 13 files changed, 90 insertions(+), 52 deletions(-) diff --git a/frappe/core/doctype/activity_log/activity_log.py b/frappe/core/doctype/activity_log/activity_log.py index efec0dc217..3f9f4f3cb0 100644 --- a/frappe/core/doctype/activity_log/activity_log.py +++ b/frappe/core/doctype/activity_log/activity_log.py @@ -44,6 +44,6 @@ def clear_activity_logs(days=None): if not days: days = 90 - - frappe.db.sql("""delete from `tabActivity Log` where \ - creation< (NOW() - INTERVAL '{0}' DAY)""".format(days)) \ No newline at end of file + frappe.db.delete("Activity Log", filters={"creation": ("<", ["NOW()", "-", f"INTERVAL {days} DAY"])}) + # frappe.db.sql("""delete from `tabActivity Log` where \ + # creation< (NOW() - INTERVAL '{0}' DAY)""".format(days)) \ No newline at end of file diff --git a/frappe/core/doctype/log_settings/log_settings.py b/frappe/core/doctype/log_settings/log_settings.py index 776fcc92e9..eea73ee748 100644 --- a/frappe/core/doctype/log_settings/log_settings.py +++ b/frappe/core/doctype/log_settings/log_settings.py @@ -13,10 +13,10 @@ class LogSettings(Document): self.clear_email_queue() def clear_error_logs(self): - # frappe.db.delete(doctype="Error Log", conditions="") - frappe.db.sql(""" DELETE FROM `tabError Log` - WHERE `creation` < (NOW() - INTERVAL '{0}' DAY) - """.format(self.clear_error_log_after)) + frappe.db.delete("Error Log", {"creation": ("<", ["NOW()", "-", f"INTERVAL {self.clear_error_log_after} DAY"])}) + # frappe.db.sql(""" DELETE FROM `tabError Log` + # WHERE `creation` < (NOW() - INTERVAL '{0}' DAY) + # """.format(self.clear_error_log_after)) def clear_activity_logs(self): from frappe.core.doctype.activity_log.activity_log import clear_activity_logs diff --git a/frappe/database/database.py b/frappe/database/database.py index 0c18ae91f7..8b4fd3ad9e 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -952,7 +952,7 @@ class Database(object): query = sql_dict.get(current_dialect) return self.sql(query, values, **kwargs) - def delete(self, doctype: str, filters: Union[Dict, List], debug=False, **kwargs): + def delete(self, doctype: str, filters: Union[Dict, List] = None, debug=False, **kwargs): """Delete rows from a table in site which match the passed filters. This does trigger DocType hooks. Simply runs a DELETE query in the database. @@ -960,14 +960,15 @@ class Database(object): """ if kwargs: filters = filters or kwargs.get("conditions") - if not filters: - raise TypeError( - "No filters passed for `frappe.db.delete`. If you wish to clear the whole " - "table, consider using `frappe.db.truncate` instead?" - ) + if "debug" not in kwargs: kwargs["debug"] = debug + if not filters: + table = doctype if doctype.startswith("__") else f"tab{doctype}" + query = f"DELETE FROM `{table}`" + return self.sql(query, **kwargs) + table = doctype if doctype.startswith("__") else f"tab{doctype}" conditions, values = self.build_conditions(filters) query = f"DELETE FROM `{table}` WHERE {conditions}" diff --git a/frappe/desk/doctype/todo/todo.py b/frappe/desk/doctype/todo/todo.py index 4696563445..388ba37e1f 100644 --- a/frappe/desk/doctype/todo/todo.py +++ b/frappe/desk/doctype/todo/todo.py @@ -40,11 +40,15 @@ class ToDo(Document): def on_trash(self): # unlink todo from linked comments - frappe.db.sql(""" - delete from `tabCommunication Link` - where link_doctype=%(doctype)s and link_name=%(name)s""", { - "doctype": self.doctype, "name": self.name + frappe.db.delete("Communication Link", { + "link_doctype": self.doctype, + "link_name": self.name }) + # frappe.db.sql(""" + # delete from `tabCommunication Link` + # where link_doctype=%(doctype)s and link_name=%(name)s""", { + # "doctype": self.doctype, "name": self.name + # }) self.update_in_reference() diff --git a/frappe/model/document.py b/frappe/model/document.py index 61160e1f01..f8adfdb3cf 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -390,9 +390,15 @@ class Document(BaseDocument): else: # no rows found, delete all rows - frappe.db.sql("""delete from `tab{0}` where parent=%s - and parenttype=%s and parentfield=%s""".format(df.options), - (self.name, self.doctype, fieldname)) + frappe.db.delete(df.options, { + "parent": self.name, + "parenttype": self.doctype, + "parentfield": fieldname + }) + + # frappe.db.sql("""delete from `tab{0}` where parent=%s + # and parenttype=%s and parentfield=%s""".format(df.options), + # (self.name, self.doctype, fieldname)) def get_doc_before_save(self): return getattr(self, '_doc_before_save', None) @@ -451,7 +457,10 @@ class Document(BaseDocument): def update_single(self, d): """Updates values for Single type Document in `tabSingles`.""" - frappe.db.sql("""delete from `tabSingles` where doctype=%s""", self.doctype) + frappe.db.delete("Singles", { + "doctype": self.doctype + }) + # frappe.db.sql("""delete from `tabSingles` where doctype=%s""", self.doctype) for field, value in d.items(): if field != "doctype": frappe.db.sql("""insert into `tabSingles` (doctype, field, value) diff --git a/frappe/patches/v12_0/set_primary_key_in_series.py b/frappe/patches/v12_0/set_primary_key_in_series.py index e5ed2204ba..a8409cbbba 100644 --- a/frappe/patches/v12_0/set_primary_key_in_series.py +++ b/frappe/patches/v12_0/set_primary_key_in_series.py @@ -1,21 +1,24 @@ import frappe def execute(): - #if current = 0, simply delete the key as it'll be recreated on first entry - frappe.db.sql('delete from `tabSeries` where current = 0') - duplicate_keys = frappe.db.sql(''' - SELECT name, max(current) as current - from - `tabSeries` - group by - name - having count(name) > 1 - ''', as_dict=True) - for row in duplicate_keys: - frappe.db.sql('delete from `tabSeries` where name = %(key)s', { - 'key': row.name - }) - if row.current: - frappe.db.sql('insert into `tabSeries`(`name`, `current`) values (%(name)s, %(current)s)', row) - frappe.db.commit() - frappe.db.sql('ALTER table `tabSeries` ADD PRIMARY KEY IF NOT EXISTS (name)') + #if current = 0, simply delete the key as it'll be recreated on first entry + frappe.db.sql('delete from `tabSeries` where current = 0') + duplicate_keys = frappe.db.sql(''' + SELECT name, max(current) as current + from + `tabSeries` + group by + name + having count(name) > 1 + ''', as_dict=True) + for row in duplicate_keys: + frappe.db.delete("Series", { + "name": row.name + }) + # frappe.db.sql('delete from `tabSeries` where name = %(key)s', { + # 'key': row.name + # }) + if row.current: + frappe.db.sql('insert into `tabSeries`(`name`, `current`) values (%(name)s, %(current)s)', row) + frappe.db.commit() + frappe.db.sql('ALTER table `tabSeries` ADD PRIMARY KEY IF NOT EXISTS (name)') diff --git a/frappe/patches/v12_0/setup_comments_from_communications.py b/frappe/patches/v12_0/setup_comments_from_communications.py index 039ceeff35..a4e3fb4261 100644 --- a/frappe/patches/v12_0/setup_comments_from_communications.py +++ b/frappe/patches/v12_0/setup_comments_from_communications.py @@ -29,4 +29,7 @@ def execute(): frappe.db.auto_commit_on_many_writes = False # clean up - frappe.db.sql("delete from `tabCommunication` where communication_type = 'Comment'") + frappe.db.delete("Communication", { + "communication_type": "Comment" + }) + # frappe.db.sql("delete from `tabCommunication` where communication_type = 'Comment'") diff --git a/frappe/patches/v13_0/remove_twilio_settings.py b/frappe/patches/v13_0/remove_twilio_settings.py index 363cbdd4b6..5d17364847 100644 --- a/frappe/patches/v13_0/remove_twilio_settings.py +++ b/frappe/patches/v13_0/remove_twilio_settings.py @@ -12,7 +12,10 @@ def execute(): frappe.delete_doc_if_exists('DocType', 'Twilio Number Group') if twilio_settings_doctype_in_integrations(): frappe.delete_doc_if_exists('DocType', 'Twilio Settings') - frappe.db.sql("delete from `tabSingles` where `doctype`=%s", 'Twilio Settings') + frappe.db.delete("Singles", { + "doctype": "Twilio Settings" + }) + # frappe.db.sql("delete from `tabSingles` where `doctype`=%s", 'Twilio Settings') def twilio_settings_doctype_in_integrations() -> bool: """Check Twilio Settings doctype exists in integrations module or not. diff --git a/frappe/utils/error.py b/frappe/utils/error.py index 07e34674fe..75b5c71da5 100644 --- a/frappe/utils/error.py +++ b/frappe/utils/error.py @@ -176,8 +176,11 @@ def collect_error_snapshots(): def clear_old_snapshots(): """Clear snapshots that are older than a month""" - frappe.db.sql("""delete from `tabError Snapshot` - where creation < (NOW() - INTERVAL '1' MONTH)""") + + frappe.db.delete("Error Snapshot", filters={"creation": ("<", ["NOW()", "-", f"INTERVAL '1' MONTH"])}) + + # frappe.db.sql("""delete from `tabError Snapshot` + # where creation < (NOW() - INTERVAL '1' MONTH)""") path = get_error_snapshot_path() today = datetime.datetime.now() diff --git a/frappe/utils/install.py b/frappe/utils/install.py index 91d8f04eb4..9d21a0d945 100644 --- a/frappe/utils/install.py +++ b/frappe/utils/install.py @@ -111,8 +111,10 @@ def before_tests(): # don't run before tests if any other app is installed return - frappe.db.sql("delete from `tabCustom Field`") - frappe.db.sql("delete from `tabEvent`") + frappe.db.delete("Custom Field") + frappe.db.delete("Event") + # frappe.db.sql("delete from `tabCustom Field`") + # frappe.db.sql("delete from `tabEvent`") frappe.db.commit() frappe.clear_cache() diff --git a/frappe/utils/password.py b/frappe/utils/password.py index c144d866f3..4bac6537ce 100644 --- a/frappe/utils/password.py +++ b/frappe/utils/password.py @@ -136,8 +136,12 @@ def update_password(user, pwd, doctype='User', fieldname='password', logout_all_ def delete_all_passwords_for(doctype, name): try: - frappe.db.sql("""delete from `__Auth` where `doctype`=%(doctype)s and `name`=%(name)s""", - { 'doctype': doctype, 'name': name }) + frappe.db.delete("__Auth", { + "doctype": doctype, + "name": name + }) + # frappe.db.sql("""delete from `__Auth` where `doctype`=%(doctype)s and `name`=%(name)s""", + # { 'doctype': doctype, 'name': name }) except Exception as e: if not frappe.db.is_missing_column(e): raise diff --git a/frappe/utils/testutils.py b/frappe/utils/testutils.py index c451d090f1..74b800bdcf 100644 --- a/frappe/utils/testutils.py +++ b/frappe/utils/testutils.py @@ -12,5 +12,8 @@ def add_custom_field(doctype, fieldname, fieldtype='Data', options=None): }).insert() def clear_custom_fields(doctype): - frappe.db.sql('delete from `tabCustom Field` where dt=%s', doctype) + frappe.db.delete("Custom Field", { + "dt": doctype + }) + # frappe.db.sql('delete from `tabCustom Field` where dt=%s', doctype) frappe.clear_cache(doctype=doctype) diff --git a/frappe/workflow/doctype/workflow_action/workflow_action.py b/frappe/workflow/doctype/workflow_action/workflow_action.py index af168d3477..6c1c31f0e5 100644 --- a/frappe/workflow/doctype/workflow_action/workflow_action.py +++ b/frappe/workflow/doctype/workflow_action/workflow_action.py @@ -259,10 +259,13 @@ def is_workflow_action_already_created(doc): def clear_workflow_actions(doctype, name): if not (doctype and name): return - - frappe.db.sql('''delete from `tabWorkflow Action` - where reference_doctype=%s and reference_name=%s''', - (doctype, name)) + frappe.db.delete("Workflow Action", { + "reference_doctype": doctype, + "reference_name": name + }) + # frappe.db.sql('''delete from `tabWorkflow Action` + # where reference_doctype=%s and reference_name=%s''', + # (doctype, name)) def get_doc_workflow_state(doc): workflow_name = get_workflow_name(doc.get('doctype')) From a86d717f17058203ced2e4a4b7b4cfffa42080ea Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Fri, 23 Jul 2021 23:31:44 +0530 Subject: [PATCH 446/495] fix: Convert indentation to tabs --- frappe/core/doctype/activity_log/feed.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/core/doctype/activity_log/feed.py b/frappe/core/doctype/activity_log/feed.py index d91a21ec44..d064022b25 100644 --- a/frappe/core/doctype/activity_log/feed.py +++ b/frappe/core/doctype/activity_log/feed.py @@ -28,10 +28,10 @@ def update_feed(doc, method=None): doctype = feed.doctype or doc.doctype name = feed.name or doc.name frappe.db.delete("Activity Log", { - "reference_doctype": doctype, - "reference_name": name, - "link_doctype": feed.link_doctype - }) + "reference_doctype": doctype, + "reference_name": name, + "link_doctype": feed.link_doctype + }) # frappe.db.sql("""delete from `tabActivity Log` # where # reference_doctype=%s and reference_name=%s From b8f2c979b579aae1288abc692448fa6947d0d288 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Sat, 24 Jul 2021 01:56:29 +0530 Subject: [PATCH 447/495] ci: Remove test suite TestFrappeHTTPRequest Remove tests for HTTPRequest under test_auth because Frappe develop CI started breaking after PR merge --- frappe/tests/test_auth.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/frappe/tests/test_auth.py b/frappe/tests/test_auth.py index 8447150006..b02b53338d 100644 --- a/frappe/tests/test_auth.py +++ b/frappe/tests/test_auth.py @@ -125,20 +125,3 @@ class TestLoginAttemptTracker(unittest.TestCase): tracker.add_failure_attempt() self.assertTrue(tracker.is_user_allowed()) - -class TestFrappeHTTPRequest(unittest.TestCase): - # test frappe.auth.HTTPRequest - def test_set_language(self): - """Check if language is set on object initialization - - This is a test to ensure that language has changed. To test correctness - of frappe.local.lang, check out the tests of frappe.translate.get_language - """ - lang_before_request = frappe.local.lang - random_lang = frappe.get_all("Language", limit=1, pluck="name")[0] - set_request(method='POST', path='/') - frappe.form_dict._lang = random_lang - HTTPRequest() - self.assertTrue(hasattr(frappe.local, "lang")) - self.assertIsInstance(frappe.local.lang, str) - self.assertNotEqual(lang_before_request, frappe.local.lang) From 538ef2168bd5c77e19726d7ffe25816d765f2c90 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Sat, 24 Jul 2021 22:58:12 +0530 Subject: [PATCH 448/495] fix: removed wrong queries --- .../core/doctype/activity_log/activity_log.py | 6 +++--- .../core/doctype/log_settings/log_settings.py | 8 ++++---- frappe/core/doctype/report/test_report.py | 2 +- frappe/core/doctype/user/user.py | 6 +++--- .../user_permission/test_user_permission.py | 2 +- .../user_permission/user_permission.py | 8 ++++---- frappe/model/__init__.py | 6 +++--- frappe/model/delete_doc.py | 4 ++-- .../delete_feedback_request_if_exists.py | 8 ++++---- frappe/permissions.py | 2 +- frappe/sessions.py | 1 - frappe/utils/error.py | 6 +++--- frappe/utils/password.py | 4 ++-- .../personal_data_deletion_request.py | 20 +++++++++---------- .../workflow_action/workflow_action.py | 4 ++-- 15 files changed, 43 insertions(+), 44 deletions(-) diff --git a/frappe/core/doctype/activity_log/activity_log.py b/frappe/core/doctype/activity_log/activity_log.py index 3f9f4f3cb0..2571330f9e 100644 --- a/frappe/core/doctype/activity_log/activity_log.py +++ b/frappe/core/doctype/activity_log/activity_log.py @@ -44,6 +44,6 @@ def clear_activity_logs(days=None): if not days: days = 90 - frappe.db.delete("Activity Log", filters={"creation": ("<", ["NOW()", "-", f"INTERVAL {days} DAY"])}) - # frappe.db.sql("""delete from `tabActivity Log` where \ - # creation< (NOW() - INTERVAL '{0}' DAY)""".format(days)) \ No newline at end of file + # frappe.db.delete("Activity Log", filters={"creation": ("<", ["NOW()", "-", f"INTERVAL {days} DAY"])}) + frappe.db.sql("""delete from `tabActivity Log` where \ + creation< (NOW() - INTERVAL '{0}' DAY)""".format(days)) \ No newline at end of file diff --git a/frappe/core/doctype/log_settings/log_settings.py b/frappe/core/doctype/log_settings/log_settings.py index eea73ee748..7f25998dc5 100644 --- a/frappe/core/doctype/log_settings/log_settings.py +++ b/frappe/core/doctype/log_settings/log_settings.py @@ -13,10 +13,10 @@ class LogSettings(Document): self.clear_email_queue() def clear_error_logs(self): - frappe.db.delete("Error Log", {"creation": ("<", ["NOW()", "-", f"INTERVAL {self.clear_error_log_after} DAY"])}) - # frappe.db.sql(""" DELETE FROM `tabError Log` - # WHERE `creation` < (NOW() - INTERVAL '{0}' DAY) - # """.format(self.clear_error_log_after)) + # frappe.db.delete("Error Log", {"creation": ("<", ["NOW()", "-", f"INTERVAL {self.clear_error_log_after} DAY"])}) + frappe.db.sql(""" DELETE FROM `tabError Log` + WHERE `creation` < (NOW() - INTERVAL '{0}' DAY) + """.format(self.clear_error_log_after)) def clear_activity_logs(self): from frappe.core.doctype.activity_log.activity_log import clear_activity_logs diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py index 2ac41cca2b..efc33b888c 100644 --- a/frappe/core/doctype/report/test_report.py +++ b/frappe/core/doctype/report/test_report.py @@ -88,7 +88,7 @@ class TestReport(unittest.TestCase): "role": "Test Has Role" }) # frappe.db.sql("""delete from `tabHas Role` where parent = %s - # and role = 'Test Has Role'""", frappe.session.user, auto_commit=1) + # and role = 'Test Has Role'""", frappe.session.user, auto_commit=1) if not frappe.db.exists('Role', 'Test Has Role'): role = frappe.get_doc({ diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 69d3b67220..7ff4b8bea9 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -383,9 +383,9 @@ class User(Document): # delete messages # frappe.db.delete("Communication", { - # "reference_doctype": "User", - # "communication_type": ("in", ("Chat", "Notification")), - # }) + # "reference_doctype": "User", + # "communication_type": ("in", ("Chat", "Notification")), + # }) frappe.db.sql("""delete from `tabCommunication` where communication_type in ('Chat', 'Notification') and reference_doctype='User' diff --git a/frappe/core/doctype/user_permission/test_user_permission.py b/frappe/core/doctype/user_permission/test_user_permission.py index 9393e47ee9..60a91e6a9b 100644 --- a/frappe/core/doctype/user_permission/test_user_permission.py +++ b/frappe/core/doctype/user_permission/test_user_permission.py @@ -14,7 +14,7 @@ class TestUserPermission(unittest.TestCase): frappe.db.delete("User Permission", { "user": ("in", ("test_bulk_creation_update@example.com", "test_user_perm1@example.com", - "nested_doc_user@example.com")) + "nested_doc_user@example.com")) }) # frappe.db.sql("""DELETE FROM `tabUser Permission` diff --git a/frappe/core/doctype/user_permission/user_permission.py b/frappe/core/doctype/user_permission/user_permission.py index 5b3822d82f..338b063a63 100644 --- a/frappe/core/doctype/user_permission/user_permission.py +++ b/frappe/core/doctype/user_permission/user_permission.py @@ -278,8 +278,8 @@ def update_applicable(already_applied, to_apply, user, doctype, docname): }) # frappe.db.sql("""DELETE FROM `tabUser Permission` - # WHERE `user`=%s - # AND `applicable_for`=%s - # AND `allow`=%s - # AND `for_value`=%s + # WHERE `user`=%s + # AND `applicable_for`=%s + # AND `allow`=%s + # AND `for_value`=%s # """,(user, applied, doctype, docname)) diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 99eaa7ff5d..7459493fcc 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -168,11 +168,11 @@ def delete_fields(args_dict, delete=0): if frappe.db.get_value("DocType", dt, "issingle"): frappe.db.delete("Singles", { "doctype": dt, - "field": ("in", ", ".join("'{}'".format(f) for f in fields)) + "field": ("in", fields) }) # frappe.db.sql(""" - # DELETE FROM `tabSingles` - # WHERE doctype='%s' AND field IN (%s) + # DELETE FROM `tabSingles` + # WHERE doctype='%s' AND field IN (%s) # """ % (dt, ", ".join("'{}'".format(f) for f in fields))) else: existing_fields = frappe.db.multisql({ diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 03510d489d..9187310a84 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -185,7 +185,7 @@ def delete_from_table(doctype, name, ignore_doctypes, doc): }) # frappe.db.sql("delete from `tabSingles` where `doctype`=%s", name) else: - frappe.db.delete(f"{doctype}", { + frappe.db.delete(doctype, { "name": name }) # frappe.db.sql("delete from `tab{0}` where `name`=%s".format(doctype), name) @@ -368,7 +368,7 @@ def clear_timeline_references(link_doctype, link_name): "link_name": link_name }) # frappe.db.sql("""DELETE FROM `tabCommunication Link` - # WHERE `tabCommunication Link`.link_doctype=%s AND `tabCommunication Link`.link_name=%s""", (link_doctype, link_name)) + # WHERE `tabCommunication Link`.link_doctype=%s AND `tabCommunication Link`.link_name=%s""", (link_doctype, link_name)) def insert_feed(doc): if ( diff --git a/frappe/patches/v12_0/delete_feedback_request_if_exists.py b/frappe/patches/v12_0/delete_feedback_request_if_exists.py index 537d509424..de799b1e21 100644 --- a/frappe/patches/v12_0/delete_feedback_request_if_exists.py +++ b/frappe/patches/v12_0/delete_feedback_request_if_exists.py @@ -5,7 +5,7 @@ def execute(): frappe.db.delete("DocType", { "name": "Feedback Request" }) - # frappe.db.sql(''' - # DELETE from `tabDocType` - # WHERE name = 'Feedback Request' - # ''') \ No newline at end of file + # frappe.db.sql(''' + #DELETE from `tabDocType` + #WHERE name = 'Feedback Request' + # ''') \ No newline at end of file diff --git a/frappe/permissions.py b/frappe/permissions.py index 293ac14f27..7724f09e78 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -6,7 +6,7 @@ import frappe import frappe.share from frappe import _, msgprint from frappe.utils import cint -from frappe.database.database import Database + rights = ("select", "read", "write", "create", "delete", "submit", "cancel", "amend", "print", "email", "report", "import", "export", "set_user_permissions", "share") diff --git a/frappe/sessions.py b/frappe/sessions.py index 3a584dfb74..f4d05538bc 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -16,7 +16,6 @@ import frappe.translate import redis from urllib.parse import unquote from frappe.cache_manager import clear_user_cache -from frappe.database.database import Database @frappe.whitelist(allow_guest=True) def clear(user=None): diff --git a/frappe/utils/error.py b/frappe/utils/error.py index 75b5c71da5..f2cbc5ec10 100644 --- a/frappe/utils/error.py +++ b/frappe/utils/error.py @@ -177,10 +177,10 @@ def collect_error_snapshots(): def clear_old_snapshots(): """Clear snapshots that are older than a month""" - frappe.db.delete("Error Snapshot", filters={"creation": ("<", ["NOW()", "-", f"INTERVAL '1' MONTH"])}) + # frappe.db.delete("Error Snapshot", filters={"creation": ("<", ["NOW()", "-", f"INTERVAL '1' MONTH"])}) - # frappe.db.sql("""delete from `tabError Snapshot` - # where creation < (NOW() - INTERVAL '1' MONTH)""") + frappe.db.sql("""delete from `tabError Snapshot` + where creation < (NOW() - INTERVAL '1' MONTH)""") path = get_error_snapshot_path() today = datetime.datetime.now() diff --git a/frappe/utils/password.py b/frappe/utils/password.py index 4bac6537ce..21e1bc9586 100644 --- a/frappe/utils/password.py +++ b/frappe/utils/password.py @@ -72,8 +72,8 @@ def remove_encrypted_password(doctype, name, fieldname='password'): }) # frappe.db.sql( - # 'DELETE FROM `__Auth` WHERE doctype = %s and name = %s and fieldname = %s', - # values=[doctype, name, fieldname] + # 'DELETE FROM `__Auth` WHERE doctype = %s and name = %s and fieldname = %s', + # values=[doctype, name, fieldname] # ) def check_password(user, pwd, doctype='User', fieldname='password', delete_tracker_cache=True): diff --git a/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py b/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py index bde6f2a4ea..96898cc159 100644 --- a/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py +++ b/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py @@ -323,17 +323,17 @@ class PersonalDataDeletionRequest(Document): def remove_unverified_record(): - frappe.db.delete("Personal Data Deletion Request", { - "status": "Pending Verification", - "creation": ("<", ["Now()", "-", "INTERVAL 7 DAY"]) - }) + # frappe.db.delete("Personal Data Deletion Request", { + # "status": "Pending Verification", + # "creation": ("<", ["Now()", "-", "INTERVAL 7 DAY"]) + # }) - # frappe.db.sql( - # """ - # DELETE FROM `tabPersonal Data Deletion Request` - # WHERE `status` = 'Pending Verification' - # AND `creation` < (NOW() - INTERVAL '7' DAY)""" - # ) + frappe.db.sql( + """ + DELETE FROM `tabPersonal Data Deletion Request` + WHERE `status` = 'Pending Verification' + AND `creation` < (NOW() - INTERVAL '7' DAY)""" + ) @frappe.whitelist(allow_guest=True) def confirm_deletion(email, name, host_name): diff --git a/frappe/workflow/doctype/workflow_action/workflow_action.py b/frappe/workflow/doctype/workflow_action/workflow_action.py index 6c1c31f0e5..5957bf8671 100644 --- a/frappe/workflow/doctype/workflow_action/workflow_action.py +++ b/frappe/workflow/doctype/workflow_action/workflow_action.py @@ -264,8 +264,8 @@ def clear_workflow_actions(doctype, name): "reference_name": name }) # frappe.db.sql('''delete from `tabWorkflow Action` - # where reference_doctype=%s and reference_name=%s''', - # (doctype, name)) + # where reference_doctype=%s and reference_name=%s''', + # (doctype, name)) def get_doc_workflow_state(doc): workflow_name = get_workflow_name(doc.get('doctype')) From 326bd463fc36994d11cf9470101ab43e7b575dd8 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Mon, 26 Jul 2021 17:09:57 +0530 Subject: [PATCH 449/495] fix: web form child table issue --- frappe/website/doctype/web_form/web_form.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index 6e4200b54b..32f7e030a6 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -542,7 +542,7 @@ def get_form_data(doctype, docname=None, web_form_name=None): # For Table fields, server-side processing for meta for field in out.web_form.web_form_fields: if field.fieldtype == "Table": - field.fields = get_in_list_view_fields(field.options) + field.fields = frappe.get_meta(field.options).fields out.update({field.fieldname: field.fields}) if field.fieldtype == "Link": From db09a85183a5068bf9792f8d2e4101ab4382eec6 Mon Sep 17 00:00:00 2001 From: leela Date: Tue, 20 Jul 2021 06:11:01 +0530 Subject: [PATCH 450/495] refactor: Add authentication for Redis Queue --- frappe/commands/redis.py | 25 +++--- frappe/commands/scheduler.py | 8 +- .../page/background_jobs/background_jobs.py | 8 +- frappe/test_runner.py | 1 + frappe/tests/test_background_jobs.py | 6 +- frappe/tests/test_redis.py | 70 ++++++++++++++++ frappe/utils/__init__.py | 2 +- frappe/utils/background_jobs.py | 80 +++++++++++++++---- frappe/utils/rq.py | 24 +++--- 9 files changed, 173 insertions(+), 51 deletions(-) create mode 100644 frappe/tests/test_redis.py diff --git a/frappe/commands/redis.py b/frappe/commands/redis.py index c608296cac..38a46c2142 100644 --- a/frappe/commands/redis.py +++ b/frappe/commands/redis.py @@ -1,7 +1,6 @@ import os import click -import redis import frappe from frappe.utils.rq import RedisQueue @@ -9,16 +8,18 @@ from frappe.installer import update_site_config @click.command('create-rq-users') @click.option('--set-admin-password', is_flag=True, default=False, help='Set new Redis admin(default user) password') -@click.option('--reset-passwords', is_flag=True, default=False, help='Remove all existing passwords') -def create_rq_users(set_admin_password=False, reset_passwords=False): +@click.option('--use-rq-auth', is_flag=True, default=False, help='Enable Redis authentication for sites') +def create_rq_users(set_admin_password=False, use_rq_auth=False): """Create Redis Queue users and add to acl and app configs. acl config file will be used by redis server while starting the server and app config is used by app while connecting to redis server. """ acl_file_path = os.path.abspath('../config/redis_queue.acl') - acl_list, user_credentials = RedisQueue.gen_acl_list( - reset_passwords=reset_passwords, set_admin_password=set_admin_password) + + with frappe.init_site(): + acl_list, user_credentials = RedisQueue.gen_acl_list( + set_admin_password=set_admin_password) with open(acl_file_path, 'w') as f: f.writelines([acl+'\n' for acl in acl_list]) @@ -29,18 +30,22 @@ def create_rq_users(set_admin_password=False, reset_passwords=False): site_config_path=common_site_config_path) update_site_config("rq_password", user_credentials['bench'][1], validate=False, site_config_path=common_site_config_path) + update_site_config("use_rq_auth", use_rq_auth, validate=False, + site_config_path=common_site_config_path) + + click.secho('* ACL and site configs are updated with new user credentials. ' + 'Please restart Redis Queue server to enable namespaces.', + fg='green') if set_admin_password: env_key = 'RQ_ADMIN_PASWORD' - click.secho('Redis admin password is successfully set up. ' + click.secho('* Redis admin password is successfully set up. ' 'Include below line in .bashrc file for system to use', - fg='green' - ) + fg='green') click.secho(f"`export {env_key}={user_credentials['default'][1]}`") click.secho('NOTE: Please save the admin password as you ' 'can not access redis server without the password', - fg='yellow' - ) + fg='yellow') commands = [ diff --git a/frappe/commands/scheduler.py b/frappe/commands/scheduler.py index d69ebb3024..f82473fd55 100755 --- a/frappe/commands/scheduler.py +++ b/frappe/commands/scheduler.py @@ -172,9 +172,13 @@ def start_scheduler(): @click.command('worker') @click.option('--queue', type=str) @click.option('--quiet', is_flag = True, default = False, help = 'Hide Log Outputs') -def start_worker(queue, quiet = False): +@click.option('-u', '--rq-username', default=None, help='Redis ACL user') +@click.option('-p', '--rq-password', default=None, help='Redis ACL user password') +def start_worker(queue, quiet = False, rq_username=None, rq_password=None): + """Site is used to find redis credentals. + """ from frappe.utils.background_jobs import start_worker - start_worker(queue, quiet = quiet) + start_worker(queue, quiet = quiet, rq_username=rq_username, rq_password=rq_password) @click.command('ready-for-migration') @click.option('--site', help='site name') diff --git a/frappe/core/page/background_jobs/background_jobs.py b/frappe/core/page/background_jobs/background_jobs.py index 847b23bd3e..1f3555e351 100644 --- a/frappe/core/page/background_jobs/background_jobs.py +++ b/frappe/core/page/background_jobs/background_jobs.py @@ -4,12 +4,12 @@ import json from typing import TYPE_CHECKING, Dict, List -from rq import Queue, Worker +from rq import Worker import frappe from frappe import _ from frappe.utils import convert_utc_to_user_timezone, format_datetime -from frappe.utils.background_jobs import get_redis_conn +from frappe.utils.background_jobs import get_redis_conn, get_queues from frappe.utils.scheduler import is_scheduler_inactive if TYPE_CHECKING: @@ -29,7 +29,7 @@ def get_info(show_failed=False) -> List[Dict]: show_failed = json.loads(show_failed) conn = get_redis_conn() - queues = Queue.all(conn) + queues = get_queues() workers = Worker.all(conn) jobs = [] @@ -75,7 +75,7 @@ def get_info(show_failed=False) -> List[Dict]: @frappe.whitelist() def remove_failed_jobs(): conn = get_redis_conn() - queues = Queue.all(conn) + queues = get_queues() for queue in queues: fail_registry = queue.failed_job_registry for job_id in fail_registry.get_job_ids(): diff --git a/frappe/test_runner.py b/frappe/test_runner.py index 0c30fbbd00..8112362f34 100644 --- a/frappe/test_runner.py +++ b/frappe/test_runner.py @@ -56,6 +56,7 @@ def main(app=None, module=None, doctype=None, verbose=False, tests=(), frappe.clear_cache() frappe.utils.scheduler.disable_scheduler() set_test_email_config() + frappe.conf.update({'bench_id': 'test_bench', 'use_rq_auth': False}) if not frappe.flags.skip_before_tests: if verbose: diff --git a/frappe/tests/test_background_jobs.py b/frappe/tests/test_background_jobs.py index 88783f14f1..48e0dd2ee9 100644 --- a/frappe/tests/test_background_jobs.py +++ b/frappe/tests/test_background_jobs.py @@ -4,7 +4,7 @@ from rq import Queue import frappe from frappe.core.page.background_jobs.background_jobs import remove_failed_jobs -from frappe.utils.background_jobs import get_redis_conn +from frappe.utils.background_jobs import get_redis_conn, rename_queue import time @@ -17,14 +17,14 @@ class TestBackgroundJobs(unittest.TestCase): queues = Queue.all(conn) for queue in queues: - if queue.name == "short": + if queue.name == rename_queue("short"): fail_registry = queue.failed_job_registry self.assertGreater(fail_registry.count, 0) remove_failed_jobs() for queue in queues: - if queue.name == "short": + if queue.name == rename_queue("short"): fail_registry = queue.failed_job_registry self.assertEqual(fail_registry.count, 0) diff --git a/frappe/tests/test_redis.py b/frappe/tests/test_redis.py new file mode 100644 index 0000000000..72af1ac699 --- /dev/null +++ b/frappe/tests/test_redis.py @@ -0,0 +1,70 @@ +import unittest +import functools + +import redis + +import frappe +from frappe.utils import get_bench_id +from frappe.utils.rq import RedisQueue +from frappe.utils.background_jobs import get_redis_conn + +def version_tuple(version): + return tuple(map(int, (version.split(".")))) + +def skip_if_redis_version_lt(version): + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + conn = get_redis_conn() + redis_version = conn.execute_command('info')['redis_version'] + if version_tuple(redis_version) < version_tuple(version): + return + return func(*args, **kwargs) + return wrapper + return decorator + +class TestRedisAuth(unittest.TestCase): + @skip_if_redis_version_lt('6.0') + def test_rq_gen_acllist(self): + """Make sure that ACL list is genrated + """ + acl_list = RedisQueue.gen_acl_list() + self.assertEqual(acl_list[1]['bench'][0], get_bench_id()) + + @skip_if_redis_version_lt('6.0') + def test_adding_redis_user(self): + acl_list = RedisQueue.gen_acl_list() + username, password = acl_list[1]['bench'] + conn = get_redis_conn() + + conn.acl_deluser(username) + _ = RedisQueue(conn).add_user(username, password) + self.assertTrue(conn.acl_getuser(username)) + conn.acl_deluser(username) + + @skip_if_redis_version_lt('6.0') + def test_rq_namespace(self): + """Make sure that user can access only their respective namespace. + """ + # Current bench ID + bench_id = frappe.conf.get('bench_id') + conn = get_redis_conn() + conn.set('rq:queue:test_bench1:abc', 'value') + conn.set(f'rq:queue:{bench_id}:abc', 'value') + + # Create new Redis Queue user + tmp_bench_id = 'test_bench1' + username, password = tmp_bench_id, 'password1' + conn.acl_deluser(username) + frappe.conf.update({'bench_id': tmp_bench_id}) + _ = RedisQueue(conn).add_user(username, password) + test_bench1_conn = RedisQueue.get_connection(username, password) + + self.assertEqual(test_bench1_conn.get('rq:queue:test_bench1:abc'), b'value') + + # User should not be able to access queues apart from their bench queues + with self.assertRaises(redis.exceptions.NoPermissionError): + test_bench1_conn.get(f'rq:queue:{bench_id}:abc') + + frappe.conf.update({'bench_id': bench_id}) + conn.acl_deluser(username) diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index bf139173d6..80c6cda98c 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -384,7 +384,7 @@ def get_bench_path(): return os.path.realpath(os.path.join(os.path.dirname(frappe.__file__), '..', '..', '..')) def get_bench_id(): - return frappe.local.conf.get('bench_id', 'DefaultBench') + return frappe.get_conf().get('bench_id', 'DefaultBench') def get_site_id(site=None): return f"{site or frappe.local.site}@{get_bench_id()}" diff --git a/frappe/utils/background_jobs.py b/frappe/utils/background_jobs.py index 8456835ca7..4241c95c5d 100755 --- a/frappe/utils/background_jobs.py +++ b/frappe/utils/background_jobs.py @@ -1,13 +1,21 @@ +import os +import socket +import time +from uuid import uuid4 +from collections import defaultdict + + import redis +from typing import List from rq import Connection, Queue, Worker from rq.logutils import setup_loghandlers -from frappe.utils import cstr -from collections import defaultdict + import frappe -import os, socket, time from frappe import _ -from uuid import uuid4 import frappe.monitor +from frappe.utils import cstr, get_bench_id +from frappe.utils.rq import RedisQueue +from frappe.utils.commands import log default_timeout = 300 @@ -131,21 +139,22 @@ def execute_job(site, method, event, job_name, kwargs, user=None, is_async=True, if is_async: frappe.destroy() -def start_worker(queue=None, quiet = False): +def start_worker(queue=None, quiet = False, rq_username=None, rq_password=None): '''Wrapper to start rq worker. Connects to redis and monitors these queues.''' with frappe.init_site(): # empty init is required to get redis_queue from common_site_config.json - redis_connection = get_redis_conn() + redis_connection = get_redis_conn(username=rq_username, password=rq_password) + queues = get_queue_list(queue, build_queue_name=True) + queue_name = queue and rename_queue(queue) if os.environ.get('CI'): setup_loghandlers('ERROR') with Connection(redis_connection): - queues = get_queue_list(queue) logging_level = "INFO" if quiet: logging_level = "WARNING" - Worker(queues, name=get_worker_name(queue)).work(logging_level = logging_level) + Worker(queues, name=get_worker_name(queue_name)).work(logging_level = logging_level) def get_worker_name(queue): '''When limiting worker to a specific queue, also append queue name to default worker name''' @@ -186,7 +195,7 @@ def get_jobs(site=None, queue=None, key='method'): return jobs_per_site -def get_queue_list(queue_list=None): +def get_queue_list(queue_list=None, build_queue_name=False): '''Defines possible queues. Also wraps a given queue in a list after validating.''' default_queue_list = list(queue_timeout) if queue_list: @@ -195,11 +204,9 @@ def get_queue_list(queue_list=None): for queue in queue_list: validate_queue(queue, default_queue_list) - - return queue_list - else: - return default_queue_list + queue_list = default_queue_list + return [rename_queue(q) for q in queue_list] if build_queue_name else queue_list def get_workers(queue): '''Returns a list of Worker objects tied to a queue object''' @@ -218,7 +225,7 @@ def get_running_jobs_in_queue(queue): def get_queue(queue, is_async=True): '''Returns a Queue object tied to a redis connection''' validate_queue(queue) - return Queue(queue, connection=get_redis_conn(), is_async=is_async) + return Queue(rename_queue(queue), connection=get_redis_conn(), is_async=is_async) def validate_queue(queue, default_queue_list=None): if not default_queue_list: @@ -227,7 +234,7 @@ def validate_queue(queue, default_queue_list=None): if queue not in default_queue_list: frappe.throw(_("Queue should be one of {0}").format(', '.join(default_queue_list))) -def get_redis_conn(): +def get_redis_conn(username=None, password=None): if not hasattr(frappe.local, 'conf'): raise Exception('You need to call frappe.init') @@ -236,11 +243,50 @@ def get_redis_conn(): global redis_connection - if not redis_connection: - redis_connection = redis.from_url(frappe.local.conf.redis_queue) + cred = frappe._dict() + if frappe.conf.get('use_rq_auth'): + if username: + cred['username'] = username + cred['password'] = password + else: + cred['username'] = frappe.get_site_config().rq_username or get_bench_id() + cred['password'] = frappe.get_site_config().rq_password + + elif os.environ.get('RQ_ADMIN_PASWORD'): + cred['username'] = 'default' + cred['password'] = os.environ.get('RQ_ADMIN_PASWORD') + try: + redis_connection = RedisQueue.get_connection(**cred) + except (redis.exceptions.AuthenticationError, redis.exceptions.ResponseError): + log(f'Wrong credentials used for {cred.username or "default user"}. ' + 'You can reset credentials using `bench create-rq-users` CLI and restart the server', + colour='red') + raise + except Exception: + log(f'Please make sure that Redis Queue runs @ {frappe.get_conf().redis_queue}', colour='red') + raise return redis_connection +def get_queues() -> List[Queue]: + """Get all the queues linked to the current bench. + """ + queues = Queue.all(connection=get_redis_conn()) + return [q for q in queues if is_queue_accessible(q)] + +def rename_queue(qname: str) -> str: + """Rename qname by adding bench name as prefix. + + Renamed queues are useful to define namespaces of customers. + """ + return f"{get_bench_id()}:{qname}" + +def is_queue_accessible(qobj: Queue) -> bool: + """Checks whether queue is relate to current bench or not. + """ + accessible_queues = [rename_queue(q) for q in list(queue_timeout)] + return qobj.name in accessible_queues + def enqueue_test_job(): enqueue('frappe.utils.background_jobs.test_job', s=100) diff --git a/frappe/utils/rq.py b/frappe/utils/rq.py index 5e9b9dcd5d..b344b0caa5 100644 --- a/frappe/utils/rq.py +++ b/frappe/utils/rq.py @@ -1,8 +1,7 @@ import redis import frappe -from frappe.utils import get_site_id, get_bench_id, random_string - +from frappe.utils import get_bench_id, random_string class RedisQueue: def __init__(self, conn): @@ -17,9 +16,10 @@ class RedisQueue: return frappe._dict(user_settings) if is_created else {} @classmethod - def get_connection(cls, username='default', password=None): - domain = frappe.local.conf.redis_queue.split("redis://", 1)[-1] - url = f"redis://{username}:{password or ''}@{domain}" + def get_connection(cls, username=None, password=None): + rq_url = frappe.local.conf.redis_queue + domain = rq_url.split("redis://", 1)[-1] + url = (username and f"redis://{username}:{password or ''}@{domain}") or rq_url conn = redis.from_url(url) conn.ping() return conn @@ -63,25 +63,21 @@ class RedisQueue: return ['+@all', '-@admin'] @classmethod - def gen_acl_list(cls, reset_passwords=False, set_admin_password=False): + def gen_acl_list(cls, set_admin_password=False): """Generate list of ACL users needed for this branch. This list contains default ACL user and the bench ACL user(used by all sites incase of ACL is enabled). """ - with frappe.init_site(): - bench_username = get_bench_id() - bench_user_rules = cls.get_acl_key_rules(include_key_prefix=True) + cls.get_acl_command_rules() - + bench_username = get_bench_id() + bench_user_rules = cls.get_acl_key_rules(include_key_prefix=True) + cls.get_acl_command_rules() bench_user_rule_str = ' '.join(bench_user_rules).strip() bench_user_password = random_string(20) - bench_user_resetpass = (reset_passwords and 'resetpass') or '' default_username = 'default' _default_user_password = random_string(20) if set_admin_password else '' default_user_password = '>'+_default_user_password if _default_user_password else 'nopass' - default_user_resetpass = (reset_passwords and set_admin_password and 'resetpass') or '' return [ - f'user {default_username} on {default_user_password} {default_user_resetpass} ~* &* +@all', - f'user {bench_username} on >{bench_user_password} {bench_user_resetpass} {bench_user_rule_str}' + f'user {default_username} on {default_user_password} ~* &* +@all', + f'user {bench_username} on >{bench_user_password} {bench_user_rule_str}' ], {'bench': (bench_username, bench_user_password), 'default': (default_username, _default_user_password)} From 36feb72c89cb34c07bbb2fea94d45d339bf1d2bd Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Tue, 27 Jul 2021 11:29:00 +0530 Subject: [PATCH 451/495] fix: add column entries in doctypes --- frappe/database/mariadb/framework_mariadb.sql | 1 + frappe/database/postgres/framework_postgres.sql | 1 + 2 files changed, 2 insertions(+) diff --git a/frappe/database/mariadb/framework_mariadb.sql b/frappe/database/mariadb/framework_mariadb.sql index a52efd01e3..f8841e9417 100644 --- a/frappe/database/mariadb/framework_mariadb.sql +++ b/frappe/database/mariadb/framework_mariadb.sql @@ -220,6 +220,7 @@ CREATE TABLE `tabDocType` ( `allow_guest_to_view` int(1) NOT NULL DEFAULT 0, `route` varchar(255) DEFAULT NULL, `is_published_field` varchar(255) DEFAULT NULL, + `website_search_field` varchar(255) DEFAULT NULL, `email_append_to` int(1) NOT NULL DEFAULT 0, `subject_field` varchar(255) DEFAULT NULL, `sender_field` varchar(255) DEFAULT NULL, diff --git a/frappe/database/postgres/framework_postgres.sql b/frappe/database/postgres/framework_postgres.sql index eeb0eecd3f..a4e94aa326 100644 --- a/frappe/database/postgres/framework_postgres.sql +++ b/frappe/database/postgres/framework_postgres.sql @@ -225,6 +225,7 @@ CREATE TABLE "tabDocType" ( "allow_guest_to_view" smallint NOT NULL DEFAULT 0, "route" varchar(255) DEFAULT NULL, "is_published_field" varchar(255) DEFAULT NULL, + "website_search_field" varchar(255) DEFAULT NULL, "email_append_to" smallint NOT NULL DEFAULT 0, "subject_field" varchar(255) DEFAULT NULL, "sender_field" varchar(255) DEFAULT NULL, From 780778d7e3ef442db9e82002123994974ace2df9 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Tue, 27 Jul 2021 11:36:14 +0530 Subject: [PATCH 452/495] chore: remove unrelated code --- frappe/migrate.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frappe/migrate.py b/frappe/migrate.py index d4060e6067..061e4c98d7 100644 --- a/frappe/migrate.py +++ b/frappe/migrate.py @@ -4,8 +4,6 @@ import json import os import sys - -from rq import queue import frappe import frappe.translate import frappe.modules.patch_handler @@ -13,7 +11,6 @@ import frappe.model.sync from frappe.utils.fixtures import sync_fixtures from frappe.utils.connections import check_connection from frappe.utils.dashboard import sync_dashboards -from frappe.utils.background_jobs import enqueue from frappe.cache_manager import clear_global_cache from frappe.desk.notifications import clear_notifications from frappe.website.utils import clear_website_cache @@ -101,7 +98,6 @@ Otherwise, check the server logs and ensure that all the required services are r frappe.publish_realtime("version-update") frappe.flags.in_migrate = False - finally: with open(touched_tables_file, 'w') as f: json.dump(list(frappe.flags.touched_tables), f, sort_keys=True, indent=4) From 574bc57abe56b621053ee7644e5eefe753d4c6a3 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Tue, 27 Jul 2021 11:38:51 +0530 Subject: [PATCH 453/495] chore: remove unrelated changes --- frappe/website/page_renderers/document_page.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frappe/website/page_renderers/document_page.py b/frappe/website/page_renderers/document_page.py index 0108a84f79..01d8ec54c5 100644 --- a/frappe/website/page_renderers/document_page.py +++ b/frappe/website/page_renderers/document_page.py @@ -1,7 +1,7 @@ import frappe from frappe.model.document import get_controller from frappe.website.page_renderers.base_template_page import BaseTemplatePage -from frappe.website.utils import build_response, cache_html +from frappe.website.utils import build_response from frappe.website.router import (get_doctypes_with_web_view, get_page_info_from_web_page_with_dynamic_routes) @@ -47,7 +47,11 @@ class DocumentPage(BaseTemplatePage): return False def render(self): - html = self.get_html() + self.doc = frappe.get_doc(self.doctype, self.docname) + self.init_context() + self.update_context() + self.post_process_context() + html = frappe.get_template(self.template_path).render(self.context) html = self.add_csrf_token(html) return build_response(self.path, html, self.http_status_code or 200, self.headers) From 2a116a59b91e8cd8766e275ea8ab2d7e50f3d4b8 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Tue, 27 Jul 2021 11:39:26 +0530 Subject: [PATCH 454/495] chore: remove unrelated changes --- frappe/website/page_renderers/template_page.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index 2ec3f51f41..5e6e57e33a 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -83,7 +83,7 @@ class TemplatePage(BaseTemplatePage): super(TemplatePage, self).post_process_context() def add_sidebar_and_breadcrumbs(self): - if self.basepath and not self.context.sidebar_items: + if self.basepath: self.context.sidebar_items = get_sidebar_items(self.context.website_sidebar, self.basepath) if self.context.add_breadcrumbs and not self.context.parents: From 27608d85900b96e843d7cc05c6c1b3e1e54c6883 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Tue, 27 Jul 2021 11:41:16 +0530 Subject: [PATCH 455/495] chore: remove unrelated changes --- frappe/website/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/website/utils.py b/frappe/website/utils.py index 419a5199df..0f5f182ea2 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -456,6 +456,7 @@ def build_response(path, data, http_status_code, headers=None): response.status_code = http_status_code response.headers["X-Page-Name"] = path.encode("ascii", errors="xmlcharrefreplace") response.headers["X-From-Cache"] = frappe.local.response.from_cache or False + add_preload_headers(response) if headers: for key, val in iteritems(headers): @@ -485,12 +486,11 @@ def set_content_type(response, data, path): return data def add_preload_headers(response): - from bs4 import BeautifulSoup, SoupStrainer + from bs4 import BeautifulSoup try: preload = [] - strainer = SoupStrainer(re.compile("script|link")) - soup = BeautifulSoup(response.data, "lxml", parse_only=strainer) + soup = BeautifulSoup(response.data, "lxml") for elem in soup.find_all('script', src=re.compile(".*")): preload.append(("script", elem.get("src"))) From ea7dd9e8df0b18fe968dad0714a07681ee796972 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Tue, 27 Jul 2021 11:42:13 +0530 Subject: [PATCH 456/495] chore: remove unrelated changes --- frappe/website/page_renderers/document_page.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/frappe/website/page_renderers/document_page.py b/frappe/website/page_renderers/document_page.py index 01d8ec54c5..f1741c681f 100644 --- a/frappe/website/page_renderers/document_page.py +++ b/frappe/website/page_renderers/document_page.py @@ -56,16 +56,6 @@ class DocumentPage(BaseTemplatePage): return build_response(self.path, html, self.http_status_code or 200, self.headers) - @cache_html - def get_html(self): - self.doc = frappe.get_doc(self.doctype, self.docname) - self.init_context() - self.update_context() - self.post_process_context() - html = frappe.get_template(self.template_path).render(self.context) - - return html - def update_context(self): self.context.doc = self.doc self.context.update(self.context.doc.as_dict()) From 266a5511c91a7ae223d29a3773643de58c68681f Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Tue, 27 Jul 2021 12:31:34 +0530 Subject: [PATCH 457/495] chore: fix sider --- frappe/search/website_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/search/website_search.py b/frappe/search/website_search.py index 1fb84531b0..70962e8172 100644 --- a/frappe/search/website_search.py +++ b/frappe/search/website_search.py @@ -38,7 +38,7 @@ class WebsiteSearch(FullTextSearch): self._items_to_index = [] - routes = get_static_pages_from_all_apps() + slugs_with_web_view(self._items_to_index ) + routes = get_static_pages_from_all_apps() + slugs_with_web_view(self._items_to_index) for i, route in enumerate(routes): From ee6d4f50b4a27e9cc8c3b5f8a821a6d9c0cf4f21 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Tue, 27 Jul 2021 13:40:00 +0530 Subject: [PATCH 458/495] chore: remove unwanted changes --- frappe/website/page_renderers/document_page.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/frappe/website/page_renderers/document_page.py b/frappe/website/page_renderers/document_page.py index f1741c681f..6b8d973ead 100644 --- a/frappe/website/page_renderers/document_page.py +++ b/frappe/website/page_renderers/document_page.py @@ -1,7 +1,7 @@ import frappe from frappe.model.document import get_controller from frappe.website.page_renderers.base_template_page import BaseTemplatePage -from frappe.website.utils import build_response +from frappe.website.utils import cache_html from frappe.website.router import (get_doctypes_with_web_view, get_page_info_from_web_page_with_dynamic_routes) @@ -47,14 +47,19 @@ class DocumentPage(BaseTemplatePage): return False def render(self): + html = self.get_html() + html = self.add_csrf_token(html) + + return self.build_response(html) + + @cache_html + def get_html(self): self.doc = frappe.get_doc(self.doctype, self.docname) self.init_context() self.update_context() self.post_process_context() html = frappe.get_template(self.template_path).render(self.context) - html = self.add_csrf_token(html) - - return build_response(self.path, html, self.http_status_code or 200, self.headers) + return html def update_context(self): self.context.doc = self.doc From 9a282428685f8d0305853392887272560a6b7830 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 28 Jul 2021 11:32:20 +0530 Subject: [PATCH 459/495] fix: Report print option --- frappe/public/html/print_template.html | 2 +- frappe/public/js/frappe/microtemplate.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/frappe/public/html/print_template.html b/frappe/public/html/print_template.html index f63a20377f..e2ff9c9c76 100644 --- a/frappe/public/html/print_template.html +++ b/frappe/public/html/print_template.html @@ -7,7 +7,7 @@ {{ title }} - + diff --git a/frappe/public/js/frappe/microtemplate.js b/frappe/public/js/frappe/microtemplate.js index 7b45db952e..151d008d3e 100644 --- a/frappe/public/js/frappe/microtemplate.js +++ b/frappe/public/js/frappe/microtemplate.js @@ -119,6 +119,10 @@ frappe.render_grid = function(opts) { // render HTML wrapper page opts.base_url = frappe.urllib.get_base_url(); opts.print_css = frappe.boot.print_css; + + opts.lang = opts.lang || frappe.boot.lang, + opts.layout_direction = opts.layout_direction || frappe.utils.is_rtl() ? "rtl" : "ltr"; + var html = frappe.render_template("print_template", opts); var w = window.open(); From 84ff1d0af991fc6be5bd755c716f656baaeef312 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Wed, 28 Jul 2021 13:48:01 +0530 Subject: [PATCH 460/495] style: removed frappe.db.sql comments --- .../core/doctype/activity_log/activity_log.py | 1 - frappe/core/doctype/activity_log/feed.py | 4 ---- .../domain_settings/domain_settings.py | 1 - frappe/core/doctype/error_log/error_log.py | 3 +-- .../core/doctype/log_settings/log_settings.py | 1 - frappe/core/doctype/report/test_report.py | 3 --- frappe/core/doctype/role/role.py | 1 - .../scheduled_job_type/scheduled_job_type.py | 5 +---- frappe/core/doctype/user/user.py | 10 --------- .../user_permission/test_user_permission.py | 6 ------ .../user_permission/user_permission.py | 21 ------------------- .../permission_manager/permission_manager.py | 13 ++++++------ .../doctype/custom_field/custom_field.py | 6 ------ .../doctype/customize_form/customize_form.py | 4 +--- frappe/defaults.py | 5 ----- .../dashboard_chart/test_dashboard_chart.py | 2 -- .../desk/doctype/desktop_icon/desktop_icon.py | 1 - frappe/desk/doctype/event/event.py | 2 -- .../doctype/route_history/route_history.py | 11 +--------- frappe/desk/doctype/tag/tag.py | 2 -- frappe/desk/doctype/todo/todo.py | 6 ------ .../unhandled_email/unhandled_email.py | 4 +--- frappe/model/__init__.py | 9 -------- frappe/model/delete_doc.py | 15 +------------ frappe/model/document.py | 6 ------ .../apply_customization_to_custom_doctype.py | 4 ---- .../sync_stripe_settings_before_migrate.py | 3 +-- .../delete_feedback_request_if_exists.py | 6 +----- .../remove_deprecated_fields_from_doctype.py | 7 +------ .../v12_0/set_primary_key_in_series.py | 3 --- .../setup_comments_from_communications.py | 3 +-- .../patches/v13_0/remove_twilio_settings.py | 3 +-- frappe/permissions.py | 2 -- frappe/sessions.py | 1 - frappe/utils/error.py | 2 -- frappe/utils/global_search.py | 13 ------------ frappe/utils/install.py | 2 -- frappe/utils/password.py | 7 ------- frappe/utils/testutils.py | 1 - .../personal_data_deletion_request.py | 5 ----- .../workflow_action/workflow_action.py | 9 +------- 41 files changed, 18 insertions(+), 195 deletions(-) diff --git a/frappe/core/doctype/activity_log/activity_log.py b/frappe/core/doctype/activity_log/activity_log.py index 2571330f9e..ce5e504f7f 100644 --- a/frappe/core/doctype/activity_log/activity_log.py +++ b/frappe/core/doctype/activity_log/activity_log.py @@ -44,6 +44,5 @@ def clear_activity_logs(days=None): if not days: days = 90 - # frappe.db.delete("Activity Log", filters={"creation": ("<", ["NOW()", "-", f"INTERVAL {days} DAY"])}) frappe.db.sql("""delete from `tabActivity Log` where \ creation< (NOW() - INTERVAL '{0}' DAY)""".format(days)) \ No newline at end of file diff --git a/frappe/core/doctype/activity_log/feed.py b/frappe/core/doctype/activity_log/feed.py index d064022b25..efac825178 100644 --- a/frappe/core/doctype/activity_log/feed.py +++ b/frappe/core/doctype/activity_log/feed.py @@ -32,10 +32,6 @@ def update_feed(doc, method=None): "reference_name": name, "link_doctype": feed.link_doctype }) - # frappe.db.sql("""delete from `tabActivity Log` - # where - # reference_doctype=%s and reference_name=%s - # and link_doctype=%s""", (doctype, name,feed.link_doctype)) frappe.get_doc({ "doctype": "Activity Log", "reference_doctype": doctype, diff --git a/frappe/core/doctype/domain_settings/domain_settings.py b/frappe/core/doctype/domain_settings/domain_settings.py index 4ed9d1e15a..a8c7c6a747 100644 --- a/frappe/core/doctype/domain_settings/domain_settings.py +++ b/frappe/core/doctype/domain_settings/domain_settings.py @@ -35,7 +35,6 @@ class DomainSettings(Document): def remove_role(role): frappe.db.delete("Has Role", {"role": role}) - # frappe.db.sql('delete from `tabHas Role` where role=%s', role) frappe.set_value('Role', role, 'disabled', 1) for domain in all_domains: diff --git a/frappe/core/doctype/error_log/error_log.py b/frappe/core/doctype/error_log/error_log.py index 9a7333995c..081b4e879d 100644 --- a/frappe/core/doctype/error_log/error_log.py +++ b/frappe/core/doctype/error_log/error_log.py @@ -20,5 +20,4 @@ def set_old_logs_as_seen(): def clear_error_logs(): '''Flush all Error Logs''' frappe.only_for('System Manager') - frappe.db.delete("Error Log") - # frappe.db.sql('''DELETE FROM `tabError Log`''') \ No newline at end of file + frappe.db.delete("Error Log") \ No newline at end of file diff --git a/frappe/core/doctype/log_settings/log_settings.py b/frappe/core/doctype/log_settings/log_settings.py index 7f25998dc5..e73aa8dac1 100644 --- a/frappe/core/doctype/log_settings/log_settings.py +++ b/frappe/core/doctype/log_settings/log_settings.py @@ -13,7 +13,6 @@ class LogSettings(Document): self.clear_email_queue() def clear_error_logs(self): - # frappe.db.delete("Error Log", {"creation": ("<", ["NOW()", "-", f"INTERVAL {self.clear_error_log_after} DAY"])}) frappe.db.sql(""" DELETE FROM `tabError Log` WHERE `creation` < (NOW() - INTERVAL '{0}' DAY) """.format(self.clear_error_log_after)) diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py index efc33b888c..1bf9893bd7 100644 --- a/frappe/core/doctype/report/test_report.py +++ b/frappe/core/doctype/report/test_report.py @@ -87,9 +87,6 @@ class TestReport(unittest.TestCase): "parent": frappe.session.user, "role": "Test Has Role" }) - # frappe.db.sql("""delete from `tabHas Role` where parent = %s - # and role = 'Test Has Role'""", frappe.session.user, auto_commit=1) - if not frappe.db.exists('Role', 'Test Has Role'): role = frappe.get_doc({ 'doctype': 'Role', diff --git a/frappe/core/doctype/role/role.py b/frappe/core/doctype/role/role.py index dee25eb476..28b444e1e7 100644 --- a/frappe/core/doctype/role/role.py +++ b/frappe/core/doctype/role/role.py @@ -39,7 +39,6 @@ class Role(Document): def remove_roles(self): frappe.db.delete("Has Role", {"role": self.name}) - # frappe.db.sql("delete from `tabHas Role` where role = %s", self.name) frappe.clear_cache() def on_update(self): 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 9882f0edfe..d3afecf378 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -109,13 +109,10 @@ class ScheduledJobType(Document): return 'long' if ('Long' in self.frequency) else 'default' def on_trash(self): - frappe.db.delete("Scheduled Job Log", { "scheduled_job_type": self.name }) - # frappe.db.sql('delete from `tabScheduled Job Log` where scheduled_job_type=%s', self.name) - - + @frappe.whitelist() def execute_event(doc: str): frappe.only_for("System Manager") diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 7ff4b8bea9..65a48fb39e 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -367,25 +367,15 @@ class User(Document): frappe.local.login_manager.logout(user=self.name) frappe.db.delete("ToDo", {"owner": self.name}) - # frappe.db.sql("""DELETE FROM `tabToDo` WHERE `owner`=%s""", (self.name,)) frappe.db.sql("""UPDATE `tabToDo` SET `assigned_by`=NULL WHERE `assigned_by`=%s""", (self.name,)) # delete events frappe.db.delete("Event", {"owner": self.name, "event_type": "Private"}) - # frappe.db.sql("""delete from `tabEvent` where owner=%s - # and event_type='Private'""", (self.name,)) # delete shares frappe.db.delete("DocShare", {"user": self.name}) - # frappe.db.sql("""delete from `tabDocShare` where user=%s""", self.name) - # delete messages - - # frappe.db.delete("Communication", { - # "reference_doctype": "User", - # "communication_type": ("in", ("Chat", "Notification")), - # }) frappe.db.sql("""delete from `tabCommunication` where communication_type in ('Chat', 'Notification') and reference_doctype='User' diff --git a/frappe/core/doctype/user_permission/test_user_permission.py b/frappe/core/doctype/user_permission/test_user_permission.py index 60a91e6a9b..c3d593ee3b 100644 --- a/frappe/core/doctype/user_permission/test_user_permission.py +++ b/frappe/core/doctype/user_permission/test_user_permission.py @@ -17,12 +17,6 @@ class TestUserPermission(unittest.TestCase): "nested_doc_user@example.com")) }) - # frappe.db.sql("""DELETE FROM `tabUser Permission` - # WHERE `user` in ( - # 'test_bulk_creation_update@example.com', - # 'test_user_perm1@example.com', - # 'nested_doc_user@example.com')""") - # frappe.delete_doc_if_exists("DocType", "Person") frappe.db.sql_ddl("DROP TABLE IF EXISTS `tabPerson`") frappe.delete_doc_if_exists("DocType", "Doc A") frappe.db.sql_ddl("DROP TABLE IF EXISTS `tabDoc A`") diff --git a/frappe/core/doctype/user_permission/user_permission.py b/frappe/core/doctype/user_permission/user_permission.py index 338b063a63..de242efe10 100644 --- a/frappe/core/doctype/user_permission/user_permission.py +++ b/frappe/core/doctype/user_permission/user_permission.py @@ -186,7 +186,6 @@ def clear_user_permissions(user, for_doctype): "user": user, "allow": for_doctype }) - # frappe.db.sql('DELETE FROM `tabUser Permission` WHERE `user`=%s AND `allow`=%s', (user, for_doctype)) frappe.clear_cache() return total @@ -244,13 +243,6 @@ def remove_applicable(perm_applied_docs, user, doctype, docname): "allow": doctype, "for_value": docname }) - # - # frappe.db.sql("""DELETE FROM `tabUser Permission` - # WHERE `user`=%s - # AND `applicable_for`=%s - # AND `allow`=%s - # AND `for_value`=%s - # """, (user, applicable_for, doctype, docname)) def remove_apply_to_all(user, doctype, docname): frappe.db.delete("User Permission", { @@ -259,12 +251,6 @@ def remove_apply_to_all(user, doctype, docname): "allow": doctype, "for_value": docname }) - # frappe.db.sql("""DELETE from `tabUser Permission` - # WHERE `user`=%s - # AND `apply_to_all_doctypes`=1 - # AND `allow`=%s - # AND `for_value`=%s - # """,(user, doctype, docname)) def update_applicable(already_applied, to_apply, user, doctype, docname): for applied in already_applied: @@ -276,10 +262,3 @@ def update_applicable(already_applied, to_apply, user, doctype, docname): "allow": doctype, "for_value": docname }) - - # frappe.db.sql("""DELETE FROM `tabUser Permission` - # WHERE `user`=%s - # AND `applicable_for`=%s - # AND `allow`=%s - # AND `for_value`=%s - # """,(user, applied, doctype, docname)) diff --git a/frappe/core/page/permission_manager/permission_manager.py b/frappe/core/page/permission_manager/permission_manager.py index 0a76283813..af8973811a 100644 --- a/frappe/core/page/permission_manager/permission_manager.py +++ b/frappe/core/page/permission_manager/permission_manager.py @@ -92,14 +92,14 @@ def update(doctype, role, permlevel, ptype, value=None): """Update role permission params Args: - doctype (str): Name of the DocType to update params for - role (str): Role to be updated for, eg "Website Manager". - permlevel (int): perm level the provided rule applies to - ptype (str): permission type, example "read", "delete", etc. - value (None, optional): value for ptype, None indicates False + doctype (str): Name of the DocType to update params for + role (str): Role to be updated for, eg "Website Manager". + permlevel (int): perm level the provided rule applies to + ptype (str): permission type, example "read", "delete", etc. + value (None, optional): value for ptype, None indicates False Returns: - str: Refresh flag is permission is updated successfully + str: Refresh flag is permission is updated successfully """ frappe.only_for("System Manager") out = update_permission_property(doctype, role, permlevel, ptype, value) @@ -114,7 +114,6 @@ def remove(doctype, role, permlevel): frappe.db.delete("Custom DocPerm", { "name": name }) - # frappe.db.sql('delete from `tabCustom DocPerm` where name=%s', name) if not frappe.get_all('Custom DocPerm', dict(parent=doctype)): frappe.throw(_('There must be atleast one permission rule.'), title=_('Cannot Remove')) diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py index 1d8f4ae67a..e266455f7a 100644 --- a/frappe/custom/doctype/custom_field/custom_field.py +++ b/frappe/custom/doctype/custom_field/custom_field.py @@ -89,12 +89,6 @@ class CustomField(Document): "doc_type": self.dt, "field_name": self.fieldname }) - # frappe.db.sql("""\ - # DELETE FROM `tabProperty Setter` - # WHERE doc_type = %s - # AND field_name = %s""", - # (self.dt, self.fieldname)) - frappe.clear_cache(doctype=self.dt) def validate_insert_after(self, meta): diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index 3b7de448d3..5431511d42 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -24,9 +24,7 @@ class CustomizeForm(Document): "doctype": "Customize Form" }) frappe.db.delete("Customize Form Field") - # frappe.db.sql("delete from tabSingles where doctype='Customize Form'") - # frappe.db.sql("delete from `tabCustomize Form Field`") - + @frappe.whitelist() def fetch_to_customize(self): self.clear_existing_doc() diff --git a/frappe/defaults.py b/frappe/defaults.py index ee73e86a91..794d30a0c8 100644 --- a/frappe/defaults.py +++ b/frappe/defaults.py @@ -128,11 +128,6 @@ def set_default(key, value, parent, parenttype="__default"): "defkey": key, "parent": parent }) - # frappe.db.sql(""" - # delete from - # `tabDefaultValue` - # where - # defkey=%s and parent=%s""", (key, parent)) if value != None: add_default(key, value, parent) else: diff --git a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py index 7133640c21..9f10522b12 100644 --- a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py @@ -65,7 +65,6 @@ class TestDashboardChart(unittest.TestCase): frappe.delete_doc('Dashboard Chart', 'Test Empty Dashboard Chart') frappe.db.delete("Error Log") - # frappe.db.sql('delete from `tabError Log`') frappe.get_doc(dict( doctype = 'Dashboard Chart', @@ -96,7 +95,6 @@ class TestDashboardChart(unittest.TestCase): frappe.delete_doc('Dashboard Chart', 'Test Empty Dashboard Chart 2') frappe.db.delete("Error Log") - # frappe.db.sql('delete from `tabError Log`') # create one data point frappe.get_doc(dict(doctype = 'Error Log', creation = '2018-06-01 00:00:00')).insert() diff --git a/frappe/desk/doctype/desktop_icon/desktop_icon.py b/frappe/desk/doctype/desktop_icon/desktop_icon.py index bc5677e9b9..283ffad967 100644 --- a/frappe/desk/doctype/desktop_icon/desktop_icon.py +++ b/frappe/desk/doctype/desktop_icon/desktop_icon.py @@ -200,7 +200,6 @@ def set_desktop_icons(visible_list, ignore_duplicate=True): frappe.db.delete("Desktop Icon", { "standard": 0 }) - # frappe.db.sql('delete from `tabDesktop Icon` where standard=0') # set standard as blocked and hidden if setting first active domain if not frappe.flags.keep_desktop_icons: diff --git a/frappe/desk/doctype/event/event.py b/frappe/desk/doctype/event/event.py index f9a0dd74fd..2182ec4c9f 100644 --- a/frappe/desk/doctype/event/event.py +++ b/frappe/desk/doctype/event/event.py @@ -341,11 +341,9 @@ def delete_events(ref_type, ref_name, delete_event=False): frappe.db.delete("Event", { "name": participation.parent }) - # frappe.db.sql("DELETE FROM `tabEvent` WHERE `name` = %(name)s", {'name': participation.parent}) frappe.db.delete("Event Participants", { "name": participation.name }) - # frappe.db.sql("DELETE FROM `tabEvent Participants ` WHERE `name` = %(name)s", {'name': participation.name}) # Close events if ends_on or repeat_till is less than now_datetime def set_status_of_events(): diff --git a/frappe/desk/doctype/route_history/route_history.py b/frappe/desk/doctype/route_history/route_history.py index bfa2711b8f..4303b7554d 100644 --- a/frappe/desk/doctype/route_history/route_history.py +++ b/frappe/desk/doctype/route_history/route_history.py @@ -35,13 +35,4 @@ def flush_old_route_records(): frappe.db.delete("Route History", { "modified": last_record_to_keep[0].modified, "user": user - }) - - # frappe.db.sql(''' - # DELETE - # FROM `tabRoute History` - # WHERE `modified` <= %(modified)s and `user`=%(modified)s - # ''', { - # "modified": last_record_to_keep[0].modified, - # "user": user - # }) \ No newline at end of file + }) \ No newline at end of file diff --git a/frappe/desk/doctype/tag/tag.py b/frappe/desk/doctype/tag/tag.py index 8b78405d06..626a2db085 100644 --- a/frappe/desk/doctype/tag/tag.py +++ b/frappe/desk/doctype/tag/tag.py @@ -127,7 +127,6 @@ def delete_tags_for_document(doc): "document_type": doc.doctype, "document_name": doc.name }) - # frappe.db.sql("""DELETE FROM `tabTag Link` WHERE `document_type`=%s AND `document_name`=%s""", (doc.doctype, doc.name)) def update_tags(doc, tags): """ @@ -170,7 +169,6 @@ def delete_tag_for_document(dt, dn, tag): "document_name": dn, "tag": tag }) - # frappe.db.sql("""DELETE FROM `tabTag Link` WHERE `document_type`=%s AND `document_name`=%s AND tag=%s""", (dt, dn, tag)) @frappe.whitelist() def get_documents_for_tag(tag): diff --git a/frappe/desk/doctype/todo/todo.py b/frappe/desk/doctype/todo/todo.py index 388ba37e1f..34fb3bb905 100644 --- a/frappe/desk/doctype/todo/todo.py +++ b/frappe/desk/doctype/todo/todo.py @@ -44,12 +44,6 @@ class ToDo(Document): "link_doctype": self.doctype, "link_name": self.name }) - # frappe.db.sql(""" - # delete from `tabCommunication Link` - # where link_doctype=%(doctype)s and link_name=%(name)s""", { - # "doctype": self.doctype, "name": self.name - # }) - self.update_in_reference() def add_assign_comment(self, text, comment_type): diff --git a/frappe/email/doctype/unhandled_email/unhandled_email.py b/frappe/email/doctype/unhandled_email/unhandled_email.py index 850007d85f..b445c98aa6 100644 --- a/frappe/email/doctype/unhandled_email/unhandled_email.py +++ b/frappe/email/doctype/unhandled_email/unhandled_email.py @@ -12,6 +12,4 @@ class UnhandledEmail(Document): def remove_old_unhandled_emails(): frappe.db.delete("Unhandled Email", { "creation": ("<", frappe.utils.add_days(frappe.utils.nowdate(), -30)) - }) - # frappe.db.sql("""DELETE FROM `tabUnhandled Email` - # WHERE creation < %s""", frappe.utils.add_days(frappe.utils.nowdate(), -30)) \ No newline at end of file + }) \ No newline at end of file diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 7459493fcc..6b38b383bf 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -156,11 +156,6 @@ def delete_fields(args_dict, delete=0): "parent": dt, "fieldname": ("in", fields) }) - # frappe.db.sql(""" - # DELETE FROM `tabDocField` - # WHERE parent='%s' AND fieldname IN (%s) - # """ % (dt, ", ".join(["'{}'".format(f) for f in fields]))) - # Delete the data/column only if delete is specified if not delete: continue @@ -170,10 +165,6 @@ def delete_fields(args_dict, delete=0): "doctype": dt, "field": ("in", fields) }) - # frappe.db.sql(""" - # DELETE FROM `tabSingles` - # WHERE doctype='%s' AND field IN (%s) - # """ % (dt, ", ".join("'{}'".format(f) for f in fields))) else: existing_fields = frappe.db.multisql({ "mariadb": "DESC `tab%s`" % dt, diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 9187310a84..44e675f3c5 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -82,14 +82,6 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa frappe.db.delete("__global_search", { "doctype": name }) - - # frappe.db.sql("delete from `tabCustom Field` where dt = %s", name) - # frappe.db.sql("delete from `tabClient Script` where dt = %s", name) - # frappe.db.sql("delete from `tabProperty Setter` where doc_type = %s", name) - # frappe.db.sql("delete from `tabReport` where ref_doctype=%s", name) - # frappe.db.sql("delete from `tabCustom DocPerm` where parent=%s", name) - # frappe.db.sql("delete from `__global_search` where doctype=%s", name) - delete_from_table(doctype, name, ignore_doctypes, None) if frappe.conf.developer_mode and not doc.custom and not ( @@ -183,13 +175,10 @@ def delete_from_table(doctype, name, ignore_doctypes, doc): frappe.db.delete("Singles", { "doctype": name }) - # frappe.db.sql("delete from `tabSingles` where `doctype`=%s", name) else: frappe.db.delete(doctype, { "name": name }) - # frappe.db.sql("delete from `tab{0}` where `name`=%s".format(doctype), name) - # get child tables if doc: tables = [d.options for d in doc.meta.get_table_fields()] @@ -367,9 +356,7 @@ def clear_timeline_references(link_doctype, link_name): "link_doctype": link_doctype, "link_name": link_name }) - # frappe.db.sql("""DELETE FROM `tabCommunication Link` - # WHERE `tabCommunication Link`.link_doctype=%s AND `tabCommunication Link`.link_name=%s""", (link_doctype, link_name)) - + def insert_feed(doc): if ( frappe.flags.in_install diff --git a/frappe/model/document.py b/frappe/model/document.py index f8adfdb3cf..b44d95716e 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -395,11 +395,6 @@ class Document(BaseDocument): "parenttype": self.doctype, "parentfield": fieldname }) - - # frappe.db.sql("""delete from `tab{0}` where parent=%s - # and parenttype=%s and parentfield=%s""".format(df.options), - # (self.name, self.doctype, fieldname)) - def get_doc_before_save(self): return getattr(self, '_doc_before_save', None) @@ -460,7 +455,6 @@ class Document(BaseDocument): frappe.db.delete("Singles", { "doctype": self.doctype }) - # frappe.db.sql("""delete from `tabSingles` where doctype=%s""", self.doctype) for field, value in d.items(): if field != "doctype": frappe.db.sql("""insert into `tabSingles` (doctype, field, value) diff --git a/frappe/patches/v11_0/apply_customization_to_custom_doctype.py b/frappe/patches/v11_0/apply_customization_to_custom_doctype.py index c13ff83a1f..c01db50d5e 100644 --- a/frappe/patches/v11_0/apply_customization_to_custom_doctype.py +++ b/frappe/patches/v11_0/apply_customization_to_custom_doctype.py @@ -31,8 +31,6 @@ def execute(): frappe.db.delete("Property Setter", { "name": prop.name }) - # frappe.db.sql('DELETE FROM `tabProperty Setter` WHERE `name`=%s', prop.name) - meta = frappe.get_meta(doctype.name) for df in meta.fields: @@ -56,6 +54,4 @@ def execute(): frappe.db.delete("Custom Field", { "name": cf.name }) - # frappe.db.sql('DELETE FROM `tabCustom Field` WHERE name=%s', cf.name) - meta.save() diff --git a/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py b/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py index 1a12d4dc1d..5c949fbb1d 100644 --- a/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py +++ b/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py @@ -19,5 +19,4 @@ def execute(): frappe.db.delete("Singles", { "doctype": "Stripe Settings" - }) - # frappe.db.sql("""DELETE FROM tabSingles WHERE doctype='Stripe Settings'""") \ No newline at end of file + }) \ No newline at end of file diff --git a/frappe/patches/v12_0/delete_feedback_request_if_exists.py b/frappe/patches/v12_0/delete_feedback_request_if_exists.py index de799b1e21..bc3c7b8f97 100644 --- a/frappe/patches/v12_0/delete_feedback_request_if_exists.py +++ b/frappe/patches/v12_0/delete_feedback_request_if_exists.py @@ -4,8 +4,4 @@ import frappe def execute(): frappe.db.delete("DocType", { "name": "Feedback Request" - }) - # frappe.db.sql(''' - #DELETE from `tabDocType` - #WHERE name = 'Feedback Request' - # ''') \ No newline at end of file + }) \ No newline at end of file diff --git a/frappe/patches/v12_0/remove_deprecated_fields_from_doctype.py b/frappe/patches/v12_0/remove_deprecated_fields_from_doctype.py index 17dfbc5ca1..9c9a79ccbf 100644 --- a/frappe/patches/v12_0/remove_deprecated_fields_from_doctype.py +++ b/frappe/patches/v12_0/remove_deprecated_fields_from_doctype.py @@ -10,9 +10,4 @@ def execute(): frappe.db.delete("Property Setter", { "property": "read_only_onload" - }) - - # frappe.db.sql(''' - # DELETE from `tabProperty Setter` - # WHERE property = 'read_only_onload' - # ''') + }) \ No newline at end of file diff --git a/frappe/patches/v12_0/set_primary_key_in_series.py b/frappe/patches/v12_0/set_primary_key_in_series.py index a8409cbbba..e8d3abdde1 100644 --- a/frappe/patches/v12_0/set_primary_key_in_series.py +++ b/frappe/patches/v12_0/set_primary_key_in_series.py @@ -15,9 +15,6 @@ def execute(): frappe.db.delete("Series", { "name": row.name }) - # frappe.db.sql('delete from `tabSeries` where name = %(key)s', { - # 'key': row.name - # }) if row.current: frappe.db.sql('insert into `tabSeries`(`name`, `current`) values (%(name)s, %(current)s)', row) frappe.db.commit() diff --git a/frappe/patches/v12_0/setup_comments_from_communications.py b/frappe/patches/v12_0/setup_comments_from_communications.py index a4e3fb4261..b72d02720e 100644 --- a/frappe/patches/v12_0/setup_comments_from_communications.py +++ b/frappe/patches/v12_0/setup_comments_from_communications.py @@ -31,5 +31,4 @@ def execute(): # clean up frappe.db.delete("Communication", { "communication_type": "Comment" - }) - # frappe.db.sql("delete from `tabCommunication` where communication_type = 'Comment'") + }) \ No newline at end of file diff --git a/frappe/patches/v13_0/remove_twilio_settings.py b/frappe/patches/v13_0/remove_twilio_settings.py index 5d17364847..8b7e5e02c2 100644 --- a/frappe/patches/v13_0/remove_twilio_settings.py +++ b/frappe/patches/v13_0/remove_twilio_settings.py @@ -15,9 +15,8 @@ def execute(): frappe.db.delete("Singles", { "doctype": "Twilio Settings" }) - # frappe.db.sql("delete from `tabSingles` where `doctype`=%s", 'Twilio Settings') def twilio_settings_doctype_in_integrations() -> bool: """Check Twilio Settings doctype exists in integrations module or not. """ - return frappe.db.exists("DocType", {'name': 'Twilio Settings', 'module': 'Integrations'}) + return frappe.db.exists("DocType", {'name': 'Twilio Settings', 'module': 'Integrations'}) \ No newline at end of file diff --git a/frappe/permissions.py b/frappe/permissions.py index 7724f09e78..82f27452fb 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -520,8 +520,6 @@ def reset_perms(doctype): delete_notification_count_for(doctype) frappe.db.delete("Custom DocPerm", {"parent": doctype}) - # frappe.db.sql("""delete from `tabCustom DocPerm` where parent=%s""", doctype) - def get_linked_doctypes(dt): return list(set([dt] + [d.options for d in frappe.get_meta(dt).get("fields", { diff --git a/frappe/sessions.py b/frappe/sessions.py index f4d05538bc..58158a2e4e 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -84,7 +84,6 @@ def delete_session(sid=None, user=None, reason="Session Expired"): logout_feed(user, reason) frappe.db.delete("Sessions", {"sid": sid}) - # frappe.db.sql("""delete from tabSessions where sid=%s""", sid) frappe.db.commit() def clear_all_sessions(reason=None): diff --git a/frappe/utils/error.py b/frappe/utils/error.py index f2cbc5ec10..d83cbb49ea 100644 --- a/frappe/utils/error.py +++ b/frappe/utils/error.py @@ -177,8 +177,6 @@ def collect_error_snapshots(): def clear_old_snapshots(): """Clear snapshots that are older than a month""" - # frappe.db.delete("Error Snapshot", filters={"creation": ("<", ["NOW()", "-", f"INTERVAL '1' MONTH"])}) - frappe.db.sql("""delete from `tabError Snapshot` where creation < (NOW() - INTERVAL '1' MONTH)""") diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index a018761370..6391fcf7ef 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -24,8 +24,6 @@ def reset(): :return: """ frappe.db.delete("__global_search") - # frappe.db.sql('DELETE FROM `__global_search`') - def get_doctypes_with_global_search(with_child_tables=True): """ @@ -150,10 +148,6 @@ def delete_global_search_records_for_doctype(doctype): frappe.db.delete("__global_search", { "doctype": doctype }) - # frappe.db.sql('''DELETE - # FROM `__global_search` - # WHERE doctype = %s''', doctype, as_dict=True) - def get_selected_fields(meta, global_search_fields): fieldnames = [df.fieldname for df in global_search_fields] @@ -403,18 +397,11 @@ def delete_for_document(doc): been deleted :param doc: Deleted document """ - frappe.db.delete("__global_search", { "doctype": doc.doctype, "name": doc.name }) - # frappe.db.sql('''DELETE - # FROM `__global_search` - # WHERE doctype = %s - # AND name = %s''', (doc.doctype, doc.name), as_dict=True) - - @frappe.whitelist() def search(text, start=0, limit=20, doctype=""): """ diff --git a/frappe/utils/install.py b/frappe/utils/install.py index 9d21a0d945..e6d4386ebe 100644 --- a/frappe/utils/install.py +++ b/frappe/utils/install.py @@ -113,8 +113,6 @@ def before_tests(): frappe.db.delete("Custom Field") frappe.db.delete("Event") - # frappe.db.sql("delete from `tabCustom Field`") - # frappe.db.sql("delete from `tabEvent`") frappe.db.commit() frappe.clear_cache() diff --git a/frappe/utils/password.py b/frappe/utils/password.py index 21e1bc9586..a097c58b31 100644 --- a/frappe/utils/password.py +++ b/frappe/utils/password.py @@ -71,11 +71,6 @@ def remove_encrypted_password(doctype, name, fieldname='password'): "fieldname": fieldname }) - # frappe.db.sql( - # 'DELETE FROM `__Auth` WHERE doctype = %s and name = %s and fieldname = %s', - # values=[doctype, name, fieldname] - # ) - def check_password(user, pwd, doctype='User', fieldname='password', delete_tracker_cache=True): '''Checks if user and password are correct, else raises frappe.AuthenticationError''' @@ -140,8 +135,6 @@ def delete_all_passwords_for(doctype, name): "doctype": doctype, "name": name }) - # frappe.db.sql("""delete from `__Auth` where `doctype`=%(doctype)s and `name`=%(name)s""", - # { 'doctype': doctype, 'name': name }) except Exception as e: if not frappe.db.is_missing_column(e): raise diff --git a/frappe/utils/testutils.py b/frappe/utils/testutils.py index 74b800bdcf..5c5bfa7976 100644 --- a/frappe/utils/testutils.py +++ b/frappe/utils/testutils.py @@ -15,5 +15,4 @@ def clear_custom_fields(doctype): frappe.db.delete("Custom Field", { "dt": doctype }) - # frappe.db.sql('delete from `tabCustom Field` where dt=%s', doctype) frappe.clear_cache(doctype=doctype) diff --git a/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py b/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py index 96898cc159..54d2c2e446 100644 --- a/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py +++ b/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py @@ -323,11 +323,6 @@ class PersonalDataDeletionRequest(Document): def remove_unverified_record(): - # frappe.db.delete("Personal Data Deletion Request", { - # "status": "Pending Verification", - # "creation": ("<", ["Now()", "-", "INTERVAL 7 DAY"]) - # }) - frappe.db.sql( """ DELETE FROM `tabPersonal Data Deletion Request` diff --git a/frappe/workflow/doctype/workflow_action/workflow_action.py b/frappe/workflow/doctype/workflow_action/workflow_action.py index 5957bf8671..e3b89ba0e5 100644 --- a/frappe/workflow/doctype/workflow_action/workflow_action.py +++ b/frappe/workflow/doctype/workflow_action/workflow_action.py @@ -139,10 +139,7 @@ def clear_old_workflow_actions(doc, user=None): "user": ("!=", user), "status": "Open" }) - # frappe.db.sql("""DELETE FROM `tabWorkflow Action` - # WHERE `reference_doctype`=%s AND `reference_name`=%s AND `user`!=%s AND `status`='Open'""", - # (doc.get('doctype'), doc.get('name'), user)) - + def update_completed_workflow_actions(doc, user=None): user = user if user else frappe.session.user frappe.db.sql("""UPDATE `tabWorkflow Action` SET `status`='Completed', `completed_by`=%s @@ -263,10 +260,6 @@ def clear_workflow_actions(doctype, name): "reference_doctype": doctype, "reference_name": name }) - # frappe.db.sql('''delete from `tabWorkflow Action` - # where reference_doctype=%s and reference_name=%s''', - # (doctype, name)) - def get_doc_workflow_state(doc): workflow_name = get_workflow_name(doc.get('doctype')) workflow_state_field = get_workflow_state_field(workflow_name) From 9a74255a2e8334b42a5b8f66799f207b0a92309a Mon Sep 17 00:00:00 2001 From: leela Date: Wed, 28 Jul 2021 13:35:08 +0530 Subject: [PATCH 461/495] style: Add row numbering for report view print format --- frappe/public/js/frappe/views/reports/print_grid.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/public/js/frappe/views/reports/print_grid.html b/frappe/public/js/frappe/views/reports/print_grid.html index e607f12b52..6f693bc932 100644 --- a/frappe/public/js/frappe/views/reports/print_grid.html +++ b/frappe/public/js/frappe/views/reports/print_grid.html @@ -11,6 +11,7 @@ + # {% for col in columns %} {% if col.name && col._id !== "_check" %} {% for row in data %} + + {{ row._index + 1 }} + {% for col in columns %} {% if col.name && col._id !== "_check" %} From b719e1481de9a629eee42559f5b0599a4c049d6f Mon Sep 17 00:00:00 2001 From: leela Date: Wed, 28 Jul 2021 17:00:12 +0530 Subject: [PATCH 462/495] Revert "refactor: set amended docname to original docname" This reverts commit d459847ae39f8e9917686c3db6340ee95103085e. --- frappe/core/doctype/doctype/test_doctype.py | 8 +- frappe/model/document.py | 9 +- frappe/model/naming.py | 83 +++---------------- frappe/patches.txt | 1 - frappe/patches/v13_0/rename_cancelled_docs.py | 27 ------ frappe/public/js/frappe/form/form.js | 24 +++--- frappe/public/js/frappe/router.js | 6 -- frappe/tests/test_naming.py | 34 -------- 8 files changed, 28 insertions(+), 164 deletions(-) delete mode 100644 frappe/patches/v13_0/rename_cancelled_docs.py diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index 4b6d0e4794..1e1a01a685 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -348,7 +348,6 @@ class TestDocType(unittest.TestCase): dump_docs = json.dumps(docs.get('docs')) cancel_all_linked_docs(dump_docs) data_link_doc.cancel() - data_doc.name = '{}-CAN-0'.format(data_doc.name) data_doc.load_from_db() self.assertEqual(data_link_doc.docstatus, 2) self.assertEqual(data_doc.docstatus, 2) @@ -372,7 +371,7 @@ class TestDocType(unittest.TestCase): for data in link_doc.get('permissions'): data.submit = 1 data.cancel = 1 - link_doc.insert(ignore_if_duplicate=True) + link_doc.insert() #create first parent doctype test_doc_1 = new_doctype('Test Doctype 1') @@ -387,7 +386,7 @@ class TestDocType(unittest.TestCase): for data in test_doc_1.get('permissions'): data.submit = 1 data.cancel = 1 - test_doc_1.insert(ignore_if_duplicate=True) + test_doc_1.insert() #crete second parent doctype doc = new_doctype('Test Doctype 2') @@ -402,7 +401,7 @@ class TestDocType(unittest.TestCase): for data in link_doc.get('permissions'): data.submit = 1 data.cancel = 1 - doc.insert(ignore_if_duplicate=True) + doc.insert() # create doctype data data_link_doc_1 = frappe.new_doc('Test Linked Doctype 1') @@ -433,7 +432,6 @@ class TestDocType(unittest.TestCase): # checking that doc for Test Doctype 2 is not canceled self.assertRaises(frappe.LinkExistsError, data_link_doc_1.cancel) - data_doc_2.name = '{}-CAN-0'.format(data_doc_2.name) data_doc.load_from_db() data_doc_2.load_from_db() self.assertEqual(data_link_doc_1.docstatus, 2) diff --git a/frappe/model/document.py b/frappe/model/document.py index e974ae2a3e..61160e1f01 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -5,7 +5,7 @@ import time from frappe import _, msgprint, is_whitelisted from frappe.utils import flt, cstr, now, get_datetime_str, file_lock, date_diff from frappe.model.base_document import BaseDocument, get_controller -from frappe.model.naming import set_new_name, gen_new_name_for_cancelled_doc +from frappe.model.naming import set_new_name from werkzeug.exceptions import NotFound, Forbidden import hashlib, json from frappe.model import optional_fields, table_fields @@ -705,6 +705,7 @@ class Document(BaseDocument): else: tmp = frappe.db.sql("""select modified, docstatus from `tab{0}` where name = %s for update""".format(self.doctype), self.name, as_dict=True) + if not tmp: frappe.throw(_("Record does not exist")) else: @@ -915,12 +916,8 @@ class Document(BaseDocument): @whitelist.__func__ def _cancel(self): - """Cancel the document. Sets `docstatus` = 2, then saves. - """ + """Cancel the document. Sets `docstatus` = 2, then saves.""" self.docstatus = 2 - new_name = gen_new_name_for_cancelled_doc(self) - frappe.rename_doc(self.doctype, self.name, new_name, force=True, show_alert=False) - self.name = new_name self.save() @whitelist.__func__ diff --git a/frappe/model/naming.py b/frappe/model/naming.py index 6dff0aaff6..fe136adce8 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -28,7 +28,7 @@ def set_new_name(doc): doc.name = None if getattr(doc, "amended_from", None): - doc.name = _get_amended_name(doc) + _set_amended_name(doc) return elif getattr(doc.meta, "issingle", False): @@ -221,15 +221,6 @@ def revert_series_if_last(key, name, doc=None): * prefix = #### and hashes = 2021 (hash doesn't exist) * will search hash in key then accordingly get prefix = "" """ - if hasattr(doc, 'amended_from'): - # do not revert if doc is amended, since cancelled docs still exist - if doc.docstatus != 2 and doc.amended_from: - return - - # for first cancelled doc - if doc.docstatus == 2 and not doc.amended_from: - name, _ = NameParser.parse_docname(doc.name, sep='-CAN-') - if ".#" in key: prefix, hashes = key.rsplit(".", 1) if "#" not in hashes: @@ -312,9 +303,16 @@ def append_number_if_name_exists(doctype, value, fieldname="name", separator="-" return value -def _get_amended_name(doc): - name, _ = NameParser(doc).parse_amended_from() - return name +def _set_amended_name(doc): + am_id = 1 + am_prefix = doc.amended_from + if frappe.db.get_value(doc.doctype, doc.amended_from, "amended_from"): + am_id = cint(doc.amended_from.split("-")[-1]) + 1 + am_prefix = "-".join(doc.amended_from.split("-")[:-1]) # except the last hyphen + + doc.name = am_prefix + "-" + str(am_id) + return doc.name + def _field_autoname(autoname, doc, skip_slicing=None): """ @@ -325,6 +323,7 @@ def _field_autoname(autoname, doc, skip_slicing=None): name = (cstr(doc.get(fieldname)) or "").strip() return name + def _prompt_autoname(autoname, doc): """ Generate a name using Prompt option. This simply means the user will have to set the name manually. @@ -355,61 +354,3 @@ def _format_autoname(autoname, doc): name = re.sub(r"(\{[\w | #]+\})", get_param_value_for_match, autoname_value) return name - -class NameParser: - """Parse document name and return all the parts of it. - - NOTE: It handles cancellend and amended doc parsing for now. It can be expanded. - """ - def __init__(self, doc): - self.doc = doc - - def parse_name(self): - if not hasattr(self.doc, "amended_from"): - return (self.doc.name, None, None) - - #If document is cancelled document - if hasattr(self.doc, "amended_from") and self.doc.docstatus == 2: - return self.parse_docname(self.doc.name, sep='-CAN-') - return self.parse_docname(self.doc.name) - - def parse_amended_from(self): - if not getattr(self.doc, 'amended_from', None): - return (None, None) - return self.parse_docname(self.doc.amended_from, '-CAN-') - - @classmethod - def parse_docname(cls, name, sep='-'): - split_list = name.rsplit(sep, 1) - - if len(split_list) == 1: - return (name, None) - return (split_list[0], split_list[1]) - -def get_cancelled_doc_latest_counter(tname, docname): - """Get the latest counter used for cancelled docs of given docname. - """ - name_prefix = f'{docname}-CAN-' - - rows = frappe.db.sql(""" - select - name - from `tab{tname}` - where - name like %(name_prefix)s and docstatus=2 - """.format(tname=tname), {'name_prefix': name_prefix+'%'}, as_dict=1) - - if not rows: - return -1 - return max([int(row.name.replace(name_prefix, '') or -1) for row in rows]) - -def gen_new_name_for_cancelled_doc(doc): - """Generate a new name for cancelled document. - """ - if getattr(doc, "amended_from", None): - name, _ = NameParser(doc).parse_amended_from() - else: - name = doc.name - - counter = get_cancelled_doc_latest_counter(doc.doctype, name) - return f'{name}-CAN-{counter+1}' diff --git a/frappe/patches.txt b/frappe/patches.txt index a9c5807df0..7605d8ea2b 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -180,4 +180,3 @@ frappe.patches.v12_0.rename_uploaded_files_with_proper_name frappe.patches.v13_0.queryreport_columns frappe.patches.v13_0.jinja_hook frappe.patches.v13_0.update_notification_channel_if_empty -frappe.patches.v13_0.rename_cancelled_docs diff --git a/frappe/patches/v13_0/rename_cancelled_docs.py b/frappe/patches/v13_0/rename_cancelled_docs.py deleted file mode 100644 index 2e99a6f3cd..0000000000 --- a/frappe/patches/v13_0/rename_cancelled_docs.py +++ /dev/null @@ -1,27 +0,0 @@ -import frappe -from frappe.model.naming import NameParser -from frappe.model.rename_doc import rename_doc - -def execute(): - """Rename already cancelled documents by adding `CAN-X` postfix instead of `-X`. - """ - for doctype in frappe.db.get_all('DocType'): - doctype = frappe.get_doc('DocType', doctype.name) - if doctype.is_submittable and frappe.db.table_exists(doctype.name): - cancelled_docs = frappe.db.get_all(doctype.name, ['amended_from', 'name'], {'docstatus':2}) - - for doc in cancelled_docs: - if '-CAN-' in doc.name: - continue - - current_name = doc.name - - if getattr(doc, "amended_from", None): - orig_name, counter = NameParser.parse_docname(doc.name) - else: - orig_name, counter = doc.name, 0 - new_name = f'{orig_name}-CAN-{counter or 0}' - - print(f"Renaming {doctype.name} record from {current_name} to {new_name}") - rename_doc(doctype.name, current_name, new_name, ignore_permissions=True, show_alert=False) - frappe.db.commit() diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 3bbc883b0c..faaa3dfbd9 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -770,36 +770,32 @@ frappe.ui.form.Form = class FrappeForm { } _cancel(btn, callback, on_error, skip_confirm) { + const me = this; const cancel_doc = () => { frappe.validated = true; - this.script_manager.trigger("before_cancel").then(() => { + me.script_manager.trigger("before_cancel").then(() => { if (!frappe.validated) { - return this.handle_save_fail(btn, on_error); + return me.handle_save_fail(btn, on_error); } - const original_name = this.docname; - const after_cancel = (r) => { + var after_cancel = function(r) { if (r.exc) { - this.handle_save_fail(btn, on_error); + me.handle_save_fail(btn, on_error); } else { frappe.utils.play_sound("cancel"); + me.refresh(); callback && callback(); - this.script_manager.trigger("after_cancel"); - frappe.run_serially([ - () => this.rename_notify(this.doctype, original_name, r.docs[0].name), - () => frappe.router.clear_re_route(this.doctype, original_name), - () => this.refresh(), - ]); + me.script_manager.trigger("after_cancel"); } }; - frappe.ui.form.save(this, "cancel", after_cancel, btn); + frappe.ui.form.save(me, "cancel", after_cancel, btn); }); } if (skip_confirm) { cancel_doc(); } else { - frappe.confirm(__("Permanently Cancel {0}?", [this.docname]), cancel_doc, this.handle_save_fail(btn, on_error)); + frappe.confirm(__("Permanently Cancel {0}?", [this.docname]), cancel_doc, me.handle_save_fail(btn, on_error)); } }; @@ -821,7 +817,7 @@ frappe.ui.form.Form = class FrappeForm { 'docname': this.doc.name }).then(is_amended => { if (is_amended) { - frappe.throw(__('This document is already amended, you cannot amend it again')); + frappe.throw(__('This document is already amended, you cannot ammend it again')); } this.validate_form_action("Amend"); var me = this; diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 3f8ce7fee2..8a29dc1040 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -234,12 +234,6 @@ frappe.router = { } }, - clear_re_route(doctype, docname) { - delete frappe.re_route[ - `${encodeURIComponent(frappe.router.slug(doctype))}/${encodeURIComponent(docname)}` - ]; - }, - set_title(sub_path) { if (frappe.route_titles[sub_path]) { frappe.utils.set_title(frappe.route_titles[sub_path]); diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py index 78071a4120..557993882f 100644 --- a/frappe/tests/test_naming.py +++ b/frappe/tests/test_naming.py @@ -116,37 +116,3 @@ class TestNaming(unittest.TestCase): self.assertEqual(current_index.get('current'), 2) frappe.db.sql("""delete from `tabSeries` where name = %s""", series) - - def test_naming_for_cancelled_and_amended_doc(self): - submittable_doctype = frappe.get_doc({ - "doctype": "DocType", - "module": "Core", - "custom": 1, - "is_submittable": 1, - "permissions": [{ - "role": "System Manager", - "read": 1 - }], - "name": 'Submittable Doctype' - }).insert(ignore_if_duplicate=True) - - doc = frappe.new_doc('Submittable Doctype') - doc.save() - original_name = doc.name - - doc.submit() - doc.cancel() - cancelled_name = doc.name - self.assertEqual(cancelled_name, "{}-CAN-0".format(original_name)) - - amended_doc = frappe.copy_doc(doc) - amended_doc.docstatus = 0 - amended_doc.amended_from = doc.name - amended_doc.save() - self.assertEqual(amended_doc.name, original_name) - - amended_doc.submit() - amended_doc.cancel() - self.assertEqual(amended_doc.name, "{}-CAN-1".format(original_name)) - - submittable_doctype.delete() From 01d275f6677f4cd18b72b12d32ba4104a5b0ce30 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 28 Jul 2021 18:55:27 +0530 Subject: [PATCH 463/495] style: Format code for better readability * Remove trailing whitespaces * Format code to fit module conventions * Add appropriate new lines between imports, classes, fn defs, etc * Added comments, docstrings & module headers --- .../core/doctype/activity_log/activity_log.py | 1 + frappe/core/doctype/activity_log/feed.py | 5 ++- .../scheduled_job_type/scheduled_job_type.py | 11 +++--- frappe/core/doctype/user/user.py | 2 ++ .../user_permission/test_user_permission.py | 5 ++- .../user_permission/user_permission.py | 29 ++++++++------- .../doctype/customize_form/customize_form.py | 11 +++--- .../desk/doctype/desktop_icon/desktop_icon.py | 4 +-- frappe/desk/doctype/event/event.py | 8 ++--- .../doctype/route_history/route_history.py | 4 +-- frappe/desk/doctype/tag/tag.py | 3 +- frappe/model/__init__.py | 7 ++-- frappe/model/delete_doc.py | 36 ++++++------------- .../apply_customization_to_custom_doctype.py | 10 +++--- .../sync_stripe_settings_before_migrate.py | 4 +-- .../delete_feedback_request_if_exists.py | 4 +-- .../v12_0/set_primary_key_in_series.py | 2 ++ .../setup_comments_from_communications.py | 2 +- .../patches/v13_0/remove_twilio_settings.py | 2 +- frappe/permissions.py | 2 +- frappe/sessions.py | 1 + frappe/tests/test_commands.py | 2 +- frappe/utils/error.py | 4 +-- frappe/utils/testutils.py | 4 +-- .../personal_data_deletion_request.py | 1 + .../workflow_action/workflow_action.py | 2 +- 26 files changed, 72 insertions(+), 94 deletions(-) diff --git a/frappe/core/doctype/activity_log/activity_log.py b/frappe/core/doctype/activity_log/activity_log.py index ce5e504f7f..efec0dc217 100644 --- a/frappe/core/doctype/activity_log/activity_log.py +++ b/frappe/core/doctype/activity_log/activity_log.py @@ -44,5 +44,6 @@ def clear_activity_logs(days=None): if not days: days = 90 + frappe.db.sql("""delete from `tabActivity Log` where \ creation< (NOW() - INTERVAL '{0}' DAY)""".format(days)) \ No newline at end of file diff --git a/frappe/core/doctype/activity_log/feed.py b/frappe/core/doctype/activity_log/feed.py index efac825178..19d7b77184 100644 --- a/frappe/core/doctype/activity_log/feed.py +++ b/frappe/core/doctype/activity_log/feed.py @@ -26,12 +26,15 @@ def update_feed(doc, method=None): feed = frappe._dict(feed) doctype = feed.doctype or doc.doctype - name = feed.name or doc.name + name = feed.name or doc.name + + # delete earlier feed frappe.db.delete("Activity Log", { "reference_doctype": doctype, "reference_name": name, "link_doctype": feed.link_doctype }) + frappe.get_doc({ "doctype": "Activity Log", "reference_doctype": doctype, 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 d3afecf378..b6515b1e79 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2019, Frappe Technologies and contributors +# Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt import json @@ -13,6 +12,7 @@ from frappe.model.document import Document from frappe.utils import get_datetime, now_datetime from frappe.utils.background_jobs import enqueue, get_jobs + class ScheduledJobType(Document): def autoname(self): self.name = ".".join(self.method.split(".")[-2:]) @@ -109,10 +109,9 @@ class ScheduledJobType(Document): return 'long' if ('Long' in self.frequency) else 'default' def on_trash(self): - frappe.db.delete("Scheduled Job Log", { - "scheduled_job_type": self.name - }) - + frappe.db.delete("Scheduled Job Log", {"scheduled_job_type": self.name}) + + @frappe.whitelist() def execute_event(doc: str): frappe.only_for("System Manager") diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 65a48fb39e..5d799f8ee9 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -18,6 +18,7 @@ from frappe.rate_limiter import rate_limit from frappe.utils.background_jobs import enqueue from frappe.core.doctype.user_type.user_type import user_linked_with_permission_on_doctype + STANDARD_USERS = ("Guest", "Administrator") @@ -366,6 +367,7 @@ class User(Document): if getattr(frappe.local, "login_manager", None): frappe.local.login_manager.logout(user=self.name) + # delete todos frappe.db.delete("ToDo", {"owner": self.name}) frappe.db.sql("""UPDATE `tabToDo` SET `assigned_by`=NULL WHERE `assigned_by`=%s""", (self.name,)) diff --git a/frappe/core/doctype/user_permission/test_user_permission.py b/frappe/core/doctype/user_permission/test_user_permission.py index c3d593ee3b..1e48de334b 100644 --- a/frappe/core/doctype/user_permission/test_user_permission.py +++ b/frappe/core/doctype/user_permission/test_user_permission.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, Frappe Technologies and Contributors -# See license.txt +# Copyright (c) 2021, Frappe Technologies and Contributors +# See LICENSE from frappe.core.doctype.user_permission.user_permission import add_user_permissions, remove_applicable from frappe.permissions import has_user_permission from frappe.core.doctype.doctype.test_doctype import new_doctype diff --git a/frappe/core/doctype/user_permission/user_permission.py b/frappe/core/doctype/user_permission/user_permission.py index de242efe10..5201ffef8d 100644 --- a/frappe/core/doctype/user_permission/user_permission.py +++ b/frappe/core/doctype/user_permission/user_permission.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, Frappe Technologies and contributors +# Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt import frappe, json @@ -179,14 +178,16 @@ def check_applicable_doc_perm(user, doctype, docname): @frappe.whitelist() def clear_user_permissions(user, for_doctype): - frappe.only_for('System Manager') - total = frappe.db.count('User Permission', filters = dict(user=user, allow=for_doctype)) + frappe.only_for("System Manager") + total = frappe.db.count("User Permission", {"user": user, "allow": for_doctype}) + if total: frappe.db.delete("User Permission", { + "allow": for_doctype, "user": user, - "allow": for_doctype }) frappe.clear_cache() + return total @frappe.whitelist() @@ -228,37 +229,35 @@ def insert_user_perm(user, doctype, docname, is_default=0, hide_descendants=0, a user_perm.is_default = is_default user_perm.hide_descendants = hide_descendants if applicable: - user_perm.applicable_for = applicable + user_perm.applicable_for = applicable user_perm.apply_to_all_doctypes = 0 else: user_perm.apply_to_all_doctypes = 1 user_perm.insert() def remove_applicable(perm_applied_docs, user, doctype, docname): - for applicable_for in perm_applied_docs: frappe.db.delete("User Permission", { - "user": user, "applicable_for": applicable_for, + "for_value": docname, "allow": doctype, - "for_value": docname + "user": user, }) -def remove_apply_to_all(user, doctype, docname): +def remove_apply_to_all(user, doctype, docname): frappe.db.delete("User Permission", { - "user": user, "apply_to_all_doctypes": 1, + "for_value": docname, "allow": doctype, - "for_value": docname + "user": user, }) def update_applicable(already_applied, to_apply, user, doctype, docname): for applied in already_applied: if applied not in to_apply: - frappe.db.delete("User Permission", { - "user": user, "applicable_for": applied, + "for_value": docname, "allow": doctype, - "for_value": docname + "user": user, }) diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index 5431511d42..8de194fb00 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -1,5 +1,5 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See LICENSE """ Customize Form is a Single DocType used to mask the Property Setter @@ -18,13 +18,12 @@ from frappe.custom.doctype.property_setter.property_setter import delete_propert from frappe.model.docfield import supports_translation from frappe.core.doctype.doctype.doctype import validate_series + class CustomizeForm(Document): def on_update(self): - frappe.db.delete("Singles", { - "doctype": "Customize Form" - }) + frappe.db.delete("Singles", {"doctype": "Customize Form"}) frappe.db.delete("Customize Form Field") - + @frappe.whitelist() def fetch_to_customize(self): self.clear_existing_doc() diff --git a/frappe/desk/doctype/desktop_icon/desktop_icon.py b/frappe/desk/doctype/desktop_icon/desktop_icon.py index 283ffad967..28c5a670cb 100644 --- a/frappe/desk/doctype/desktop_icon/desktop_icon.py +++ b/frappe/desk/doctype/desktop_icon/desktop_icon.py @@ -197,9 +197,7 @@ def set_desktop_icons(visible_list, ignore_duplicate=True): # clear all custom only if setup is not complete if not int(frappe.defaults.get_defaults().setup_complete or 0): - frappe.db.delete("Desktop Icon", { - "standard": 0 - }) + frappe.db.delete("Desktop Icon", {"standard": 0}) # set standard as blocked and hidden if setting first active domain if not frappe.flags.keep_desktop_icons: diff --git a/frappe/desk/doctype/event/event.py b/frappe/desk/doctype/event/event.py index 2182ec4c9f..e7e7be530b 100644 --- a/frappe/desk/doctype/event/event.py +++ b/frappe/desk/doctype/event/event.py @@ -338,12 +338,8 @@ def delete_events(ref_type, ref_name, delete_event=False): total_participants = frappe.get_all("Event Participants", filters={"parenttype": "Event", "parent": participation.parent}) if len(total_participants) <= 1: - frappe.db.delete("Event", { - "name": participation.parent - }) - frappe.db.delete("Event Participants", { - "name": participation.name - }) + frappe.db.delete("Event", {"name": participation.parent}) + frappe.db.delete("Event Participants", {"name": participation.name}) # Close events if ends_on or repeat_till is less than now_datetime def set_status_of_events(): diff --git a/frappe/desk/doctype/route_history/route_history.py b/frappe/desk/doctype/route_history/route_history.py index 4303b7554d..a179119861 100644 --- a/frappe/desk/doctype/route_history/route_history.py +++ b/frappe/desk/doctype/route_history/route_history.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies and contributors +# Copyright (c) 2021, Frappe Technologies and contributors # For license information, please see license.txt import frappe @@ -8,6 +7,7 @@ from frappe.model.document import Document class RouteHistory(Document): pass + def flush_old_route_records(): """Deletes all route records except last 500 records per user""" diff --git a/frappe/desk/doctype/tag/tag.py b/frappe/desk/doctype/tag/tag.py index 626a2db085..2341d721e2 100644 --- a/frappe/desk/doctype/tag/tag.py +++ b/frappe/desk/doctype/tag/tag.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt @@ -124,7 +123,7 @@ def delete_tags_for_document(doc): return frappe.db.delete("Tag Link", { - "document_type": doc.doctype, + "document_type": doc.doctype, "document_name": doc.name }) diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 6b38b383bf..79b41936c3 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -151,11 +151,12 @@ def delete_fields(args_dict, delete=0): fields = args_dict[dt] if not fields: continue - + frappe.db.delete("DocField", { "parent": dt, - "fieldname": ("in", fields) + "fieldname": ("in", fields), }) + # Delete the data/column only if delete is specified if not delete: continue @@ -163,7 +164,7 @@ def delete_fields(args_dict, delete=0): if frappe.db.get_value("DocType", dt, "issingle"): frappe.db.delete("Singles", { "doctype": dt, - "field": ("in", fields) + "field": ("in", fields), }) else: existing_fields = frappe.db.multisql({ diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 44e675f3c5..fbbf1a4852 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -64,24 +64,14 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa update_flags(doc, flags, ignore_permissions) check_permission_and_not_submitted(doc) - frappe.db.delete("Custom Field", { - "dt": name - }) - frappe.db.delete("Client Script", { - "dt": name - }) - frappe.db.delete("Property Setter", { - "doc_type": name - }) - frappe.db.delete("Report", { - "ref_doctype": name - }) - frappe.db.delete("Custom DocPerm", { - "parent": name - }) - frappe.db.delete("__global_search", { - "doctype": name - }) + + frappe.db.delete("Custom Field", {"dt": name}) + frappe.db.delete("Client Script", {"dt": name}) + frappe.db.delete("Property Setter", {"doc_type": name}) + frappe.db.delete("Report", {"ref_doctype": name}) + frappe.db.delete("Custom DocPerm", {"parent": name}) + frappe.db.delete("__global_search", {"doctype": name}) + delete_from_table(doctype, name, ignore_doctypes, None) if frappe.conf.developer_mode and not doc.custom and not ( @@ -172,13 +162,9 @@ def update_naming_series(doc): def delete_from_table(doctype, name, ignore_doctypes, doc): if doctype!="DocType" and doctype==name: - frappe.db.delete("Singles", { - "doctype": name - }) + frappe.db.delete("Singles", {"doctype": name}) else: - frappe.db.delete(doctype, { - "name": name - }) + frappe.db.delete(doctype, {"name": name}) # get child tables if doc: tables = [d.options for d in doc.meta.get_table_fields()] @@ -356,7 +342,7 @@ def clear_timeline_references(link_doctype, link_name): "link_doctype": link_doctype, "link_name": link_name }) - + def insert_feed(doc): if ( frappe.flags.in_install diff --git a/frappe/patches/v11_0/apply_customization_to_custom_doctype.py b/frappe/patches/v11_0/apply_customization_to_custom_doctype.py index c01db50d5e..7e84c5ae24 100644 --- a/frappe/patches/v11_0/apply_customization_to_custom_doctype.py +++ b/frappe/patches/v11_0/apply_customization_to_custom_doctype.py @@ -28,9 +28,8 @@ def execute(): for prop in property_setters: property_setter_map[prop.field_name] = prop - frappe.db.delete("Property Setter", { - "name": prop.name - }) + frappe.db.delete("Property Setter", {"name": prop.name}) + meta = frappe.get_meta(doctype.name) for df in meta.fields: @@ -51,7 +50,6 @@ def execute(): df = frappe.new_doc('DocField', meta, 'fields') df.update(cf) meta.fields.append(df) - frappe.db.delete("Custom Field", { - "name": cf.name - }) + frappe.db.delete("Custom Field", {"name": cf.name}) + meta.save() diff --git a/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py b/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py index 5c949fbb1d..901ab66bfd 100644 --- a/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py +++ b/frappe/patches/v11_0/sync_stripe_settings_before_migrate.py @@ -17,6 +17,4 @@ def execute(): settings.secret_key = secret_key settings.save(ignore_permissions=True) - frappe.db.delete("Singles", { - "doctype": "Stripe Settings" - }) \ No newline at end of file + frappe.db.delete("Singles", {"doctype": "Stripe Settings"}) diff --git a/frappe/patches/v12_0/delete_feedback_request_if_exists.py b/frappe/patches/v12_0/delete_feedback_request_if_exists.py index bc3c7b8f97..c1bf46b14a 100644 --- a/frappe/patches/v12_0/delete_feedback_request_if_exists.py +++ b/frappe/patches/v12_0/delete_feedback_request_if_exists.py @@ -2,6 +2,4 @@ import frappe def execute(): - frappe.db.delete("DocType", { - "name": "Feedback Request" - }) \ No newline at end of file + frappe.db.delete("DocType", {"name": "Feedback Request"}) diff --git a/frappe/patches/v12_0/set_primary_key_in_series.py b/frappe/patches/v12_0/set_primary_key_in_series.py index e8d3abdde1..b2139f63b6 100644 --- a/frappe/patches/v12_0/set_primary_key_in_series.py +++ b/frappe/patches/v12_0/set_primary_key_in_series.py @@ -11,6 +11,7 @@ def execute(): name having count(name) > 1 ''', as_dict=True) + for row in duplicate_keys: frappe.db.delete("Series", { "name": row.name @@ -18,4 +19,5 @@ def execute(): if row.current: frappe.db.sql('insert into `tabSeries`(`name`, `current`) values (%(name)s, %(current)s)', row) frappe.db.commit() + frappe.db.sql('ALTER table `tabSeries` ADD PRIMARY KEY IF NOT EXISTS (name)') diff --git a/frappe/patches/v12_0/setup_comments_from_communications.py b/frappe/patches/v12_0/setup_comments_from_communications.py index b72d02720e..11e02965f1 100644 --- a/frappe/patches/v12_0/setup_comments_from_communications.py +++ b/frappe/patches/v12_0/setup_comments_from_communications.py @@ -31,4 +31,4 @@ def execute(): # clean up frappe.db.delete("Communication", { "communication_type": "Comment" - }) \ No newline at end of file + }) diff --git a/frappe/patches/v13_0/remove_twilio_settings.py b/frappe/patches/v13_0/remove_twilio_settings.py index 8b7e5e02c2..7efaf876e2 100644 --- a/frappe/patches/v13_0/remove_twilio_settings.py +++ b/frappe/patches/v13_0/remove_twilio_settings.py @@ -19,4 +19,4 @@ def execute(): def twilio_settings_doctype_in_integrations() -> bool: """Check Twilio Settings doctype exists in integrations module or not. """ - return frappe.db.exists("DocType", {'name': 'Twilio Settings', 'module': 'Integrations'}) \ No newline at end of file + return frappe.db.exists("DocType", {'name': 'Twilio Settings', 'module': 'Integrations'}) diff --git a/frappe/permissions.py b/frappe/permissions.py index 82f27452fb..33aef4ab41 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -7,11 +7,11 @@ import frappe.share from frappe import _, msgprint from frappe.utils import cint + rights = ("select", "read", "write", "create", "delete", "submit", "cancel", "amend", "print", "email", "report", "import", "export", "set_user_permissions", "share") - def check_admin_or_system_manager(user=None): if not user: user = frappe.session.user diff --git a/frappe/sessions.py b/frappe/sessions.py index 0202931e70..4f769ea88f 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -76,6 +76,7 @@ def get_sessions_to_clear(user=None, keep_current=False, device=None): def delete_session(sid=None, user=None, reason="Session Expired"): from frappe.core.doctype.activity_log.feed import logout_feed + frappe.cache().hdel("session", sid) frappe.cache().hdel("last_db_session_update", sid) if sid and not user: diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py index 54103f0151..f687f70228 100644 --- a/frappe/tests/test_commands.py +++ b/frappe/tests/test_commands.py @@ -433,6 +433,6 @@ class TestCommands(BaseTestCommands): for output in ["legacy", "plain", "table", "json"]: self.execute(f"bench version -f {output}") self.assertEqual(self.returncode, 0) - + self.execute("bench version -f invalid") self.assertEqual(self.returncode, 2) diff --git a/frappe/utils/error.py b/frappe/utils/error.py index d83cbb49ea..05b578d7e8 100644 --- a/frappe/utils/error.py +++ b/frappe/utils/error.py @@ -176,9 +176,9 @@ def collect_error_snapshots(): def clear_old_snapshots(): """Clear snapshots that are older than a month""" - + frappe.db.sql("""delete from `tabError Snapshot` - where creation < (NOW() - INTERVAL '1' MONTH)""") + where creation < (NOW() - INTERVAL '1' MONTH)""") path = get_error_snapshot_path() today = datetime.datetime.now() diff --git a/frappe/utils/testutils.py b/frappe/utils/testutils.py index 5c5bfa7976..9a2b2da791 100644 --- a/frappe/utils/testutils.py +++ b/frappe/utils/testutils.py @@ -12,7 +12,5 @@ def add_custom_field(doctype, fieldname, fieldtype='Data', options=None): }).insert() def clear_custom_fields(doctype): - frappe.db.delete("Custom Field", { - "dt": doctype - }) + frappe.db.delete("Custom Field", {"dt": doctype}) frappe.clear_cache(doctype=doctype) diff --git a/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py b/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py index 54d2c2e446..63ba96d138 100644 --- a/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py +++ b/frappe/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py @@ -330,6 +330,7 @@ def remove_unverified_record(): AND `creation` < (NOW() - INTERVAL '7' DAY)""" ) + @frappe.whitelist(allow_guest=True) def confirm_deletion(email, name, host_name): if not verify_request(): diff --git a/frappe/workflow/doctype/workflow_action/workflow_action.py b/frappe/workflow/doctype/workflow_action/workflow_action.py index e3b89ba0e5..5eedc27d9c 100644 --- a/frappe/workflow/doctype/workflow_action/workflow_action.py +++ b/frappe/workflow/doctype/workflow_action/workflow_action.py @@ -139,7 +139,7 @@ def clear_old_workflow_actions(doc, user=None): "user": ("!=", user), "status": "Open" }) - + def update_completed_workflow_actions(doc, user=None): user = user if user else frappe.session.user frappe.db.sql("""UPDATE `tabWorkflow Action` SET `status`='Completed', `completed_by`=%s From a836b2de0c041fe17ca2530a5bf856719ad4f51c Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 28 Jul 2021 18:57:51 +0530 Subject: [PATCH 464/495] perf: Use truncate instead of drop Changed DML DROP statements to use DDL TRUNCATE for better performance wherever possible --- frappe/core/doctype/error_log/error_log.py | 2 +- frappe/utils/install.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/core/doctype/error_log/error_log.py b/frappe/core/doctype/error_log/error_log.py index 081b4e879d..3d66253b08 100644 --- a/frappe/core/doctype/error_log/error_log.py +++ b/frappe/core/doctype/error_log/error_log.py @@ -20,4 +20,4 @@ def set_old_logs_as_seen(): def clear_error_logs(): '''Flush all Error Logs''' frappe.only_for('System Manager') - frappe.db.delete("Error Log") \ No newline at end of file + frappe.db.truncate("Error Log") diff --git a/frappe/utils/install.py b/frappe/utils/install.py index e6d4386ebe..3d6a2fed97 100644 --- a/frappe/utils/install.py +++ b/frappe/utils/install.py @@ -111,9 +111,9 @@ def before_tests(): # don't run before tests if any other app is installed return - frappe.db.delete("Custom Field") - frappe.db.delete("Event") - frappe.db.commit() + frappe.db.truncate("Custom Field") + frappe.db.truncate("Event") + frappe.clear_cache() # complete setup if missing From 7dac03cea01f534ccaf3df01d34ffc71ecca4d05 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 28 Jul 2021 18:59:18 +0530 Subject: [PATCH 465/495] fix: Re-introduce code erreneously taken out in previous commits Due to bulk updations, some statements were missed out/got deleted. This commit re-introduces them. --- frappe/core/doctype/report/test_report.py | 2 +- .../doctype/user_permission/test_user_permission.py | 12 +++++++----- frappe/desk/doctype/route_history/route_history.py | 11 +++++------ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py index 1bf9893bd7..9c953db1f0 100644 --- a/frappe/core/doctype/report/test_report.py +++ b/frappe/core/doctype/report/test_report.py @@ -81,12 +81,12 @@ class TestReport(unittest.TestCase): self.assertDictEqual({'name': 'Administrator', 'user_type': 'System User', 'email': 'admin@example.com'}, admin_dict) def test_report_permissions(self): - frappe.set_user('test@example.com') frappe.db.delete("Has Role", { "parent": frappe.session.user, "role": "Test Has Role" }) + frappe.db.commit() if not frappe.db.exists('Role', 'Test Has Role'): role = frappe.get_doc({ 'doctype': 'Role', diff --git a/frappe/core/doctype/user_permission/test_user_permission.py b/frappe/core/doctype/user_permission/test_user_permission.py index 1e48de334b..85db846982 100644 --- a/frappe/core/doctype/user_permission/test_user_permission.py +++ b/frappe/core/doctype/user_permission/test_user_permission.py @@ -9,13 +9,15 @@ import unittest class TestUserPermission(unittest.TestCase): def setUp(self): - + test_users = ( + "test_bulk_creation_update@example.com", + "test_user_perm1@example.com", + "nested_doc_user@example.com", + ) frappe.db.delete("User Permission", { - "user": ("in", ("test_bulk_creation_update@example.com", - "test_user_perm1@example.com", - "nested_doc_user@example.com")) + "user": ("in", test_users) }) - + frappe.delete_doc_if_exists("DocType", "Person") frappe.db.sql_ddl("DROP TABLE IF EXISTS `tabPerson`") frappe.delete_doc_if_exists("DocType", "Doc A") frappe.db.sql_ddl("DROP TABLE IF EXISTS `tabDoc A`") diff --git a/frappe/desk/doctype/route_history/route_history.py b/frappe/desk/doctype/route_history/route_history.py index a179119861..95872440c7 100644 --- a/frappe/desk/doctype/route_history/route_history.py +++ b/frappe/desk/doctype/route_history/route_history.py @@ -24,15 +24,14 @@ def flush_old_route_records(): for user in users: user = user[0] last_record_to_keep = frappe.db.get_all('Route History', - filters={ - 'user': user, - }, + filters={'user': user}, limit=1, limit_start=500, fields=['modified'], - order_by='modified desc') + order_by='modified desc' + ) frappe.db.delete("Route History", { - "modified": last_record_to_keep[0].modified, + "modified": ("<=", last_record_to_keep[0].modified), "user": user - }) \ No newline at end of file + }) From 3ca7fa77967a49b1d391cecd01e33a45d4e42012 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 27 Jul 2021 01:12:19 +0530 Subject: [PATCH 466/495] refactor(minor): Make frappe.db.delete DRY-er --- frappe/database/database.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index 8b4fd3ad9e..ee2d062a81 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -958,20 +958,17 @@ class Database(object): Doctype name can be passed directly, it will be pre-pended with `tab`. """ - if kwargs: - filters = filters or kwargs.get("conditions") + values = () + filters = filters or kwargs.get("conditions") + table = doctype if doctype.startswith("__") else f"tab{doctype}" + query = f"DELETE FROM `{table}`" if "debug" not in kwargs: kwargs["debug"] = debug - if not filters: - table = doctype if doctype.startswith("__") else f"tab{doctype}" - query = f"DELETE FROM `{table}`" - return self.sql(query, **kwargs) - - table = doctype if doctype.startswith("__") else f"tab{doctype}" - conditions, values = self.build_conditions(filters) - query = f"DELETE FROM `{table}` WHERE {conditions}" + if filters: + conditions, values = self.build_conditions(filters) + query = f"{query} WHERE {conditions}" return self.sql(query, values, **kwargs) From d63affc73251cd565bdc5e232eafd4a77f20a6a0 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 27 Jul 2021 02:12:19 +0530 Subject: [PATCH 467/495] refactor(minor): Use ORM instead of raw delete queries Modified query building for "IN" statements, as well as older condition builders to use frappe.db.delete --- frappe/defaults.py | 23 +++++++------------ frappe/desk/doctype/todo/todo.py | 13 +++++++---- frappe/email/queue.py | 10 ++------ .../v12_0/set_primary_key_in_series.py | 3 ++- 4 files changed, 20 insertions(+), 29 deletions(-) diff --git a/frappe/defaults.py b/frappe/defaults.py index 794d30a0c8..d4c338388d 100644 --- a/frappe/defaults.py +++ b/frappe/defaults.py @@ -154,29 +154,23 @@ def clear_default(key=None, value=None, parent=None, name=None, parenttype=None) :param name: Default ID. :param parenttype: Clear defaults table for a particular type e.g. **User**. """ - conditions = [] - values = [] + filters = {} if name: - conditions.append("name=%s") - values.append(name) + filters.update({"name": name}) else: if key: - conditions.append("defkey=%s") - values.append(key) + filters.update({"defkey": key}) if value: - conditions.append("defvalue=%s") - values.append(value) + filters.update({"defvalue": value}) if parent: - conditions.append("parent=%s") - values.append(parent) + filters.update({"parent": parent}) if parenttype: - conditions.append("parenttype=%s") - values.append(parenttype) + filters.update({"parenttype": parenttype}) if parent: clear_defaults_cache(parent) @@ -184,11 +178,10 @@ def clear_default(key=None, value=None, parent=None, name=None, parenttype=None) clear_defaults_cache("__default") clear_defaults_cache("__global") - if not conditions: + if not filters: raise Exception("[clear_default] No key specified.") - frappe.db.sql("""delete from tabDefaultValue where {0}""".format(" and ".join(conditions)), - tuple(values)) + frappe.db.delete("DefaultValue", filters) _clear_cache(parent) diff --git a/frappe/desk/doctype/todo/todo.py b/frappe/desk/doctype/todo/todo.py index 34fb3bb905..01eed41b4f 100644 --- a/frappe/desk/doctype/todo/todo.py +++ b/frappe/desk/doctype/todo/todo.py @@ -39,11 +39,7 @@ class ToDo(Document): self.update_in_reference() def on_trash(self): - # unlink todo from linked comments - frappe.db.delete("Communication Link", { - "link_doctype": self.doctype, - "link_name": self.name - }) + self.delete_communication_links() self.update_in_reference() def add_assign_comment(self, text, comment_type): @@ -52,6 +48,13 @@ class ToDo(Document): frappe.get_doc(self.reference_type, self.reference_name).add_comment(comment_type, text) + def delete_communication_links(self): + # unlink todo from linked comments + return frappe.db.delete("Communication Link", { + "link_doctype": self.doctype, + "link_name": self.name + }) + def update_in_reference(self): if not (self.reference_type and self.reference_name): return diff --git a/frappe/email/queue.py b/frappe/email/queue.py index 40f1c7be3a..ef59302bab 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -173,14 +173,8 @@ def clear_outbox(days=None): WHERE `priority`=0 AND `modified` < (NOW() - INTERVAL '{0}' DAY)""".format(days)) if email_queues: - # TODO: email_queues IN frappe.db.sql - frappe.db.sql("""DELETE FROM `tabEmail Queue` WHERE `name` IN ({0})""".format( - ','.join(['%s']*len(email_queues) - )), tuple(email_queues)) - - frappe.db.sql("""DELETE FROM `tabEmail Queue Recipient` WHERE `parent` IN ({0})""".format( - ','.join(['%s']*len(email_queues) - )), tuple(email_queues)) + frappe.db.delete("Email Queue", {"name": ("in", email_queues)}) + frappe.db.delete("Email Queue Recipient", {"parent": ("in", email_queues)}) def set_expiry_for_email_queue(): ''' Mark emails as expire that has not sent for 7 days. diff --git a/frappe/patches/v12_0/set_primary_key_in_series.py b/frappe/patches/v12_0/set_primary_key_in_series.py index b2139f63b6..83a903fc2d 100644 --- a/frappe/patches/v12_0/set_primary_key_in_series.py +++ b/frappe/patches/v12_0/set_primary_key_in_series.py @@ -2,7 +2,8 @@ import frappe def execute(): #if current = 0, simply delete the key as it'll be recreated on first entry - frappe.db.sql('delete from `tabSeries` where current = 0') + frappe.db.delete("Series", {"current": 0}) + duplicate_keys = frappe.db.sql(''' SELECT name, max(current) as current from From dfccae524618cd56b91e6da9f1091ea933ef6816 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 29 Jul 2021 02:12:19 +0530 Subject: [PATCH 468/495] perf(minor): Permission Manager remove API Delete using the filters directly instead of selecting rows in one query and deleting those rows in another. DBMS would have to scan the table twice prior --- frappe/core/page/permission_manager/permission_manager.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/frappe/core/page/permission_manager/permission_manager.py b/frappe/core/page/permission_manager/permission_manager.py index af8973811a..2a99283dda 100644 --- a/frappe/core/page/permission_manager/permission_manager.py +++ b/frappe/core/page/permission_manager/permission_manager.py @@ -110,11 +110,9 @@ def remove(doctype, role, permlevel): frappe.only_for("System Manager") setup_custom_perms(doctype) - name = frappe.get_value('Custom DocPerm', dict(parent=doctype, role=role, permlevel=permlevel)) - frappe.db.delete("Custom DocPerm", { - "name": name - }) - if not frappe.get_all('Custom DocPerm', dict(parent=doctype)): + frappe.db.delete("Custom DocPerm", {"parent": doctype, "role": role, "permlevel": permlevel}) + + if not frappe.get_all('Custom DocPerm', {"parent": doctype}): frappe.throw(_('There must be atleast one permission rule.'), title=_('Cannot Remove')) validate_permissions_for_doctype(doctype, for_remove=True, alert=True) From b40721de4b488025d484fa8b9abe5217778dd60e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 29 Jul 2021 12:53:43 +0530 Subject: [PATCH 469/495] style: Remove extra whitespace --- frappe/database/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index ee2d062a81..2070ba676b 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -968,7 +968,7 @@ class Database(object): if filters: conditions, values = self.build_conditions(filters) - query = f"{query} WHERE {conditions}" + query = f"{query} WHERE {conditions}" return self.sql(query, values, **kwargs) From 5bb01511aa3e30aa7994ca2dfac0cf0d266d13e6 Mon Sep 17 00:00:00 2001 From: shariquerik Date: Thu, 29 Jul 2021 17:50:43 +0530 Subject: [PATCH 470/495] fix: new-doc-1 not found error --- frappe/public/js/frappe/form/dashboard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/dashboard.js b/frappe/public/js/frappe/form/dashboard.js index b2b0c11d54..420704149b 100644 --- a/frappe/public/js/frappe/form/dashboard.js +++ b/frappe/public/js/frappe/form/dashboard.js @@ -204,7 +204,7 @@ frappe.ui.form.Dashboard = class FormDashboard { $(this).removeClass('hidden'); } }); - this.set_open_count(); + !this.frm.is_new() && this.set_open_count(); } init_data() { From ff973477a3e429ac82d8e5cb95ea0377b48b80be Mon Sep 17 00:00:00 2001 From: leela Date: Thu, 29 Jul 2021 20:32:03 +0530 Subject: [PATCH 471/495] fix: Use bench path as default bench_id --- frappe/tests/test_background_jobs.py | 6 +++--- frappe/utils/__init__.py | 2 +- frappe/utils/background_jobs.py | 20 ++++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/frappe/tests/test_background_jobs.py b/frappe/tests/test_background_jobs.py index 48e0dd2ee9..188f3e166f 100644 --- a/frappe/tests/test_background_jobs.py +++ b/frappe/tests/test_background_jobs.py @@ -4,7 +4,7 @@ from rq import Queue import frappe from frappe.core.page.background_jobs.background_jobs import remove_failed_jobs -from frappe.utils.background_jobs import get_redis_conn, rename_queue +from frappe.utils.background_jobs import get_redis_conn, generate_qname import time @@ -17,14 +17,14 @@ class TestBackgroundJobs(unittest.TestCase): queues = Queue.all(conn) for queue in queues: - if queue.name == rename_queue("short"): + if queue.name == generate_qname("short"): fail_registry = queue.failed_job_registry self.assertGreater(fail_registry.count, 0) remove_failed_jobs() for queue in queues: - if queue.name == rename_queue("short"): + if queue.name == generate_qname("short"): fail_registry = queue.failed_job_registry self.assertEqual(fail_registry.count, 0) diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 80c6cda98c..68366eb234 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -384,7 +384,7 @@ def get_bench_path(): return os.path.realpath(os.path.join(os.path.dirname(frappe.__file__), '..', '..', '..')) def get_bench_id(): - return frappe.get_conf().get('bench_id', 'DefaultBench') + return frappe.get_conf().get('bench_id', get_bench_path().strip('/').replace('/', '-')) def get_site_id(site=None): return f"{site or frappe.local.site}@{get_bench_id()}" diff --git a/frappe/utils/background_jobs.py b/frappe/utils/background_jobs.py index 4241c95c5d..f0bd06aff4 100755 --- a/frappe/utils/background_jobs.py +++ b/frappe/utils/background_jobs.py @@ -145,7 +145,7 @@ def start_worker(queue=None, quiet = False, rq_username=None, rq_password=None): # empty init is required to get redis_queue from common_site_config.json redis_connection = get_redis_conn(username=rq_username, password=rq_password) queues = get_queue_list(queue, build_queue_name=True) - queue_name = queue and rename_queue(queue) + queue_name = queue and generate_qname(queue) if os.environ.get('CI'): setup_loghandlers('ERROR') @@ -206,7 +206,7 @@ def get_queue_list(queue_list=None, build_queue_name=False): validate_queue(queue, default_queue_list) else: queue_list = default_queue_list - return [rename_queue(q) for q in queue_list] if build_queue_name else queue_list + return [generate_qname(qtype) for qtype in queue_list] if build_queue_name else queue_list def get_workers(queue): '''Returns a list of Worker objects tied to a queue object''' @@ -222,10 +222,10 @@ def get_running_jobs_in_queue(queue): jobs.append(current_job) return jobs -def get_queue(queue, is_async=True): +def get_queue(qtype, is_async=True): '''Returns a Queue object tied to a redis connection''' - validate_queue(queue) - return Queue(rename_queue(queue), connection=get_redis_conn(), is_async=is_async) + validate_queue(qtype) + return Queue(generate_qname(qtype), connection=get_redis_conn(), is_async=is_async) def validate_queue(queue, default_queue_list=None): if not default_queue_list: @@ -274,17 +274,17 @@ def get_queues() -> List[Queue]: queues = Queue.all(connection=get_redis_conn()) return [q for q in queues if is_queue_accessible(q)] -def rename_queue(qname: str) -> str: - """Rename qname by adding bench name as prefix. +def generate_qname(qtype: str) -> str: + """Generate qname by combining bench ID and queue type. - Renamed queues are useful to define namespaces of customers. + qnames are useful to define namespaces of customers. """ - return f"{get_bench_id()}:{qname}" + return f"{get_bench_id()}:{qtype}" def is_queue_accessible(qobj: Queue) -> bool: """Checks whether queue is relate to current bench or not. """ - accessible_queues = [rename_queue(q) for q in list(queue_timeout)] + accessible_queues = [generate_qname(q) for q in list(queue_timeout)] return qobj.name in accessible_queues def enqueue_test_job(): From eb9d2bcd649b6dba7e764ae5f34a61c8b0c51d9c Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Thu, 15 Jul 2021 01:21:31 +0530 Subject: [PATCH 472/495] feat: Query builder --- frappe/__init__.py | 4 +++ frappe/query_builder/__init__.py | 1 + frappe/query_builder/qb.py | 53 ++++++++++++++++++++++++++++++++ requirements.txt | 1 + 4 files changed, 59 insertions(+) create mode 100644 frappe/query_builder/__init__.py create mode 100644 frappe/query_builder/qb.py diff --git a/frappe/__init__.py b/frappe/__init__.py index 1c978945c7..39e21efb9e 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -28,6 +28,8 @@ from .exceptions import * from .utils.jinja import (get_jenv, get_template, render_template, get_email_from_template, get_jloader) from .utils.lazy_loader import lazy_import +from frappe.query_builder import query_builder + # Lazy imports faker = lazy_import('faker') @@ -118,6 +120,7 @@ def set_user_lang(user, user_language=None): # local-globals db = local("db") +qb = local("qb") conf = local("conf") form = form_dict = local("form_dict") request = local("request") @@ -202,6 +205,7 @@ def init(site, sites_path=None, new_site=False): local.form_dict = _dict() local.session = _dict() local.dev_server = _dev_server + local.qb = query_builder(local.conf.db_type) setup_module_map() diff --git a/frappe/query_builder/__init__.py b/frappe/query_builder/__init__.py new file mode 100644 index 0000000000..da1748beec --- /dev/null +++ b/frappe/query_builder/__init__.py @@ -0,0 +1 @@ +from frappe.query_builder.qb import qb as query_builder diff --git a/frappe/query_builder/qb.py b/frappe/query_builder/qb.py new file mode 100644 index 0000000000..6fa54661e8 --- /dev/null +++ b/frappe/query_builder/qb.py @@ -0,0 +1,53 @@ +from pypika import MySQLQuery, Order, PostgreSQLQuery +from pypika import functions as fn +from pypika import terms +from pypika.queries import Schema, Table + +def qb(db_type): + if not db_type: + db_type = "mariadb" + selecter = {"mariadb": MariaDB, "postgres": Postgres} + return selecter[db_type] + +class common: + fn = fn + terms = terms + desc = Order.desc + Schema = Schema + +class MariaDB(MySQLQuery,common): + Field = terms.Field + + def __init__(self) -> None: + super().__init__() + + @classmethod + def from_(cls, class_name, *args, **kwargs): + if isinstance(class_name,str): + class_name = "tab"+class_name + return super().from_(class_name, *args, **kwargs) + +class Postgres(PostgreSQLQuery,common): + postgres_field = {"table_name": "relname", "table_rows": "n_tup_ins"} + information_schema_translation = {"tables": "pg_stat_all_tables"} + + def __init__(self) -> None: + super().__init__() + + @classmethod + def Field(cls, fieldName, *args, **kwargs): + if fieldName in cls.postgres_field: + fieldName = cls.postgres_field[fieldName] + return terms.Field(fieldName, *args, **kwargs) + + @classmethod + def from_(cls, class_name, *args, **kwargs): + if isinstance(class_name, Table): + if class_name._schema: + if class_name._schema._name == "information_schema": + class_name = cls.information_schema_translation[class_name._table_name] + + elif isinstance(class_name, str): + class_name = "tab" + class_name + + return super().from_(class_name, *args, **kwargs) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 0791f01b27..51327953d5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -49,6 +49,7 @@ pyngrok~=5.0.5 pyOpenSSL~=20.0.1 pyotp~=2.6.0 PyPDF2~=1.26.0 +PyPika~=0.48.6 pypng~=0.0.20 PyQRCode~=1.2.1 python-dateutil~=2.8.1 From 49105ad08a59e60c3b7fdde9705adcce42ff75f0 Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Thu, 15 Jul 2021 01:22:06 +0530 Subject: [PATCH 473/495] refactor: qb in build_table_count_cache --- frappe/cache_manager.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/frappe/cache_manager.py b/frappe/cache_manager.py index 9f09f26be8..9de05907f5 100644 --- a/frappe/cache_manager.py +++ b/frappe/cache_manager.py @@ -141,17 +141,13 @@ def build_table_count_cache(): return _cache = frappe.cache() - data = frappe.db.multisql({ - "mariadb": """ - SELECT table_name AS name, - table_rows AS count - FROM information_schema.tables""", - "postgres": """ - SELECT "relname" AS name, - "n_tup_ins" AS count - FROM "pg_stat_all_tables" - """ - }, as_dict=1) + name = frappe.qb.Field("table_name").as_("name") + count = frappe.qb.Field("table_rows").as_("count") + information_schema = frappe.qb.Schema("information_schema") + + q = frappe.qb.from_(information_schema.tables).select(name, count).get_sql() + + data = frappe.db.sql(q, as_dict=1) counts = {d.get('name').lstrip('tab'): d.get('count', None) for d in data} _cache.set_value("information_schema:counts", counts) From 8b82815ba65c6baff0b064e1ab2d3ec54c794804 Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Thu, 15 Jul 2021 01:22:27 +0530 Subject: [PATCH 474/495] refactor: qb in get_all_empty_tables_by_module --- frappe/config/__init__.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/frappe/config/__init__.py b/frappe/config/__init__.py index 62a877be24..8fe110d393 100644 --- a/frappe/config/__init__.py +++ b/frappe/config/__init__.py @@ -39,18 +39,13 @@ def get_modules_from_app(app): ) def get_all_empty_tables_by_module(): - empty_tables = set(r[0] for r in frappe.db.multisql({ - "mariadb": """ - SELECT table_name - FROM information_schema.tables - WHERE table_rows = 0 and table_schema = "{}" - """.format(frappe.conf.db_name), - "postgres": """ - SELECT "relname" as "table_name" - FROM "pg_stat_all_tables" - WHERE n_tup_ins = 0 - """ - })) + table_rows = frappe.qb.Field("table_rows") + table_name = frappe.qb.Field("table_name") + information_schema = frappe.qb.Schema("information_schema") + + q = frappe.qb.from_(information_schema.tables).select(table_name).where(table_rows == 0).get_sql() + + empty_tables = set(r[0] for r in frappe.db.sql(q)) results = frappe.get_all("DocType", fields=["name", "module"]) empty_tables_by_module = {} From a8222126e815142dd073b4437008ebcdcb8e70f1 Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Thu, 15 Jul 2021 01:24:16 +0530 Subject: [PATCH 475/495] refactor: qb in after_rename --- frappe/core/doctype/doctype/doctype.py | 5 +---- frappe/query_builder/qb.py | 10 +++++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 3cdc45ea08..22be912abc 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -396,10 +396,7 @@ class DocType(Document): frappe.db.sql("""update tabSingles set value=%s where doctype=%s and field='name' and value = %s""", (new, new, old)) else: - frappe.db.multisql({ - "mariadb": f"RENAME TABLE `tab{old}` TO `tab{new}`", - "postgres": f"ALTER TABLE `tab{old}` RENAME TO `tab{new}`" - }) + frappe.db.sql(frappe.qb.rename_table(old,new)) frappe.db.commit() # Do not rename and move files and folders for custom doctype diff --git a/frappe/query_builder/qb.py b/frappe/query_builder/qb.py index 6fa54661e8..2dce79eca3 100644 --- a/frappe/query_builder/qb.py +++ b/frappe/query_builder/qb.py @@ -26,6 +26,10 @@ class MariaDB(MySQLQuery,common): if isinstance(class_name,str): class_name = "tab"+class_name return super().from_(class_name, *args, **kwargs) + + @staticmethod + def rename_table(old_name, new_name): + return f"RENAME TABLE `tab{old_name}` TO `tab{new_name}`" class Postgres(PostgreSQLQuery,common): postgres_field = {"table_name": "relname", "table_rows": "n_tup_ins"} @@ -50,4 +54,8 @@ class Postgres(PostgreSQLQuery,common): elif isinstance(class_name, str): class_name = "tab" + class_name - return super().from_(class_name, *args, **kwargs) \ No newline at end of file + return super().from_(class_name, *args, **kwargs) + + @staticmethod + def rename_table(old_name, new_name): + return f"ALTER TABLE `tab{old_name}` RENAME TO `tab{new_name}`" From 43b0d31cf822b4607b5686e19ac9c45a55208204 Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Thu, 15 Jul 2021 01:26:01 +0530 Subject: [PATCH 476/495] refactor: qb in delete_fields --- frappe/model/__init__.py | 12 +----------- frappe/query_builder/qb.py | 8 ++++++++ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 79b41936c3..1f72272df1 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -167,17 +167,7 @@ def delete_fields(args_dict, delete=0): "field": ("in", fields), }) else: - existing_fields = frappe.db.multisql({ - "mariadb": "DESC `tab%s`" % dt, - "postgres": """ - SELECT - COLUMN_NAME - FROM - information_schema.COLUMNS - WHERE - TABLE_NAME = 'tab%s'; - """ % dt, - }) + existing_fields = frappe.db.sql(frappe.qb.DESC(dt)) existing_fields = existing_fields and [e[0] for e in existing_fields] or [] fields_need_to_delete = set(fields) & set(existing_fields) if not fields_need_to_delete: diff --git a/frappe/query_builder/qb.py b/frappe/query_builder/qb.py index 2dce79eca3..d085c01027 100644 --- a/frappe/query_builder/qb.py +++ b/frappe/query_builder/qb.py @@ -31,6 +31,10 @@ class MariaDB(MySQLQuery,common): def rename_table(old_name, new_name): return f"RENAME TABLE `tab{old_name}` TO `tab{new_name}`" + @staticmethod + def DESC(dt): + return f"DESC `tab{dt}`" + class Postgres(PostgreSQLQuery,common): postgres_field = {"table_name": "relname", "table_rows": "n_tup_ins"} information_schema_translation = {"tables": "pg_stat_all_tables"} @@ -59,3 +63,7 @@ class Postgres(PostgreSQLQuery,common): @staticmethod def rename_table(old_name, new_name): return f"ALTER TABLE `tab{old_name}` RENAME TO `tab{new_name}`" + + @staticmethod + def DESC(dt): + return f"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME = 'tab{dt}'" From 90cd4f708ca6b7fb353510bb70c466dce988a28f Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Thu, 15 Jul 2021 01:29:17 +0530 Subject: [PATCH 477/495] refactor: qb in patch v13 increase_password_length --- frappe/patches/v13_0/increase_password_length.py | 5 +---- frappe/query_builder/qb.py | 8 ++++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/frappe/patches/v13_0/increase_password_length.py b/frappe/patches/v13_0/increase_password_length.py index 1bb1979051..3f0e93e72c 100644 --- a/frappe/patches/v13_0/increase_password_length.py +++ b/frappe/patches/v13_0/increase_password_length.py @@ -1,7 +1,4 @@ import frappe def execute(): - frappe.db.multisql({ - "mariadb": "ALTER TABLE `__Auth` MODIFY `password` TEXT NOT NULL", - "postgres": 'ALTER TABLE "__Auth" ALTER COLUMN "password" TYPE TEXT' - }) + frappe.db.sql(frappe.qb.change_table_type(tb = "__Auth",col = "password",type = "TEXT")) diff --git a/frappe/query_builder/qb.py b/frappe/query_builder/qb.py index d085c01027..ddf514ab2c 100644 --- a/frappe/query_builder/qb.py +++ b/frappe/query_builder/qb.py @@ -35,6 +35,10 @@ class MariaDB(MySQLQuery,common): def DESC(dt): return f"DESC `tab{dt}`" + @staticmethod + def change_table_type(tb, col, type): + return f"ALTER TABLE `{tb}` MODIFY `{col}` {type} NOT NULL" + class Postgres(PostgreSQLQuery,common): postgres_field = {"table_name": "relname", "table_rows": "n_tup_ins"} information_schema_translation = {"tables": "pg_stat_all_tables"} @@ -67,3 +71,7 @@ class Postgres(PostgreSQLQuery,common): @staticmethod def DESC(dt): return f"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME = 'tab{dt}'" + + @staticmethod + def change_table_type(tb, col, type): + return f'ALTER TABLE "{tb}" ALTER COLUMN "{col}" TYPE {type}' From 2f7c78b266a09e27beaf418b42f47249a7e46170 Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Thu, 15 Jul 2021 02:00:50 +0530 Subject: [PATCH 478/495] refactor: qb in patch v12 set_correct_assign_value_in_docs --- .../v12_0/set_correct_assign_value_in_docs.py | 32 +++++++++---------- frappe/query_builder/custom_functions.py | 12 +++++++ frappe/query_builder/qb.py | 21 ++++++++---- 3 files changed, 42 insertions(+), 23 deletions(-) create mode 100644 frappe/query_builder/custom_functions.py diff --git a/frappe/patches/v12_0/set_correct_assign_value_in_docs.py b/frappe/patches/v12_0/set_correct_assign_value_in_docs.py index 65a635c170..72e71c8314 100644 --- a/frappe/patches/v12_0/set_correct_assign_value_in_docs.py +++ b/frappe/patches/v12_0/set_correct_assign_value_in_docs.py @@ -1,25 +1,23 @@ import frappe + def execute(): - frappe.reload_doc('desk', 'doctype', 'todo') + frappe.reload_doc("desk", "doctype", "todo") - query = ''' - SELECT - name, reference_type, reference_name, {} as assignees - FROM - `tabToDo` - WHERE - COALESCE(reference_type, '') != '' AND - COALESCE(reference_name, '') != '' AND - status != 'Cancelled' - GROUP BY - reference_type, reference_name - ''' + ToDo = frappe.qb.Table("ToDo") + assignees = frappe.qb.GROUP_CONCAT("owner").distinct().as_("assignees") - assignments = frappe.db.multisql({ - 'mariadb': query.format('GROUP_CONCAT(DISTINCT `owner`)'), - 'postgres': query.format('STRING_AGG(DISTINCT "owner", ",")') - }, as_dict=True) + q = ( + frappe.qb.from_(ToDo) + .select(ToDo.name, ToDo.reference_type, assignees) + .where(frappe.qb.fn.Coalesce(ToDo.reference_type, "") != "") + .where(frappe.qb.fn.Coalesce(ToDo.reference_name, "") != "") + .where(ToDo.status != "Cancelled") + .groupby(ToDo.reference_type, ToDo.reference_name) + .get_sql() + ) + + assignments = frappe.db.sql(q, as_dict=True) for doc in assignments: assignments = doc.assignees.split(',') diff --git a/frappe/query_builder/custom_functions.py b/frappe/query_builder/custom_functions.py new file mode 100644 index 0000000000..446a4df2d2 --- /dev/null +++ b/frappe/query_builder/custom_functions.py @@ -0,0 +1,12 @@ +from pypika import functions as fn + + + +class GROUP_CONCAT(fn.DistinctOptionFunction): + def __init__(self, col, alias=None): + super(GROUP_CONCAT, self).__init__("GROUP_CONCAT", col, alias=alias) + + +class STRING_AGG(fn.DistinctOptionFunction): + def __init__(self, col, val=",", alias=None): + super(STRING_AGG, self).__init__("STRING_AGG", col, val, alias=alias) diff --git a/frappe/query_builder/qb.py b/frappe/query_builder/qb.py index ddf514ab2c..fb92eed56b 100644 --- a/frappe/query_builder/qb.py +++ b/frappe/query_builder/qb.py @@ -2,6 +2,7 @@ from pypika import MySQLQuery, Order, PostgreSQLQuery from pypika import functions as fn from pypika import terms from pypika.queries import Schema, Table +from .custom_functions import GROUP_CONCAT, STRING_AGG def qb(db_type): if not db_type: @@ -14,19 +15,25 @@ class common: terms = terms desc = Order.desc Schema = Schema + @staticmethod + def Table(classname:str, *args, **kwargs): + if not classname.startswith("__"): + classname = "tab" + classname + return Table(classname, *args, **kwargs) -class MariaDB(MySQLQuery,common): +class MariaDB(common, MySQLQuery,): Field = terms.Field + GROUP_CONCAT = GROUP_CONCAT def __init__(self) -> None: super().__init__() - + @classmethod def from_(cls, class_name, *args, **kwargs): if isinstance(class_name,str): class_name = "tab"+class_name return super().from_(class_name, *args, **kwargs) - + @staticmethod def rename_table(old_name, new_name): return f"RENAME TABLE `tab{old_name}` TO `tab{new_name}`" @@ -39,9 +46,11 @@ class MariaDB(MySQLQuery,common): def change_table_type(tb, col, type): return f"ALTER TABLE `{tb}` MODIFY `{col}` {type} NOT NULL" -class Postgres(PostgreSQLQuery,common): + +class Postgres(common, PostgreSQLQuery,): postgres_field = {"table_name": "relname", "table_rows": "n_tup_ins"} information_schema_translation = {"tables": "pg_stat_all_tables"} + GROUP_CONCAT = STRING_AGG def __init__(self) -> None: super().__init__() @@ -63,11 +72,11 @@ class Postgres(PostgreSQLQuery,common): class_name = "tab" + class_name return super().from_(class_name, *args, **kwargs) - + @staticmethod def rename_table(old_name, new_name): return f"ALTER TABLE `tab{old_name}` RENAME TO `tab{new_name}`" - + @staticmethod def DESC(dt): return f"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME = 'tab{dt}'" From 3f081a55e5272bb743599d0b3aff3535c7717c6c Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Thu, 15 Jul 2021 02:12:12 +0530 Subject: [PATCH 479/495] refactor: qb in Website_analytics.get_data --- .../website_analytics/website_analytics.py | 38 ++++++------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/frappe/website/report/website_analytics/website_analytics.py b/frappe/website/report/website_analytics/website_analytics.py index d141972679..683eeba487 100644 --- a/frappe/website/report/website_analytics/website_analytics.py +++ b/frappe/website/report/website_analytics/website_analytics.py @@ -56,33 +56,19 @@ class WebsiteAnalytics(object): ] def get_data(self): - pg_query = """ - SELECT - path, - COUNT(*) as count, - COUNT(CASE WHEN CAST(is_unique as Integer) = 1 THEN 1 END) as unique_count - FROM `tabWeb Page View` - WHERE coalesce("tabWeb Page View".creation, '0001-01-01') BETWEEN %s AND %s - GROUP BY path - ORDER BY count desc - """ + Web_Page_View = frappe.qb.Table("Web Page View") + count_all = frappe.qb.fn.Count("*").as_("count") + case = frappe.qb.terms.Case().when(Web_Page_View.is_unique == "1", "1") + count_is_unique = frappe.qb.fn.Count(case).as_("unique_count") - mariadb_query = """ - SELECT - path, - COUNT(*) as count, - COUNT(CASE WHEN is_unique = 1 THEN 1 END) as unique_count - FROM `tabWeb Page View` - WHERE creation BETWEEN %s AND %s - GROUP BY path - ORDER BY count desc - """ - - data = frappe.db.multisql({ - "mariadb": mariadb_query, - "postgres": pg_query - }, (self.filters.from_date, self.filters.to_date)) - return data + curr = ( + frappe.qb.from_(Web_Page_View) + .select("path", count_all, count_is_unique) + .where(frappe.qb.fn.Coalesce(Web_Page_View.creation, "0001-01-01")[self.filters.from_date:self.filters.to_date]) + .groupby(Web_Page_View.path) + .orderby("count", Order=frappe.qb.desc).get_sql() + ) + return frappe.db.sql(curr) def _get_query_for_mariadb(self): filters_range = self.filters.range From c05a0328115049050a3a6aaf16ce9aaad5561326 Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Fri, 23 Jul 2021 10:38:33 +0530 Subject: [PATCH 480/495] refactor: qb in global_search.search --- frappe/query_builder/custom_functions.py | 46 +++++++++++++++++++++--- frappe/query_builder/qb.py | 10 ++++-- frappe/utils/global_search.py | 34 ++++++------------ 3 files changed, 59 insertions(+), 31 deletions(-) diff --git a/frappe/query_builder/custom_functions.py b/frappe/query_builder/custom_functions.py index 446a4df2d2..6b6dd137ab 100644 --- a/frappe/query_builder/custom_functions.py +++ b/frappe/query_builder/custom_functions.py @@ -1,12 +1,50 @@ from pypika import functions as fn +from pypika.utils import builder class GROUP_CONCAT(fn.DistinctOptionFunction): - def __init__(self, col, alias=None): - super(GROUP_CONCAT, self).__init__("GROUP_CONCAT", col, alias=alias) + def __init__(self, col, alias=None): + super(GROUP_CONCAT, self).__init__("GROUP_CONCAT", col, alias=alias) class STRING_AGG(fn.DistinctOptionFunction): - def __init__(self, col, val=",", alias=None): - super(STRING_AGG, self).__init__("STRING_AGG", col, val, alias=alias) + def __init__(self, col, val=",", alias=None): + super(STRING_AGG, self).__init__("STRING_AGG", col, val, alias=alias) + +class Match(fn.DistinctOptionFunction): + def __init__(self, col, *args, **kwargs): + alias = kwargs.get("alias") + super(Match, self).__init__(" MATCH", col, *args, alias=alias) + self._Against = False + + def get_function_sql(self, **kwargs): + s = super(fn.DistinctOptionFunction, self).get_function_sql(**kwargs) + + # n = len(self.name) + 1 + if self._Against: + return s + f" AGAINST ('+{self._Against}*' IN BOOLEAN MODE)" + return s + + @builder + def Against(self, b): + self._Against = b + + +class TO_TSVECTOR(fn.DistinctOptionFunction): + def __init__(self, col, *args, **kwargs): + alias = kwargs.get("alias") + super(TO_TSVECTOR, self).__init__("TO_TSVECTOR", col, *args, alias=alias) + self._PLAINTO_TSQUERY = False + + def get_function_sql(self, **kwargs): + s = super(fn.DistinctOptionFunction, self).get_function_sql(**kwargs) + + # n = len(self.name) + 1 + if self._PLAINTO_TSQUERY: + return s + f" @@ PLAINTO_TSQUERY('{self._PLAINTO_TSQUERY}')" + return s + + @builder + def Against(self, b): + self._PLAINTO_TSQUERY = b diff --git a/frappe/query_builder/qb.py b/frappe/query_builder/qb.py index fb92eed56b..2c1e3e103a 100644 --- a/frappe/query_builder/qb.py +++ b/frappe/query_builder/qb.py @@ -2,7 +2,7 @@ from pypika import MySQLQuery, Order, PostgreSQLQuery from pypika import functions as fn from pypika import terms from pypika.queries import Schema, Table -from .custom_functions import GROUP_CONCAT, STRING_AGG +import frappe.query_builder.custom_functions as SpecialFuncs def qb(db_type): if not db_type: @@ -15,6 +15,7 @@ class common: terms = terms desc = Order.desc Schema = Schema + @staticmethod def Table(classname:str, *args, **kwargs): if not classname.startswith("__"): @@ -23,7 +24,9 @@ class common: class MariaDB(common, MySQLQuery,): Field = terms.Field - GROUP_CONCAT = GROUP_CONCAT + GROUP_CONCAT = SpecialFuncs.GROUP_CONCAT + Match = SpecialFuncs.Match + def __init__(self) -> None: super().__init__() @@ -50,7 +53,8 @@ class MariaDB(common, MySQLQuery,): class Postgres(common, PostgreSQLQuery,): postgres_field = {"table_name": "relname", "table_rows": "n_tup_ins"} information_schema_translation = {"tables": "pg_stat_all_tables"} - GROUP_CONCAT = STRING_AGG + GROUP_CONCAT = SpecialFuncs.STRING_AGG + Match = SpecialFuncs.TO_TSVECTOR def __init__(self) -> None: super().__init__() diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index 6391fcf7ef..62db9d8e1b 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -423,39 +423,25 @@ def search(text, start=0, limit=20, doctype=""): if not text: continue - conditions = '1=1' - offset = '' + global_search = frappe.qb.Table("__global_search") - mariadb_text = frappe.db.escape('+' + text + '*') + rank = frappe.qb.Match(global_search.content).Against(text).as_("rank") - mariadb_fields = '`doctype`, `name`, `content`, MATCH (`content`) AGAINST ({} IN BOOLEAN MODE) AS rank'.format(mariadb_text) - postgres_fields = '`doctype`, `name`, `content`, TO_TSVECTOR("content") @@ PLAINTO_TSQUERY({}) AS rank'.format(frappe.db.escape(text)) - - values = {} + q = (frappe.qb.from_(global_search) + .select(global_search.doctype,global_search.name,global_search.content,rank) + .orderby("rank", order=frappe.qb.desc) + .limit(limit)) if doctype: - conditions = '`doctype` = %(doctype)s' - values['doctype'] = doctype + q = q.where(global_search.doctype == doctype) elif allowed_doctypes: - conditions = '`doctype` IN %(allowed_doctypes)s' - values['allowed_doctypes'] = tuple(allowed_doctypes) + q = q.where(global_search.doctype.isin(allowed_doctypes)) if int(start) > 0: - offset = 'OFFSET {}'.format(start) + q = q.offset(int(start)) - common_query = """ - SELECT {fields} - FROM `__global_search` - WHERE {conditions} - ORDER BY rank DESC - LIMIT {limit} - {offset} - """ - result = frappe.db.multisql({ - 'mariadb': common_query.format(fields=mariadb_fields, conditions=conditions, limit=limit, offset=offset), - 'postgres': common_query.format(fields=postgres_fields, conditions=conditions, limit=limit, offset=offset) - }, values=values, as_dict=True) + result = frappe.db.sql(q.get_sql(),as_dict=True) results.extend(result) From dd36b2a528097be2139da64df82842e435ce5dfc Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Fri, 23 Jul 2021 18:39:27 +0530 Subject: [PATCH 481/495] refactor: move to get_query_builder --- frappe/__init__.py | 4 +-- frappe/query_builder/__init__.py | 2 +- frappe/query_builder/{qb.py => builder.py} | 30 ++++++++++++------- .../{custom_functions.py => functions.py} | 2 +- 4 files changed, 24 insertions(+), 14 deletions(-) rename frappe/query_builder/{qb.py => builder.py} (80%) rename frappe/query_builder/{custom_functions.py => functions.py} (100%) diff --git a/frappe/__init__.py b/frappe/__init__.py index 39e21efb9e..24faf840bd 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -28,7 +28,7 @@ from .exceptions import * from .utils.jinja import (get_jenv, get_template, render_template, get_email_from_template, get_jloader) from .utils.lazy_loader import lazy_import -from frappe.query_builder import query_builder +from frappe.query_builder import get_query_builder # Lazy imports faker = lazy_import('faker') @@ -205,7 +205,7 @@ def init(site, sites_path=None, new_site=False): local.form_dict = _dict() local.session = _dict() local.dev_server = _dev_server - local.qb = query_builder(local.conf.db_type) + local.qb = get_query_builder(local.conf.db_type) setup_module_map() diff --git a/frappe/query_builder/__init__.py b/frappe/query_builder/__init__.py index da1748beec..00356ab679 100644 --- a/frappe/query_builder/__init__.py +++ b/frappe/query_builder/__init__.py @@ -1 +1 @@ -from frappe.query_builder.qb import qb as query_builder +from frappe.query_builder.builder import get_query_builder diff --git a/frappe/query_builder/qb.py b/frappe/query_builder/builder.py similarity index 80% rename from frappe/query_builder/qb.py rename to frappe/query_builder/builder.py index 2c1e3e103a..db16047ce9 100644 --- a/frappe/query_builder/qb.py +++ b/frappe/query_builder/builder.py @@ -1,15 +1,25 @@ -from pypika import MySQLQuery, Order, PostgreSQLQuery +from pypika import MySQLQuery, Order, PostgreSQLQuery, Query from pypika import functions as fn from pypika import terms from pypika.queries import Schema, Table -import frappe.query_builder.custom_functions as SpecialFuncs +import frappe.query_builder.functions as SpecialFuncs -def qb(db_type): + +def get_query_builder(db_type: str) -> Query: + """[return the query builder object] + + Args: + db_type (str): [string value of the db used] + + Returns: + Query: [Query object] + """ if not db_type: db_type = "mariadb" selecter = {"mariadb": MariaDB, "postgres": Postgres} return selecter[db_type] + class common: fn = fn terms = terms @@ -17,24 +27,24 @@ class common: Schema = Schema @staticmethod - def Table(classname:str, *args, **kwargs): + def Table(classname: str, *args, **kwargs) -> Table: if not classname.startswith("__"): - classname = "tab" + classname + classname = f"tab{classname}" return Table(classname, *args, **kwargs) -class MariaDB(common, MySQLQuery,): + +class MariaDB(common, MySQLQuery): Field = terms.Field GROUP_CONCAT = SpecialFuncs.GROUP_CONCAT Match = SpecialFuncs.Match - def __init__(self) -> None: super().__init__() @classmethod def from_(cls, class_name, *args, **kwargs): - if isinstance(class_name,str): - class_name = "tab"+class_name + if isinstance(class_name, str): + class_name = f"tab{class_name}" return super().from_(class_name, *args, **kwargs) @staticmethod @@ -50,7 +60,7 @@ class MariaDB(common, MySQLQuery,): return f"ALTER TABLE `{tb}` MODIFY `{col}` {type} NOT NULL" -class Postgres(common, PostgreSQLQuery,): +class Postgres(common, PostgreSQLQuery): postgres_field = {"table_name": "relname", "table_rows": "n_tup_ins"} information_schema_translation = {"tables": "pg_stat_all_tables"} GROUP_CONCAT = SpecialFuncs.STRING_AGG diff --git a/frappe/query_builder/custom_functions.py b/frappe/query_builder/functions.py similarity index 100% rename from frappe/query_builder/custom_functions.py rename to frappe/query_builder/functions.py index 6b6dd137ab..3b45d78726 100644 --- a/frappe/query_builder/custom_functions.py +++ b/frappe/query_builder/functions.py @@ -2,7 +2,6 @@ from pypika import functions as fn from pypika.utils import builder - class GROUP_CONCAT(fn.DistinctOptionFunction): def __init__(self, col, alias=None): super(GROUP_CONCAT, self).__init__("GROUP_CONCAT", col, alias=alias) @@ -12,6 +11,7 @@ class STRING_AGG(fn.DistinctOptionFunction): def __init__(self, col, val=",", alias=None): super(STRING_AGG, self).__init__("STRING_AGG", col, val, alias=alias) + class Match(fn.DistinctOptionFunction): def __init__(self, col, *args, **kwargs): alias = kwargs.get("alias") From ee3c84beef82ae37be04414866827510c8d0ebff Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Sat, 24 Jul 2021 00:28:43 +0530 Subject: [PATCH 482/495] style: typecast to string inside db.sql --- frappe/cache_manager.py | 8 ++--- frappe/config/__init__.py | 4 +-- frappe/database/database.py | 1 + frappe/database/postgres/database.py | 1 + .../v12_0/set_correct_assign_value_in_docs.py | 11 +++---- frappe/utils/global_search.py | 31 ++++++++++++------- .../website_analytics/website_analytics.py | 16 ++++++---- 7 files changed, 42 insertions(+), 30 deletions(-) diff --git a/frappe/cache_manager.py b/frappe/cache_manager.py index 9de05907f5..c17ae583ed 100644 --- a/frappe/cache_manager.py +++ b/frappe/cache_manager.py @@ -141,13 +141,13 @@ def build_table_count_cache(): return _cache = frappe.cache() - name = frappe.qb.Field("table_name").as_("name") - count = frappe.qb.Field("table_rows").as_("count") + table_name = frappe.qb.Field("table_name").as_("name") + table_rows = frappe.qb.Field("table_rows").as_("count") information_schema = frappe.qb.Schema("information_schema") - q = frappe.qb.from_(information_schema.tables).select(name, count).get_sql() + query = frappe.qb.from_(information_schema.tables).select(table_name, table_rows) - data = frappe.db.sql(q, as_dict=1) + data = frappe.db.sql(query, as_dict=1) counts = {d.get('name').lstrip('tab'): d.get('count', None) for d in data} _cache.set_value("information_schema:counts", counts) diff --git a/frappe/config/__init__.py b/frappe/config/__init__.py index 8fe110d393..47f1849a43 100644 --- a/frappe/config/__init__.py +++ b/frappe/config/__init__.py @@ -43,9 +43,9 @@ def get_all_empty_tables_by_module(): table_name = frappe.qb.Field("table_name") information_schema = frappe.qb.Schema("information_schema") - q = frappe.qb.from_(information_schema.tables).select(table_name).where(table_rows == 0).get_sql() + query = frappe.qb.from_(information_schema.tables).select(table_name).where(table_rows == 0) - empty_tables = set(r[0] for r in frappe.db.sql(q)) + empty_tables = set(r[0] for r in frappe.db.sql(query)) results = frappe.get_all("DocType", fields=["name", "module"]) empty_tables_by_module = {} diff --git a/frappe/database/database.py b/frappe/database/database.py index 2070ba676b..bf97317f99 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -104,6 +104,7 @@ class Database(object): {"name": "a%", "owner":"test@example.com"}) """ + query = str(query) if re.search(r'ifnull\(', query, flags=re.IGNORECASE): # replaces ifnull in query with coalesce query = re.sub(r'ifnull\(', 'coalesce(', query, flags=re.IGNORECASE) diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py index 8235277e30..32cd42eb94 100644 --- a/frappe/database/postgres/database.py +++ b/frappe/database/postgres/database.py @@ -297,6 +297,7 @@ class PostgresDatabase(Database): def modify_query(query): """"Modifies query according to the requirements of postgres""" # replace ` with " for definitions + query = str(query) query = query.replace('`', '"') query = replace_locate_with_strpos(query) # select from requires "" diff --git a/frappe/patches/v12_0/set_correct_assign_value_in_docs.py b/frappe/patches/v12_0/set_correct_assign_value_in_docs.py index 72e71c8314..dd493bc52c 100644 --- a/frappe/patches/v12_0/set_correct_assign_value_in_docs.py +++ b/frappe/patches/v12_0/set_correct_assign_value_in_docs.py @@ -7,24 +7,23 @@ def execute(): ToDo = frappe.qb.Table("ToDo") assignees = frappe.qb.GROUP_CONCAT("owner").distinct().as_("assignees") - q = ( + query = ( frappe.qb.from_(ToDo) .select(ToDo.name, ToDo.reference_type, assignees) .where(frappe.qb.fn.Coalesce(ToDo.reference_type, "") != "") .where(frappe.qb.fn.Coalesce(ToDo.reference_name, "") != "") .where(ToDo.status != "Cancelled") .groupby(ToDo.reference_type, ToDo.reference_name) - .get_sql() ) - assignments = frappe.db.sql(q, as_dict=True) + assignments = frappe.db.sql(query, as_dict=True) for doc in assignments: - assignments = doc.assignees.split(',') + assignments = doc.assignees.split(",") frappe.db.set_value( doc.reference_type, doc.reference_name, - '_assign', + "_assign", frappe.as_json(assignments), update_modified=False - ) + ) \ No newline at end of file diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index 62db9d8e1b..7754c87823 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -411,14 +411,16 @@ def search(text, start=0, limit=20, doctype=""): :param limit: number of results to return, default 20 :return: Array of result objects """ - from frappe.desk.doctype.global_search_settings.global_search_settings import get_doctypes_for_global_search + from frappe.desk.doctype.global_search_settings.global_search_settings import ( + get_doctypes_for_global_search, + ) results = [] sorted_results = [] allowed_doctypes = get_doctypes_for_global_search() - for text in set(text.split('&')): + for text in set(text.split("&")): text = text.strip() if not text: continue @@ -427,21 +429,24 @@ def search(text, start=0, limit=20, doctype=""): rank = frappe.qb.Match(global_search.content).Against(text).as_("rank") - q = (frappe.qb.from_(global_search) - .select(global_search.doctype,global_search.name,global_search.content,rank) + query = ( + frappe.qb.from_(global_search) + .select( + global_search.doctype, global_search.name, global_search.content, rank + ) .orderby("rank", order=frappe.qb.desc) - .limit(limit)) + .limit(limit) + ) if doctype: - q = q.where(global_search.doctype == doctype) + query = query.where(global_search.doctype == doctype) elif allowed_doctypes: - q = q.where(global_search.doctype.isin(allowed_doctypes)) + query = query.where(global_search.doctype.isin(allowed_doctypes)) - if int(start) > 0: - q = q.offset(int(start)) + if start > 0: + query = query.offset(start) - - result = frappe.db.sql(q.get_sql(),as_dict=True) + result = frappe.db.sql(query, as_dict=True) results.extend(result) @@ -452,7 +457,9 @@ def search(text, start=0, limit=20, doctype=""): try: meta = frappe.get_meta(r.doctype) if meta.image_field: - r.image = frappe.db.get_value(r.doctype, r.name, meta.image_field) + r.image = frappe.db.get_value( + r.doctype, r.name, meta.image_field + ) except Exception: frappe.clear_messages() diff --git a/frappe/website/report/website_analytics/website_analytics.py b/frappe/website/report/website_analytics/website_analytics.py index 683eeba487..2992108e75 100644 --- a/frappe/website/report/website_analytics/website_analytics.py +++ b/frappe/website/report/website_analytics/website_analytics.py @@ -56,17 +56,21 @@ class WebsiteAnalytics(object): ] def get_data(self): - Web_Page_View = frappe.qb.Table("Web Page View") + WebPageView = frappe.qb.Table("Web Page View") count_all = frappe.qb.fn.Count("*").as_("count") - case = frappe.qb.terms.Case().when(Web_Page_View.is_unique == "1", "1") + case = frappe.qb.terms.Case().when(WebPageView.is_unique == "1", "1") count_is_unique = frappe.qb.fn.Count(case).as_("unique_count") curr = ( - frappe.qb.from_(Web_Page_View) + frappe.qb.from_(WebPageView) .select("path", count_all, count_is_unique) - .where(frappe.qb.fn.Coalesce(Web_Page_View.creation, "0001-01-01")[self.filters.from_date:self.filters.to_date]) - .groupby(Web_Page_View.path) - .orderby("count", Order=frappe.qb.desc).get_sql() + .where( + frappe.qb.fn.Coalesce(WebPageView.creation, "0001-01-01")[ + self.filters.from_date : self.filters.to_date + ] + ) + .groupby(WebPageView.path) + .orderby("count", Order=frappe.qb.desc) ) return frappe.db.sql(curr) From 45d1afe739eabead499a989f5474d9ffc6a53d94 Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Sat, 24 Jul 2021 01:43:18 +0530 Subject: [PATCH 483/495] refactor: move rename, change_column_type, DESC to db --- frappe/core/doctype/doctype/doctype.py | 2 +- frappe/database/database.py | 4 ++ frappe/database/mariadb/database.py | 15 ++++++++ frappe/database/postgres/database.py | 19 +++++++++- frappe/model/__init__.py | 2 +- .../patches/v13_0/increase_password_length.py | 2 +- frappe/query_builder/builder.py | 38 +++---------------- 7 files changed, 44 insertions(+), 38 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 22be912abc..f868b68cfa 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -396,7 +396,7 @@ class DocType(Document): frappe.db.sql("""update tabSingles set value=%s where doctype=%s and field='name' and value = %s""", (new, new, old)) else: - frappe.db.sql(frappe.qb.rename_table(old,new)) + frappe.db.rename_table(old,new) frappe.db.commit() # Do not rename and move files and folders for custom doctype diff --git a/frappe/database/database.py b/frappe/database/database.py index bf97317f99..9c84ec3492 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -1042,6 +1042,10 @@ class Database(object): ), tuple(insert_list)) insert_list = [] + @staticmethod + def add_tab(table_name: str) -> str: + return f"tab{table_name}" if not table_name.startswith("__") else table_name + def enqueue_jobs_after_commit(): from frappe.utils.background_jobs import execute_job, get_queue diff --git a/frappe/database/mariadb/database.py b/frappe/database/mariadb/database.py index 879c8394d7..f2b3ad718d 100644 --- a/frappe/database/mariadb/database.py +++ b/frappe/database/mariadb/database.py @@ -1,3 +1,5 @@ +from typing import List, Tuple, Union + import pymysql from pymysql.constants import ER, FIELD_TYPE from pymysql.converters import conversions, escape_string @@ -123,6 +125,19 @@ class MariaDBDatabase(Database): def is_type_datetime(code): return code in (pymysql.DATE, pymysql.DATETIME) + def rename_table(self, old_name: str, new_name: str) -> Union[List,Tuple]: + old_name = self.add_tab(old_name) + new_name = self.add_tab(new_name) + return self.sql(f"RENAME TABLE `{old_name}` TO `{new_name}`") + + def DESC(self, doctype: str) -> Union[List,Tuple]: + doctype = self.add_tab(doctype) + return self.sql(f"DESC `{doctype}`") + + def change_column_type(self, table: str, column: str, type: str) -> Union[List,Tuple]: + table = self.add_tab(table) + return self.sql(f"ALTER TABLE `{table}` MODIFY `{column}` {type} NOT NULL") + # exception types @staticmethod def is_deadlocked(e): diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py index 32cd42eb94..dd967e6ca7 100644 --- a/frappe/database/postgres/database.py +++ b/frappe/database/postgres/database.py @@ -1,12 +1,14 @@ import re -import frappe +from typing import List, Tuple, Union + import psycopg2 import psycopg2.extensions -from frappe.utils import cstr from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT +import frappe from frappe.database.database import Database from frappe.database.postgres.schema import PostgresTable +from frappe.utils import cstr # cast decimals as floats DEC2FLOAT = psycopg2.extensions.new_type( @@ -170,6 +172,19 @@ class PostgresDatabase(Database): def is_data_too_long(e): return e.pgcode == '22001' + def rename_table(self, old_name: str, new_name: str) -> Union[List,Tuple]: + old_name = self.add_tab(old_name) + new_name = self.add_tab(new_name) + return self.sql(f"ALTER TABLE `{old_name}` RENAME TO `{new_name}`") + + def DESC(self, doctype: str)-> Union[List,Tuple]: + doctype = self.add_tab(doctype) + return self.sql(f"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME = '{doctype}'") + + def change_column_type(self, table: str, column: str, type: str) -> Union[List,Tuple]: + table = self.add_tab(table) + return self.sql(f'ALTER TABLE "{table}" ALTER COLUMN "{column}" TYPE {type}') + def create_auth_table(self): self.sql_ddl("""create table if not exists "__Auth" ( "doctype" VARCHAR(140) NOT NULL, diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 1f72272df1..22a735e3f0 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -167,7 +167,7 @@ def delete_fields(args_dict, delete=0): "field": ("in", fields), }) else: - existing_fields = frappe.db.sql(frappe.qb.DESC(dt)) + existing_fields = frappe.db.DESC(dt) existing_fields = existing_fields and [e[0] for e in existing_fields] or [] fields_need_to_delete = set(fields) & set(existing_fields) if not fields_need_to_delete: diff --git a/frappe/patches/v13_0/increase_password_length.py b/frappe/patches/v13_0/increase_password_length.py index 3f0e93e72c..e2a39d6724 100644 --- a/frappe/patches/v13_0/increase_password_length.py +++ b/frappe/patches/v13_0/increase_password_length.py @@ -1,4 +1,4 @@ import frappe def execute(): - frappe.db.sql(frappe.qb.change_table_type(tb = "__Auth",col = "password",type = "TEXT")) + frappe.db.change_column_type(table = "__Auth",column = "password",type = "TEXT") diff --git a/frappe/query_builder/builder.py b/frappe/query_builder/builder.py index db16047ce9..0531f33fff 100644 --- a/frappe/query_builder/builder.py +++ b/frappe/query_builder/builder.py @@ -9,10 +9,10 @@ def get_query_builder(db_type: str) -> Query: """[return the query builder object] Args: - db_type (str): [string value of the db used] + db_type (str): [string value of the db used] Returns: - Query: [Query object] + Query: [Query object] """ if not db_type: db_type = "mariadb" @@ -38,27 +38,12 @@ class MariaDB(common, MySQLQuery): GROUP_CONCAT = SpecialFuncs.GROUP_CONCAT Match = SpecialFuncs.Match - def __init__(self) -> None: - super().__init__() - @classmethod def from_(cls, class_name, *args, **kwargs): if isinstance(class_name, str): class_name = f"tab{class_name}" return super().from_(class_name, *args, **kwargs) - @staticmethod - def rename_table(old_name, new_name): - return f"RENAME TABLE `tab{old_name}` TO `tab{new_name}`" - - @staticmethod - def DESC(dt): - return f"DESC `tab{dt}`" - - @staticmethod - def change_table_type(tb, col, type): - return f"ALTER TABLE `{tb}` MODIFY `{col}` {type} NOT NULL" - class Postgres(common, PostgreSQLQuery): postgres_field = {"table_name": "relname", "table_rows": "n_tup_ins"} @@ -66,9 +51,6 @@ class Postgres(common, PostgreSQLQuery): GROUP_CONCAT = SpecialFuncs.STRING_AGG Match = SpecialFuncs.TO_TSVECTOR - def __init__(self) -> None: - super().__init__() - @classmethod def Field(cls, fieldName, *args, **kwargs): if fieldName in cls.postgres_field: @@ -80,21 +62,11 @@ class Postgres(common, PostgreSQLQuery): if isinstance(class_name, Table): if class_name._schema: if class_name._schema._name == "information_schema": - class_name = cls.information_schema_translation[class_name._table_name] + class_name = cls.information_schema_translation[ + class_name._table_name + ] elif isinstance(class_name, str): class_name = "tab" + class_name return super().from_(class_name, *args, **kwargs) - - @staticmethod - def rename_table(old_name, new_name): - return f"ALTER TABLE `tab{old_name}` RENAME TO `tab{new_name}`" - - @staticmethod - def DESC(dt): - return f"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME = 'tab{dt}'" - - @staticmethod - def change_table_type(tb, col, type): - return f'ALTER TABLE "{tb}" ALTER COLUMN "{col}" TYPE {type}' From ce10e367251217382ab6a21d0caf047510ef901b Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Wed, 28 Jul 2021 16:31:41 +0530 Subject: [PATCH 484/495] refactor: move all pypika functions to a module --- .../v12_0/set_correct_assign_value_in_docs.py | 8 +- frappe/query_builder/__init__.py | 3 +- frappe/query_builder/builder.py | 38 ++------- frappe/query_builder/custom.py | 83 +++++++++++++++++++ frappe/query_builder/functions.py | 63 ++++---------- frappe/query_builder/utils.py | 39 +++++++++ frappe/utils/global_search.py | 3 +- .../website_analytics/website_analytics.py | 11 ++- 8 files changed, 161 insertions(+), 87 deletions(-) create mode 100644 frappe/query_builder/custom.py create mode 100644 frappe/query_builder/utils.py diff --git a/frappe/patches/v12_0/set_correct_assign_value_in_docs.py b/frappe/patches/v12_0/set_correct_assign_value_in_docs.py index dd493bc52c..d06d87726b 100644 --- a/frappe/patches/v12_0/set_correct_assign_value_in_docs.py +++ b/frappe/patches/v12_0/set_correct_assign_value_in_docs.py @@ -5,13 +5,15 @@ def execute(): frappe.reload_doc("desk", "doctype", "todo") ToDo = frappe.qb.Table("ToDo") - assignees = frappe.qb.GROUP_CONCAT("owner").distinct().as_("assignees") + from frappe.query_builder.functions import GroupConcat + assignees = GroupConcat("owner").distinct().as_("assignees") + from frappe.query_builder.functions import Coalesce query = ( frappe.qb.from_(ToDo) .select(ToDo.name, ToDo.reference_type, assignees) - .where(frappe.qb.fn.Coalesce(ToDo.reference_type, "") != "") - .where(frappe.qb.fn.Coalesce(ToDo.reference_name, "") != "") + .where(Coalesce(ToDo.reference_type, "") != "") + .where(Coalesce(ToDo.reference_name, "") != "") .where(ToDo.status != "Cancelled") .groupby(ToDo.reference_type, ToDo.reference_name) ) diff --git a/frappe/query_builder/__init__.py b/frappe/query_builder/__init__.py index 00356ab679..34f7d43386 100644 --- a/frappe/query_builder/__init__.py +++ b/frappe/query_builder/__init__.py @@ -1 +1,2 @@ -from frappe.query_builder.builder import get_query_builder +from frappe.query_builder.utils import get_query_builder +import frappe.query_builder.functions diff --git a/frappe/query_builder/builder.py b/frappe/query_builder/builder.py index 0531f33fff..f8cc73ff4b 100644 --- a/frappe/query_builder/builder.py +++ b/frappe/query_builder/builder.py @@ -1,27 +1,7 @@ -from pypika import MySQLQuery, Order, PostgreSQLQuery, Query -from pypika import functions as fn -from pypika import terms +from pypika import MySQLQuery, Order, PostgreSQLQuery, terms from pypika.queries import Schema, Table -import frappe.query_builder.functions as SpecialFuncs - - -def get_query_builder(db_type: str) -> Query: - """[return the query builder object] - - Args: - db_type (str): [string value of the db used] - - Returns: - Query: [Query object] - """ - if not db_type: - db_type = "mariadb" - selecter = {"mariadb": MariaDB, "postgres": Postgres} - return selecter[db_type] - class common: - fn = fn terms = terms desc = Order.desc Schema = Schema @@ -35,8 +15,6 @@ class common: class MariaDB(common, MySQLQuery): Field = terms.Field - GROUP_CONCAT = SpecialFuncs.GROUP_CONCAT - Match = SpecialFuncs.Match @classmethod def from_(cls, class_name, *args, **kwargs): @@ -46,15 +24,13 @@ class MariaDB(common, MySQLQuery): class Postgres(common, PostgreSQLQuery): - postgres_field = {"table_name": "relname", "table_rows": "n_tup_ins"} - information_schema_translation = {"tables": "pg_stat_all_tables"} - GROUP_CONCAT = SpecialFuncs.STRING_AGG - Match = SpecialFuncs.TO_TSVECTOR + field_translation = {"table_name": "relname", "table_rows": "n_tup_ins"} + schema_translation = {"tables": "pg_stat_all_tables"} @classmethod def Field(cls, fieldName, *args, **kwargs): - if fieldName in cls.postgres_field: - fieldName = cls.postgres_field[fieldName] + if fieldName in cls.field_translation: + fieldName = cls.field_translation[fieldName] return terms.Field(fieldName, *args, **kwargs) @classmethod @@ -62,11 +38,11 @@ class Postgres(common, PostgreSQLQuery): if isinstance(class_name, Table): if class_name._schema: if class_name._schema._name == "information_schema": - class_name = cls.information_schema_translation[ + class_name = cls.schema_translation[ class_name._table_name ] elif isinstance(class_name, str): - class_name = "tab" + class_name + class_name = f"tab{class_name}" return super().from_(class_name, *args, **kwargs) diff --git a/frappe/query_builder/custom.py b/frappe/query_builder/custom.py new file mode 100644 index 0000000000..5aaed463d9 --- /dev/null +++ b/frappe/query_builder/custom.py @@ -0,0 +1,83 @@ +from typing import Optional + +from pypika.functions import DistinctOptionFunction +from pypika.utils import builder + +import frappe + + +class GROUP_CONCAT(DistinctOptionFunction): + def __init__(self, column: str, alias: Optional[str] = None): + """[ Implements the group concat function read more about it at https://www.geeksforgeeks.org/mysql-group_concat-function ] + Args: + column (str): [ name of the column you want to concat] + alias (Optional[str], optional): [ is this an alias? ]. Defaults to None. + """ + super(GROUP_CONCAT, self).__init__("GROUP_CONCAT", column, alias=alias) + + +class STRING_AGG(DistinctOptionFunction): + def __init__(self, column: str, separator: str = ",", alias: Optional[str] = None): + """[ Implements the group concat function read more about it at https://docs.microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-ver15 ] + + Args: + column (str): [ name of the column you want to concat ] + separator (str, optional): [separator to be used]. Defaults to ",". + alias (Optional[str], optional): [description]. Defaults to None. + """ + super(STRING_AGG, self).__init__("STRING_AGG", column, separator, alias=alias) + + +class MATCH(DistinctOptionFunction): + def __init__(self, column: str, *args, **kwargs): + """[ Implementation of Match Against read more about it https://dev.mysql.com/doc/refman/8.0/en/fulltext-search.html#function_match ] + + Args: + column (str):[ column to search in ] + """ + alias = kwargs.get("alias") + super(MATCH, self).__init__(" MATCH", column, *args, alias=alias) + self._Against = False + + def get_function_sql(self, **kwargs): + s = super(DistinctOptionFunction, self).get_function_sql(**kwargs) + + if self._Against: + return f"{s} AGAINST ({frappe.db.escape(f'+{self._Against}*')} IN BOOLEAN MODE)" + return s + + @builder + def Against(self, text: str): + """[ Text that has to be searched against ] + + Args: + text (str): [ the text string that we match it against ] + """ + self._Against = text + + +class TO_TSVECTOR(DistinctOptionFunction): + def __init__(self, column: str, *args, **kwargs): + """[ Implementation of TO_TSVECTOR read more about it https://www.postgresql.org/docs/9.1/textsearch-controls.html] + + Args: + column (str): [ column to search in ] + """ + alias = kwargs.get("alias") + super(TO_TSVECTOR, self).__init__("TO_TSVECTOR", column, *args, alias=alias) + self._PLAINTO_TSQUERY = False + + def get_function_sql(self, **kwargs): + s = super(DistinctOptionFunction, self).get_function_sql(**kwargs) + if self._PLAINTO_TSQUERY: + return f"{s} @@ PLAINTO_TSQUERY({frappe.db.escape(self._PLAINTO_TSQUERY)})" + return s + + @builder + def Against(self, text: str): + """[ Text that has to be searched against ] + + Args: + text (str): [ the text string that we match it against ] + """ + self._PLAINTO_TSQUERY = text diff --git a/frappe/query_builder/functions.py b/frappe/query_builder/functions.py index 3b45d78726..119c4e6c4f 100644 --- a/frappe/query_builder/functions.py +++ b/frappe/query_builder/functions.py @@ -1,50 +1,17 @@ -from pypika import functions as fn -from pypika.utils import builder +from pypika.functions import * +from frappe.query_builder.utils import ImportMapper, db_type +from frappe.query_builder.custom import GROUP_CONCAT, STRING_AGG, MATCH, TO_TSVECTOR +GroupConcat = ImportMapper( + { + db_type.MARIADB: GROUP_CONCAT, + db_type.POSTGRES: STRING_AGG + } +) -class GROUP_CONCAT(fn.DistinctOptionFunction): - def __init__(self, col, alias=None): - super(GROUP_CONCAT, self).__init__("GROUP_CONCAT", col, alias=alias) - - -class STRING_AGG(fn.DistinctOptionFunction): - def __init__(self, col, val=",", alias=None): - super(STRING_AGG, self).__init__("STRING_AGG", col, val, alias=alias) - - -class Match(fn.DistinctOptionFunction): - def __init__(self, col, *args, **kwargs): - alias = kwargs.get("alias") - super(Match, self).__init__(" MATCH", col, *args, alias=alias) - self._Against = False - - def get_function_sql(self, **kwargs): - s = super(fn.DistinctOptionFunction, self).get_function_sql(**kwargs) - - # n = len(self.name) + 1 - if self._Against: - return s + f" AGAINST ('+{self._Against}*' IN BOOLEAN MODE)" - return s - - @builder - def Against(self, b): - self._Against = b - - -class TO_TSVECTOR(fn.DistinctOptionFunction): - def __init__(self, col, *args, **kwargs): - alias = kwargs.get("alias") - super(TO_TSVECTOR, self).__init__("TO_TSVECTOR", col, *args, alias=alias) - self._PLAINTO_TSQUERY = False - - def get_function_sql(self, **kwargs): - s = super(fn.DistinctOptionFunction, self).get_function_sql(**kwargs) - - # n = len(self.name) + 1 - if self._PLAINTO_TSQUERY: - return s + f" @@ PLAINTO_TSQUERY('{self._PLAINTO_TSQUERY}')" - return s - - @builder - def Against(self, b): - self._PLAINTO_TSQUERY = b +Match = ImportMapper( + { + db_type.MARIADB: MATCH, + db_type.POSTGRES: TO_TSVECTOR + } +) diff --git a/frappe/query_builder/utils.py b/frappe/query_builder/utils.py new file mode 100644 index 0000000000..d8e5698e66 --- /dev/null +++ b/frappe/query_builder/utils.py @@ -0,0 +1,39 @@ +from enum import Enum +from typing import Any, Callable, Dict, Optional + +from pypika import Query + +import frappe +from .builder import MariaDB, Postgres + + +class db_type(Enum): + MARIADB = "mariadb" + POSTGRES = "postgres" + + +class ImportMapper: + def __init__(self, func_map: Dict[db_type, Callable]) -> None: + self.func_map = func_map + + def __call__(self, *args: Any, **kwds: Any) -> Callable: + db = db_type.MARIADB + if frappe.conf.db_type: + db = db_type(frappe.conf.db_type) + return self.func_map[db](*args, **kwds) + + +def get_query_builder(type_of_db: Optional[str]) -> Query: + """[return the query builder object] + + Args: + type_of_db (str): [string value of the db used] + + Returns: + Query: [Query object] + """ + db = db_type["MARIADB"] + if type_of_db: + db = db_type(type_of_db) + selecter = {db_type.MARIADB: MariaDB, db_type.POSTGRES: Postgres} + return selecter[db] diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index 7754c87823..e6e0bcb141 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -427,7 +427,8 @@ def search(text, start=0, limit=20, doctype=""): global_search = frappe.qb.Table("__global_search") - rank = frappe.qb.Match(global_search.content).Against(text).as_("rank") + from frappe.query_builder.functions import Match + rank = Match(global_search.content).Against(text).as_("rank") query = ( frappe.qb.from_(global_search) diff --git a/frappe/website/report/website_analytics/website_analytics.py b/frappe/website/report/website_analytics/website_analytics.py index 2992108e75..74e22d3dbb 100644 --- a/frappe/website/report/website_analytics/website_analytics.py +++ b/frappe/website/report/website_analytics/website_analytics.py @@ -57,15 +57,20 @@ class WebsiteAnalytics(object): def get_data(self): WebPageView = frappe.qb.Table("Web Page View") - count_all = frappe.qb.fn.Count("*").as_("count") + + from frappe.query_builder.functions import Count + + count_all = Count("*").as_("count") case = frappe.qb.terms.Case().when(WebPageView.is_unique == "1", "1") - count_is_unique = frappe.qb.fn.Count(case).as_("unique_count") + count_is_unique = Count(case).as_("unique_count") + + from frappe.query_builder.functions import Coalesce curr = ( frappe.qb.from_(WebPageView) .select("path", count_all, count_is_unique) .where( - frappe.qb.fn.Coalesce(WebPageView.creation, "0001-01-01")[ + Coalesce(WebPageView.creation, "0001-01-01")[ self.filters.from_date : self.filters.to_date ] ) From 681d0ab995ff9ea599b6e50c05dbabcee68614b4 Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Thu, 29 Jul 2021 02:33:44 +0530 Subject: [PATCH 485/495] test: for qb Match and GroupConcat --- frappe/query_builder/utils.py | 3 +-- frappe/tests/test_query_builder.py | 35 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 frappe/tests/test_query_builder.py diff --git a/frappe/query_builder/utils.py b/frappe/query_builder/utils.py index d8e5698e66..e2276e15c0 100644 --- a/frappe/query_builder/utils.py +++ b/frappe/query_builder/utils.py @@ -11,7 +11,6 @@ class db_type(Enum): MARIADB = "mariadb" POSTGRES = "postgres" - class ImportMapper: def __init__(self, func_map: Dict[db_type, Callable]) -> None: self.func_map = func_map @@ -32,7 +31,7 @@ def get_query_builder(type_of_db: Optional[str]) -> Query: Returns: Query: [Query object] """ - db = db_type["MARIADB"] + db = db_type.MARIADB if type_of_db: db = db_type(type_of_db) selecter = {db_type.MARIADB: MariaDB, db_type.POSTGRES: Postgres} diff --git a/frappe/tests/test_query_builder.py b/frappe/tests/test_query_builder.py new file mode 100644 index 0000000000..6cbd3b2950 --- /dev/null +++ b/frappe/tests/test_query_builder.py @@ -0,0 +1,35 @@ +import unittest +from typing import Callable + +import frappe +from frappe.query_builder.functions import GroupConcat, Match +from frappe.query_builder.utils import db_type + + +def CheckDB(dbtype: db_type) -> Callable: + return unittest.skipIf( + db_type(frappe.conf.db_type) != dbtype, f"Only runs for{db_type}" + ) + +@CheckDB(dbtype=db_type.MARIADB) +class TestCustomFunctionsMariaDB(unittest.TestCase): + def test_concat(self): + self.assertEqual("GROUP_CONCAT('Notes')", GroupConcat("Notes").get_sql()) + + def test_match(self): + query = Match("Notes").Against("text") + self.assertEqual( + " MATCH('Notes') AGAINST ('+text*' IN BOOLEAN MODE)", query.get_sql() + ) + + +@CheckDB(dbtype=db_type.POSTGRES) +class TestCustomFunctionsPostgres(unittest.TestCase): + def test_concat(self): + self.assertEqual("STRING_AGG('Notes',',')", GroupConcat("Notes").get_sql()) + + def test_match(self): + query = Match("Notes").Against("text") + self.assertEqual( + "TO_TSVECTOR('Notes') @@ PLAINTO_TSQUERY('text')", query.get_sql() + ) From 639b666a0de55c79cfd65192446fd792d9f0c65e Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Thu, 29 Jul 2021 03:57:03 +0530 Subject: [PATCH 486/495] test: for qb builder classes --- frappe/query_builder/builder.py | 42 ++++++++++++++---------------- frappe/tests/test_query_builder.py | 41 ++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 23 deletions(-) diff --git a/frappe/query_builder/builder.py b/frappe/query_builder/builder.py index f8cc73ff4b..4878212aa2 100644 --- a/frappe/query_builder/builder.py +++ b/frappe/query_builder/builder.py @@ -7,20 +7,20 @@ class common: Schema = Schema @staticmethod - def Table(classname: str, *args, **kwargs) -> Table: - if not classname.startswith("__"): - classname = f"tab{classname}" - return Table(classname, *args, **kwargs) + def Table(class_name: str, *args, **kwargs) -> Table: + if not class_name.startswith("__"): + class_name = f"tab{class_name}" + return Table(class_name, *args, **kwargs) class MariaDB(common, MySQLQuery): Field = terms.Field @classmethod - def from_(cls, class_name, *args, **kwargs): - if isinstance(class_name, str): - class_name = f"tab{class_name}" - return super().from_(class_name, *args, **kwargs) + def from_(cls, table, *args, **kwargs): + if isinstance(table, str): + table = cls.Table(table) + return super().from_(table, *args, **kwargs) class Postgres(common, PostgreSQLQuery): @@ -28,21 +28,19 @@ class Postgres(common, PostgreSQLQuery): schema_translation = {"tables": "pg_stat_all_tables"} @classmethod - def Field(cls, fieldName, *args, **kwargs): - if fieldName in cls.field_translation: - fieldName = cls.field_translation[fieldName] - return terms.Field(fieldName, *args, **kwargs) + def Field(cls, field_name, *args, **kwargs): + if field_name in cls.field_translation: + field_name = cls.field_translation[field_name] + return terms.Field(field_name, *args, **kwargs) @classmethod - def from_(cls, class_name, *args, **kwargs): - if isinstance(class_name, Table): - if class_name._schema: - if class_name._schema._name == "information_schema": - class_name = cls.schema_translation[ - class_name._table_name - ] + def from_(cls, table, *args, **kwargs): + if isinstance(table, Table): + if table._schema: + if table._schema._name == "information_schema": + table = cls.schema_translation[table._table_name] - elif isinstance(class_name, str): - class_name = f"tab{class_name}" + elif isinstance(table, str): + table = cls.Table(table) - return super().from_(class_name, *args, **kwargs) + return super().from_(table, *args, **kwargs) diff --git a/frappe/tests/test_query_builder.py b/frappe/tests/test_query_builder.py index 6cbd3b2950..b5e520ce30 100644 --- a/frappe/tests/test_query_builder.py +++ b/frappe/tests/test_query_builder.py @@ -8,9 +8,10 @@ from frappe.query_builder.utils import db_type def CheckDB(dbtype: db_type) -> Callable: return unittest.skipIf( - db_type(frappe.conf.db_type) != dbtype, f"Only runs for{db_type}" + db_type(frappe.conf.db_type) != dbtype, f"Only runs for {dbtype.value}" ) + @CheckDB(dbtype=db_type.MARIADB) class TestCustomFunctionsMariaDB(unittest.TestCase): def test_concat(self): @@ -33,3 +34,41 @@ class TestCustomFunctionsPostgres(unittest.TestCase): self.assertEqual( "TO_TSVECTOR('Notes') @@ PLAINTO_TSQUERY('text')", query.get_sql() ) + + +class TestBuilderBase(object): + def test_adding_tabs(self): + self.assertEqual("tabNotes", frappe.qb.Table("Notes").get_sql()) + self.assertEqual("__Auth", frappe.qb.Table("__Auth").get_sql()) + + +@CheckDB(dbtype=db_type.MARIADB) +class TestBuilderMaria(unittest.TestCase, TestBuilderBase): + def test_adding_tabs_in_from(self): + self.assertEqual( + "SELECT * FROM `tabNotes`", frappe.qb.from_("Notes").select("*").get_sql() + ) + self.assertEqual( + "SELECT * FROM `__Auth`", frappe.qb.from_("__Auth").select("*").get_sql() + ) + + +@CheckDB(dbtype=db_type.POSTGRES) +class TestBuilderPostgres(unittest.TestCase, TestBuilderBase): + def test_adding_tabs_in_from(self): + self.assertEqual( + 'SELECT * FROM "tabNotes"', frappe.qb.from_("Notes").select("*").get_sql() + ) + self.assertEqual( + 'SELECT * FROM "__Auth"', frappe.qb.from_("__Auth").select("*").get_sql() + ) + + def test_replace_tables(self): + info_schema = frappe.qb.Schema("information_schema") + self.assertEqual( + 'SELECT * FROM "pg_stat_all_tables"', + frappe.qb.from_(info_schema.tables).select("*").get_sql(), + ) + + def test_replace_fields_post(self): + self.assertEqual("relname", frappe.qb.Field("table_name").get_sql()) From 9423861c340d996964df3dcdda70fb1266e2d8d3 Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Thu, 29 Jul 2021 16:30:23 +0530 Subject: [PATCH 487/495] test: for db rename, change_column_type, describe --- frappe/tests/test_db.py | 99 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index 04c9a525b1..efb782bd3e 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -12,6 +12,8 @@ from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.utils import random_string from frappe.utils.testutils import clear_custom_fields +from .test_query_builder import CheckDB, db_type + class TestDB(unittest.TestCase): def test_get_value(self): @@ -146,7 +148,7 @@ class TestDB(unittest.TestCase): # Create documents under that doctype and query them via ORM for _ in range(10): - docfields = { key.lower(): random_string(10) for key in fields } + docfields = {key.lower(): random_string(10) for key in fields} doc = frappe.get_doc({"doctype": test_doctype, "description": random_string(20), **docfields}) doc.insert() created_docs.append(doc.name) @@ -189,3 +191,98 @@ class TestDB(unittest.TestCase): for doc in created_docs: frappe.delete_doc(test_doctype, doc) clear_custom_fields(test_doctype) + +@CheckDB(db_type.MARIADB) +class TestDDLCommandsMaria(unittest.TestCase): + test_table_name = "TestNotes" + + def setUp(self) -> None: + frappe.db.commit() + frappe.db.sql( + f""" + CREATE TABLE `tab{self.test_table_name}` (`id` INT NULL,PRIMARY KEY (`id`)); + """ + ) + + def tearDown(self) -> None: + frappe.db.sql(f"DROP TABLE tab{self.test_table_name};") + self.test_table_name = "TestNotes" + + def test_rename(self) -> None: + new_table_name = f"{self.test_table_name}_new" + frappe.db.rename_table(self.test_table_name, new_table_name) + check_exists = frappe.db.sql( + f""" + SELECT * FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_NAME = N'tab{new_table_name}'; + """ + ) + self.assertGreater(len(check_exists), 0) + self.assertIn(f"tab{new_table_name}", check_exists[0]) + + # * so this table is deleted after the rename + self.test_table_name = new_table_name + + def test_describe(self) -> None: + self.assertEqual( + (("id", "int(11)", "NO", "PRI", None, ""),), + frappe.db.DESC(self.test_table_name), + ) + + def test_change_type(self) -> None: + frappe.db.change_column_type("TestNotes", "id", "varchar(255)") + test_table_description = frappe.db.sql(f"DESC tab{self.test_table_name};") + self.assertGreater(len(test_table_description), 0) + self.assertIn("varchar(255)", test_table_description[0]) + + +@CheckDB(dbtype=db_type.POSTGRES) +class TestDDLCommandsPost(unittest.TestCase): + test_table_name = "TestNotes" + + def setUp(self) -> None: + frappe.db.sql( + f""" + CREATE TABLE "tab{self.test_table_name}" ("id" INT NULL,PRIMARY KEY ("id")) + """ + ) + + def tearDown(self) -> None: + frappe.db.sql(f'DROP TABLE "tab{self.test_table_name}"') + self.test_table_name = "TestNotes" + + def test_rename(self) -> None: + new_table_name = f"{self.test_table_name}_new" + frappe.db.rename_table(self.test_table_name, new_table_name) + check_exists = frappe.db.sql( + f""" + SELECT EXISTS ( + SELECT FROM information_schema.tables + WHERE table_name = 'tab{new_table_name}' + ); + """ + ) + self.assertTrue(check_exists[0][0]) + + # * so this table is deleted after the rename + self.test_table_name = new_table_name + + def test_describe(self) -> None: + self.assertEqual([("id",)], frappe.db.DESC(self.test_table_name)) + + def test_change_type(self) -> None: + frappe.db.change_column_type(self.test_table_name, "id", "varchar(255)") + check_change = frappe.db.sql( + f""" + SELECT + table_name, + column_name, + data_type + FROM + information_schema.columns + WHERE + table_name = 'tab{self.test_table_name}' + """ + ) + self.assertGreater(len(check_change), 0) + self.assertIn("character varying", check_change[0]) From 7671e784872ca64437738f7b6d794aab68556592 Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Fri, 30 Jul 2021 10:48:17 +0530 Subject: [PATCH 488/495] style: minor fixes and improvements --- frappe/config/__init__.py | 2 +- frappe/core/doctype/doctype/doctype.py | 2 +- frappe/database/mariadb/database.py | 6 +++--- frappe/database/postgres/database.py | 6 +++--- frappe/model/__init__.py | 2 +- .../v12_0/set_correct_assign_value_in_docs.py | 4 +--- .../patches/v13_0/increase_password_length.py | 2 +- frappe/query_builder/__init__.py | 1 - frappe/query_builder/builder.py | 15 ++++++++++++--- frappe/tests/test_db.py | 4 ++-- frappe/tests/test_query_builder.py | 1 + frappe/utils/global_search.py | 4 +--- .../website_analytics/website_analytics.py | 18 +++++++----------- 13 files changed, 34 insertions(+), 33 deletions(-) diff --git a/frappe/config/__init__.py b/frappe/config/__init__.py index 47f1849a43..caf169404d 100644 --- a/frappe/config/__init__.py +++ b/frappe/config/__init__.py @@ -45,7 +45,7 @@ def get_all_empty_tables_by_module(): query = frappe.qb.from_(information_schema.tables).select(table_name).where(table_rows == 0) - empty_tables = set(r[0] for r in frappe.db.sql(query)) + empty_tables = {(r[0] for r in frappe.db.sql(query))} results = frappe.get_all("DocType", fields=["name", "module"]) empty_tables_by_module = {} diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index f868b68cfa..c8efda6a55 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -396,7 +396,7 @@ class DocType(Document): frappe.db.sql("""update tabSingles set value=%s where doctype=%s and field='name' and value = %s""", (new, new, old)) else: - frappe.db.rename_table(old,new) + frappe.db.rename_table(old, new) frappe.db.commit() # Do not rename and move files and folders for custom doctype diff --git a/frappe/database/mariadb/database.py b/frappe/database/mariadb/database.py index f2b3ad718d..d218ea0338 100644 --- a/frappe/database/mariadb/database.py +++ b/frappe/database/mariadb/database.py @@ -125,16 +125,16 @@ class MariaDBDatabase(Database): def is_type_datetime(code): return code in (pymysql.DATE, pymysql.DATETIME) - def rename_table(self, old_name: str, new_name: str) -> Union[List,Tuple]: + def rename_table(self, old_name: str, new_name: str) -> Union[List, Tuple]: old_name = self.add_tab(old_name) new_name = self.add_tab(new_name) return self.sql(f"RENAME TABLE `{old_name}` TO `{new_name}`") - def DESC(self, doctype: str) -> Union[List,Tuple]: + def describe(self, doctype: str) -> Union[List, Tuple]: doctype = self.add_tab(doctype) return self.sql(f"DESC `{doctype}`") - def change_column_type(self, table: str, column: str, type: str) -> Union[List,Tuple]: + def change_column_type(self, table: str, column: str, type: str) -> Union[List, Tuple]: table = self.add_tab(table) return self.sql(f"ALTER TABLE `{table}` MODIFY `{column}` {type} NOT NULL") diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py index dd967e6ca7..9c48b2960d 100644 --- a/frappe/database/postgres/database.py +++ b/frappe/database/postgres/database.py @@ -172,16 +172,16 @@ class PostgresDatabase(Database): def is_data_too_long(e): return e.pgcode == '22001' - def rename_table(self, old_name: str, new_name: str) -> Union[List,Tuple]: + def rename_table(self, old_name: str, new_name: str) -> Union[List, Tuple]: old_name = self.add_tab(old_name) new_name = self.add_tab(new_name) return self.sql(f"ALTER TABLE `{old_name}` RENAME TO `{new_name}`") - def DESC(self, doctype: str)-> Union[List,Tuple]: + def describe(self, doctype: str)-> Union[List, Tuple]: doctype = self.add_tab(doctype) return self.sql(f"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME = '{doctype}'") - def change_column_type(self, table: str, column: str, type: str) -> Union[List,Tuple]: + def change_column_type(self, table: str, column: str, type: str) -> Union[List, Tuple]: table = self.add_tab(table) return self.sql(f'ALTER TABLE "{table}" ALTER COLUMN "{column}" TYPE {type}') diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 22a735e3f0..4aa8fb9000 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -167,7 +167,7 @@ def delete_fields(args_dict, delete=0): "field": ("in", fields), }) else: - existing_fields = frappe.db.DESC(dt) + existing_fields = frappe.db.describe(dt) existing_fields = existing_fields and [e[0] for e in existing_fields] or [] fields_need_to_delete = set(fields) & set(existing_fields) if not fields_need_to_delete: diff --git a/frappe/patches/v12_0/set_correct_assign_value_in_docs.py b/frappe/patches/v12_0/set_correct_assign_value_in_docs.py index d06d87726b..90766b5f64 100644 --- a/frappe/patches/v12_0/set_correct_assign_value_in_docs.py +++ b/frappe/patches/v12_0/set_correct_assign_value_in_docs.py @@ -1,14 +1,12 @@ import frappe - +from frappe.query_builder.functions import GroupConcat, Coalesce def execute(): frappe.reload_doc("desk", "doctype", "todo") ToDo = frappe.qb.Table("ToDo") - from frappe.query_builder.functions import GroupConcat assignees = GroupConcat("owner").distinct().as_("assignees") - from frappe.query_builder.functions import Coalesce query = ( frappe.qb.from_(ToDo) .select(ToDo.name, ToDo.reference_type, assignees) diff --git a/frappe/patches/v13_0/increase_password_length.py b/frappe/patches/v13_0/increase_password_length.py index e2a39d6724..62ca2ed779 100644 --- a/frappe/patches/v13_0/increase_password_length.py +++ b/frappe/patches/v13_0/increase_password_length.py @@ -1,4 +1,4 @@ import frappe def execute(): - frappe.db.change_column_type(table = "__Auth",column = "password",type = "TEXT") + frappe.db.change_column_type(table="__Auth", column="password", type="TEXT") diff --git a/frappe/query_builder/__init__.py b/frappe/query_builder/__init__.py index 34f7d43386..798c34b6cc 100644 --- a/frappe/query_builder/__init__.py +++ b/frappe/query_builder/__init__.py @@ -1,2 +1 @@ from frappe.query_builder.utils import get_query_builder -import frappe.query_builder.functions diff --git a/frappe/query_builder/builder.py b/frappe/query_builder/builder.py index 4878212aa2..d0f452e781 100644 --- a/frappe/query_builder/builder.py +++ b/frappe/query_builder/builder.py @@ -1,7 +1,8 @@ from pypika import MySQLQuery, Order, PostgreSQLQuery, terms from pypika.queries import Schema, Table -class common: + +class Base: terms = terms desc = Order.desc Schema = Schema @@ -13,7 +14,7 @@ class common: return Table(class_name, *args, **kwargs) -class MariaDB(common, MySQLQuery): +class MariaDB(Base, MySQLQuery): Field = terms.Field @classmethod @@ -23,9 +24,17 @@ class MariaDB(common, MySQLQuery): return super().from_(table, *args, **kwargs) -class Postgres(common, PostgreSQLQuery): +class Postgres(Base, PostgreSQLQuery): field_translation = {"table_name": "relname", "table_rows": "n_tup_ins"} schema_translation = {"tables": "pg_stat_all_tables"} + # TODO: Find a better way to do this + # These are interdependent query changes that need fixing. These + # translations happen in the same query. But there is no check to see if + # the Fields are changed only when a particular `information_schema` schema + # is used. Replacing them is not straightforward because the "from_" + # function can not see the arguments passed to the "select" function as + # they are two different objects. The quick fix used here is to replace the + # Field names in the "Field" function. @classmethod def Field(cls, field_name, *args, **kwargs): diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index efb782bd3e..9839df1197 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -226,7 +226,7 @@ class TestDDLCommandsMaria(unittest.TestCase): def test_describe(self) -> None: self.assertEqual( (("id", "int(11)", "NO", "PRI", None, ""),), - frappe.db.DESC(self.test_table_name), + frappe.db.describe(self.test_table_name), ) def test_change_type(self) -> None: @@ -268,7 +268,7 @@ class TestDDLCommandsPost(unittest.TestCase): self.test_table_name = new_table_name def test_describe(self) -> None: - self.assertEqual([("id",)], frappe.db.DESC(self.test_table_name)) + self.assertEqual([("id",)], frappe.db.describe(self.test_table_name)) def test_change_type(self) -> None: frappe.db.change_column_type(self.test_table_name, "id", "varchar(255)") diff --git a/frappe/tests/test_query_builder.py b/frappe/tests/test_query_builder.py index b5e520ce30..d5a74a33bd 100644 --- a/frappe/tests/test_query_builder.py +++ b/frappe/tests/test_query_builder.py @@ -4,6 +4,7 @@ from typing import Callable import frappe from frappe.query_builder.functions import GroupConcat, Match from frappe.query_builder.utils import db_type +from frappe.query_builder.functions import GroupConcat, Match def CheckDB(dbtype: db_type) -> Callable: diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index e6e0bcb141..072e3a7c62 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -414,6 +414,7 @@ def search(text, start=0, limit=20, doctype=""): from frappe.desk.doctype.global_search_settings.global_search_settings import ( get_doctypes_for_global_search, ) + from frappe.query_builder.functions import Match results = [] sorted_results = [] @@ -426,10 +427,7 @@ def search(text, start=0, limit=20, doctype=""): continue global_search = frappe.qb.Table("__global_search") - - from frappe.query_builder.functions import Match rank = Match(global_search.content).Against(text).as_("rank") - query = ( frappe.qb.from_(global_search) .select( diff --git a/frappe/website/report/website_analytics/website_analytics.py b/frappe/website/report/website_analytics/website_analytics.py index 74e22d3dbb..6cc0fb7f97 100644 --- a/frappe/website/report/website_analytics/website_analytics.py +++ b/frappe/website/report/website_analytics/website_analytics.py @@ -1,11 +1,14 @@ # Copyright (c) 2013, Frappe Technologies and contributors # For license information, please see license.txt -import frappe from datetime import datetime + +import frappe +from frappe.query_builder.functions import Coalesce, Count from frappe.utils import getdate from frappe.utils.dateutils import get_dates_from_timegrain + def execute(filters=None): return WebsiteAnalytics(filters).run() @@ -57,27 +60,20 @@ class WebsiteAnalytics(object): def get_data(self): WebPageView = frappe.qb.Table("Web Page View") - - from frappe.query_builder.functions import Count - count_all = Count("*").as_("count") case = frappe.qb.terms.Case().when(WebPageView.is_unique == "1", "1") count_is_unique = Count(case).as_("unique_count") - from frappe.query_builder.functions import Coalesce - - curr = ( + query = ( frappe.qb.from_(WebPageView) .select("path", count_all, count_is_unique) .where( - Coalesce(WebPageView.creation, "0001-01-01")[ - self.filters.from_date : self.filters.to_date - ] + Coalesce(WebPageView.creation, "0001-01-01")[self.filters.from_date:self.filters.to_date] ) .groupby(WebPageView.path) .orderby("count", Order=frappe.qb.desc) ) - return frappe.db.sql(curr) + return frappe.db.sql(query) def _get_query_for_mariadb(self): filters_range = self.filters.range From a017c89afc4253e2d9ff04514fd9a05f7b5beede Mon Sep 17 00:00:00 2001 From: hasnain2808 Date: Fri, 30 Jul 2021 11:31:55 +0530 Subject: [PATCH 489/495] fix: validation for website search field --- frappe/core/doctype/doctype/doctype.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 3cdc45ea08..fac0fc7e2d 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -927,6 +927,13 @@ def validate_fields(meta): if meta.is_published_field not in fieldname_list: frappe.throw(_("Is Published Field must be a valid fieldname"), InvalidFieldNameError) + def check_website_search_field(meta): + if not meta.website_search_field: + return + + if meta.website_search_field not in fieldname_list: + frappe.throw(_("Website Search Field must be a valid fieldname"), InvalidFieldNameError) + def check_timeline_field(meta): if not meta.timeline_field: return @@ -1046,6 +1053,7 @@ def validate_fields(meta): check_title_field(meta) check_timeline_field(meta) check_is_published_field(meta) + check_website_search_field(meta) check_sort_field(meta) check_image_field(meta) From 42dc8a180f3dacd3a6024c90bd55740248a32372 Mon Sep 17 00:00:00 2001 From: saxenabhishek Date: Fri, 30 Jul 2021 11:48:12 +0530 Subject: [PATCH 490/495] refactor: get_table_name, run_only_if --- frappe/__init__.py | 2 +- frappe/database/database.py | 7 ++----- frappe/database/mariadb/database.py | 14 +++++++------- frappe/database/postgres/database.py | 14 +++++++------- frappe/query_builder/builder.py | 8 ++++---- frappe/query_builder/functions.py | 10 +++++----- frappe/query_builder/utils.py | 20 ++++++++------------ frappe/tests/test_db.py | 6 +++--- frappe/tests/test_query_builder.py | 15 +++++++-------- frappe/utils/__init__.py | 3 +++ 10 files changed, 47 insertions(+), 52 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 24faf840bd..b4728f9ac3 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -205,7 +205,7 @@ def init(site, sites_path=None, new_site=False): local.form_dict = _dict() local.session = _dict() local.dev_server = _dev_server - local.qb = get_query_builder(local.conf.db_type) + local.qb = get_query_builder(local.conf.db_type or "mariadb") setup_module_map() diff --git a/frappe/database/database.py b/frappe/database/database.py index 9c84ec3492..b1dec95139 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -14,7 +14,7 @@ import frappe.model.meta from frappe import _ from time import time -from frappe.utils import now, getdate, cast_fieldtype, get_datetime +from frappe.utils import now, getdate, cast_fieldtype, get_datetime, get_table_name from frappe.model.utils.link_count import flush_local_link_count @@ -961,7 +961,7 @@ class Database(object): """ values = () filters = filters or kwargs.get("conditions") - table = doctype if doctype.startswith("__") else f"tab{doctype}" + table = get_table_name(doctype) query = f"DELETE FROM `{table}`" if "debug" not in kwargs: @@ -1042,9 +1042,6 @@ class Database(object): ), tuple(insert_list)) insert_list = [] - @staticmethod - def add_tab(table_name: str) -> str: - return f"tab{table_name}" if not table_name.startswith("__") else table_name def enqueue_jobs_after_commit(): from frappe.utils.background_jobs import execute_job, get_queue diff --git a/frappe/database/mariadb/database.py b/frappe/database/mariadb/database.py index d218ea0338..5dd6d9e58a 100644 --- a/frappe/database/mariadb/database.py +++ b/frappe/database/mariadb/database.py @@ -7,7 +7,7 @@ from pymysql.converters import conversions, escape_string import frappe from frappe.database.database import Database from frappe.database.mariadb.schema import MariaDBTable -from frappe.utils import UnicodeWithAttrs, cstr, get_datetime +from frappe.utils import UnicodeWithAttrs, cstr, get_datetime, get_table_name class MariaDBDatabase(Database): @@ -126,17 +126,17 @@ class MariaDBDatabase(Database): return code in (pymysql.DATE, pymysql.DATETIME) def rename_table(self, old_name: str, new_name: str) -> Union[List, Tuple]: - old_name = self.add_tab(old_name) - new_name = self.add_tab(new_name) + old_name = get_table_name(old_name) + new_name = get_table_name(new_name) return self.sql(f"RENAME TABLE `{old_name}` TO `{new_name}`") def describe(self, doctype: str) -> Union[List, Tuple]: - doctype = self.add_tab(doctype) - return self.sql(f"DESC `{doctype}`") + table_name = get_table_name(doctype) + return self.sql(f"DESC `{table_name}`") def change_column_type(self, table: str, column: str, type: str) -> Union[List, Tuple]: - table = self.add_tab(table) - return self.sql(f"ALTER TABLE `{table}` MODIFY `{column}` {type} NOT NULL") + table_name = get_table_name(table) + return self.sql(f"ALTER TABLE `{table_name}` MODIFY `{column}` {type} NOT NULL") # exception types @staticmethod diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py index 9c48b2960d..0b73c8b44b 100644 --- a/frappe/database/postgres/database.py +++ b/frappe/database/postgres/database.py @@ -8,7 +8,7 @@ from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT import frappe from frappe.database.database import Database from frappe.database.postgres.schema import PostgresTable -from frappe.utils import cstr +from frappe.utils import cstr, get_table_name # cast decimals as floats DEC2FLOAT = psycopg2.extensions.new_type( @@ -173,17 +173,17 @@ class PostgresDatabase(Database): return e.pgcode == '22001' def rename_table(self, old_name: str, new_name: str) -> Union[List, Tuple]: - old_name = self.add_tab(old_name) - new_name = self.add_tab(new_name) + old_name = get_table_name(old_name) + new_name = get_table_name(new_name) return self.sql(f"ALTER TABLE `{old_name}` RENAME TO `{new_name}`") def describe(self, doctype: str)-> Union[List, Tuple]: - doctype = self.add_tab(doctype) - return self.sql(f"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME = '{doctype}'") + table_name = get_table_name(doctype) + return self.sql(f"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME = '{table_name}'") def change_column_type(self, table: str, column: str, type: str) -> Union[List, Tuple]: - table = self.add_tab(table) - return self.sql(f'ALTER TABLE "{table}" ALTER COLUMN "{column}" TYPE {type}') + table_name = get_table_name(table) + return self.sql(f'ALTER TABLE "{table_name}" ALTER COLUMN "{column}" TYPE {type}') def create_auth_table(self): self.sql_ddl("""create table if not exists "__Auth" ( diff --git a/frappe/query_builder/builder.py b/frappe/query_builder/builder.py index d0f452e781..da1533fb1a 100644 --- a/frappe/query_builder/builder.py +++ b/frappe/query_builder/builder.py @@ -1,5 +1,6 @@ from pypika import MySQLQuery, Order, PostgreSQLQuery, terms from pypika.queries import Schema, Table +from frappe.utils import get_table_name class Base: @@ -8,10 +9,9 @@ class Base: Schema = Schema @staticmethod - def Table(class_name: str, *args, **kwargs) -> Table: - if not class_name.startswith("__"): - class_name = f"tab{class_name}" - return Table(class_name, *args, **kwargs) + def Table(table_name: str, *args, **kwargs) -> Table: + table_name = get_table_name(table_name) + return Table(table_name, *args, **kwargs) class MariaDB(Base, MySQLQuery): diff --git a/frappe/query_builder/functions.py b/frappe/query_builder/functions.py index 119c4e6c4f..5ccb266945 100644 --- a/frappe/query_builder/functions.py +++ b/frappe/query_builder/functions.py @@ -1,17 +1,17 @@ from pypika.functions import * -from frappe.query_builder.utils import ImportMapper, db_type +from frappe.query_builder.utils import ImportMapper, db_type_is from frappe.query_builder.custom import GROUP_CONCAT, STRING_AGG, MATCH, TO_TSVECTOR GroupConcat = ImportMapper( { - db_type.MARIADB: GROUP_CONCAT, - db_type.POSTGRES: STRING_AGG + db_type_is.MARIADB: GROUP_CONCAT, + db_type_is.POSTGRES: STRING_AGG } ) Match = ImportMapper( { - db_type.MARIADB: MATCH, - db_type.POSTGRES: TO_TSVECTOR + db_type_is.MARIADB: MATCH, + db_type_is.POSTGRES: TO_TSVECTOR } ) diff --git a/frappe/query_builder/utils.py b/frappe/query_builder/utils.py index e2276e15c0..b52a3606e8 100644 --- a/frappe/query_builder/utils.py +++ b/frappe/query_builder/utils.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import Any, Callable, Dict, Optional +from typing import Any, Callable, Dict from pypika import Query @@ -7,22 +7,20 @@ import frappe from .builder import MariaDB, Postgres -class db_type(Enum): +class db_type_is(Enum): MARIADB = "mariadb" POSTGRES = "postgres" class ImportMapper: - def __init__(self, func_map: Dict[db_type, Callable]) -> None: + def __init__(self, func_map: Dict[db_type_is, Callable]) -> None: self.func_map = func_map def __call__(self, *args: Any, **kwds: Any) -> Callable: - db = db_type.MARIADB - if frappe.conf.db_type: - db = db_type(frappe.conf.db_type) + db = db_type_is(frappe.conf.db_type or "mariadb") return self.func_map[db](*args, **kwds) -def get_query_builder(type_of_db: Optional[str]) -> Query: +def get_query_builder(type_of_db: str) -> Query: """[return the query builder object] Args: @@ -31,8 +29,6 @@ def get_query_builder(type_of_db: Optional[str]) -> Query: Returns: Query: [Query object] """ - db = db_type.MARIADB - if type_of_db: - db = db_type(type_of_db) - selecter = {db_type.MARIADB: MariaDB, db_type.POSTGRES: Postgres} - return selecter[db] + db = db_type_is(type_of_db) + picks = {db_type_is.MARIADB: MariaDB, db_type_is.POSTGRES: Postgres} + return picks[db] diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index 9839df1197..044ce455d9 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -12,7 +12,7 @@ from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.utils import random_string from frappe.utils.testutils import clear_custom_fields -from .test_query_builder import CheckDB, db_type +from .test_query_builder import run_only_if, db_type_is class TestDB(unittest.TestCase): @@ -192,7 +192,7 @@ class TestDB(unittest.TestCase): frappe.delete_doc(test_doctype, doc) clear_custom_fields(test_doctype) -@CheckDB(db_type.MARIADB) +@run_only_if(db_type_is.MARIADB) class TestDDLCommandsMaria(unittest.TestCase): test_table_name = "TestNotes" @@ -236,7 +236,7 @@ class TestDDLCommandsMaria(unittest.TestCase): self.assertIn("varchar(255)", test_table_description[0]) -@CheckDB(dbtype=db_type.POSTGRES) +@run_only_if(db_type_is.POSTGRES) class TestDDLCommandsPost(unittest.TestCase): test_table_name = "TestNotes" diff --git a/frappe/tests/test_query_builder.py b/frappe/tests/test_query_builder.py index d5a74a33bd..d155dd95db 100644 --- a/frappe/tests/test_query_builder.py +++ b/frappe/tests/test_query_builder.py @@ -3,17 +3,16 @@ from typing import Callable import frappe from frappe.query_builder.functions import GroupConcat, Match -from frappe.query_builder.utils import db_type -from frappe.query_builder.functions import GroupConcat, Match +from frappe.query_builder.utils import db_type_is -def CheckDB(dbtype: db_type) -> Callable: +def run_only_if(dbtype: db_type_is) -> Callable: return unittest.skipIf( - db_type(frappe.conf.db_type) != dbtype, f"Only runs for {dbtype.value}" + db_type_is(frappe.conf.db_type) != dbtype, f"Only runs for {dbtype.value}" ) -@CheckDB(dbtype=db_type.MARIADB) +@run_only_if(db_type_is.MARIADB) class TestCustomFunctionsMariaDB(unittest.TestCase): def test_concat(self): self.assertEqual("GROUP_CONCAT('Notes')", GroupConcat("Notes").get_sql()) @@ -25,7 +24,7 @@ class TestCustomFunctionsMariaDB(unittest.TestCase): ) -@CheckDB(dbtype=db_type.POSTGRES) +@run_only_if(db_type_is.POSTGRES) class TestCustomFunctionsPostgres(unittest.TestCase): def test_concat(self): self.assertEqual("STRING_AGG('Notes',',')", GroupConcat("Notes").get_sql()) @@ -43,7 +42,7 @@ class TestBuilderBase(object): self.assertEqual("__Auth", frappe.qb.Table("__Auth").get_sql()) -@CheckDB(dbtype=db_type.MARIADB) +@run_only_if(db_type_is.MARIADB) class TestBuilderMaria(unittest.TestCase, TestBuilderBase): def test_adding_tabs_in_from(self): self.assertEqual( @@ -54,7 +53,7 @@ class TestBuilderMaria(unittest.TestCase, TestBuilderBase): ) -@CheckDB(dbtype=db_type.POSTGRES) +@run_only_if(db_type_is.POSTGRES) class TestBuilderPostgres(unittest.TestCase, TestBuilderBase): def test_adding_tabs_in_from(self): self.assertEqual( diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 68366eb234..29883398e4 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -849,3 +849,6 @@ def groupby_metric(iterable: typing.Dict[str, list], key: str): for item in items: records.setdefault(item[key], {}).setdefault(category, []).append(item) return records + +def get_table_name(table_name: str) -> str: + return f"tab{table_name}" if not table_name.startswith("__") else table_name \ No newline at end of file From 94701fdb7d0e042e9d10fcf1b1921df095af7882 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 30 Jul 2021 12:27:22 +0530 Subject: [PATCH 491/495] feat(utils): frappe.utils.get_table_name Fetch table name from doctype/__ table name passed --- frappe/utils/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 68366eb234..b97585aa04 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -849,3 +849,6 @@ def groupby_metric(iterable: typing.Dict[str, list], key: str): for item in items: records.setdefault(item[key], {}).setdefault(category, []).append(item) return records + +def get_table_name(table_name: str) -> str: + return f"tab{table_name}" if not table_name.startswith("__") else table_name From 43cde9010922c7c1ac9c6e535843d835e78fbe0a Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 30 Jul 2021 12:28:27 +0530 Subject: [PATCH 492/495] fix(backups): Allow individual backups of all tables Due to previous logic, only tables under DocType table were allowed to take partial backups. This change allows backup to be taken for deprecated doctypes too. --- frappe/utils/backups.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frappe/utils/backups.py b/frappe/utils/backups.py index 908be52452..f13710dcfe 100644 --- a/frappe/utils/backups.py +++ b/frappe/utils/backups.py @@ -116,16 +116,16 @@ class BackupGenerator: def setup_backup_tables(self): """Sets self.backup_includes, self.backup_excludes based on passed args""" - existing_doctypes = set([x.name for x in frappe.get_all("DocType")]) + existing_tables = frappe.db.get_tables() def get_tables(doctypes): tables = [] for doctype in doctypes: - if doctype and doctype in existing_doctypes: - if doctype.startswith("tab"): - tables.append(doctype) - else: - tables.append("tab" + doctype) + if not doctype: + continue + table = frappe.utils.get_table_name(doctype) + if table in existing_tables: + tables.append(table) return tables passed_tables = { From 5350210ad56d66e21c1f54b633667e0b54337992 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 30 Jul 2021 12:31:07 +0530 Subject: [PATCH 493/495] fix: Add patch to remove Data Import Legacy This patch removes the document record from DocType list/table and adds a message directing how existing data should be handled. --- frappe/patches.txt | 1 + frappe/patches/v14_0/__init__.py | 0 .../patches/v14_0/drop_data_import_legacy.py | 22 +++++++++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 frappe/patches/v14_0/__init__.py create mode 100644 frappe/patches/v14_0/drop_data_import_legacy.py diff --git a/frappe/patches.txt b/frappe/patches.txt index 7605d8ea2b..493c4dc9f6 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -180,3 +180,4 @@ frappe.patches.v12_0.rename_uploaded_files_with_proper_name frappe.patches.v13_0.queryreport_columns frappe.patches.v13_0.jinja_hook frappe.patches.v13_0.update_notification_channel_if_empty +frappe.patches.v14_0.drop_data_import_legacy diff --git a/frappe/patches/v14_0/__init__.py b/frappe/patches/v14_0/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/patches/v14_0/drop_data_import_legacy.py b/frappe/patches/v14_0/drop_data_import_legacy.py new file mode 100644 index 0000000000..2037930c9f --- /dev/null +++ b/frappe/patches/v14_0/drop_data_import_legacy.py @@ -0,0 +1,22 @@ +import frappe +import click + + +def execute(): + doctype = "Data Import Legacy" + table = frappe.utils.get_table_name(doctype) + + # delete the doctype record to avoid broken links + frappe.db.delete("DocType", {"name": doctype}) + + # leaving table in database for manual cleanup + click.secho( + f"`{doctype}` has been deprecated. The DocType is deleted, but the data still" + " exists on the database. If this data is worth recovering, you may export it" + f" using\n\n\tbench --site {frappe.local.site} backup -i '{doctype}'\n\nAfter" + " this, the table will continue to persist in the database, until you choose" + " to remove it yourself. If you want to drop the table, you may run\n\n\tbench" + f" --site {frappe.local.site} execute frappe.db.sql --args \"('DROP TABLE IF" + f" EXISTS `{table}`', )\"\n", + fg="yellow", + ) From 07d192f95a231ee286b8ce3eca77313dd78e5385 Mon Sep 17 00:00:00 2001 From: leela Date: Fri, 30 Jul 2021 13:02:45 +0530 Subject: [PATCH 494/495] fix: incoming emails are not sending to the task owners --- frappe/core/doctype/communication/mixins.py | 9 +++++++++ frappe/desk/doctype/todo/todo.py | 11 ++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/communication/mixins.py b/frappe/core/doctype/communication/mixins.py index 09a8a0ac22..52cd370890 100644 --- a/frappe/core/doctype/communication/mixins.py +++ b/frappe/core/doctype/communication/mixins.py @@ -3,6 +3,7 @@ from frappe import _ from frappe.core.utils import get_parent_doc from frappe.utils import parse_addr, get_formatted_email, get_url from frappe.email.doctype.email_account.email_account import EmailAccount +from frappe.desk.doctype.todo.todo import ToDo class CommunicationEmailMixin: """Mixin class to handle communication mails. @@ -76,6 +77,7 @@ class CommunicationEmailMixin: if is_inbound_mail_communcation: cc.append(self.get_owner()) cc = set(cc) - {self.sender_mailid} + cc.update(self.get_assignees()) cc = set(cc) - set(self.filter_thread_notification_disbled_users(cc)) cc = cc - set(self.mail_recipients(is_inbound_mail_communcation=is_inbound_mail_communcation)) @@ -201,6 +203,13 @@ class CommunicationEmailMixin: self.mail_cc(is_inbound_mail_communcation = is_inbound_mail_communcation, include_sender=include_sender) return set(all_ids) - set(final_ids) + def get_assignees(self): + """Get owners of the reference document. + """ + filters = {'status': 'Open', 'reference_name': self.reference_name, + 'reference_type': self.reference_doctype} + return ToDo.get_owners(filters) + @staticmethod def filter_thread_notification_disbled_users(emails): """Filter users based on notifications for email threads setting is disabled. diff --git a/frappe/desk/doctype/todo/todo.py b/frappe/desk/doctype/todo/todo.py index 4696563445..4151cddac0 100644 --- a/frappe/desk/doctype/todo/todo.py +++ b/frappe/desk/doctype/todo/todo.py @@ -5,11 +5,13 @@ import frappe import json from frappe.model.document import Document -from frappe.utils import get_fullname +from frappe.utils import get_fullname, parse_addr exclude_from_linked_with = True class ToDo(Document): + DocType = 'ToDo' + def validate(self): self._assignment = None if self.is_new(): @@ -84,6 +86,13 @@ class ToDo(Document): else: raise + @classmethod + def get_owners(cls, filters=None): + """Returns list of owners after applying filters on todo's. + """ + rows = frappe.get_all(cls.DocType, filters=filters or {}, fields=['owner']) + return [parse_addr(row.owner)[1] for row in rows if row.owner] + # NOTE: todo is viewable if a user is an owner, or set as assigned_to value, or has any role that is allowed to access ToDo doctype. def on_doctype_update(): frappe.db.add_index("ToDo", ["reference_type", "reference_name"]) From b1c6ccffe155cc813d9dd89da004668d98dddde6 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 30 Jul 2021 13:09:12 +0530 Subject: [PATCH 495/495] style: Remove redundant brackets --- frappe/config/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/config/__init__.py b/frappe/config/__init__.py index caf169404d..e7f0f1a763 100644 --- a/frappe/config/__init__.py +++ b/frappe/config/__init__.py @@ -45,7 +45,7 @@ def get_all_empty_tables_by_module(): query = frappe.qb.from_(information_schema.tables).select(table_name).where(table_rows == 0) - empty_tables = {(r[0] for r in frappe.db.sql(query))} + empty_tables = {r[0] for r in frappe.db.sql(query)} results = frappe.get_all("DocType", fields=["name", "module"]) empty_tables_by_module = {}