From 6417c87afe2746bab4b1a1cb861b25a172f228a7 Mon Sep 17 00:00:00 2001 From: Felipe Orellana Date: Wed, 25 Mar 2020 22:12:48 +0000 Subject: [PATCH 01/25] feat: Adds small improvements to chat notifications --- frappe/public/js/frappe/chat.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/chat.js b/frappe/public/js/frappe/chat.js index 810de89874..9d82b4d593 100644 --- a/frappe/public/js/frappe/chat.js +++ b/frappe/public/js/frappe/chat.js @@ -1566,7 +1566,11 @@ class extends Component { const alert = // TODO: ellipses content ` - ${frappe.user.first_name(r.user)}: ${r.content} + + + + + ${frappe.user.first_name(r.user)}: ${r.content} ` frappe.show_alert(alert, 15, { @@ -1575,6 +1579,11 @@ class extends Component { this.base.firstChild._component.toggle() }.bind(this, r) }) + frappe.notify(`${frappe.user.first_name(r.user)}`, { + body: r.content, + icon: frappe.user.image(r.user), + requireInteraction: true + }) } if ( r.room === state.room.name ) { From 945ee717920432614bbceced721642f9ea09d509 Mon Sep 17 00:00:00 2001 From: Felipe Orellana Date: Wed, 25 Mar 2020 22:38:14 +0000 Subject: [PATCH 02/25] feat: adds notification tag to avoid spamming recipient --- frappe/public/js/frappe/chat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/chat.js b/frappe/public/js/frappe/chat.js index 9d82b4d593..04633bd920 100644 --- a/frappe/public/js/frappe/chat.js +++ b/frappe/public/js/frappe/chat.js @@ -1582,7 +1582,7 @@ class extends Component { frappe.notify(`${frappe.user.first_name(r.user)}`, { body: r.content, icon: frappe.user.image(r.user), - requireInteraction: true + tag: r.user }) } From 29d01a1e699df22f6d3d9410f5abb639941512a8 Mon Sep 17 00:00:00 2001 From: Rohan Date: Fri, 10 Apr 2020 11:42:18 +0530 Subject: [PATCH 03/25] fix: remove old quote structure Co-Authored-By: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/public/js/frappe/chat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/chat.js b/frappe/public/js/frappe/chat.js index 04633bd920..8b1c09ac93 100644 --- a/frappe/public/js/frappe/chat.js +++ b/frappe/public/js/frappe/chat.js @@ -1568,7 +1568,7 @@ class extends Component { - + ${frappe.user.first_name(r.user)}: ${r.content} From f96fe1d8ea51d651aea0693fe8aa34696ed3c95a Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 13 Apr 2020 12:24:24 +0530 Subject: [PATCH 04/25] feat: dont save document if not updated This checks if form is dirty and only then allows form saving --- frappe/public/js/frappe/form/form.js | 2 +- frappe/public/js/frappe/form/save.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index b55c822ba6..e714418375 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -1052,7 +1052,7 @@ frappe.ui.form.Form = class FrappeForm { } is_dirty() { - return this.doc.__unsaved; + return !!this.doc.__unsaved; } is_new() { diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js index fbc35634f4..d40b3ed341 100644 --- a/frappe/public/js/frappe/form/save.js +++ b/frappe/public/js/frappe/form/save.js @@ -21,7 +21,7 @@ frappe.ui.form.save = function (frm, action, callback, btn) { remove_empty_rows(); $(frm.wrapper).addClass('validated-form'); - if (check_mandatory()) { + if (frm.is_dirty() && check_mandatory()) { _call({ method: "frappe.desk.form.save.savedocs", args: { doc: frm.doc, action: action }, @@ -36,6 +36,7 @@ frappe.ui.form.save = function (frm, action, callback, btn) { freeze_message: freeze_message }); } else { + frappe.show_alert({message: __("Document not updated"), indicator: "yellow"}); $(btn).prop("disabled", false); } }; From c188de894712da59516da16ce17889c352ce7d0f Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 13 Apr 2020 16:10:34 +0530 Subject: [PATCH 05/25] fix: No access for private files by default --- frappe/core/doctype/file/file.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 6633884bb3..cfad752c1d 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -517,7 +517,7 @@ class File(Document): delete_file(self.thumbnail_url) def is_downloadable(self): - return self.is_private and has_permission(self, 'read') + return has_permission(self, 'read') def get_extension(self): '''returns split filename and extension''' @@ -712,7 +712,7 @@ def remove_all(dt, dn, from_delete=False): def has_permission(doc, ptype=None, user=None): - permission = True + permission = False if doc.is_private else True if doc.attached_to_doctype and doc.attached_to_name: attached_to_doctype = doc.attached_to_doctype From 310f32175bf50aab6e6e0682002ff9cf36a872ae Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 13 Apr 2020 17:07:38 +0530 Subject: [PATCH 06/25] fix: Allow private files if owner is accessing --- frappe/core/doctype/file/file.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index cfad752c1d..7f763ea9fc 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -712,7 +712,11 @@ def remove_all(dt, dn, from_delete=False): def has_permission(doc, ptype=None, user=None): - permission = False if doc.is_private else True + has_access = False + user = user or frappe.session.user + + if not doc.is_private or doc.owner == user or user == 'Administrator': + has_access = True if doc.attached_to_doctype and doc.attached_to_name: attached_to_doctype = doc.attached_to_doctype @@ -722,20 +726,20 @@ def has_permission(doc, ptype=None, user=None): ref_doc = frappe.get_doc(attached_to_doctype, attached_to_name) if ptype in ['write', 'create', 'delete']: - permission = ref_doc.has_permission('write') + has_access = ref_doc.has_permission('write') - if ptype == 'delete' and permission == False: + if ptype == 'delete' and not has_access: frappe.throw(_("Cannot delete file as it belongs to {0} {1} for which you do not have permissions").format( doc.attached_to_doctype, doc.attached_to_name), frappe.PermissionError) else: - permission = ref_doc.has_permission('read') + has_access = ref_doc.has_permission('read') except frappe.DoesNotExistError: # if parent doc is not created before file is created - # we cannot check its permission so allow the file - permission = True + # we cannot check its permission so we will use file's permission + pass - return permission + return has_access def remove_file_by_url(file_url, doctype=None, name=None): From e57f005622cf962667a2dbf9775122c2b54ce099 Mon Sep 17 00:00:00 2001 From: ci2014 Date: Mon, 13 Apr 2020 14:30:44 +0200 Subject: [PATCH 07/25] fix: frappe.utils in get_context (#9844) * Remove point from frappe.utils in get_context For example frappe.utils.data.today() does not work. If we change it and use frappeutils.data.today() it works. This has to do with Jinja. Any better ideas? * fix: frappe.utils in context Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/email/doctype/notification/notification.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index 6a3dd89873..8c011ade65 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -335,4 +335,4 @@ def evaluate_alert(doc, alert, event): frappe.utils.get_link_to_form('Error Log', error_log.name))) def get_context(doc): - return {"doc": doc, "nowdate": nowdate, "frappe.utils": frappe.utils} + return {"doc": doc, "nowdate": nowdate, "frappe": frappe._dict(utils=frappe.utils)} From 8a85c2fe02e0fa1415d14cdc25397b960c50c4f8 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 13 Apr 2020 18:53:18 +0530 Subject: [PATCH 08/25] refactor: separate custom doctypes and reports --- frappe/desk/desktop.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frappe/desk/desktop.py b/frappe/desk/desktop.py index ce02a9b397..fdea95ff7f 100644 --- a/frappe/desk/desktop.py +++ b/frappe/desk/desktop.py @@ -276,9 +276,13 @@ def get_table_with_counts(): def get_custom_reports_and_doctypes(module): return [ _dict({ - "label": "Custom", - "links": get_custom_doctype_list(module) + get_custom_report_list(module) - }) + "label": _("Custom Documents"), + "links": get_custom_doctype_list(module) + }), + _dict({ + "label": _("Custom Reports"), + "links": get_custom_report_list(module) + }), ] def get_custom_doctype_list(module): From 796d94cc6dfde4fd869a0c31bc90a0bfaa2d6945 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 13 Apr 2020 20:11:11 +0530 Subject: [PATCH 09/25] fix: minor (#9927) --- frappe/desk/reportview.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 9bb12a4ec8..6102be61ce 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -367,8 +367,11 @@ def scrub_user_tags(tagcount): return rlist # used in building query in queries.py -def get_match_cond(doctype): - cond = DatabaseQuery(doctype).build_match_conditions() +def get_match_cond(doctype, as_condition=True): + cond = DatabaseQuery(doctype).build_match_conditions(as_condition=as_condition) + if not as_condition: + return cond + return ((' and ' + cond) if cond else "").replace("%", "%%") def build_match_conditions(doctype, user=None, as_condition=True): From e7dcce45e6c6eb6d1388d349070932ad6e77d0eb Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Tue, 14 Apr 2020 12:21:06 +0530 Subject: [PATCH 10/25] fix: do not show hidden field in report builder --- frappe/public/js/frappe/views/reports/report_view.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index b479c4c101..43540f449d 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -837,6 +837,9 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { fieldtype: 'MultiCheck', columns: 2, options: columns[this.doctype] + .filter(df => { + return !df.hidden; + }) .map(df => ({ label: __(df.label), value: df.fieldname, @@ -858,6 +861,9 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { fieldtype: 'MultiCheck', columns: 2, options: columns[cdt] + .filter(df => { + return !df.hidden; + }) .map(df => ({ label: __(df.label), value: df.fieldname, From 80f27f6081a1c9f09a6d0785e6d73723e9c9e40b Mon Sep 17 00:00:00 2001 From: Himanshu Date: Tue, 14 Apr 2020 13:01:49 +0530 Subject: [PATCH 11/25] fix: remove Customer & Supplier to hooks (#9874) --- frappe/email/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/__init__.py b/frappe/email/__init__.py index f99536f9a8..d58b35040e 100644 --- a/frappe/email/__init__.py +++ b/frappe/email/__init__.py @@ -65,7 +65,7 @@ def get_communication_doctype(doctype, txt, searchfield, start, page_len, filter com_doctypes = [] if len(txt)<2: - for name in ["Customer", "Supplier"]: + for name in frappe.get_hooks("communication_doctypes"): try: module = load_doctype_module(name, suffix='_dashboard') if hasattr(module, 'get_data'): From a9a7474bec32542a4f68a2a187e61dd61a1bdfc5 Mon Sep 17 00:00:00 2001 From: prssanna Date: Fri, 10 Apr 2020 18:46:57 +0530 Subject: [PATCH 12/25] 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 35398e8879768d2298efbe3fa841db5e8541712a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 15 Apr 2020 02:30:25 +0530 Subject: [PATCH 13/25] fix: hide mandatory property for html, section / column break, button fieldtype --- frappe/core/doctype/docfield/docfield.json | 3 ++- .../doctype/customize_form_field/customize_form_field.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json index 9b04ebb7ad..4614dd09c4 100644 --- a/frappe/core/doctype/docfield/docfield.json +++ b/frappe/core/doctype/docfield/docfield.json @@ -102,6 +102,7 @@ }, { "default": "0", + "depends_on": "eval:!in_list([\"Section Break\", \"Column Break\", \"Button\", \"HTML\"], doc.fieldtype)", "fieldname": "reqd", "fieldtype": "Check", "in_list_view": 1, @@ -452,7 +453,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-16 14:49:49.672099", + "modified": "2020-04-15 02:26:03.310781", "modified_by": "Administrator", "module": "Core", "name": "DocField", diff --git a/frappe/custom/doctype/customize_form_field/customize_form_field.json b/frappe/custom/doctype/customize_form_field/customize_form_field.json index 34778a76e9..350d159541 100644 --- a/frappe/custom/doctype/customize_form_field/customize_form_field.json +++ b/frappe/custom/doctype/customize_form_field/customize_form_field.json @@ -93,6 +93,7 @@ }, { "default": "0", + "depends_on": "eval:!in_list([\"Section Break\", \"Column Break\", \"Button\", \"HTML\"], doc.fieldtype)", "fieldname": "reqd", "fieldtype": "Check", "label": "Mandatory", @@ -385,7 +386,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-04-07 14:53:40.619043", + "modified": "2020-04-15 02:26:59.673750", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form Field", From a5daf8ffa146d72a65082a1863f43d4abc99237d Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Wed, 15 Apr 2020 19:34:12 +0530 Subject: [PATCH 14/25] 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 15/25] 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 16/25] 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 17/25] 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 %} -
+