From 739d0cd20fc5171a830869d52ecbac8d4b1ac89c Mon Sep 17 00:00:00 2001 From: Naren Date: Thu, 12 Mar 2020 17:40:32 +0530 Subject: [PATCH 01/49] fix: sticky footer logic implemented --- frappe/public/css/website.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frappe/public/css/website.css b/frappe/public/css/website.css index eecb7005e4..dd317952d9 100644 --- a/frappe/public/css/website.css +++ b/frappe/public/css/website.css @@ -1,7 +1,19 @@ /* the element that this class is applied to, should have a max width for this to work*/ +html { + height: 100%; +} body { + min-height: 100%; + display: flex; + flex-direction: column; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; } +body > div { + flex: 1 0 auto; +} +footer { + flex-shrink: 0; +} a { cursor: pointer; } From bdc9893820c5dbc0079d191010f1df52c8744f78 Mon Sep 17 00:00:00 2001 From: Naren Date: Fri, 13 Mar 2020 15:30:02 +0530 Subject: [PATCH 02/49] fix: logic moved from css to scss file --- frappe/public/css/website.css | 12 ------------ frappe/public/scss/website.scss | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/frappe/public/css/website.css b/frappe/public/css/website.css index dd317952d9..eecb7005e4 100644 --- a/frappe/public/css/website.css +++ b/frappe/public/css/website.css @@ -1,19 +1,7 @@ /* the element that this class is applied to, should have a max width for this to work*/ -html { - height: 100%; -} body { - min-height: 100%; - display: flex; - flex-direction: column; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; } -body > div { - flex: 1 0 auto; -} -footer { - flex-shrink: 0; -} a { cursor: pointer; } diff --git a/frappe/public/scss/website.scss b/frappe/public/scss/website.scss index a297054ee2..9901eb7fbf 100644 --- a/frappe/public/scss/website.scss +++ b/frappe/public/scss/website.scss @@ -4,8 +4,23 @@ @import "multilevel-dropdown"; @import "website-image"; +html { + height: 100%; +} + body { + min-height: 100%; + display: flex; + flex-direction: column; font-size: 16px; + + > div { + flex: 1 0 auto; + } +} + +footer { + flex-shrink: 0; } .navbar.bg-dark { From 6624f3a1af1c6328279c1d74303d479fd4ae41ba Mon Sep 17 00:00:00 2001 From: Saurabh Date: Fri, 3 Apr 2020 18:12:32 +0530 Subject: [PATCH 03/49] enhancement: provision to scheduled server scripts execution --- .../scheduled_job_type.json | 9 +++++++- .../scheduled_job_type/scheduled_job_type.py | 7 +++++- .../doctype/server_script/server_script.json | 14 +++++++++--- .../doctype/server_script/server_script.py | 22 +++++++++++++++++++ .../server_script/server_script_utils.py | 3 ++- frappe/utils/safe_exec.py | 3 +++ 6 files changed, 52 insertions(+), 6 deletions(-) diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json index e2ec921679..48c98c0bc4 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json @@ -12,6 +12,7 @@ "engine": "InnoDB", "field_order": [ "stopped", + "execute_via_server_script", "method", "frequency", "cron_format", @@ -63,6 +64,12 @@ "options": "All\nHourly\nHourly Long\nDaily\nDaily Long\nWeekly\nWeekly Long\nMonthly\nMonthly Long\nCron\nYearly\nAnnual", "read_only": 1, "reqd": 1 + }, + { + "default": "0", + "fieldname": "execute_via_server_script", + "fieldtype": "Check", + "label": "Execute Via Server Script" } ], "in_create": 1, @@ -72,7 +79,7 @@ "link_fieldname": "scheduled_job_type" } ], - "modified": "2019-12-09 11:10:21.259929", + "modified": "2020-04-03 17:14:13.899031", "modified_by": "Administrator", "module": "Core", "name": "Scheduled Job Type", diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py index 3cd994ebfa..b1b867d527 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -70,7 +70,12 @@ class ScheduledJobType(Document): self.scheduler_log = None try: self.log_status('Start') - frappe.get_attr(self.method)() + if self.execute_via_server_script: + script_name = frappe.db.get_value("Server Script", {'api_method': self.method}) + if script_name: + frappe.get_doc('Server Script', script_name).execute_scheduled_method() + else: + frappe.get_attr(self.method)() frappe.db.commit() self.log_status('Complete') except Exception: diff --git a/frappe/core/doctype/server_script/server_script.json b/frappe/core/doctype/server_script/server_script.json index 36c297cc26..aeed982162 100644 --- a/frappe/core/doctype/server_script/server_script.json +++ b/frappe/core/doctype/server_script/server_script.json @@ -11,6 +11,7 @@ "column_break_3", "reference_doctype", "doctype_event", + "scheduler_event", "api_method", "allow_guest", "section_break_8", @@ -22,7 +23,7 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Script Type", - "options": "DocType Event\nAPI", + "options": "DocType Event\nScheduler Event\nAPI", "reqd": 1 }, { @@ -47,7 +48,7 @@ "options": "Before Insert\nBefore Save\nAfter Save\nBefore Submit\nAfter Submit\nBefore Cancel\nAfter Cancel\nBefore Delete\nAfter Delete\nBefore Save (Submitted Document)\nAfter Save (Submitted Document)" }, { - "depends_on": "eval:doc.script_type==='API'", + "depends_on": "eval:doc.script_type==='API' || doc.script_type==='Scheduler Event'", "fieldname": "api_method", "fieldtype": "Data", "label": "API Method" @@ -72,10 +73,17 @@ { "fieldname": "section_break_8", "fieldtype": "Section Break" + }, + { + "depends_on": "eval:doc.script_type==='Scheduler Event'", + "fieldname": "scheduler_event", + "fieldtype": "Select", + "label": "Scheduler Event", + "options": "All\nHourly\nHourly Long\nDaily\nDaily Long\nWeekly\nWeekly Long\nMonthly\nMonthly Long\nCron\nYearly\nAnnual" } ], "links": [], - "modified": "2019-12-17 12:55:07.389775", + "modified": "2020-04-03 16:33:08.765621", "modified_by": "Administrator", "module": "Core", "name": "Server Script", diff --git a/frappe/core/doctype/server_script/server_script.py b/frappe/core/doctype/server_script/server_script.py index e2c6d3b7b0..c651da2269 100644 --- a/frappe/core/doctype/server_script/server_script.py +++ b/frappe/core/doctype/server_script/server_script.py @@ -8,6 +8,7 @@ import frappe from frappe.model.document import Document from frappe.utils.safe_exec import safe_exec + class ServerScript(Document): @staticmethod def validate(): @@ -15,6 +16,7 @@ class ServerScript(Document): @staticmethod def on_update(): + setup_scheduler_events() frappe.cache().delete_value('server_script_map') def execute_method(self): @@ -31,3 +33,23 @@ class ServerScript(Document): # execute event safe_exec(self.script, None, dict(doc = doc)) + def execute_scheduled_method(self): + if self.script_type == 'Scheduler Event': + safe_exec(self.script) + else: + # wrong report type! + raise frappe.DoesNotExistError + +def setup_scheduler_events(): + enabled_server_scripts = frappe.get_all('Server Script', + fields=('name', 'doctype_event','api_method', 'scheduler_event'), + filters={'disabled': 0, 'script_type': 'Scheduler Event'}) + + for script in enabled_server_scripts: + if not frappe.db.exists('Scheduled Job Type', dict(method=script.api_method)): + frappe.get_doc(dict( + doctype = 'Scheduled Job Type', + method = script.api_method, + frequency = script.scheduler_event, + has_server_script=1 + )).insert() \ No newline at end of file diff --git a/frappe/core/doctype/server_script/server_script_utils.py b/frappe/core/doctype/server_script/server_script_utils.py index 2e1a5ae8bb..e03504f30b 100644 --- a/frappe/core/doctype/server_script/server_script_utils.py +++ b/frappe/core/doctype/server_script/server_script_utils.py @@ -66,6 +66,7 @@ def get_server_script_map(): script_map.setdefault(script.reference_doctype, {}).setdefault(script.doctype_event, []).append(script.name) else: script_map.setdefault('_api', {})[script.api_method] = script.name + frappe.cache().set_value('server_script_map', script_map) - return script_map + return script_map \ No newline at end of file diff --git a/frappe/utils/safe_exec.py b/frappe/utils/safe_exec.py index 62d0286e03..ed949d8a01 100644 --- a/frappe/utils/safe_exec.py +++ b/frappe/utils/safe_exec.py @@ -11,6 +11,7 @@ from frappe.website.utils import (get_shade, get_toc, get_next_link) from frappe.modules import scrub from frappe.www.printview import get_visible_columns import frappe.exceptions +import frappe.integrations.utils class ServerScriptNotEnabled(frappe.PermissionError): pass @@ -79,6 +80,8 @@ def get_safe_globals(): user=user, csrf_token=frappe.local.session.data.csrf_token if getattr(frappe.local, "session", None) else '' ), + make_get_request = frappe.integrations.utils.make_get_request, + make_post_request = frappe.integrations.utils.make_post_request, socketio_port=frappe.conf.socketio_port, get_hooks=frappe.get_hooks, ), From 30e7d9e1512c2d9d774844c15dd30b41beee5797 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Mon, 6 Apr 2020 12:05:48 +0530 Subject: [PATCH 04/49] feat: link server scripts in scheduler --- .../scheduled_job_type.json | 14 ++++--- .../scheduled_job_type/scheduled_job_type.py | 4 +- .../doctype/server_script/server_script.js | 42 ++++++++++++++++++- .../doctype/server_script/server_script.json | 12 +----- .../doctype/server_script/server_script.py | 42 +++++++++++++------ 5 files changed, 81 insertions(+), 33 deletions(-) diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json index 48c98c0bc4..2a9c1a4573 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.json @@ -12,8 +12,8 @@ "engine": "InnoDB", "field_order": [ "stopped", - "execute_via_server_script", "method", + "server_script", "frequency", "cron_format", "last_execution", @@ -66,10 +66,12 @@ "reqd": 1 }, { - "default": "0", - "fieldname": "execute_via_server_script", - "fieldtype": "Check", - "label": "Execute Via Server Script" + "fieldname": "server_script", + "fieldtype": "Link", + "label": "Server Script", + "options": "Server Script", + "read_only": 1, + "search_index": 1 } ], "in_create": 1, @@ -79,7 +81,7 @@ "link_fieldname": "scheduled_job_type" } ], - "modified": "2020-04-03 17:14:13.899031", + "modified": "2020-04-05 17:27:33.480562", "modified_by": "Administrator", "module": "Core", "name": "Scheduled Job Type", diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py index b1b867d527..c179054550 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -70,8 +70,8 @@ class ScheduledJobType(Document): self.scheduler_log = None try: self.log_status('Start') - if self.execute_via_server_script: - script_name = frappe.db.get_value("Server Script", {'api_method': self.method}) + if self.server_script: + script_name = frappe.db.get_value("Server Script", self.server_script) if script_name: frappe.get_doc('Server Script', script_name).execute_scheduled_method() else: diff --git a/frappe/core/doctype/server_script/server_script.js b/frappe/core/doctype/server_script/server_script.js index eea8558456..d7f4c3e536 100644 --- a/frappe/core/doctype/server_script/server_script.js +++ b/frappe/core/doctype/server_script/server_script.js @@ -2,7 +2,45 @@ // For license information, please see license.txt frappe.ui.form.on('Server Script', { - // refresh: function(frm) { + refresh: function(frm) { + if(frm.doc.script_type === 'Scheduler Event' && !frm.doc.disabled){ + frm.add_custom_button('Schedule Script', function() { + var d = new frappe.ui.Dialog({ + title: "Schedule Script Execution", + fields: [ + { + fieldname: "event_type", + label: __('Select Event Type'), + fieldtype: "Select", + options: "All\nHourly\nDaily\nWeekly\nMonthly\nYearly\nHourly Long\nDaily Long\nWeekly Long\nMonthly Long" + }, + ], + primary_action_label: __('Schedule Script'), + primary_action: () => { + d.get_primary_btn().attr('disabled', true); + var data = d.get_values(); + d.hide(); + if(data) { + frm.events.schedule_script(frm, data); + } + + } + }); + + d.show(); + + }); + } + }, + + schedule_script(frm, data){ + frm.call({ + method: "frappe.core.doctype.server_script.server_script.setup_scheduler_events", + args: { + 'script_name': frm.doc.name, + 'frequency': data.event_type + } + }) + } - // } }); diff --git a/frappe/core/doctype/server_script/server_script.json b/frappe/core/doctype/server_script/server_script.json index aeed982162..bef3dfc60c 100644 --- a/frappe/core/doctype/server_script/server_script.json +++ b/frappe/core/doctype/server_script/server_script.json @@ -11,7 +11,6 @@ "column_break_3", "reference_doctype", "doctype_event", - "scheduler_event", "api_method", "allow_guest", "section_break_8", @@ -48,7 +47,7 @@ "options": "Before Insert\nBefore Save\nAfter Save\nBefore Submit\nAfter Submit\nBefore Cancel\nAfter Cancel\nBefore Delete\nAfter Delete\nBefore Save (Submitted Document)\nAfter Save (Submitted Document)" }, { - "depends_on": "eval:doc.script_type==='API' || doc.script_type==='Scheduler Event'", + "depends_on": "eval:doc.script_type==='API'", "fieldname": "api_method", "fieldtype": "Data", "label": "API Method" @@ -73,17 +72,10 @@ { "fieldname": "section_break_8", "fieldtype": "Section Break" - }, - { - "depends_on": "eval:doc.script_type==='Scheduler Event'", - "fieldname": "scheduler_event", - "fieldtype": "Select", - "label": "Scheduler Event", - "options": "All\nHourly\nHourly Long\nDaily\nDaily Long\nWeekly\nWeekly Long\nMonthly\nMonthly Long\nCron\nYearly\nAnnual" } ], "links": [], - "modified": "2020-04-03 16:33:08.765621", + "modified": "2020-04-06 11:24:38.161555", "modified_by": "Administrator", "module": "Core", "name": "Server Script", diff --git a/frappe/core/doctype/server_script/server_script.py b/frappe/core/doctype/server_script/server_script.py index c651da2269..9522b77b4b 100644 --- a/frappe/core/doctype/server_script/server_script.py +++ b/frappe/core/doctype/server_script/server_script.py @@ -7,6 +7,7 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils.safe_exec import safe_exec +from frappe import _ class ServerScript(Document): @@ -16,7 +17,6 @@ class ServerScript(Document): @staticmethod def on_update(): - setup_scheduler_events() frappe.cache().delete_value('server_script_map') def execute_method(self): @@ -40,16 +40,32 @@ class ServerScript(Document): # wrong report type! raise frappe.DoesNotExistError -def setup_scheduler_events(): - enabled_server_scripts = frappe.get_all('Server Script', - fields=('name', 'doctype_event','api_method', 'scheduler_event'), - filters={'disabled': 0, 'script_type': 'Scheduler Event'}) +@frappe.whitelist() +def setup_scheduler_events(script_name, frequency): + method = frappe.scrub(script_name) + '_' + frequency.lower() + scheduled_script = frappe.db.get_value('Scheduled Job Type', + dict(method=method)) - for script in enabled_server_scripts: - if not frappe.db.exists('Scheduled Job Type', dict(method=script.api_method)): - frappe.get_doc(dict( - doctype = 'Scheduled Job Type', - method = script.api_method, - frequency = script.scheduler_event, - has_server_script=1 - )).insert() \ No newline at end of file + if not scheduled_script: + doc = frappe.get_doc(dict( + doctype = 'Scheduled Job Type', + method = method, + frequency = frequency, + server_script = script_name + )) + + doc.insert() + + frappe.msgprint(_('Enabled scheduled execution for script {0}').format(script_name)) + + else: + doc = frappe.get_doc('Scheduled Job Type', scheduled_script) + doc.update(dict( + doctype = 'Scheduled Job Type', + method = method, + frequency = frequency, + server_script = script_name + )) + doc.save() + + frappe.msgprint(_('Scheduled execution for script {0} has updated').format(script_name)) From 24a8b036393c6aa71db16cb0fccf17064505c403 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Wed, 8 Apr 2020 20:27:38 +0530 Subject: [PATCH 05/49] chore: improve error message on delete --- frappe/model/delete_doc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index d77898020d..b6db154bd4 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -210,7 +210,7 @@ def check_permission_and_not_submitted(doc): # check if submitted if doc.docstatus == 1: - frappe.msgprint(_("{0} {1}: Submitted Record cannot be deleted.").format(_(doc.doctype), doc.name), + frappe.msgprint(_("{0} {1}: Submitted Record cannot be deleted. You must Cancel it first.").format(_(doc.doctype), doc.name), raise_exception=True) def check_if_doc_is_linked(doc, method="Delete"): From 5cc11910adced726619a81a4b3824aa298a8beb3 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 10 Apr 2020 13:34:38 +0530 Subject: [PATCH 06/49] fix: return Job object on frappe.enqueue_doc --- frappe/utils/background_jobs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/background_jobs.py b/frappe/utils/background_jobs.py index 03f063e058..4b37e850f0 100755 --- a/frappe/utils/background_jobs.py +++ b/frappe/utils/background_jobs.py @@ -73,7 +73,7 @@ def enqueue(method, queue='default', timeout=None, event=None, def enqueue_doc(doctype, name=None, method=None, queue='default', timeout=300, now=False, **kwargs): '''Enqueue a method to be run on a document''' - enqueue('frappe.utils.background_jobs.run_doc_method', doctype=doctype, name=name, + return enqueue('frappe.utils.background_jobs.run_doc_method', doctype=doctype, name=name, doc_method=method, queue=queue, timeout=timeout, now=now, **kwargs) def run_doc_method(doctype, name, doc_method, **kwargs): From 0a0045e9cc7cddcc77da1f8b7a42b4dd5d0c3e4e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 10 Apr 2020 13:38:28 +0530 Subject: [PATCH 07/49] chore: raise exception via frappe.msgprint --- frappe/core/doctype/file/file.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 6633884bb3..485885ec24 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -608,8 +608,7 @@ def get_local_image(file_url): try: image = Image.open(file_path) except IOError: - frappe.msgprint(_("Unable to read file format for {0}").format(file_url)) - raise + frappe.msgprint(_("Unable to read file format for {0}").format(file_url), raise_exception=True) content = None From 275a678bd658b75bd3a5f7e3b68722d58bce0533 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 13 Apr 2020 11:42:02 +0530 Subject: [PATCH 08/49] fix: site cleanup in case of errors on new-site Added _new_site_cleanup to handle exceptions raised in case of interuppted install of Frappe site. Deletes folders after calling _drop_site --- frappe/commands/site.py | 61 +++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/frappe/commands/site.py b/frappe/commands/site.py index fd43cc8cf3..055d4317a5 100755 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -1,13 +1,25 @@ -from __future__ import unicode_literals, absolute_import, print_function +# imports - standard imports +import atexit +import compileall +import hashlib +import os +import re +import shutil +import sys + +# imports - third party imports import click -import hashlib, os, sys, compileall, re +from pymysql.err import OperationalError +from six import text_type + +# imports - module imports import frappe from frappe import _ -from frappe.commands import pass_context, get_site +from frappe.commands import get_site, pass_context from frappe.commands.scheduler import _is_scheduler_enabled from frappe.installer import update_site_config -from frappe.utils import touch_file, get_site_path -from six import text_type +from frappe.utils import get_site_path, touch_file + @click.command('new-site') @click.argument('site') @@ -68,32 +80,33 @@ def _new_site(db_name, site, mariadb_root_username=None, mariadb_root_password=N make_site_dirs() - installing = None - try: - installing = touch_file(get_site_path('locks', 'installing.lock')) + installing = touch_file(get_site_path('locks', 'installing.lock')) + atexit.register(_new_site_cleanup, site, mariadb_root_username, mariadb_root_password) - install_db(root_login=mariadb_root_username, root_password=mariadb_root_password, - db_name=db_name, admin_password=admin_password, verbose=verbose, - source_sql=source_sql, force=force, reinstall=reinstall, db_password=db_password, db_type=db_type, db_host=db_host, db_port=db_port, no_mariadb_socket=no_mariadb_socket) + install_db(root_login=mariadb_root_username, root_password=mariadb_root_password, + db_name=db_name, admin_password=admin_password, verbose=verbose, + source_sql=source_sql, force=force, reinstall=reinstall, db_password=db_password, db_type=db_type, db_host=db_host, db_port=db_port, no_mariadb_socket=no_mariadb_socket) + apps_to_install = ['frappe'] + (frappe.conf.get("install_apps") or []) + (list(install_apps) or []) + for app in apps_to_install: + _install_app(app, verbose=verbose, set_as_patched=not source_sql) - apps_to_install = ['frappe'] + (frappe.conf.get("install_apps") or []) + (list(install_apps) or []) - for app in apps_to_install: - _install_app(app, verbose=verbose, set_as_patched=not source_sql) + os.remove(installing) - frappe.utils.scheduler.toggle_scheduler(enable_scheduler) - frappe.db.commit() + frappe.utils.scheduler.toggle_scheduler(enable_scheduler) + frappe.db.commit() - scheduler_status = "disabled" if frappe.utils.scheduler.is_scheduler_disabled() else "enabled" - print("*** Scheduler is", scheduler_status, "***") + scheduler_status = "disabled" if frappe.utils.scheduler.is_scheduler_disabled() else "enabled" + print("*** Scheduler is", scheduler_status, "***") - except frappe.exceptions.ImproperDBConfigurationError: - _drop_site(site, mariadb_root_username, mariadb_root_password, force=True) +def _new_site_cleanup(site, mariadb_root_username, mariadb_root_password): + installing = get_site_path('locks', 'installing.lock') - finally: - if installing and os.path.exists(installing): - os.remove(installing) + if installing and os.path.exists(installing): + if mariadb_root_password: + _drop_site(site, mariadb_root_username, mariadb_root_password, force=True) + shutil.rmtree(site) - frappe.destroy() + frappe.destroy() @click.command('restore') @click.argument('sql-file-path') From 41c6c09fb9e157df5b867a42d3f5926f9437e736 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 13 Apr 2020 12:05:46 +0530 Subject: [PATCH 09/49] feat: handle failed cases in publish_realtime --- frappe/public/js/frappe/form/dashboard.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/dashboard.js b/frappe/public/js/frappe/form/dashboard.js index c4c739dbcc..3b6ccd9a5c 100644 --- a/frappe/public/js/frappe/form/dashboard.js +++ b/frappe/public/js/frappe/form/dashboard.js @@ -125,8 +125,9 @@ frappe.ui.form.Dashboard = Class.extend({ }, format_percent: function(title, percent) { - var width = cint(percent) < 1 ? 1 : cint(percent); - var progress_class = "progress-bar-success"; + const percentage = cint(percent); + const width = percentage < 0 ? 100 : percentage; + const progress_class = percentage < 0 ? "progress-bar-danger" : "progress-bar-success"; return [{ title: title, From 30e0fa7561b9d21caff689fedbd943e7dd8f33c6 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 13 Apr 2020 12:21:31 +0530 Subject: [PATCH 10/49] style: remove unused imports --- frappe/commands/site.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/frappe/commands/site.py b/frappe/commands/site.py index 055d4317a5..540f250918 100755 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -9,8 +9,6 @@ import sys # imports - third party imports import click -from pymysql.err import OperationalError -from six import text_type # imports - module imports import frappe From b0e236badd319bd247c1e215cdee53d38aafb5cb Mon Sep 17 00:00:00 2001 From: Afshan Date: Mon, 13 Apr 2020 18:07:08 +0530 Subject: [PATCH 11/49] workflow for github actions to check translation formats --- .../workflows/check_translation_format.yml | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/check_translation_format.yml diff --git a/.github/workflows/check_translation_format.yml b/.github/workflows/check_translation_format.yml new file mode 100644 index 0000000000..e5d2031182 --- /dev/null +++ b/.github/workflows/check_translation_format.yml @@ -0,0 +1,42 @@ +name: Check format +on: + pull_request: + branches: + - master + - develop +jobs: + check_translation: + name: Check format + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Setup python3 + uses: actions/setup-python@v1 + with: + python-version: 3.6 + - name: Run check_translation for pull_request + run: | + import os + import subprocess + import re + errors_encounter = 0 + pattern = re.compile(r"_\(([\"']{,3})(?P((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P((?!\5).)*)\5)*(\s*,\s*(.)*?\s*(,\s*([\"'])(?P((?!\11).)*)\11)*)*\)") + temp = re.compile(r"_\(([\"']{,3})") + subprocess.run('git fetch origin '+os.environ['GITHUB_BASE_REF']+ ' :' +os.environ['GITHUB_BASE_REF']+ ' -q', shell=True) + files = subprocess.check_output('git diff --name-only '+os.environ['GITHUB_BASE_REF'], shell=True) + files = files.decode('utf-8') + files = files.split() + for file in files: + with open(file, 'r') as f: + for num, line in enumerate(f, 1): + all_matches = temp.finditer(line) + if all_matches: + for match in all_matches: + verify = pattern.search(line) + if not verify: + errors_encounter += 1 + print(num) + print(line) + if errors_encounter > 0 : + assert 1+1 == 3 + shell: python \ No newline at end of file From e6ff382d14af27140e13ea5014bb60e4cca5f65d Mon Sep 17 00:00:00 2001 From: Afshan Date: Mon, 13 Apr 2020 18:39:14 +0530 Subject: [PATCH 12/49] printing proper error message --- .github/workflows/check_translation_format.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check_translation_format.yml b/.github/workflows/check_translation_format.yml index e5d2031182..6b8ba91e99 100644 --- a/.github/workflows/check_translation_format.yml +++ b/.github/workflows/check_translation_format.yml @@ -1,4 +1,4 @@ -name: Check format +name: frappe_linter on: pull_request: branches: @@ -35,8 +35,9 @@ jobs: verify = pattern.search(line) if not verify: errors_encounter += 1 - print(num) - print(line) + print('A syntax error has been discovered at line number: ' + num) + print('Syntax error occured with: ' + line) if errors_encounter > 0 : + print('You can visit "https://frappe.io/docs/user/en/translations" to resolve this error.') assert 1+1 == 3 shell: python \ No newline at end of file From fe1dd4d88c9c2e538dd95b3fa16d416ac3890390 Mon Sep 17 00:00:00 2001 From: Afshan Date: Mon, 13 Apr 2020 18:40:44 +0530 Subject: [PATCH 13/49] renamed .yml file --- .../{check_translation_format.yml => translation_linter.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{check_translation_format.yml => translation_linter.yml} (100%) diff --git a/.github/workflows/check_translation_format.yml b/.github/workflows/translation_linter.yml similarity index 100% rename from .github/workflows/check_translation_format.yml rename to .github/workflows/translation_linter.yml From 821934db3d715d5f7415e97b38b8fa9247c36382 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 14 Apr 2020 12:27:26 +0530 Subject: [PATCH 14/49] feat: enable customizations for hidden sections --- frappe/public/js/frappe/views/desktop/desktop.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/views/desktop/desktop.js b/frappe/public/js/frappe/views/desktop/desktop.js index 12d33affa7..9f0df6e172 100644 --- a/frappe/public/js/frappe/views/desktop/desktop.js +++ b/frappe/public/js/frappe/views/desktop/desktop.js @@ -203,8 +203,8 @@ class DesktopPage { this.allow_customization && this.make_customization_link(); let create_shortcuts_and_cards = () => { - this.data.shortcuts.items.length && this.make_shortcuts(); - this.data.cards.items.length && this.make_cards(); + this.make_shortcuts(); + this.make_cards(); if (this.allow_customization) { // Move the widget group up to align with labels if customization is allowed @@ -212,7 +212,7 @@ class DesktopPage { } }; - if (!this.sections["onboarding"] && this.data.charts.items.length) { + if (!this.sections["onboarding"]) { this.make_charts().then(() => { create_shortcuts_and_cards(); }); From a9a7474bec32542a4f68a2a187e61dd61a1bdfc5 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 10 Apr 2020 18:46:57 +0530 Subject: [PATCH 15/49] fix: allow allow_on_submit to be set in customize form if docfield value is set (cherry picked from commit 57a3463cde4ee29dcdfac8daa664025fe3dd3662) --- frappe/custom/doctype/customize_form/customize_form.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index 68848d26f6..ab005bd428 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -208,9 +208,11 @@ class CustomizeForm(Document): self.validate_fieldtype_change(df, meta_df[0].get(property), df.get(property)) elif property == "allow_on_submit" and df.get(property): - frappe.msgprint(_("Row {0}: Not allowed to enable Allow on Submit for standard fields")\ - .format(df.idx)) - continue + if not frappe.db.get_value("DocField", + {"parent": self.doc_type, "fieldname": df.fieldname}, "allow_on_submit"): + frappe.msgprint(_("Row {0}: Not allowed to enable Allow on Submit for standard fields")\ + .format(df.idx)) + continue elif property == "reqd" and \ ((frappe.db.get_value("DocField", From 724983704679ee1a3cee762de1b8fde78e6c7cfb Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 15 Apr 2020 13:23:36 +0530 Subject: [PATCH 16/49] fix: ignore fieldtype check validation if new field type has unspecified maxlength --- frappe/custom/doctype/customize_form/customize_form.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index 68848d26f6..2524650d94 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -369,7 +369,12 @@ class CustomizeForm(Document): for allowed_changes in allowed_fieldtype_change: if (old_value in allowed_changes and new_value in allowed_changes): allowed = True - if frappe.db.type_map.get(old_value)[1] > frappe.db.type_map.get(new_value)[1]: + old_value_length = cint(frappe.db.type_map.get(old_value)[1]) + new_value_length = cint(frappe.db.type_map.get(new_value)[1]) + + # Ignore fieldtype check validation if new field type has unspecified maxlength + # Changes like DATA to TEXT, where new_value_lenth equals 0 will not be validated + if new_value_length and (old_value_length > new_value_length): self.check_length_for_fieldtypes.append({'df': df, 'old_value': old_value}) self.validate_fieldtype_length() else: @@ -381,7 +386,7 @@ class CustomizeForm(Document): def validate_fieldtype_length(self): for field in self.check_length_for_fieldtypes: df = field.get('df') - max_length = frappe.db.type_map.get(df.fieldtype)[1] + max_length = cint(frappe.db.type_map.get(df.fieldtype)[1]) fieldname = df.fieldname docs = frappe.db.sql(''' SELECT name, {fieldname}, LENGTH({fieldname}) AS len From 7696f11e3ce03e3442a1a54dc5046ff9d283998b Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 15 Apr 2020 13:24:01 +0530 Subject: [PATCH 17/49] feat: update test to check DATA to Text change --- frappe/tests/test_db_update.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/tests/test_db_update.py b/frappe/tests/test_db_update.py index 34378de3af..f243aa268f 100644 --- a/frappe/tests/test_db_update.py +++ b/frappe/tests/test_db_update.py @@ -11,6 +11,7 @@ class TestDBUpdate(unittest.TestCase): frappe.reload_doctype('User', force=True) frappe.model.meta.trim_tables('User') make_property_setter(doctype, 'bio', 'fieldtype', 'Text', 'Data') + make_property_setter(doctype, 'middle_name', 'fieldtype', 'Data', 'Text') make_property_setter(doctype, 'enabled', 'default', '1', 'Int') frappe.db.updatedb(doctype) From 9b9bbd8cef77204f45bcab1a5e60318dd9350948 Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 15 Apr 2020 14:08:52 +0530 Subject: [PATCH 18/49] format: fix whitespace Co-Authored-By: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/public/scss/website.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/scss/website.scss b/frappe/public/scss/website.scss index 9901eb7fbf..6f82e25ee0 100644 --- a/frappe/public/scss/website.scss +++ b/frappe/public/scss/website.scss @@ -10,8 +10,8 @@ html { body { min-height: 100%; - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; font-size: 16px; > div { From a5daf8ffa146d72a65082a1863f43d4abc99237d Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Wed, 15 Apr 2020 19:34:12 +0530 Subject: [PATCH 19/49] fix: check if field is a default field before getting df (#9953) --- frappe/model/base_document.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 13b2b61bef..9ab1ef7799 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -506,9 +506,13 @@ class BaseDocument(object): fetch_from_fieldname = _df.fetch_from.split('.')[-1] value = values[fetch_from_fieldname] if _df.fieldtype == 'Small Text' or _df.fieldtype == 'Text' or _df.fieldtype == 'Data': - fetch_from_df = frappe.get_meta(doctype).get_field(fetch_from_fieldname) - fetch_from_ft = fetch_from_df.get('fieldtype') + if fetch_from_fieldname in default_fields: + from frappe.model.meta import get_default_df + fetch_from_df = get_default_df(fetch_from_fieldname) + else: + fetch_from_df = frappe.get_meta(doctype).get_field(fetch_from_fieldname) + fetch_from_ft = fetch_from_df.get('fieldtype') if fetch_from_ft == 'Text Editor' and value: value = unescape_html(strip_html(value)) setattr(self, _df.fieldname, value) From e323441c1538b5e9bcfa964acefbd5d14524c462 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 17 Mar 2020 17:12:10 +0530 Subject: [PATCH 20/49] feat: Add Web View to compose webpages with components --- frappe/core/doctype/doctype/doctype.py | 4 +- frappe/utils/jinja.py | 15 +- frappe/website/doctype/css_class/__init__.py | 0 frappe/website/doctype/css_class/css_class.js | 8 + .../website/doctype/css_class/css_class.json | 60 ++++++ frappe/website/doctype/css_class/css_class.py | 10 + .../doctype/css_class/test_css_class.py | 10 + frappe/website/doctype/web_view/__init__.py | 0 .../doctype/web_view/templates/web_view.html | 65 +++++++ .../web_view/templates/web_view_row.html | 4 + .../website/doctype/web_view/test_web_view.py | 10 + frappe/website/doctype/web_view/web_view.js | 8 + frappe/website/doctype/web_view/web_view.json | 89 +++++++++ frappe/website/doctype/web_view/web_view.py | 59 ++++++ .../website/doctype/web_view_item/__init__.py | 0 .../doctype/web_view_item/web_view_item.json | 115 +++++++++++ .../doctype/web_view_item/web_view_item.py | 10 + .../doctype/website_theme/website_theme.js | 178 ------------------ .../doctype/website_theme/website_theme.json | 72 ++++++- .../doctype/website_theme/website_theme.py | 19 +- .../website_theme/website_theme_template.scss | 23 +++ 21 files changed, 568 insertions(+), 191 deletions(-) create mode 100644 frappe/website/doctype/css_class/__init__.py create mode 100644 frappe/website/doctype/css_class/css_class.js create mode 100644 frappe/website/doctype/css_class/css_class.json create mode 100644 frappe/website/doctype/css_class/css_class.py create mode 100644 frappe/website/doctype/css_class/test_css_class.py create mode 100644 frappe/website/doctype/web_view/__init__.py create mode 100644 frappe/website/doctype/web_view/templates/web_view.html create mode 100644 frappe/website/doctype/web_view/templates/web_view_row.html create mode 100644 frappe/website/doctype/web_view/test_web_view.py create mode 100644 frappe/website/doctype/web_view/web_view.js create mode 100644 frappe/website/doctype/web_view/web_view.json create mode 100644 frappe/website/doctype/web_view/web_view.py create mode 100644 frappe/website/doctype/web_view_item/__init__.py create mode 100644 frappe/website/doctype/web_view_item/web_view_item.json create mode 100644 frappe/website/doctype/web_view_item/web_view_item.py create mode 100644 frappe/website/doctype/website_theme/website_theme_template.scss diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index f970f51419..f7c9cbe28a 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -206,7 +206,7 @@ class DocType(Document): if d.fieldtype: if (not getattr(d, "fieldname", None)): if d.label: - d.fieldname = d.label.strip().lower().replace(' ','_') + d.fieldname = d.label.strip().lower().replace(' ','_').strip('?') if d.fieldname in restricted: d.fieldname = d.fieldname + '1' if d.fieldtype=='Section Break': @@ -914,7 +914,7 @@ def validate_fields(meta): if not d.permlevel: d.permlevel = 0 if d.fieldtype not in table_fields: d.allow_bulk_edit = 0 if not d.fieldname: - d.fieldname = d.fieldname.lower() + d.fieldname = d.fieldname.lower().strip('?') check_illegal_characters(d.fieldname) check_invalid_fieldnames(meta.get("name"), d.fieldname) diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py index 7c3b9b0482..bc26490422 100644 --- a/frappe/utils/jinja.py +++ b/frappe/utils/jinja.py @@ -68,10 +68,7 @@ def render_template(template, context, is_path=None, safe_render=True): if not template: return "" - # if it ends with .html then its a freaking path, not html - if (is_path - or template.startswith("templates/") - or (template.endswith('.html') and '\n' not in template)): + if (is_path or guess_is_path(template)): return get_jenv().get_template(template).render(context) else: if safe_render and ".__" in template: @@ -81,6 +78,16 @@ def render_template(template, context, is_path=None, safe_render=True): except TemplateError: throw(title="Jinja Template Error", msg="
{template}
{tb}
".format(template=template, tb=get_traceback())) +def guess_is_path(template): + # template can be passed as a path or content + # if its single line and ends with a html, then its probably a path + if not '\n' in template and '.' in template: + extn = template.rsplit('.')[-1] + if extn in ('html', 'css', 'scss', 'py'): + return True + + return False + def get_jloader(): import frappe diff --git a/frappe/website/doctype/css_class/__init__.py b/frappe/website/doctype/css_class/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/website/doctype/css_class/css_class.js b/frappe/website/doctype/css_class/css_class.js new file mode 100644 index 0000000000..4544e249bf --- /dev/null +++ b/frappe/website/doctype/css_class/css_class.js @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Frappe Technologies and contributors +// For license information, please see license.txt + +frappe.ui.form.on('CSS Class', { + // refresh: function(frm) { + + // } +}); diff --git a/frappe/website/doctype/css_class/css_class.json b/frappe/website/doctype/css_class/css_class.json new file mode 100644 index 0000000000..2a7e1e010e --- /dev/null +++ b/frappe/website/doctype/css_class/css_class.json @@ -0,0 +1,60 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2020-03-17 15:03:31.431344", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "is_global", + "is_dynamic", + "css" + ], + "fields": [ + { + "default": "0", + "fieldname": "is_global", + "fieldtype": "Check", + "label": "Is Global?" + }, + { + "fieldname": "css", + "fieldtype": "Code", + "in_list_view": 1, + "label": "CSS", + "reqd": 1 + }, + { + "default": "0", + "description": "Website Theme elements are accessible as Jinja variables. Example: \"{{ primary_color }}\"", + "fieldname": "is_dynamic", + "fieldtype": "Check", + "label": "Is Dynamic?" + } + ], + "links": [], + "modified": "2020-03-17 17:01:14.874631", + "modified_by": "Administrator", + "module": "Website", + "name": "CSS Class", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Website Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/frappe/website/doctype/css_class/css_class.py b/frappe/website/doctype/css_class/css_class.py new file mode 100644 index 0000000000..cb9e7483d4 --- /dev/null +++ b/frappe/website/doctype/css_class/css_class.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class CSSClass(Document): + pass diff --git a/frappe/website/doctype/css_class/test_css_class.py b/frappe/website/doctype/css_class/test_css_class.py new file mode 100644 index 0000000000..551b44e3f2 --- /dev/null +++ b/frappe/website/doctype/css_class/test_css_class.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestCSSClass(unittest.TestCase): + pass diff --git a/frappe/website/doctype/web_view/__init__.py b/frappe/website/doctype/web_view/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/website/doctype/web_view/templates/web_view.html b/frappe/website/doctype/web_view/templates/web_view.html new file mode 100644 index 0000000000..8515432f5b --- /dev/null +++ b/frappe/website/doctype/web_view/templates/web_view.html @@ -0,0 +1,65 @@ +{% extends "templates/web.html" %} + +{% block page_content %} + +{% if css_rules or css %} + +{% endif %} + +{% macro render_element(element) %} + {% if element.element_type=='Content' %} + {{ element.web_content_html }} + {% elif element.element_type=='Image' %} + + {% endif %} +{% endmacro %} + + +{% for section in sections %} +
+
+ {% if section.section_intro %} +
{{ section.section_intro }}
+ {% endif %} + {% if section.section_type == 'List' %} + {% for element in section.elements %} + {{ render_element(element) }} + {% endfor %} + {% elif section.section_type == 'Grid' %} +
+ {% for element in section.elements %} +
+ {{ render_element(element) }} +
+ {% endfor %} +
+ {% elif section.section_type == 'Tabbed' %} + +
+ {% for element in section.elements %} +
+ {{ render_element(element) }} +
+ {% endfor %} +
+ + {% endif %} +
+
+{% endfor %} +{% endblock %} + + \ No newline at end of file diff --git a/frappe/website/doctype/web_view/templates/web_view_row.html b/frappe/website/doctype/web_view/templates/web_view_row.html new file mode 100644 index 0000000000..2b999819cb --- /dev/null +++ b/frappe/website/doctype/web_view/templates/web_view_row.html @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/frappe/website/doctype/web_view/test_web_view.py b/frappe/website/doctype/web_view/test_web_view.py new file mode 100644 index 0000000000..70dc1ca906 --- /dev/null +++ b/frappe/website/doctype/web_view/test_web_view.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestWebView(unittest.TestCase): + pass diff --git a/frappe/website/doctype/web_view/web_view.js b/frappe/website/doctype/web_view/web_view.js new file mode 100644 index 0000000000..449c0949b6 --- /dev/null +++ b/frappe/website/doctype/web_view/web_view.js @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Frappe Technologies and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Web View', { + // refresh: function(frm) { + + // } +}); diff --git a/frappe/website/doctype/web_view/web_view.json b/frappe/website/doctype/web_view/web_view.json new file mode 100644 index 0000000000..964cc1fbae --- /dev/null +++ b/frappe/website/doctype/web_view/web_view.json @@ -0,0 +1,89 @@ +{ + "actions": [], + "allow_guest_to_view": 1, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:title", + "creation": "2020-03-16 15:28:03.828741", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "route", + "published", + "items", + "css" + ], + "fields": [ + { + "fieldname": "items", + "fieldtype": "Table", + "label": "Items", + "options": "Web View Item", + "reqd": 1 + }, + { + "fieldname": "title", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Title", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "route", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Route", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "published", + "fieldtype": "Check", + "label": "Published" + }, + { + "fieldname": "css", + "fieldtype": "Code", + "label": "CSS" + } + ], + "has_web_view": 1, + "links": [], + "modified": "2020-03-16 18:06:47.024221", + "modified_by": "Administrator", + "module": "Website", + "name": "Web View", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Website Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "creation", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/frappe/website/doctype/web_view/web_view.py b/frappe/website/doctype/web_view/web_view.py new file mode 100644 index 0000000000..7c8c9d76eb --- /dev/null +++ b/frappe/website/doctype/web_view/web_view.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.website.website_generator import WebsiteGenerator +from frappe.utils import markdown +import frappe + +class WebView(WebsiteGenerator): + def get_context(self, context): + # group items into sections + context.sections = [] + context.css_rules = [] + for item in self.items: + if not context.sections and item.element_type!='Section': + self.add_default_section(context) + + if item.element_type=='Section': + item.elements = [] + context.sections.append(item) + + if item.section_intro: + item.section_intro = markdown(item.section_intro) + + else: + if item.hide: + continue + + if item.web_content_type == 'Markdown': + item.web_content_html = markdown(item.web_content_markdown) + + if item.title: + item.element_id = frappe.scrub(item.title) + + context.sections[-1].elements.append(item) + + if item.element_class: + css, is_dynamic = frappe.db.get_value('CSS Class', item.element_class, ['css', 'is_dynamic']) + if is_dynamic: + css = frappe.render_template(css, self.get_theme()) + context.css_rules.append(css) + + def get_theme(self): + # get theme properties + if not hasattr(self, '_theme'): + default_theme = frappe.db.get_value("Website Settings", "Website Settings", "website_theme") + self._theme = frappe.get_value('Website Theme', default_theme, '*') + return self._theme + + def add_default_section(self, context): + # add a default section if not added + context.section.append(dict( + element_type='Section', + section_type='List', + title='Default Section', + elements=[] + )) diff --git a/frappe/website/doctype/web_view_item/__init__.py b/frappe/website/doctype/web_view_item/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/website/doctype/web_view_item/web_view_item.json b/frappe/website/doctype/web_view_item/web_view_item.json new file mode 100644 index 0000000000..7be1c35415 --- /dev/null +++ b/frappe/website/doctype/web_view_item/web_view_item.json @@ -0,0 +1,115 @@ +{ + "actions": [], + "creation": "2020-03-16 15:25:17.530296", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "element_type", + "title", + "hide", + "column_break_3", + "columns", + "element_class", + "section_break_5", + "section_type", + "web_content_type", + "web_content_html", + "web_content_markdown", + "image_url", + "section_intro" + ], + "fields": [ + { + "fieldname": "element_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Element Type", + "options": "Section\nContent\nHero\nParagraph\nImage\nQuote", + "reqd": 1 + }, + { + "depends_on": "eval:doc.element_type==='Section'", + "fieldname": "section_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Section Type", + "options": "\nList\nTabbed\nGrid" + }, + { + "depends_on": "eval:doc.element_type==='Content'", + "fieldname": "web_content_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Web Content Type", + "options": "\nHTML\nMarkdown" + }, + { + "depends_on": "eval:doc.web_content_type==='HTML'", + "fieldname": "web_content_html", + "fieldtype": "HTML Editor", + "label": "Web Content HTML" + }, + { + "depends_on": "eval:doc.web_content_type==='Markdown'", + "fieldname": "web_content_markdown", + "fieldtype": "Markdown Editor", + "label": "Web Content Markdown" + }, + { + "fieldname": "title", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Title" + }, + { + "fieldname": "columns", + "fieldtype": "Int", + "label": "Columns" + }, + { + "fieldname": "element_class", + "fieldtype": "Link", + "label": "Element Class", + "options": "CSS Class" + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:doc.element_type==='Image'", + "fieldname": "image_url", + "fieldtype": "Small Text", + "label": "Image URL" + }, + { + "depends_on": "eval:doc.element_type==='Section'", + "fieldname": "section_intro", + "fieldtype": "Markdown Editor", + "label": "Section Intro" + }, + { + "default": "0", + "fieldname": "hide", + "fieldtype": "Check", + "label": "Hide" + } + ], + "istable": 1, + "links": [], + "modified": "2020-03-17 17:06:37.157763", + "modified_by": "Administrator", + "module": "Website", + "name": "Web View Item", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/frappe/website/doctype/web_view_item/web_view_item.py b/frappe/website/doctype/web_view_item/web_view_item.py new file mode 100644 index 0000000000..cc440305c0 --- /dev/null +++ b/frappe/website/doctype/web_view_item/web_view_item.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class WebViewItem(Document): + pass diff --git a/frappe/website/doctype/website_theme/website_theme.js b/frappe/website/doctype/website_theme/website_theme.js index b39bd5defe..75ecbe15e3 100644 --- a/frappe/website/doctype/website_theme/website_theme.js +++ b/frappe/website/doctype/website_theme/website_theme.js @@ -6,7 +6,6 @@ frappe.ui.form.on('Website Theme', { frm.clear_custom_buttons(); frm.toggle_display(["module", "custom"], !frappe.boot.developer_mode); - frm.trigger('setup_configure_theme'); frm.trigger('set_default_theme_button_and_indicator'); if (!frm.doc.custom && !frappe.boot.developer_mode) { @@ -17,96 +16,6 @@ frappe.ui.form.on('Website Theme', { } }, - setup_configure_theme(frm) { - frm.add_custom_button(__('Configure Theme'), () => { - const d = new frappe.ui.Dialog({ - title: __('Configure Theme'), - fields: [ - { - label: __('Font Styles'), - fieldtype: 'Section Break' - }, - { - label: __('Google Font'), - fieldtype: 'Data', - fieldname: 'google_font', - description: __('Add the name of a "Google Web Font" e.g. "Open Sans"') - }, - { - label: __('Font Size (px)'), - fieldtype: 'Int', - fieldname: 'font_size', - default: 16 - }, - { - label: __('Theme Colors'), - fieldtype: 'Section Break', - }, - { - label: __('Primary Color'), - fieldtype: 'Color', - fieldname: 'primary_color' - }, - { - label: __('Dark Color'), - fieldtype: 'Color', - fieldname: 'dark_color' - }, - { - label: __('Text Color'), - fieldtype: 'Color', - fieldname: 'text_color' - }, - { - label: __('Background Color'), - fieldtype: 'Color', - fieldname: 'background_color' - }, - { - label: __('Misc'), - fieldtype: 'Section Break', - }, - { - label: __('Navbar Style'), - fieldtype: 'Select', - fieldname: 'navbar_style', - options: [ - 'Light', - 'Dark' - ], - default: 'Light' - }, - { - label: __('Enable Shadows'), - fieldtype: 'Check', - fieldname: 'enable_shadows' - }, - { - label: __('Enable Gradients'), - fieldtype: 'Check', - fieldname: 'enable_gradients' - }, - { - label: __('Rounded Corners'), - fieldtype: 'Check', - fieldname: 'enable_rounded', - default: 1 - }, - ], - primary_action: (values) => { - frm.set_value('theme_json', JSON.stringify(values)); - frm.events.set_theme_from_config(frm, values); - d.hide(); - } - }); - - if (frm.doc.theme_json) { - d.set_values(JSON.parse(frm.doc.theme_json)); - } - d.show(); - }); - }, - set_default_theme_button_and_indicator(frm) { frappe.db.get_single_value('Website Settings', 'website_theme') .then(value => { @@ -122,92 +31,5 @@ frappe.ui.form.on('Website Theme', { } } }); - }, - - set_theme_from_config(frm, config) { - const { - google_font, - font_size, - primary_color, - dark_color, - text_color, - background_color, - navbar_style, - enable_shadows, - enable_gradients, - enable_rounded - } = config; - - let scss_lines = []; - let js_lines = []; - if (google_font) { - const google_font_slug = google_font.split(' ').join('+'); - const font_family_default = `'-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif'`; - scss_lines.push( - `@import url('https://fonts.googleapis.com/css?family=${google_font_slug}:400,300,400italic,700&subset=latin,latin-ext');`, - `$font-family-sans-serif: "${google_font}", ${font_family_default};` - ); - } - if (primary_color) { - scss_lines.push( - `$primary: ${primary_color};` - ); - } - if (dark_color) { - scss_lines.push( - `$dark: ${dark_color};` - ); - } - if (text_color) { - scss_lines.push( - `$body-color: ${text_color};` - ); - } - if (background_color) { - scss_lines.push( - `$body-bg: ${background_color};` - ); - } - - scss_lines.push( - `$enable-shadows: ${Boolean(enable_shadows)};` - ); - - scss_lines.push( - `$enable-gradients: ${Boolean(enable_gradients)};` - ); - - scss_lines.push( - `$enable-rounded: ${Boolean(enable_rounded)};` - ); - - if (font_size) { - scss_lines.push( - '\n', - `body {\n\tfont-size: ${font_size}px;\n}` - ); - } - - if (navbar_style === 'Dark') { - if (!(frm.doc.js || '').includes(`.addClass('navbar-dark bg-dark')`)) { - js_lines.push( - `frappe.ready(() => {`, - `\t$('.navbar').removeClass('navbar-light bg-white').addClass('navbar-dark bg-dark')`, - `})` - ); - } - } - - scss_lines.push( - `@import "frappe/public/scss/website";`, - '\n' - ); - - // set scss - frm.set_value('theme_scss', scss_lines.join('\n')); - - // set js - const js = frm.doc.js || ''; - frm.set_value('js', js_lines.join('\n') + js); } }); diff --git a/frappe/website/doctype/website_theme/website_theme.json b/frappe/website/doctype/website_theme/website_theme.json index 0d59384f3c..63e70f9668 100644 --- a/frappe/website/doctype/website_theme/website_theme.json +++ b/frappe/website/doctype/website_theme/website_theme.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "field:theme", "creation": "2015-02-18 12:46:38.168929", @@ -9,7 +10,18 @@ "theme", "module", "custom", + "configuration_section", + "google_font", + "font_size", + "column_break_7", + "primary_color", + "text_color", + "light_color", + "dark_color", + "background_color", + "stylesheet_section", "theme_scss", + "custom_scss", "theme_json", "theme_url", "custom_js_section", @@ -43,7 +55,8 @@ "fieldname": "theme_scss", "fieldtype": "Code", "label": "Theme", - "options": "SCSS" + "options": "SCSS", + "read_only": 1 }, { "fieldname": "theme_url", @@ -68,9 +81,64 @@ "hidden": 1, "label": "Theme JSON", "options": "JSON" + }, + { + "fieldname": "configuration_section", + "fieldtype": "Section Break", + "label": "Configuration" + }, + { + "fieldname": "google_font", + "fieldtype": "Data", + "label": "Google Font" + }, + { + "fieldname": "font_size", + "fieldtype": "Data", + "label": "Font Size" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "primary_color", + "fieldtype": "Color", + "label": "Primary Color" + }, + { + "fieldname": "text_color", + "fieldtype": "Color", + "label": "Text Color" + }, + { + "fieldname": "dark_color", + "fieldtype": "Color", + "label": "Dark Color" + }, + { + "fieldname": "background_color", + "fieldtype": "Color", + "label": "Background Color" + }, + { + "fieldname": "stylesheet_section", + "fieldtype": "Section Break", + "label": "Stylesheet" + }, + { + "fieldname": "custom_scss", + "fieldtype": "Code", + "label": "Custom SCSS" + }, + { + "fieldname": "light_color", + "fieldtype": "Color", + "label": "Light Color" } ], - "modified": "2019-06-14 18:36:21.283390", + "links": [], + "modified": "2020-03-17 16:52:18.541152", "modified_by": "Administrator", "module": "Website", "name": "Website Theme", diff --git a/frappe/website/doctype/website_theme/website_theme.py b/frappe/website/doctype/website_theme/website_theme.py index f16fc90d79..9a20bab5f7 100644 --- a/frappe/website/doctype/website_theme/website_theme.py +++ b/frappe/website/doctype/website_theme/website_theme.py @@ -10,6 +10,7 @@ from os.path import join as join_path, exists as path_exists class WebsiteTheme(Document): def validate(self): self.validate_if_customizable() + self.render_theme() self.validate_theme() def on_update(self): @@ -35,12 +36,16 @@ class WebsiteTheme(Document): if self.is_standard_and_not_valid_user(): frappe.throw(_("Please Duplicate this Website Theme to customize.")) + def render_theme(self): + if self.google_font: + self.google_font = self.google_font.replace(' ', '+') + self.theme_scss = frappe.render_template('frappe/website/doctype/website_theme/website_theme_template.scss', self.as_dict()) + def validate_theme(self): '''Generate theme css if theme_scss has changed''' - if self.theme_scss: - doc_before_save = self.get_doc_before_save() - if doc_before_save is None or self.theme_scss != doc_before_save.theme_scss: - self.generate_bootstrap_theme() + doc_before_save = self.get_doc_before_save() + if doc_before_save is None or get_scss(self) != get_scss(doc_before_save): + self.generate_bootstrap_theme() def export_doc(self): """Export to standard folder `[module]/website_theme/[name]/[name].json`.""" @@ -59,7 +64,7 @@ class WebsiteTheme(Document): file_name = frappe.scrub(self.name) + '_' + frappe.generate_hash('Website Theme', 8) + '.css' output_path = join_path(frappe.utils.get_bench_path(), 'sites', 'assets', 'css', file_name) - content = self.theme_scss + content = get_scss(self) content = content.replace('\n', '\\n') command = ['node', 'generate_bootstrap_theme.js', output_path, content] @@ -116,3 +121,7 @@ def generate_theme_files_if_not_exist(): doc.save() except Exception: frappe.log_error(frappe.get_traceback(), "Theme File Generation Failed") + +def get_scss(doc): + return doc.theme_scss + '\n' + doc.custom_scss + diff --git a/frappe/website/doctype/website_theme/website_theme_template.scss b/frappe/website/doctype/website_theme/website_theme_template.scss new file mode 100644 index 0000000000..6267eb531d --- /dev/null +++ b/frappe/website/doctype/website_theme/website_theme_template.scss @@ -0,0 +1,23 @@ +{% if google_font %} +@import url('https://fonts.googleapis.com/css?family={{ google_font }}:400,400italic,600&subset=latin,latin-ext'); +$font-family-sans-serif: "{{ google_font }}", '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif'; +{% endif %} + +{% if primary_color %}$primary: {{ primary_color }};{% endif %} +{% if dark_color %}$dark: {{ dark_color }};{% endif %} +{% if text_color %}$body-color: {{ text_color }};{% endif %} +{% if background_color %}$body-bg: {{ background_color }};{% endif %} + +$enable-shadows: {{ enable_shadows and "true" or "false" }}; +$enable-gradients: {{ enable_gradients and "true" or "false" }}; +$enable-rounded: {{ enable_rounded and "true" or "false" }}; + +@import "frappe/public/scss/website"; + +body { + {% if font_size %} + font-size: {{ font_size }}; + {% endif %} + font-smoothing: antialiased; + -webkit-font-smoothing: antialiased; +} From 0322ab198d3f1fbf63ee8427a30615ce50eaec0d Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 18 Mar 2020 18:51:03 +0530 Subject: [PATCH 21/49] feat: make Web View embeddable and allow navbar and footer as Web Views --- frappe/templates/base.html | 12 +++- .../doctype/web_view/templates/web_view.html | 60 +----------------- .../web_view/templates/web_view_content.html | 62 +++++++++++++++++++ frappe/website/doctype/web_view/web_view.py | 51 +++++++++------ .../website_settings/test_website_settings.py | 10 +++ .../website_settings/website_settings.json | 38 ++++++++++++ .../website_settings/website_settings.py | 12 ++++ .../doctype/website_theme/website_theme.json | 9 ++- .../website_theme/website_theme_template.scss | 4 +- 9 files changed, 176 insertions(+), 82 deletions(-) create mode 100644 frappe/website/doctype/web_view/templates/web_view_content.html create mode 100644 frappe/website/doctype/website_settings/test_website_settings.py diff --git a/frappe/templates/base.html b/frappe/templates/base.html index 1c5f286442..2a241c4843 100644 --- a/frappe/templates/base.html +++ b/frappe/templates/base.html @@ -62,7 +62,11 @@ {%- endblock -%} {%- block navbar -%} - {% include "templates/includes/navbar/navbar.html" %} + {%- if navbar_content -%} + {{ navbar_content }} + {%- else -%} + {% include "templates/includes/navbar/navbar.html" %} + {%- endif -%} {%- endblock -%} {% block content %} @@ -70,7 +74,11 @@ {% endblock %} {%- block footer -%} - {% include "templates/includes/footer/footer.html" %} + {%- if footer_content -%} + {{ footer_content }} + {%- else -%} + {% include "templates/includes/footer/footer.html" %} + {%- endif -%} {%- endblock -%} {% block base_scripts %} diff --git a/frappe/website/doctype/web_view/templates/web_view.html b/frappe/website/doctype/web_view/templates/web_view.html index 8515432f5b..bf993c05fb 100644 --- a/frappe/website/doctype/web_view/templates/web_view.html +++ b/frappe/website/doctype/web_view/templates/web_view.html @@ -1,65 +1,7 @@ {% extends "templates/web.html" %} {% block page_content %} - -{% if css_rules or css %} - -{% endif %} - -{% macro render_element(element) %} - {% if element.element_type=='Content' %} - {{ element.web_content_html }} - {% elif element.element_type=='Image' %} - - {% endif %} -{% endmacro %} - - -{% for section in sections %} -
-
- {% if section.section_intro %} -
{{ section.section_intro }}
- {% endif %} - {% if section.section_type == 'List' %} - {% for element in section.elements %} - {{ render_element(element) }} - {% endfor %} - {% elif section.section_type == 'Grid' %} -
- {% for element in section.elements %} -
- {{ render_element(element) }} -
- {% endfor %} -
- {% elif section.section_type == 'Tabbed' %} - -
- {% for element in section.elements %} -
- {{ render_element(element) }} -
- {% endfor %} -
- - {% endif %} -
-
-{% endfor %} + {% include "frappe/website/doctype/web_view/templates/web_view_content.html" %} {% endblock %} \ No newline at end of file diff --git a/frappe/website/doctype/web_view/templates/web_view_content.html b/frappe/website/doctype/web_view/templates/web_view_content.html new file mode 100644 index 0000000000..dd14e80d39 --- /dev/null +++ b/frappe/website/doctype/web_view/templates/web_view_content.html @@ -0,0 +1,62 @@ +{%- if css_rules or css -%} + + +{%- endif -%} + +{%- macro render_element(element) -%} + {%- if element.element_type=='Content' -%} + {{ element.web_content_html }} + {%- elif element.element_type=='Image' -%} + + {%- endif -%} +{%- endmacro -%} + +{%- for section in sections -%} +
+
+ {%- if section.section_intro -%} + +
{{ section.section_intro }}
+ {%- endif -%} + + {%- if section.section_type == 'List' -%} + {%- for element in section.elements -%} + {{ render_element(element) }} + {%- endfor -%} + + {%- elif section.section_type == 'Grid' -%} +
+ {%- for element in section.elements -%} +
+ {{ render_element(element) }} +
+ {%- endfor -%} +
+ + {%- elif section.section_type == 'Tabbed' -%} + +
+ {%- for element in section.elements -%} +
+ {{ render_element(element) }} +
+ {%- endfor -%} +
+ + {%- endif -%} +
+
+{%- endfor -%} \ No newline at end of file diff --git a/frappe/website/doctype/web_view/web_view.py b/frappe/website/doctype/web_view/web_view.py index 7c8c9d76eb..92f564553c 100644 --- a/frappe/website/doctype/web_view/web_view.py +++ b/frappe/website/doctype/web_view/web_view.py @@ -18,29 +18,44 @@ class WebView(WebsiteGenerator): self.add_default_section(context) if item.element_type=='Section': - item.elements = [] - context.sections.append(item) - - if item.section_intro: - item.section_intro = markdown(item.section_intro) - + self.add_section(context, item) else: - if item.hide: - continue + self.add_item(context, item) - if item.web_content_type == 'Markdown': - item.web_content_html = markdown(item.web_content_markdown) + self.add_css_class(context, item) - if item.title: - item.element_id = frappe.scrub(item.title) + return context - context.sections[-1].elements.append(item) + def add_section(self, context, item): + item.elements = [] + context.sections.append(item) - if item.element_class: - css, is_dynamic = frappe.db.get_value('CSS Class', item.element_class, ['css', 'is_dynamic']) - if is_dynamic: - css = frappe.render_template(css, self.get_theme()) - context.css_rules.append(css) + if item.section_intro: + item.section_intro = markdown(item.section_intro) + + def add_item(self, context, item): + if item.hide: + return + + if item.web_content_type == 'Markdown': + item.web_content_html = markdown(item.web_content_markdown) + + if item.title: + item.element_id = frappe.scrub(item.title) + + context.sections[-1].elements.append(item) + + def add_css_class(self, context, item): + # add css class definitions selected by the user + if item.element_class and not item.hide: + css, is_dynamic = frappe.db.get_value('CSS Class', item.element_class, ['css', 'is_dynamic']) + if is_dynamic: + css = frappe.render_template(css, self.get_theme()) + context.css_rules.append(css) + + def render_content(self): + # webview can be rendered as an object (see footer) + return frappe.render_template("frappe/website/doctype/web_view/templates/web_view_content.html", self.get_context(self.as_dict())) def get_theme(self): # get theme properties diff --git a/frappe/website/doctype/website_settings/test_website_settings.py b/frappe/website/doctype/website_settings/test_website_settings.py new file mode 100644 index 0000000000..9eca957713 --- /dev/null +++ b/frappe/website/doctype/website_settings/test_website_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestWebsiteSettings(unittest.TestCase): + pass diff --git a/frappe/website/doctype/website_settings/website_settings.json b/frappe/website/doctype/website_settings/website_settings.json index 39ffa2329f..a730f4746f 100644 --- a/frappe/website/doctype/website_settings/website_settings.json +++ b/frappe/website/doctype/website_settings/website_settings.json @@ -19,11 +19,15 @@ "set_banner_from_image", "favicon", "top_bar", + "top_bar_type", + "top_bar_web_view", "navbar_search", "top_bar_items", "banner", "banner_html", "footer", + "footer_type", + "footer_web_view", "copyright", "address", "footer_items", @@ -130,11 +134,13 @@ }, { "default": "0", + "depends_on": "eval:doc.top_bar_type==='Standard'", "fieldname": "navbar_search", "fieldtype": "Check", "label": "Include Search in Top Bar" }, { + "depends_on": "eval:doc.top_bar_type==='Standard'", "fieldname": "top_bar_items", "fieldtype": "Table", "label": "Top Bar Items", @@ -160,17 +166,20 @@ "label": "Footer" }, { + "depends_on": "eval:doc.footer_type==='Standard'", "fieldname": "copyright", "fieldtype": "Data", "label": "Copyright" }, { + "depends_on": "eval:doc.footer_type==='Standard'", "description": "Address and other legal information you may want to put in the footer.", "fieldname": "address", "fieldtype": "Text Editor", "label": "Address" }, { + "depends_on": "eval:doc.footer_type==='Standard'", "fieldname": "footer_items", "fieldtype": "Table", "label": "Footer Items", @@ -178,6 +187,7 @@ }, { "default": "0", + "depends_on": "eval:doc.footer_type==='Standard'", "fieldname": "hide_footer_signup", "fieldtype": "Check", "label": "Hide Footer Signup" @@ -319,6 +329,34 @@ "fieldname": "authorize_api_indexing_access", "fieldtype": "Button", "label": "Authorize API Indexing Access" + }, + { + "default": "Standard", + "fieldname": "footer_type", + "fieldtype": "Select", + "label": "Footer Type", + "options": "Standard\nWeb View" + }, + { + "depends_on": "eval:doc.footer_type==='Web View'", + "fieldname": "footer_web_view", + "fieldtype": "Link", + "label": "Footer Web View", + "options": "Web View" + }, + { + "default": "Standard", + "fieldname": "top_bar_type", + "fieldtype": "Select", + "label": "Top Bar Type", + "options": "Standard\nWeb View" + }, + { + "depends_on": "eval:doc.top_bar_type==='Web View'", + "fieldname": "top_bar_web_view", + "fieldtype": "Link", + "label": "Top Bar Web View", + "options": "Web View" } ], "icon": "fa fa-cog", diff --git a/frappe/website/doctype/website_settings/website_settings.py b/frappe/website/doctype/website_settings/website_settings.py index 4356b1aa82..49b93fae1d 100644 --- a/frappe/website/doctype/website_settings/website_settings.py +++ b/frappe/website/doctype/website_settings/website_settings.py @@ -149,6 +149,7 @@ def get_website_settings(): context[key] = context[key][-1] add_website_theme(context) + add_webviews(context, settings) if not context.get("favicon"): context["favicon"] = "/assets/frappe/images/favicon.png" @@ -158,6 +159,17 @@ def get_website_settings(): return context +def add_webviews(context, settings): + # render footer as webview, not standard view + # see base.html for how this is handled + if settings.footer_type=='Web View' and settings.footer_web_view: + context.footer_content = frappe.get_doc('Web View', + settings.footer_web_view).render_content() + + if settings.top_bar_type=='Web View' and settings.top_bar_web_view: + context.navbar_content = frappe.get_doc('Web View', + settings.top_bar_web_view).render_content() + def get_items(parentfield): all_top_items = frappe.db.sql("""\ select * from `tabTop Bar Item` diff --git a/frappe/website/doctype/website_theme/website_theme.json b/frappe/website/doctype/website_theme/website_theme.json index 63e70f9668..5704b97e0b 100644 --- a/frappe/website/doctype/website_theme/website_theme.json +++ b/frappe/website/doctype/website_theme/website_theme.json @@ -13,6 +13,7 @@ "configuration_section", "google_font", "font_size", + "font_properties", "column_break_7", "primary_color", "text_color", @@ -135,10 +136,16 @@ "fieldname": "light_color", "fieldtype": "Color", "label": "Light Color" + }, + { + "default": "300,600", + "fieldname": "font_properties", + "fieldtype": "Data", + "label": "Font Properties" } ], "links": [], - "modified": "2020-03-17 16:52:18.541152", + "modified": "2020-03-18 18:24:57.469492", "modified_by": "Administrator", "module": "Website", "name": "Website Theme", diff --git a/frappe/website/doctype/website_theme/website_theme_template.scss b/frappe/website/doctype/website_theme/website_theme_template.scss index 6267eb531d..ea6775a73d 100644 --- a/frappe/website/doctype/website_theme/website_theme_template.scss +++ b/frappe/website/doctype/website_theme/website_theme_template.scss @@ -1,6 +1,6 @@ {% if google_font %} -@import url('https://fonts.googleapis.com/css?family={{ google_font }}:400,400italic,600&subset=latin,latin-ext'); -$font-family-sans-serif: "{{ google_font }}", '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif'; +@import url('https://fonts.googleapis.com/css?family={{ google_font }}:{{ font_properties }}&display=swap'); +$font-family-sans-serif: "{{ google_font }}", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; {% endif %} {% if primary_color %}$primary: {{ primary_color }};{% endif %} From 6b5a47c5997b64cd09e520ee7a62134d8ddfa451 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 27 Mar 2020 11:29:50 +0530 Subject: [PATCH 22/49] fix: minor bug fixes --- frappe/templates/web.html | 2 +- .../web_view/templates/web_view_content.html | 21 ++++++++++++++++--- .../doctype/web_view_item/web_view_item.json | 8 ++++++- .../doctype/website_theme/website_theme.json | 9 +++++++- .../doctype/website_theme/website_theme.py | 2 -- .../website_theme/website_theme_template.scss | 4 +--- 6 files changed, 35 insertions(+), 11 deletions(-) diff --git a/frappe/templates/web.html b/frappe/templates/web.html index e61672124a..d2d38a6320 100644 --- a/frappe/templates/web.html +++ b/frappe/templates/web.html @@ -13,7 +13,7 @@ {% block page_container %} -
+