From 11fffbf9a40bb235bfbbcbf0a064b9ec4cef0717 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 23 Dec 2020 12:07:31 +0530 Subject: [PATCH 1/6] fix(minor): validate links in customize form --- frappe/core/doctype/doctype/doctype.py | 43 +++++++-------------- frappe/core/doctype/doctype/test_doctype.py | 18 ++++++--- 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 7c0c270277..2c6bf4d9e1 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -79,7 +79,7 @@ class DocType(Document): self.make_repeatable() self.validate_nestedset() self.validate_website() - self.validate_links_table_fieldnames() + validate_links_table_fieldnames(self) if not self.is_new(): self.before_update = frappe.get_doc('DocType', self.name) @@ -282,7 +282,6 @@ class DocType(Document): def on_update(self): """Update database schema, make controller templates if `custom` is not set and clear cache.""" - self.delete_duplicate_custom_fields() try: frappe.db.updatedb(self.name, Meta(self)) except Exception as e: @@ -321,18 +320,6 @@ class DocType(Document): clear_linked_doctype_cache() - def delete_duplicate_custom_fields(self): - if not (frappe.db.table_exists(self.name) and frappe.db.table_exists("Custom Field")): - return - - fields = [d.fieldname for d in self.fields if d.fieldtype in data_fieldtypes] - if fields: - frappe.db.sql('''delete from - `tabCustom Field` - where - dt = {0} and fieldname in ({1}) - '''.format('%s', ', '.join(['%s'] * len(fields))), tuple([self.name] + fields), as_dict=True) - def sync_global_search(self): '''If global search settings are changed, rebuild search properties for this table''' global_search_fields_before_update = [d.fieldname for d in @@ -666,24 +653,22 @@ class DocType(Document): if not re.match("^(?![\W])[^\d_\s][\w ]+$", name, **flags): frappe.throw(_("DocType's name should start with a letter and it can only consist of letters, numbers, spaces and underscores"), frappe.NameError) - def validate_links_table_fieldnames(self): - """Validate fieldnames in Links table""" - if frappe.flags.in_patch: return - if frappe.flags.in_fixtures: return - if not self.links: return - - for index, link in enumerate(self.links): - meta = frappe.get_meta(link.link_doctype) - if not meta.get_field(link.link_fieldname): - message = _("Row #{0}: Could not find field {1} in {2} DocType").format(index+1, frappe.bold(link.link_fieldname), frappe.bold(link.link_doctype)) - frappe.throw(message, InvalidFieldNameError, _("Invalid Fieldname")) - +def validate_links_table_fieldnames(meta): + """Validate fieldnames in Links table""" + if frappe.flags.in_patch: return + if frappe.flags.in_fixtures: return + if not meta.links: return + for index, link in enumerate(meta.links): + link_meta = frappe.get_meta(link.link_doctype) + if not link_meta.get_field(link.link_fieldname): + message = _("Row #{0}: Could not find field {1} in {2} DocType").format(index+1, frappe.bold(link.link_fieldname), frappe.bold(link.link_doctype)) + frappe.throw(message, InvalidFieldNameError, _("Invalid Fieldname")) def validate_fields_for_doctype(doctype): - doc = frappe.get_doc("DocType", doctype) - doc.delete_duplicate_custom_fields() - validate_fields(frappe.get_meta(doctype, cached=False)) + meta = frappe.get_meta(doctype, cached=False) + validate_links_table_fieldnames(meta) + validate_fields(meta) # this is separate because it is also called via custom field def validate_fields(meta): diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index 10169073e5..047d1aa6e2 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -5,12 +5,18 @@ from __future__ import unicode_literals import frappe import unittest -from frappe.core.doctype.doctype.doctype import UniqueFieldnameError, IllegalMandatoryError, DoctypeLinkError, WrongOptionsDoctypeLinkError,\ - HiddenAndMandatoryWithoutDefaultError, CannotIndexedError, InvalidFieldNameError, CannotCreateStandardDoctypeError +from frappe.core.doctype.doctype.doctype import (UniqueFieldnameError, + IllegalMandatoryError, + DoctypeLinkError, + WrongOptionsDoctypeLinkError, + HiddenAndMandatoryWithoutDefaultError, + CannotIndexedError, + InvalidFieldNameError, + CannotCreateStandardDoctypeError, + validate_links_table_fieldnames) # test_records = frappe.get_test_records('DocType') - class TestDocType(unittest.TestCase): def test_validate_name(self): self.assertRaises(frappe.NameError, new_doctype("_Some DocType").insert) @@ -459,7 +465,7 @@ class TestDocType(unittest.TestCase): 'link_doctype': "User", 'link_fieldname': "first_name" }) - doc.validate_links_table_fieldnames() # no error + validate_links_table_fieldnames(doc) # no error doc.links = [] # reset links table # check invalid doctype @@ -467,7 +473,7 @@ class TestDocType(unittest.TestCase): 'link_doctype': "User2", 'link_fieldname': "first_name" }) - self.assertRaises(frappe.DoesNotExistError, doc.validate_links_table_fieldnames) + self.assertRaises(frappe.DoesNotExistError, validate_links_table_fieldnames, doc) doc.links = [] # reset links table # check invalid fieldname @@ -475,7 +481,7 @@ class TestDocType(unittest.TestCase): 'link_doctype': "User", 'link_fieldname': "a_field_that_does_not_exists" }) - self.assertRaises(InvalidFieldNameError, doc.validate_links_table_fieldnames) + self.assertRaises(InvalidFieldNameError, validate_links_table_fieldnames, doc) def new_doctype(name, unique=0, depends_on='', fields=None): From 42a0edc70d5e4196169ca05aafe7c3482e3ab1d8 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 23 Dec 2020 12:44:26 +0530 Subject: [PATCH 2/6] fix(minor): fix link in User doctype --- frappe/core/doctype/user/user.json | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/frappe/core/doctype/user/user.json b/frappe/core/doctype/user/user.json index ca3bb4c987..22d3f296b3 100644 --- a/frappe/core/doctype/user/user.json +++ b/frappe/core/doctype/user/user.json @@ -53,7 +53,6 @@ "thread_notify", "send_me_a_copy", "allowed_in_mentions", - "email_inbox", "user_emails", "sb_allow_modules", "modules_html", @@ -641,11 +640,6 @@ "link_doctype": "User Permission", "link_fieldname": "user" }, - { - "group": "Settings", - "link_doctype": "Assignment Rule", - "link_fieldname": "user" - }, { "group": "Settings", "link_doctype": "Document Follow", @@ -663,7 +657,11 @@ } ], "max_attachments": 5, +<<<<<<< HEAD "modified": "2020-12-24 19:48:49.677800", +======= + "modified": "2020-12-23 12:43:51.470188", +>>>>>>> bfdf108343 (fix(minor): fix link in User doctype) "modified_by": "Administrator", "module": "Core", "name": "User", @@ -691,10 +689,11 @@ } ], "quick_entry": 1, + "route": "user", "search_fields": "full_name", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", "title_field": "full_name", "track_changes": 1 -} +} \ No newline at end of file From 4c32dc37650679d36108f991a814d6b5804250bc Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 23 Dec 2020 15:27:03 +0530 Subject: [PATCH 3/6] fix(tests): routing, permission and other fixes --- frappe/core/doctype/doctype/doctype.py | 5 ----- frappe/core/doctype/user/test_user.py | 7 ++++++- frappe/core/doctype/user/user.py | 6 +++--- frappe/model/naming.py | 1 - frappe/website/context.py | 2 +- frappe/website/doctype/blog_post/blog_post.json | 2 +- .../doctype/website_route_meta/test_website_route_meta.py | 1 + 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 2c6bf4d9e1..a59bef9fc7 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -26,8 +26,6 @@ from frappe.database.schema import validate_column_name, validate_column_length from frappe.model.docfield import supports_translation from frappe.modules.import_file import get_file_path from frappe.model.meta import Meta -from frappe.desk.utils import get_doctype_route - class InvalidFieldNameError(frappe.ValidationError): pass class UniqueFieldnameError(frappe.ValidationError): pass @@ -190,9 +188,6 @@ class DocType(Document): def validate_website(self): """Ensure that website generator has field 'route'""" - if not self.istable and not self.route: - self.route = get_doctype_route(self.name) - if self.route: self.route = self.route.strip('/') diff --git a/frappe/core/doctype/user/test_user.py b/frappe/core/doctype/user/test_user.py index fb1fa4aff9..77b61f22bb 100644 --- a/frappe/core/doctype/user/test_user.py +++ b/frappe/core/doctype/user/test_user.py @@ -20,6 +20,7 @@ class TestUser(unittest.TestCase): frappe.db.set_value("System Settings", "System Settings", "enable_password_policy", 0) frappe.db.set_value("System Settings", "System Settings", "minimum_password_score", "") frappe.db.set_value("System Settings", "System Settings", "password_reset_limit", 3) + frappe.set_user('Administrator') def test_user_type(self): new_user = frappe.get_doc(dict(doctype='User', email='test-for-type@example.com', @@ -106,13 +107,17 @@ class TestUser(unittest.TestCase): frappe.set_user("testperm@example.com") me = frappe.get_doc("User", "testperm@example.com") - self.assertRaises(frappe.PermissionError, me.add_roles, "System Manager") + me.add_roles("System Manager") + + # system manager is not added (it is reset) + self.assertFalse('System Manager' in [d.role for d in me.roles]) frappe.set_user("Administrator") me = frappe.get_doc("User", "testperm@example.com") me.add_roles("System Manager") + # system manager now added by Administrator self.assertTrue("System Manager" in [d.role for d in me.get("roles")]) # def test_deny_multiple_sessions(self): diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 6cc4104886..6f5c805a54 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -85,9 +85,9 @@ class User(Document): def validate_roles(self): if self.role_profile_name: - role_profile = frappe.get_doc('Role Profile', self.role_profile_name) - self.set('roles', []) - self.append_roles(*[role.role for role in role_profile.roles]) + role_profile = frappe.get_doc('Role Profile', self.role_profile_name) + self.set('roles', []) + self.append_roles(*[role.role for role in role_profile.roles]) def validate_user_image(self): if self.user_image and len(self.user_image) > 2000: diff --git a/frappe/model/naming.py b/frappe/model/naming.py index c2e074990e..e954debe6f 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -67,7 +67,6 @@ def set_new_name(doc): frappe.get_meta(doc.doctype).get_field("name_case") ) - def set_name_from_naming_options(autoname, doc): """ Get a name based on the autoname field option diff --git a/frappe/website/context.py b/frappe/website/context.py index 34460c3be4..4236971aec 100644 --- a/frappe/website/context.py +++ b/frappe/website/context.py @@ -274,7 +274,7 @@ def add_metatags(context): # Get meta tags from Website Route meta # they can override the defaults set above - route = context.route + route = context.path if route == '': # homepage route = frappe.db.get_single_value('Website Settings', 'home_page') diff --git a/frappe/website/doctype/blog_post/blog_post.json b/frappe/website/doctype/blog_post/blog_post.json index 28d90c2301..909cecf867 100644 --- a/frappe/website/doctype/blog_post/blog_post.json +++ b/frappe/website/doctype/blog_post/blog_post.json @@ -200,7 +200,7 @@ "is_published_field": "published", "links": [], "max_attachments": 5, - "modified": "2020-12-15 15:20:43.616046", + "modified": "2020-12-23 14:28:36.311389", "modified_by": "Administrator", "module": "Website", "name": "Blog Post", diff --git a/frappe/website/doctype/website_route_meta/test_website_route_meta.py b/frappe/website/doctype/website_route_meta/test_website_route_meta.py index c02dc398bf..0ccedb0ca4 100644 --- a/frappe/website/doctype/website_route_meta/test_website_route_meta.py +++ b/frappe/website/doctype/website_route_meta/test_website_route_meta.py @@ -8,6 +8,7 @@ import unittest from frappe.utils import set_request from frappe.website.render import render +test_dependencies = ['Blog Post'] class TestWebsiteRouteMeta(unittest.TestCase): def test_meta_tag_generation(self): blogs = frappe.get_all('Blog Post', fields=['name', 'route'], From 54cee87826ce1d592f6683f88c945c9a37ae968b Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 23 Dec 2020 23:26:38 +0530 Subject: [PATCH 4/6] fix(minor): tests + routing --- cypress/integration/awesome_bar.js | 2 +- cypress/integration/control_link.js | 2 +- cypress/integration/form.js | 12 ++++----- cypress/integration/grid_pagination.js | 8 +++--- cypress/integration/table_multiselect.js | 2 +- cypress/support/commands.js | 2 +- .../doctype/auto_repeat/auto_repeat.js | 2 +- .../deleted_document/deleted_document_list.js | 2 +- frappe/core/doctype/doctype/doctype.js | 4 +-- .../permission_manager/permission_manager.js | 2 +- frappe/desk/page/leaderboard/leaderboard.js | 2 +- .../document_follow/test_document_follow.py | 25 +++++++++++-------- .../doctype/notification/notification.js | 2 +- .../google_calendar/google_calendar.js | 2 +- .../google_contacts/google_contacts.js | 2 +- .../doctype/google_drive/google_drive.js | 2 +- .../doctype/print_format/test_print_format.py | 4 +-- frappe/public/js/frappe/chat.js | 15 ++++++----- frappe/public/js/frappe/form/form.js | 10 ++------ frappe/public/js/frappe/form/linked_with.js | 2 +- .../js/frappe/form/multi_select_dialog.js | 2 +- .../frappe/ui/notifications/notifications.js | 2 +- .../js/frappe/utils/energy_point_utils.js | 2 +- frappe/public/js/frappe/utils/help_links.js | 10 ++++---- frappe/public/js/frappe/utils/utils.js | 6 ++--- frappe/public/js/frappe/views/factory.js | 2 +- .../public/js/frappe/views/file/file_view.js | 2 +- frappe/public/js/frappe/views/formview.js | 4 +-- .../js/frappe/views/kanban/kanban_view.js | 2 +- .../js/frappe/views/reports/query_report.js | 6 ++--- .../js/frappe/widgets/onboarding_widget.js | 6 ++--- .../energy_point_log/energy_point_log.js | 2 +- .../workflow_action/workflow_action_list.js | 2 +- 33 files changed, 73 insertions(+), 79 deletions(-) diff --git a/cypress/integration/awesome_bar.js b/cypress/integration/awesome_bar.js index b20e812aae..805c0daa03 100644 --- a/cypress/integration/awesome_bar.js +++ b/cypress/integration/awesome_bar.js @@ -6,7 +6,7 @@ context('Awesome Bar', () => { }); beforeEach(() => { - cy.get('.navbar-header .navbar-home').click(); + cy.get('.navbar .navbar-home').click(); }); it('navigates to doctype list', () => { diff --git a/cypress/integration/control_link.js b/cypress/integration/control_link.js index eb69bf82c2..30c323a256 100644 --- a/cypress/integration/control_link.js +++ b/cypress/integration/control_link.js @@ -77,7 +77,7 @@ context('Control Link', () => { cy.get('.frappe-control[data-fieldname=link] .link-btn') .should('be.visible') .click(); - cy.location('hash').should('eq', `/app/Form/ToDo/${todos[0]}`); + cy.location('hash').should('eq', `/app/todo/${todos[0]}`); }); }); }); diff --git a/cypress/integration/form.js b/cypress/integration/form.js index dad26c9a8d..48f65214f5 100644 --- a/cypress/integration/form.js +++ b/cypress/integration/form.js @@ -7,7 +7,7 @@ context('Form', () => { }); }); it('create a new form', () => { - cy.visit('/app/Form/ToDo/New ToDo 1'); + cy.visit('/app/todo/new'); cy.fill_field('description', 'this is a test todo', 'Text Editor').blur(); cy.wait(300); cy.get('.page-title').should('contain', 'Not Saved'); @@ -18,20 +18,18 @@ context('Form', () => { }).as('form_save'); cy.get('.primary-action').click(); cy.wait('@form_save').its('status').should('eq', 200); - cy.visit('/app/List/ToDo'); - cy.location('hash').should('eq', '/app/List/ToDo/List'); + cy.visit('/app/todo'); cy.get('h1').should('be.visible').and('contain', 'To Do'); cy.get('.list-row').should('contain', 'this is a test todo'); }); it('navigates between documents with child table list filters applied', () => { - cy.visit('/app/List/Contact'); - cy.location('hash').should('eq', '/app/List/Contact/List'); + cy.visit('/app/contact'); cy.get('.tag-filters-area .btn:contains("Add Filter")').click(); cy.get('.fieldname-select-area').should('exist'); cy.get('.fieldname-select-area input').type('Number{enter}', { force: true }); cy.get('.filter-field .input-with-feedback.form-control').type('123', { force: true }); cy.get('.filter-box .btn:contains("Apply")').click({ force: true }); - cy.visit('/app/Form/Contact/Test Form Contact 3'); + cy.visit('/app/contact/Test Form Contact 3'); cy.get('.prev-doc').should('be.visible').click(); cy.get('.msgprint-dialog .modal-body').contains('No further records').should('be.visible'); cy.get('.btn-modal-close:visible').click(); @@ -50,7 +48,7 @@ context('Form', () => { let website_input = 'website.in'; let expectBackgroundColor = 'rgb(255, 220, 220)'; - cy.visit('/app/Form/Contact/New Contact 1'); + cy.visit('/app/contact/new'); cy.get('.frappe-control[data-fieldname="email_ids"]').as('table'); cy.get('@table').find('button.grid-add-row').click(); cy.get('.grid-body .rows [data-fieldname="email_id"]').click(); diff --git a/cypress/integration/grid_pagination.js b/cypress/integration/grid_pagination.js index 0a09d2875b..87c0fb0af4 100644 --- a/cypress/integration/grid_pagination.js +++ b/cypress/integration/grid_pagination.js @@ -11,14 +11,14 @@ context('Grid Pagination', () => { }); }); it('creates pages for child table', () => { - cy.visit('/app/Form/Contact/Test Contact'); + cy.visit('/app/contact/Test Contact'); cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); cy.get('@table').find('.current-page-number').should('contain', '1'); cy.get('@table').find('.total-page-number').should('contain', '20'); cy.get('@table').find('.grid-body .grid-row').should('have.length', 50); }); it('goes to the next and previous page', () => { - cy.visit('/app/Form/Contact/Test Contact'); + cy.visit('/app/contact/Test Contact'); cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); cy.get('@table').find('.next-page').click(); cy.get('@table').find('.current-page-number').should('contain', '2'); @@ -28,7 +28,7 @@ context('Grid Pagination', () => { cy.get('@table').find('.grid-body .grid-row').first().should('have.attr', 'data-idx', '1'); }); it('adds and deletes rows and changes page', () => { - cy.visit('/app/Form/Contact/Test Contact'); + cy.visit('/app/contact/Test Contact'); cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); cy.get('@table').find('button.grid-add-row').click(); cy.get('@table').find('.grid-body .row-index').should('contain', 1001); @@ -41,7 +41,7 @@ context('Grid Pagination', () => { cy.get('@table').find('.total-page-number').should('contain', '20'); }); // it('deletes all rows', ()=> { - // cy.visit('/app/Form/Contact/Test Contact'); + // cy.visit('/app/contact/Test Contact'); // cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); // cy.get('@table').find('.grid-heading-row .grid-row-check').click({force: true}); // cy.get('@table').find('button.grid-remove-all-rows').click(); diff --git a/cypress/integration/table_multiselect.js b/cypress/integration/table_multiselect.js index e75baf05f1..3dfccfe484 100644 --- a/cypress/integration/table_multiselect.js +++ b/cypress/integration/table_multiselect.js @@ -46,6 +46,6 @@ context('Table MultiSelect', () => { cy.get(`.list-subject:contains("table multiselect")`).last().find('a').click(); cy.get('.frappe-control[data-fieldname="users"] .form-control .tb-selected-value').as('existing_value'); cy.get('@existing_value').find('.btn-link-to-form').click(); - cy.location('hash').should('contain', 'Form/User/test@erpnext.com'); + cy.location('pathname').should('contain', '/user/test@erpnext.com'); }); }); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 5c6bb6e46f..96b5168684 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -209,7 +209,7 @@ Cypress.Commands.add('awesomebar', text => { }); Cypress.Commands.add('new_form', doctype => { - let route = `form/${doctype}/new`; + let route = `${doctype.toLowerCase().replace(' ', '-')}/new`; cy.visit(`/app/${route}`); cy.get('body').should('have.attr', 'data-route', route); cy.get('body').should('have.attr', 'data-ajax-state', 'complete'); diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.js b/frappe/automation/doctype/auto_repeat/auto_repeat.js index b57ed94e4d..30b3b17fb4 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.js +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.js @@ -30,7 +30,7 @@ frappe.ui.form.on('Auto Repeat', { refresh: function(frm) { // auto repeat message if (frm.is_new()) { - let customize_form_link = `${__('Customize Form')}`; + let customize_form_link = `${__('Customize Form')}`; frm.dashboard.set_headline(__('To configure Auto Repeat, enable "Allow Auto Repeat" from {0}.', [customize_form_link])); } diff --git a/frappe/core/doctype/deleted_document/deleted_document_list.js b/frappe/core/doctype/deleted_document/deleted_document_list.js index 81a8992a19..268936e8a8 100644 --- a/frappe/core/doctype/deleted_document/deleted_document_list.js +++ b/frappe/core/doctype/deleted_document/deleted_document_list.js @@ -11,7 +11,7 @@ frappe.listview_settings["Deleted Document"] = { if (r.message) { function body(docnames) { const html = docnames.map(docname => { - return `
  • ${docname}
  • `; + return `
  • ${docname}
  • `; }); return "
      " + html.join(""); } diff --git a/frappe/core/doctype/doctype/doctype.js b/frappe/core/doctype/doctype/doctype.js index cff918d189..3e2a423b06 100644 --- a/frappe/core/doctype/doctype/doctype.js +++ b/frappe/core/doctype/doctype/doctype.js @@ -24,11 +24,11 @@ frappe.ui.form.on('DocType', { if (!frm.is_new() && !frm.doc.istable) { if (frm.doc.issingle) { frm.add_custom_button(__('Go to {0}', [frm.doc.name]), () => { - window.open(`/app/form/${frm.doc.name}`); + window.open(`/app/${frappe.router.slug(frm.doc.name)}`); }); } else { frm.add_custom_button(__('Go to {0} List', [frm.doc.name]), () => { - window.open(`/app/list/${frm.doc.name}/list`); + window.open(`/app/${frappe.router.slug(frm.doc.name)}`); }); } } diff --git a/frappe/core/page/permission_manager/permission_manager.js b/frappe/core/page/permission_manager/permission_manager.js index 8d00952e8d..90747b8aae 100644 --- a/frappe/core/page/permission_manager/permission_manager.js +++ b/frappe/core/page/permission_manager/permission_manager.js @@ -311,7 +311,7 @@ frappe.PermissionEngine = class PermissionEngine { }, callback: function (r) { r.message = $.map(r.message, function (p) { - return $.format('{1}', [p, p]); + return $.format('{1}', [p, p]); }); frappe.msgprint(__("Users with role {0}:", [__(role)]) + "
      " + r.message.join("
      ")); diff --git a/frappe/desk/page/leaderboard/leaderboard.js b/frappe/desk/page/leaderboard/leaderboard.js index 5209dc0fab..aaba36f25f 100644 --- a/frappe/desk/page/leaderboard/leaderboard.js +++ b/frappe/desk/page/leaderboard/leaderboard.js @@ -348,7 +348,7 @@ class Leaderboard { return fieldname === this.options.selected_filter_item; })); - const link = `/app/Form/${this.options.selected_doctype}/${item.name}`; + const link = `/app/${frappe.router.slug(this.options.selected_doctype)}/${item.name}`; const name_html = item.formatted_name ? `${item.formatted_name}` : ` ${item.name} `; diff --git a/frappe/email/doctype/document_follow/test_document_follow.py b/frappe/email/doctype/document_follow/test_document_follow.py index 1208a6c5c1..1ac2d19305 100644 --- a/frappe/email/doctype/document_follow/test_document_follow.py +++ b/frappe/email/doctype/document_follow/test_document_follow.py @@ -8,15 +8,15 @@ import unittest import frappe.desk.form.document_follow as document_follow class TestDocumentFollow(unittest.TestCase): - - def test_add_subscription_and_send_mail(self): + def test_document_follow(self): user = get_user() event_doc = get_event() event_doc.description = "This is a test description for sending mail" event_doc.save(ignore_version=False) - doc = document_follow.follow_document("Event", event_doc.name , user.name, force=True) + document_follow.unfollow_document("Event", event_doc.name, user.name) + doc = document_follow.follow_document("Event", event_doc.name, user.name) self.assertEquals(doc.user, user.name) document_follow.send_hourly_updates() @@ -45,12 +45,15 @@ def get_event(): return doc def get_user(): - doc = frappe.new_doc("User") - doc.email = "test@docsub.com" - doc.first_name = "Test" - doc.last_name = "User" - doc.send_welcome_email = 0 - doc.document_follow_notify = 1 - doc.document_follow_frequency = "Hourly" - doc.insert() + if frappe.db.exists('User', 'test@docsub.com'): + doc = frappe.get_doc('User', 'test@docsub.com') + else: + doc = frappe.new_doc("User") + doc.email = "test@docsub.com" + doc.first_name = "Test" + doc.last_name = "User" + doc.send_welcome_email = 0 + doc.document_follow_notify = 1 + doc.document_follow_frequency = "Hourly" + doc.insert() return doc \ No newline at end of file diff --git a/frappe/email/doctype/notification/notification.js b/frappe/email/doctype/notification/notification.js index a016d480aa..c999f5f160 100644 --- a/frappe/email/doctype/notification/notification.js +++ b/frappe/email/doctype/notification/notification.js @@ -198,7 +198,7 @@ frappe.ui.form.on('Notification', { frappe.notification.setup_example_message(frm); if (frm.doc.channel === 'SMS' && frm.doc.__islocal) { frm.set_df_property('channel', - 'description', `To use SMS Channel, initialize SMS Settings.`); + 'description', `To use SMS Channel, initialize SMS Settings.`); } else { frm.set_df_property('channel', 'description', ` `); } diff --git a/frappe/integrations/doctype/google_calendar/google_calendar.js b/frappe/integrations/doctype/google_calendar/google_calendar.js index 198c8e2706..f30c52b2f2 100644 --- a/frappe/integrations/doctype/google_calendar/google_calendar.js +++ b/frappe/integrations/doctype/google_calendar/google_calendar.js @@ -4,7 +4,7 @@ frappe.ui.form.on("Google Calendar", { refresh: function(frm) { if (frm.is_new()) { - frm.dashboard.set_headline(__("To use Google Calendar, enable {0}.", [`${__('Google Settings')}`])); + frm.dashboard.set_headline(__("To use Google Calendar, enable {0}.", [`${__('Google Settings')}`])); } frappe.realtime.on("import_google_calendar", (data) => { diff --git a/frappe/integrations/doctype/google_contacts/google_contacts.js b/frappe/integrations/doctype/google_contacts/google_contacts.js index 029821b3bb..7cbef46699 100644 --- a/frappe/integrations/doctype/google_contacts/google_contacts.js +++ b/frappe/integrations/doctype/google_contacts/google_contacts.js @@ -4,7 +4,7 @@ frappe.ui.form.on('Google Contacts', { refresh: function(frm) { if (!frm.doc.enable) { - frm.dashboard.set_headline(__("To use Google Contacts, enable {0}.", [`${__('Google Settings')}`])); + frm.dashboard.set_headline(__("To use Google Contacts, enable {0}.", [`${__('Google Settings')}`])); } frappe.realtime.on('import_google_contacts', (data) => { diff --git a/frappe/integrations/doctype/google_drive/google_drive.js b/frappe/integrations/doctype/google_drive/google_drive.js index 1463f8725d..c314d02e7e 100644 --- a/frappe/integrations/doctype/google_drive/google_drive.js +++ b/frappe/integrations/doctype/google_drive/google_drive.js @@ -4,7 +4,7 @@ frappe.ui.form.on('Google Drive', { refresh: function(frm) { if (!frm.doc.enable) { - frm.dashboard.set_headline(__("To use Google Drive, enable {0}.", [`${__('Google Settings')}`])); + frm.dashboard.set_headline(__("To use Google Drive, enable {0}.", [`${__('Google Settings')}`])); } frappe.realtime.on("upload_to_google_drive", (data) => { diff --git a/frappe/printing/doctype/print_format/test_print_format.py b/frappe/printing/doctype/print_format/test_print_format.py index e8375ae5e7..7e30bda23e 100644 --- a/frappe/printing/doctype/print_format/test_print_format.py +++ b/frappe/printing/doctype/print_format/test_print_format.py @@ -11,8 +11,8 @@ test_records = frappe.get_test_records('Print Format') class TestPrintFormat(unittest.TestCase): def test_print_user(self, style=None): print_html = frappe.get_print("User", "Administrator", style=style) - self.assertTrue("" in print_html) - self.assertTrue(re.findall('
      [\s]*administrator[\s]*
      ', print_html)) + self.assertTrue("" in print_html) + self.assertTrue(re.findall('
      [\s]*administrator[\s]*
      ', print_html)) return print_html def test_print_user_standard(self): diff --git a/frappe/public/js/frappe/chat.js b/frappe/public/js/frappe/chat.js index 61fa89deab..1674a348b8 100644 --- a/frappe/public/js/frappe/chat.js +++ b/frappe/public/js/frappe/chat.js @@ -192,7 +192,7 @@ frappe.quick_edit = (doctype, docname, fn) => { }) const dialog = new frappe.ui.Dialog({ - title: __(`Edit ${doctype} (${docname})`), + title: __('Edit') + `${doctype} (${docname})`, fields: required, action: { primary: { @@ -227,7 +227,7 @@ frappe.quick_edit = (doctype, docname, fn) => { `) $element.find('.qe-fp').click(() => { dialog.hide() - frappe.set_route(`Form/${doctype}/${docname}`) + frappe.set_route('Form', doctype, docname) }) dialog.show() @@ -2221,7 +2221,7 @@ class extends Component { const item = { } if ( ["Group", "Visitor"].includes(props.type) ) { - item.route = `Form/Chat Room/${props.name}` + item.route = `chat-room/${props.name}` item.title = props.room_name item.image = props.avatar @@ -2232,14 +2232,13 @@ class extends Component { item.subtitle = `${users.join(", ")} typing...` } else item.subtitle = props.type === "Group" ? - __(`${props.users.length} ${frappe._.pluralize('member', props.users.length)}`) - : - "" + `${props.users.length} ${frappe._.pluralize('member', props.users.length)}` + : "" } else { const user = props.owner === frappe.session.user ? frappe._.squash(props.users) : props.owner - item.route = `Form/User/${user}` + item.route = `user/${user}` item.title = frappe.user.full_name(user) item.image = frappe.user.image(user) @@ -2405,7 +2404,7 @@ class extends Component { onclick: this.onclick}, props.room_type === "Group" && !me? h("div",{class:"chat-bubble-author"}, - h("a", { onclick: () => { frappe.set_route(`Form/User/${props.user}`) } }, + h("a", { onclick: () => { frappe.set_route('Form', 'User', props.user) } }, frappe.user.full_name(props.user) ) ) : null, diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 5c3ef3d33f..6930873893 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -996,7 +996,7 @@ frappe.ui.form.Form = class FrappeForm { return; } - frappe.re_route[frappe.router.get_sub_path()] = 'Form/' + encodeURIComponent(this.doctype) + '/' + encodeURIComponent(name); + frappe.re_route[frappe.router.get_sub_path()] = `${encodeURIComponent(frappe.router.slug(this.doctype))}/${encodeURIComponent(name)}`; frappe.set_route('Form', this.doctype, name); } @@ -1506,13 +1506,7 @@ frappe.ui.form.Form = class FrappeForm { const escaped_name = encodeURIComponent(value); - return repl('%(label)s', { - color: get_color(doc || {}), - doctype: df.options, - escaped_name: escaped_name, - label: label, - name: value - }); + return `${label}'` } else { return ''; } diff --git a/frappe/public/js/frappe/form/linked_with.js b/frappe/public/js/frappe/form/linked_with.js index 9be04f438a..20db7bdb7c 100644 --- a/frappe/public/js/frappe/form/linked_with.js +++ b/frappe/public/js/frappe/form/linked_with.js @@ -157,7 +157,7 @@ frappe.ui.form.LinkedWith = class LinkedWith { return ``; diff --git a/frappe/public/js/frappe/form/multi_select_dialog.js b/frappe/public/js/frappe/form/multi_select_dialog.js index dba8d54843..0eae43e2f1 100644 --- a/frappe/public/js/frappe/form/multi_select_dialog.js +++ b/frappe/public/js/frappe/form/multi_select_dialog.js @@ -236,7 +236,7 @@ frappe.ui.form.MultiSelectDialog = class MultiSelectDialog { ${ head ? `${__(frappe.model.unscrub(column))}` : (column !== "name" ? `${__(result[column] || '')}` - : ` + : ` ${__(result[column] || '')}`)} `; }); diff --git a/frappe/public/js/frappe/ui/notifications/notifications.js b/frappe/public/js/frappe/ui/notifications/notifications.js index 1ebb7ca196..8b08c80660 100644 --- a/frappe/public/js/frappe/ui/notifications/notifications.js +++ b/frappe/public/js/frappe/ui/notifications/notifications.js @@ -427,7 +427,7 @@ class EventsView extends BaseNotificaitonsView { location = `, ${event.location}`; } - return ` + return `
      ${event.subject}
      diff --git a/frappe/public/js/frappe/utils/energy_point_utils.js b/frappe/public/js/frappe/utils/energy_point_utils.js index 9ebf041141..bc8ab071a4 100644 --- a/frappe/public/js/frappe/utils/energy_point_utils.js +++ b/frappe/public/js/frappe/utils/energy_point_utils.js @@ -13,7 +13,7 @@ Object.assign(frappe.energy_points, { const separator = ` - `; const formatted_log = ` -
      ${this.get_form_log_message(log)} + ${this.get_form_log_message(log)} ${log.reason ? separator + log.reason : ''} `; return formatted_log; diff --git a/frappe/public/js/frappe/utils/help_links.js b/frappe/public/js/frappe/utils/help_links.js index 0c4cf37599..6665db39c1 100644 --- a/frappe/public/js/frappe/utils/help_links.js +++ b/frappe/public/js/frappe/utils/help_links.js @@ -12,7 +12,7 @@ frappe.help.help_links['modules/Setup'] = [ { label: 'Printing', url: 'http://frappe.github.io/erpnext/user/manual/en/setting-up/print/' }, ] -frappe.help.help_links['List/User'] = [ +frappe.help.help_links['user'] = [ { label: 'Adding Users', url: 'https://frappe.github.io/erpnext/user/manual/en/setting-up/users-and-permissions/adding-users' }, { label: 'Rename User', url: 'https://frappe.github.io/erpnext/user/manual/en/setting-up/articles/rename-user' }, ] @@ -25,19 +25,19 @@ frappe.help.help_links['user-permissions'] = [ { label: 'User Permissions', url: 'https://frappe.github.io/erpnext/user/manual/en/setting-up/users-and-permissions/user-permissions' }, ] -frappe.help.help_links['Form/System Settings'] = [ +frappe.help.help_links['system-settings'] = [ { label: 'System Settings', url: 'https://frappe.github.io/erpnext/user/manual/en/setting-up/settings/system-settings' }, ] -frappe.help.help_links['List/Email Account'] = [ +frappe.help.help_links['email-account'] = [ { label: 'Email Account', url: 'https://frappe.github.io/erpnext/user/manual/en/setting-up/email/email-account' }, ] -frappe.help.help_links['List/Notification'] = [ +frappe.help.help_links['notification'] = [ { label: 'Notification', url: 'https://frappe.github.io/erpnext/user/manual/en/setting-up/email/email-alerts' }, ] -frappe.help.help_links['Form/Print Settings'] = [ +frappe.help.help_links['print-settings'] = [ { label: 'Print Settings', url: 'https://frappe.github.io/erpnext/user/manual/en/setting-up/print/print-settings' }, ] diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index 5314bbe800..9b2b61fce4 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -1145,17 +1145,17 @@ Object.assign(frappe.utils, { route = `${doctype_slug}/new`; break; case "Calendar": - route = `${doctype_slug}/view/calendar/Default`; + route = `${doctype_slug}/view/calendar/default`; break; default: - frappe.throw({ message: __("Not a valid DocType view:") + item.doc_view, title: __("Unknown View") }); + frappe.throw({ message: __("Not a valid view:") + item.doc_view, title: __("Unknown View") }); route = ""; } } } else if (type === "report" && item.is_query_report) { route = "query-report/" + item.name; } else if (type === "report") { - route = frappe.router.slug(item.doctype) + "/view/report/" + item.name; + route = frappe.router.slug(item.name) + "/view/report"; } else if (type === "page") { route = item.name; } else if (type === "dashboard") { diff --git a/frappe/public/js/frappe/views/factory.js b/frappe/public/js/frappe/views/factory.js index 5148835984..34dab6aad8 100644 --- a/frappe/public/js/frappe/views/factory.js +++ b/frappe/public/js/frappe/views/factory.js @@ -13,7 +13,7 @@ frappe.views.Factory = class Factory { var page_name = frappe.get_route_str(), me = this; - if(frappe.pages[page_name] && !page_name.includes("Form/")) { + if(frappe.pages[page_name]) { frappe.container.change_to(page_name); if(me.on_show) { me.on_show(); diff --git a/frappe/public/js/frappe/views/file/file_view.js b/frappe/public/js/frappe/views/file/file_view.js index a19286c962..5e2b618828 100644 --- a/frappe/public/js/frappe/views/file/file_view.js +++ b/frappe/public/js/frappe/views/file/file_view.js @@ -292,7 +292,7 @@ frappe.views.FileView = class FileView extends frappe.views.ListView { acc += '/' + curr; } return acc; - }, '/app/List/File'); + }, '/app/file'); return `${folder}`; }) diff --git a/frappe/public/js/frappe/views/formview.js b/frappe/public/js/frappe/views/formview.js index 65b7d44e9b..22cefc0fff 100644 --- a/frappe/public/js/frappe/views/formview.js +++ b/frappe/public/js/frappe/views/formview.js @@ -10,7 +10,7 @@ frappe.views.FormFactory = class FormFactory extends frappe.views.Factory { if (!frappe.views.formview[doctype_layout]) { frappe.model.with_doctype(doctype, () => { - this.page = frappe.container.add_page("form/" + doctype_layout); + this.page = frappe.container.add_page(doctype_layout); frappe.views.formview[doctype_layout] = this.page; this.make_and_show(doctype, route); }); @@ -103,7 +103,7 @@ frappe.views.FormFactory = class FormFactory extends frappe.views.Factory { } render(doctype_layout, name) { - frappe.container.change_to("form/" + doctype_layout); + frappe.container.change_to(doctype_layout); frappe.views.formview[doctype_layout].frm.refresh(name); } } diff --git a/frappe/public/js/frappe/views/kanban/kanban_view.js b/frappe/public/js/frappe/views/kanban/kanban_view.js index 7d53648893..665cf096e4 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_view.js +++ b/frappe/public/js/frappe/views/kanban/kanban_view.js @@ -335,7 +335,7 @@ frappe.views.KanbanView.show_kanban_dialog = function (doctype, show_existing) {

      ${__('No fields found that can be used as a Kanban Column. Use the Customize Form to add a Custom Field of type "Select".')}

      - + ${__('Customize Form')}
      diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 9950122b26..80d0aa71da 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -771,7 +771,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { ${no_of_reports_html}

      `; - let get_item_html = item => `${item.name}`; + let get_item_html = item => `${item.name}`; warning_message += reports.map(get_item_html).join(', '); @@ -806,8 +806,8 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { const data = r.message; // Rememeber the name of Prepared Report doc this.prepared_report_doc_name = data.name; - let alert_message = `Report initiated. You can track its status - here`; + let alert_message = `` + + __('Report initiated, click to view status') + ``; frappe.show_alert({message: alert_message, indicator: 'orange'}, 10); this.toggle_nothing_to_show(true); }); diff --git a/frappe/public/js/frappe/widgets/onboarding_widget.js b/frappe/public/js/frappe/widgets/onboarding_widget.js index b70c7d320d..52bb13d3a0 100644 --- a/frappe/public/js/frappe/widgets/onboarding_widget.js +++ b/frappe/public/js/frappe/widgets/onboarding_widget.js @@ -196,9 +196,9 @@ export default class OnboardingWidget extends Widget { show_form_tour(step) { let route; if (step.is_single) { - route = `Form/${step.reference_document}`; + route = frappe.router.slug(step.reference_document); } else { - route = `Form/${step.reference_document}/${__('New')} ${__(step.reference_document)} 1`; + route = `${frappe.router.slug(step.reference_document)}/new`; } let current_route = frappe.get_route(); @@ -331,7 +331,7 @@ export default class OnboardingWidget extends Widget { frappe.route_hooks.after_save = callback; } - frappe.set_route(`Form/${step.reference_document}/${__('New')} ${__(step.reference_document)} 1`); + frappe.set_route('Form', step.reference_document, 'new'); } show_quick_entry(step) { diff --git a/frappe/social/doctype/energy_point_log/energy_point_log.js b/frappe/social/doctype/energy_point_log/energy_point_log.js index 2d4a1e6f2e..74483e73b5 100644 --- a/frappe/social/doctype/energy_point_log/energy_point_log.js +++ b/frappe/social/doctype/energy_point_log/energy_point_log.js @@ -38,6 +38,6 @@ frappe.ui.form.on('Energy Point Log', { make_reference_name_link(frm) { let dt = frm.doc.reference_doctype; let dn = frm.doc.reference_name; - frm.fields_dict.reference_name.$input_wrapper.find('.control-value').wrapInner(``); + frm.fields_dict.reference_name.$input_wrapper.find('.control-value').wrapInner(``); } }); diff --git a/frappe/workflow/doctype/workflow_action/workflow_action_list.js b/frappe/workflow/doctype/workflow_action/workflow_action_list.js index c922aba477..288ecfe678 100644 --- a/frappe/workflow/doctype/workflow_action/workflow_action_list.js +++ b/frappe/workflow/doctype/workflow_action/workflow_action_list.js @@ -13,7 +13,7 @@ frappe.listview_settings['Workflow Action'] = { ? encodeURIComponent(docname) : docname; - const link = '/app/Form/' + doctype + '/' + docname; + const link = '/app/' + frappe.router.slug(doctype) + '/' + docname; return link; } }; \ No newline at end of file From ec2b3c5b186d847158565a9a753af3cf91a77f76 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 24 Dec 2020 14:20:42 +0530 Subject: [PATCH 5/6] fix(tests): add a timeout since, faster execution? --- frappe/tests/test_scheduler.py | 3 ++- frappe/utils/background_jobs.py | 7 ++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/frappe/tests/test_scheduler.py b/frappe/tests/test_scheduler.py index 75d075ed4e..d5344c60b5 100644 --- a/frappe/tests/test_scheduler.py +++ b/frappe/tests/test_scheduler.py @@ -19,7 +19,6 @@ def test_timeout_10(): def test_method(): pass - class TestScheduler(TestCase): def setUp(self): purge_pending_jobs() @@ -44,6 +43,8 @@ class TestScheduler(TestCase): job.db_set('last_execution', '2010-01-01 00:00:00') frappe.db.commit() + time.sleep(0.5) + # 1st job is in the queue (or running), don't enqueue it again self.assertFalse(job.enqueue()) frappe.db.sql('DELETE FROM `tabScheduled Job Log` WHERE `scheduled_job_type`=%s', job.name) diff --git a/frappe/utils/background_jobs.py b/frappe/utils/background_jobs.py index 447156e5a1..bd1f1154a9 100755 --- a/frappe/utils/background_jobs.py +++ b/frappe/utils/background_jobs.py @@ -181,12 +181,9 @@ def get_jobs(site=None, queue=None, key='method'): jobs = q.jobs + get_running_jobs_in_queue(q) for job in jobs: if job.kwargs.get('site'): - if site is None: + # if job belongs to current site, or if all jobs are requested + if (job.kwargs['site'] == site) or site is None: add_to_dict(job) - - elif job.kwargs['site'] == site: - add_to_dict(job) - else: print('No site found in job', job.__dict__) From bfde4c2c4c3b8ef20db69e2a70b94dfd9e07abcc Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 24 Dec 2020 15:16:28 +0530 Subject: [PATCH 6/6] fix(conflict) --- frappe/core/doctype/user/user.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frappe/core/doctype/user/user.json b/frappe/core/doctype/user/user.json index 22d3f296b3..0e684a3dd4 100644 --- a/frappe/core/doctype/user/user.json +++ b/frappe/core/doctype/user/user.json @@ -657,11 +657,7 @@ } ], "max_attachments": 5, -<<<<<<< HEAD "modified": "2020-12-24 19:48:49.677800", -======= - "modified": "2020-12-23 12:43:51.470188", ->>>>>>> bfdf108343 (fix(minor): fix link in User doctype) "modified_by": "Administrator", "module": "Core", "name": "User",