diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index f2f43f10f8..f342c0709e 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -105,3 +105,5 @@ jobs: - name: UI Tests run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests frappe --headless --parallel --ci-build-id $GITHUB_RUN_ID + env: + CYPRESS_RECORD_KEY: 4a48f41c-11b3-425b-aa88-c58048fa69eb diff --git a/CODEOWNERS b/CODEOWNERS index 92723ab035..2dff157294 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -4,13 +4,10 @@ # the repo. Unless a later match takes precedence, * @frappe/frappe-review-team -website/ @prssanna -web_form/ @prssanna templates/ @surajshetty3416 www/ @surajshetty3416 integrations/ @leela patches/ @surajshetty3416 -dashboard/ @prssanna email/ @leela event_streaming/ @ruchamahabal data_import* @netchampfaris diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index c16de497ec..ca58e78870 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -661,7 +661,7 @@ def run_ui_tests(context, app, headless=False, parallel=True, ci_build_id=None): frappe.commands.popen("yarn add cypress@^6 cypress-file-upload@^5 --no-lockfile") # run for headless mode - run_or_open = 'run --browser firefox --record --key 4a48f41c-11b3-425b-aa88-c58048fa69eb' if headless else 'open' + run_or_open = 'run --browser firefox --record' if headless else 'open' command = '{site_env} {password_env} {cypress} {run_or_open}' formatted_command = command.format(site_env=site_env, password_env=password_env, cypress=cypress_path, run_or_open=run_or_open) @@ -770,19 +770,23 @@ def set_config(context, key, value, global_=False, parse=False, as_dict=False): @click.command('version') def get_version(): "Show the versions of all the installed apps" + from git import Repo from frappe.utils.change_log import get_app_branch frappe.init('') - for m in sorted(frappe.get_all_apps()): - branch_name = get_app_branch(m) - module = frappe.get_module(m) - app_hooks = frappe.get_module(m + ".hooks") + for app in sorted(frappe.get_all_apps()): + branch_name = get_app_branch(app) + module = frappe.get_module(app) + app_hooks = frappe.get_module(app + ".hooks") + repo = Repo(frappe.get_app_path(app, "..")) + branch = repo.head.ref.name + commit = repo.head.ref.commit.hexsha[:7] if hasattr(app_hooks, '{0}_version'.format(branch_name)): - print("{0} {1}".format(m, getattr(app_hooks, '{0}_version'.format(branch_name)))) + click.echo("{0} {1} {2} ({3})".format(app, getattr(app_hooks, '{0}_version'.format(branch_name)), branch, commit)) elif hasattr(module, "__version__"): - print("{0} {1}".format(m, module.__version__)) + click.echo("{0} {1} {2} ({3})".format(app, module.__version__, branch, commit)) @click.command('rebuild-global-search') diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index d35c118550..7ffbe6781d 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -85,8 +85,6 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = if attachments: add_attachments(comm.name, attachments) - frappe.db.commit() - if cint(send_email): if not comm.get_outgoing_email_account(): frappe.throw(msg=OUTGOING_EMAIL_ACCOUNT_MISSING, exc=frappe.OutgoingEmailError) diff --git a/frappe/desk/desktop.py b/frappe/desk/desktop.py index 0a7d436169..cdab7d6d1b 100644 --- a/frappe/desk/desktop.py +++ b/frappe/desk/desktop.py @@ -368,6 +368,7 @@ def get_desktop_page(page): 'allow_customization': not wspace.doc.disable_user_customization } except DoesNotExistError: + frappe.log_error(frappe.get_traceback()) return {} @frappe.whitelist() diff --git a/frappe/desk/doctype/workspace/workspace.py b/frappe/desk/doctype/workspace/workspace.py index 0b5babc8d9..41b0227f2a 100644 --- a/frappe/desk/doctype/workspace/workspace.py +++ b/frappe/desk/doctype/workspace/workspace.py @@ -43,19 +43,19 @@ class Workspace(Document): def get_link_groups(self): cards = [] - current_card = { + current_card = frappe._dict({ "label": "Link", "type": "Card Break", "icon": None, "hidden": False, - } + }) card_links = [] for link in self.links: link = link.as_dict() if link.type == "Card Break": - if card_links and (not current_card.only_for or current_card.only_for == frappe.get_system_settings('country')): + if card_links and (not current_card.only_for or current_card.only_for == frappe.get_system_settings('country')): current_card['links'] = card_links cards.append(current_card) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index af696e116d..9447e60529 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -83,11 +83,15 @@ class BaseDocument(object): @property def meta(self): - if not hasattr(self, "_meta"): + if not getattr(self, "_meta", None): self._meta = frappe.get_meta(self.doctype) return self._meta + def __getstate__(self): + self._meta = None + return self.__dict__ + def update(self, d): """ Update multiple fields of a doctype using a dictionary of key-value pairs. diff --git a/frappe/public/js/frappe/form/sidebar/attachments.js b/frappe/public/js/frappe/form/sidebar/attachments.js index 4f116df63e..538534e5cf 100644 --- a/frappe/public/js/frappe/form/sidebar/attachments.js +++ b/frappe/public/js/frappe/form/sidebar/attachments.js @@ -11,7 +11,7 @@ frappe.ui.form.Attachments = class Attachments { this.parent.find(".add-attachment-btn").click(function() { me.new_attachment(); }); - this.add_attachment_wrapper = this.parent.find(".add_attachment").parent(); + this.add_attachment_wrapper = this.parent.find(".add-attachment-btn"); this.attachments_label = this.parent.find(".attachments-label"); } max_reached(raise_exception=false) { @@ -39,7 +39,7 @@ frappe.ui.form.Attachments = class Attachments { this.parent.find(".attachment-row").remove(); var max_reached = this.max_reached(); - this.add_attachment_wrapper.toggleClass("hide", !max_reached); + this.add_attachment_wrapper.toggle(!max_reached); // add attachment objects var attachments = this.get_attachments(); diff --git a/frappe/public/js/frappe/form/templates/form_links.html b/frappe/public/js/frappe/form/templates/form_links.html index e16516e652..57edb69a15 100644 --- a/frappe/public/js/frappe/form/templates/form_links.html +++ b/frappe/public/js/frappe/form/templates/form_links.html @@ -5,7 +5,7 @@ {% } %}
{% for (let j=0; j < transactions[i].items.length; j++) { %} {% let doctype = transactions[i].items[j]; %} diff --git a/frappe/public/js/frappe/form/templates/report_links.html b/frappe/public/js/frappe/form/templates/report_links.html index 3b69586d32..b820ec3a52 100644 --- a/frappe/public/js/frappe/form/templates/report_links.html +++ b/frappe/public/js/frappe/form/templates/report_links.html @@ -5,7 +5,7 @@ {% } %}
{% for (let j=0; j < reports[i].items.length; j++) { %} {% let report = reports[i].items[j]; %} diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js index 26fd8d667d..0eeb5d9ffc 100755 --- a/frappe/public/js/frappe/views/communication.js +++ b/frappe/public/js/frappe/views/communication.js @@ -728,7 +728,7 @@ frappe.views.CommunicationComposer = class { const SALUTATION_END_COMMENT = ""; if (this.real_name && !message.includes(SALUTATION_END_COMMENT)) { this.message = ` -

${__('Dear')} ${this.real_name},

+

${__('Dear {0},', [this.real_name], 'Salutation in new email')},

${SALUTATION_END_COMMENT}
${message} `; diff --git a/frappe/public/js/frappe/widgets/links_widget.js b/frappe/public/js/frappe/widgets/links_widget.js index 84758db592..2cc824899f 100644 --- a/frappe/public/js/frappe/widgets/links_widget.js +++ b/frappe/public/js/frappe/widgets/links_widget.js @@ -64,6 +64,7 @@ export default class LinksWidget extends Widget { const opts = { name: item.link_to, type: item.link_type, + doctype: item.doctype, is_query_report: item.is_query_report }; diff --git a/frappe/templates/includes/login/login.js b/frappe/templates/includes/login/login.js index 355e8151f9..4400578862 100644 --- a/frappe/templates/includes/login/login.js +++ b/frappe/templates/includes/login/login.js @@ -291,10 +291,18 @@ var verify_token = function (event) { } var request_otp = function (r) { - const otp_form_template = '{{ _("Verification") }}{{ _("Verify") }}'; $('.login-content').empty(); $('.login-content:visible').append( - $('
').attr({ 'id': 'twofactor_div' }).html(otp_form_template) + `
+
+
+ {{ _("Verification") }} +
+
+ + +
+
` ); // add event handler for submit button verify_token(); diff --git a/frappe/templates/test/_test_base.html b/frappe/templates/test/_test_base.html index 17caf8df1b..1d5019df37 100644 --- a/frappe/templates/test/_test_base.html +++ b/frappe/templates/test/_test_base.html @@ -1,20 +1,9 @@ - {%- block style %} - {% if colocated_css -%} - - {%- endif %} - {%- endblock -%} - {% include "templates/includes/breadcrumbs.html" %}

This is for testing

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

This is for testing

+ {% block content %}{% endblock %} + {%- block script %} + {% if colocated_js -%} + + {%- endif %} + {%- endblock %} + + diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py index 5c19d3935c..f1c4f3b3f5 100644 --- a/frappe/tests/test_website.py +++ b/frappe/tests/test_website.py @@ -244,14 +244,22 @@ class TestWebsite(unittest.TestCase): self.assertIn("", content) self.assertIn("background-color: var(--bg-color);", content) + def test_raw_assets_are_loaded(self): + content = get_response_content('/_test/assets/js_asset.min.js') + # minified js files should not be passed through jinja renderer + self.assertEqual("//{% if title %} {{title}} {% endif %}\nconsole.log('in');", content) + + content = get_response_content('/_test/assets/css_asset.css') + self.assertEqual("""body{color:red}""", content) + def test_breadcrumbs(self): content = get_response_content('/_test/_test_folder/_test_page') - self.assertIn('Test TOC', content) + self.assertIn('Test Folder', content) self.assertIn(' Test Page', content) content = get_response_content('/_test/_test_folder/index') self.assertIn(' Test', content) - self.assertIn('Test TOC', content) + self.assertIn('Test Folder', content) def test_get_context_without_context_object(self): content = get_response_content('/_test/_test_no_context') diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index 5134313c83..3ece8ff5d0 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -70,7 +70,7 @@ class TemplatePage(BaseTemplatePage): self.set_pymodule() self.update_context() - self.setup_template() + self.setup_template_source() self.load_colocated_files() self.set_properties_from_source() self.post_process_context() @@ -118,7 +118,7 @@ class TemplatePage(BaseTemplatePage): if os.path.exists(os.path.join(self.app_path, self.pymodule_path)): self.pymodule_name = self.app + "." + self.pymodule_path.replace(os.path.sep, ".")[:-3] - def setup_template(self): + def setup_template_source(self): '''Setup template source, frontmatter and markdown conversion''' self.source = self.get_raw_template() self.extract_frontmatter() @@ -126,7 +126,6 @@ class TemplatePage(BaseTemplatePage): def update_context(self): self.set_page_properties() - self.set_properties_from_source() self.context.build_version = frappe.utils.get_build_version() if self.pymodule_name: @@ -150,8 +149,7 @@ class TemplatePage(BaseTemplatePage): def set_page_properties(self): self.context.base_template = self.context.base_template \ - or get_base_template(self.path) \ - or 'templates/web.html' + or get_base_template(self.path) self.context.basepath = self.basepath self.context.basename = self.basename self.context.name = self.name @@ -203,13 +201,10 @@ class TemplatePage(BaseTemplatePage): frappe.errprint(frappe.utils.get_traceback()) def render_template(self): - if self.source: + if self.template_path.endswith('min.js'): + html = self.source # static + else: html = frappe.render_template(self.source, self.context) - elif self.template_path: - if self.path.endswith('min.js'): - html = self.get_raw_template() # static - else: - html = frappe.get_template(self.template_path).render(self.context) return html diff --git a/frappe/website/router.py b/frappe/website/router.py index a9e2f68fe5..9809a73e48 100644 --- a/frappe/website/router.py +++ b/frappe/website/router.py @@ -270,7 +270,7 @@ def get_doctypes_with_web_view(): doctypes_with_web_view = frappe.get_all('DocType', fields=['name', 'module'], filters=dict(has_web_view=1)) module_app_map = frappe.local.module_app - doctypes += [d.name for d in doctypes_with_web_view if module_app_map[frappe.scrub(d.module)] in installed_apps] + doctypes += [d.name for d in doctypes_with_web_view if module_app_map.get(frappe.scrub(d.module)) in installed_apps] return doctypes return frappe.cache().get_value('doctypes_with_web_view', _get) diff --git a/frappe/www/_test/_test_folder/_test_page.html b/frappe/www/_test/_test_folder/_test_page.html index 123d619e38..79bc96c568 100644 --- a/frappe/www/_test/_test_folder/_test_page.html +++ b/frappe/www/_test/_test_folder/_test_page.html @@ -1,3 +1,4 @@ +{% extends base_template_path %} {% block content %} {% include "templates/includes/web_sidebar.html" %}

Test content

diff --git a/frappe/www/_test/_test_folder/_test_page.py b/frappe/www/_test/_test_folder/_test_page.py index 1813a06bac..3d4a645f9b 100644 --- a/frappe/www/_test/_test_folder/_test_page.py +++ b/frappe/www/_test/_test_folder/_test_page.py @@ -1,3 +1,3 @@ def get_context(context): - context.base_template_path = 'frappe/templates/test/_test_base.html' + context.base_template_path = 'frappe/templates/test/_test_base_breadcrumbs.html' context.add_breadcrumbs = 1 diff --git a/frappe/www/_test/_test_folder/index.md b/frappe/www/_test/_test_folder/index.md index 1a5a9e7f81..ca8c55e9d5 100644 --- a/frappe/www/_test/_test_folder/index.md +++ b/frappe/www/_test/_test_folder/index.md @@ -1,9 +1,9 @@ --- -title: Test TOC +title: Test Folder add_breadcrumbs: 1 show_sidebar: 1 +base_template: templates/web.html --- - # Index {index} \ No newline at end of file diff --git a/frappe/www/_test/assets/__init__.py b/frappe/www/_test/assets/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/www/_test/assets/css_asset.css b/frappe/www/_test/assets/css_asset.css new file mode 100644 index 0000000000..363a06397a --- /dev/null +++ b/frappe/www/_test/assets/css_asset.css @@ -0,0 +1 @@ +body{color:red} \ No newline at end of file diff --git a/frappe/www/_test/assets/js_asset.min.js b/frappe/www/_test/assets/js_asset.min.js new file mode 100644 index 0000000000..e039292259 --- /dev/null +++ b/frappe/www/_test/assets/js_asset.min.js @@ -0,0 +1,2 @@ +//{% if title %} {{title}} {% endif %} +console.log('in'); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index d031f58685..bd1b5ba3ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1381,12 +1381,12 @@ component-bind@1.0.0: resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= -component-emitter@1.2.1, component-emitter@^1.2.0: +component-emitter@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= -component-emitter@~1.3.0: +component-emitter@^1.2.0, component-emitter@~1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== @@ -1713,28 +1713,14 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@^3.0.1: +debug@^3.0.1, debug@^3.1.0, debug@^3.2.6: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" -debug@^3.1.0, debug@^3.2.6: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -debug@^4.1.1, debug@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - -debug@^4.2.0, debug@^4.3.1: +debug@^4.1.1, debug@^4.2.0, debug@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== @@ -1748,6 +1734,13 @@ debug@~3.1.0: dependencies: ms "2.0.0" +debug@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + decamelize@^1.0.0, decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -6484,11 +6477,11 @@ socket.io-client@2.4.0: to-array "0.1.4" socket.io-parser@~3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.0.tgz#2b52a96a509fdf31440ba40fed6094c7d4f1262f" - integrity sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng== + version "3.3.2" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.2.tgz#ef872009d0adcf704f2fbe830191a14752ad50b6" + integrity sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg== dependencies: - component-emitter "1.2.1" + component-emitter "~1.3.0" debug "~3.1.0" isarray "2.0.1"