From cdcfe844cbe4a6647433a3b31c53d5af99deb795 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 5 Nov 2020 12:32:51 +0530 Subject: [PATCH 01/10] feat: Init unified desk [WIP] --- frappe/boot.py | 15 ++++ frappe/core/doctype/role/role.json | 86 ++++++++++++++++++- frappe/public/js/frappe/ui/toolbar/toolbar.js | 16 ++-- 3 files changed, 110 insertions(+), 7 deletions(-) diff --git a/frappe/boot.py b/frappe/boot.py index a513fed53b..6293b5b6f5 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -88,6 +88,7 @@ def get_bootinfo(): bootinfo.frequently_visited_links = frequently_visited_links() bootinfo.link_preview_doctypes = get_link_preview_doctypes() bootinfo.additional_filters_config = get_additional_filters_from_hooks() + bootinfo.desk_settings = get_desk_settings() return bootinfo @@ -308,3 +309,17 @@ def get_additional_filters_from_hooks(): return filter_config +def get_desk_settings(): + role_list = frappe.get_all('Role', fields=['*'], filters=dict( + name=['in', frappe.get_roles()] + )) + desk_settings = {} + + desk_properties = ("search_bar", "notification", "chat", "list_sidebar", + "bulk_actions", "view_switcher", "form_sidebar", "timeline", "dashboard") + + for role in role_list: + for key in desk_properties: + desk_settings[key] = desk_settings[key] or role[key] + + return desk_settings \ No newline at end of file diff --git a/frappe/core/doctype/role/role.json b/frappe/core/doctype/role/role.json index 0ad15ba10b..92f8edd12c 100644 --- a/frappe/core/doctype/role/role.json +++ b/frappe/core/doctype/role/role.json @@ -13,7 +13,19 @@ "column_break_4", "disabled", "desk_access", - "two_factor_auth" + "two_factor_auth", + "navigation_settings_section", + "search_bar", + "notification", + "chat", + "list_settings_section", + "list_sidebar", + "bulk_actions", + "view_switcher", + "form_settings_section", + "form_sidebar", + "timeline", + "dashboard" ], "fields": [ { @@ -60,12 +72,82 @@ { "fieldname": "column_break_4", "fieldtype": "Column Break" + }, + { + "fieldname": "navigation_settings_section", + "fieldtype": "Section Break", + "label": "Navigation Settings" + }, + { + "default": "0", + "fieldname": "search_bar", + "fieldtype": "Check", + "label": "Search Bar" + }, + { + "default": "0", + "fieldname": "notification", + "fieldtype": "Check", + "label": "Notification" + }, + { + "default": "1", + "fieldname": "chat", + "fieldtype": "Check", + "label": "Chat" + }, + { + "fieldname": "list_settings_section", + "fieldtype": "Section Break", + "label": "List Settings" + }, + { + "default": "0", + "fieldname": "list_sidebar", + "fieldtype": "Check", + "label": "Sidebar" + }, + { + "default": "0", + "fieldname": "bulk_actions", + "fieldtype": "Check", + "label": "Bulk Actions" + }, + { + "fieldname": "form_settings_section", + "fieldtype": "Section Break", + "label": "Form Settings" + }, + { + "default": "0", + "fieldname": "form_sidebar", + "fieldtype": "Check", + "label": "Sidebar" + }, + { + "default": "0", + "fieldname": "timeline", + "fieldtype": "Check", + "label": "Timeline" + }, + { + "default": "0", + "fieldname": "dashboard", + "fieldtype": "Check", + "label": "Dashboard" + }, + { + "default": "0", + "fieldname": "view_switcher", + "fieldtype": "Check", + "label": "View Switcher" } ], "icon": "fa fa-bookmark", "idx": 1, + "index_web_pages_for_search": 1, "links": [], - "modified": "2020-08-06 15:42:59.036960", + "modified": "2020-11-05 01:25:10.536546", "modified_by": "Administrator", "module": "Core", "name": "Role", diff --git a/frappe/public/js/frappe/ui/toolbar/toolbar.js b/frappe/public/js/frappe/ui/toolbar/toolbar.js index 8ff79aad8b..83b83003b0 100644 --- a/frappe/public/js/frappe/ui/toolbar/toolbar.js +++ b/frappe/public/js/frappe/ui/toolbar/toolbar.js @@ -12,10 +12,7 @@ frappe.ui.toolbar.Toolbar = class { })); $('.dropdown-toggle').dropdown(); - let awesome_bar = new frappe.search.AwesomeBar(); - awesome_bar.setup("#navbar-search"); - // awesome_bar.setup("#modal-search"); - + this.setup_awesomebar(); this.setup_notifications(); this.make(); } @@ -157,8 +154,17 @@ frappe.ui.toolbar.Toolbar = class { } } + setup_awesomebar() { + if (frappe.boot.desk_settings.search_bar) { + let awesome_bar = new frappe.search.AwesomeBar(); + awesome_bar.setup("#navbar-search"); + } + } + setup_notifications () { - this.notifications = new frappe.ui.Notifications(); + if (frappe.boot.desk_settings.notifications) { + this.notifications = new frappe.ui.Notifications(); + } } } From f1d7e2a212290f281f93fd37e8380be44320450b Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 5 Nov 2020 12:33:54 +0530 Subject: [PATCH 02/10] feat: Open List or Form in new window --- frappe/core/doctype/doctype/doctype.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.js b/frappe/core/doctype/doctype/doctype.js index b3469abf29..e236464b95 100644 --- a/frappe/core/doctype/doctype/doctype.js +++ b/frappe/core/doctype/doctype/doctype.js @@ -24,11 +24,12 @@ 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]), () => { - frappe.set_route('Form', frm.doc.name); + window.open(`/desk#Form/${frm.doc.name}`); + // frappe.set_route('Form', frm.doc.name); }); } else { frm.add_custom_button(__('Go to {0} List', [frm.doc.name]), () => { - frappe.set_route('List', frm.doc.name, 'List'); + window.open(`/desk#List/${frm.doc.name}/List`); }); } } From f608fc95164491e7e5efec90f64df7ab29a60886 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 11 Nov 2020 08:50:54 +0530 Subject: [PATCH 03/10] feat: Enable desk for Website User --- frappe/auth.py | 14 ++++---------- frappe/boot.py | 4 ++-- frappe/core/doctype/user/user.py | 21 +++++++++------------ frappe/www/desk.py | 4 ++-- 4 files changed, 17 insertions(+), 26 deletions(-) diff --git a/frappe/auth.py b/frappe/auth.py index 6d51629c58..3b18c8adb9 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -164,16 +164,10 @@ class LoginManager: self.full_name = " ".join(filter(None, [self.info.first_name, self.info.last_name])) - if self.info.user_type=="Website User": - frappe.local.cookie_manager.set_cookie("system_user", "no") - if not resume: - frappe.local.response["message"] = "No App" - frappe.local.response["home_page"] = '/' + get_home_page() - else: - frappe.local.cookie_manager.set_cookie("system_user", "yes") - if not resume: - frappe.local.response['message'] = 'Logged In' - frappe.local.response["home_page"] = "/desk" + frappe.local.cookie_manager.set_cookie("system_user", "yes") + if not resume: + frappe.local.response['message'] = 'Logged In' + frappe.local.response["home_page"] = "/desk" if not resume: frappe.response["full_name"] = self.full_name diff --git a/frappe/boot.py b/frappe/boot.py index 6293b5b6f5..bb1cff40f9 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -226,7 +226,7 @@ def load_translations(bootinfo): def get_user_info(): user_info = frappe.db.get_all('User', fields=['`name`', 'full_name as fullname', 'user_image as image', 'gender', 'email', 'username', 'bio', 'location', 'interest', 'banner_image', 'allowed_in_mentions'], - filters=dict(enabled=1, user_type=['!=', 'Website User'])) + filters=dict(enabled=1)) user_info_map = {d.name: d for d in user_info} @@ -320,6 +320,6 @@ def get_desk_settings(): for role in role_list: for key in desk_properties: - desk_settings[key] = desk_settings[key] or role[key] + desk_settings[key] = desk_settings.get(key) or role.get(key) return desk_settings \ No newline at end of file diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 16f4986c35..9fc6658f29 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -182,20 +182,17 @@ class User(Document): def share_with_self(self): - if self.user_type=="System User": - frappe.share.add(self.doctype, self.name, self.name, write=1, share=1, - flags={"ignore_share_permission": True}) - else: - frappe.share.remove(self.doctype, self.name, self.name, - flags={"ignore_share_permission": True, "ignore_permissions": True}) + frappe.share.add(self.doctype, self.name, self.name, write=1, share=1, + flags={"ignore_share_permission": True}) def validate_share(self, docshare): - if docshare.user == self.name: - if self.user_type=="System User": - if docshare.share != 1: - frappe.throw(_("Sorry! User should have complete access to their own record.")) - else: - frappe.throw(_("Sorry! Sharing with Website User is prohibited.")) + pass + # if docshare.user == self.name: + # if self.user_type=="System User": + # if docshare.share != 1: + # frappe.throw(_("Sorry! User should have complete access to their own record.")) + # else: + # frappe.throw(_("Sorry! Sharing with Website User is prohibited.")) def send_password_notification(self, new_password): try: diff --git a/frappe/www/desk.py b/frappe/www/desk.py index b3974df852..764235f583 100644 --- a/frappe/www/desk.py +++ b/frappe/www/desk.py @@ -14,8 +14,8 @@ import frappe.sessions def get_context(context): if frappe.session.user == "Guest": frappe.throw(_("Log in to access this page."), frappe.PermissionError) - elif frappe.db.get_value("User", frappe.session.user, "user_type") == "Website User": - frappe.throw(_("You are not permitted to access this page."), frappe.PermissionError) + # elif frappe.db.get_value("User", frappe.session.user, "user_type") == "Website User": + # frappe.throw(_("You are not permitted to access this page."), frappe.PermissionError) hooks = frappe.get_hooks() try: From 243d5f788950c433993dc80fdc797aa8523d4438 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 10 Nov 2020 16:27:54 +0530 Subject: [PATCH 04/10] feat(wip): clean routes, not hashes --- frappe/public/js/frappe/desk.js | 2 +- frappe/public/js/frappe/form/form.js | 2 +- frappe/public/js/frappe/router.js | 113 ++++++++++-------- frappe/public/js/frappe/ui/dialog.js | 11 ++ .../js/frappe/ui/toolbar/awesome_bar.js | 6 - frappe/public/js/frappe/views/container.js | 2 +- 6 files changed, 77 insertions(+), 59 deletions(-) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 0e425abf25..153beabf3d 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -75,7 +75,7 @@ frappe.Application = Class.extend({ if (frappe.boot) { if (localStorage.getItem("session_last_route")) { - window.location.hash = localStorage.getItem("session_last_route"); + frappe.set_route(localStorage.getItem("session_last_route")); localStorage.removeItem("session_last_route"); } diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 27de748c95..837fa09b1f 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -1007,7 +1007,7 @@ frappe.ui.form.Form = class FrappeForm { return; } - frappe.re_route[window.location.hash] = '#Form/' + encodeURIComponent(this.doctype) + '/' + encodeURIComponent(name); + frappe.re_route[frappe.get_sub_path()] = 'Form/' + encodeURIComponent(this.doctype) + '/' + encodeURIComponent(name); frappe.set_route('Form', this.doctype, name); } diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 3c729f4af2..a4315ec359 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -13,39 +13,59 @@ frappe.view_factory = {}; frappe.view_factories = []; frappe.route_options = null; frappe.route_hooks = {}; +frappe._cur_route = null; + +$(window).on('hashchange', function() { + // save the title + + if (window.location.hash) { + console.log(window.location.hash); + let sub_path = frappe.get_sub_path(window.location.hash); + window.location.hash = ''; + frappe.push_state(sub_path); + } +}); frappe.route = function() { // Application is not yet initiated if (!frappe.app) return; - if(frappe.re_route[window.location.hash] !== undefined) { + let sub_path = frappe.get_route_str(); + + if (frappe.re_route[sub_path] !== undefined) { // after saving a doc, for example, // "New DocType 1" and the renamed "TestDocType", both exist in history // now if we try to go back, // it doesn't allow us to go back to the one prior to "New DocType 1" // Hence if this check is true, instead of changing location hash, // we just do a back to go to the doc previous to the "New DocType 1" - var re_route_val = frappe.get_route_str(frappe.re_route[window.location.hash]); - var cur_route_val = frappe.get_route_str(frappe._cur_route); - if (decodeURIComponent(re_route_val) === decodeURIComponent(cur_route_val)) { + var re_route_val = frappe.get_route_str(frappe.re_route[sub_path]); + if (decodeURIComponent(re_route_val) === decodeURIComponent(sub_path)) { window.history.back(); return; } else { - window.location.hash = frappe.re_route[window.location.hash]; + frappe.set_route(re_route_val); + return; } } - frappe._cur_route = window.location.hash; + frappe._cur_route = sub_path; - var route = frappe.get_route(); + let route = frappe.get_route(); if (route === false) { return; } frappe.route_history.push(route); - if(route[0]) { + // set title + frappe.route_titles[sub_path] = frappe._original_title || document.title; + + // hide open dialog + frappe.ui.hide_open_dialog(); + + if (route[0]) { const title_cased_route = frappe.utils.to_title_case(route[0]); if (title_cased_route === 'Workspace') { frappe.views.pageview.show(''); @@ -71,18 +91,17 @@ frappe.route = function() { } - if(frappe.route_titles[window.location.hash]) { - frappe.utils.set_title(frappe.route_titles[window.location.hash]); + if (frappe.route_titles[sub_path]) { + frappe.utils.set_title(frappe.route_titles[sub_path]); } else { setTimeout(function() { frappe.route_titles[frappe.get_route_str()] = frappe._original_title || document.title; }, 1000); } - if(window.mixpanel) { - window.mixpanel.track(route.slice(0, 2).join(' ')); - } -} + frappe.route.trigger('change'); + +}; frappe.get_route = function(route) { // for app @@ -91,7 +110,7 @@ frappe.get_route = function(route) { var parts = null; var doc_name = route[route.length - 1]; // if the last part contains ? then check if it is valid query string - if(doc_name.indexOf("?") < doc_name.indexOf("=")){ + if (doc_name.indexOf("?") < doc_name.indexOf("=")) { parts = doc_name.split("?"); route[route.length - 1] = parts[0]; } else { @@ -112,7 +131,7 @@ frappe.get_route = function(route) { } frappe.get_prev_route = function() { - if(frappe.route_history && frappe.route_history.length > 1) { + if (frappe.route_history && frappe.route_history.length > 1) { return frappe.route_history[frappe.route_history.length - 2]; } else { return []; @@ -122,7 +141,7 @@ frappe.get_prev_route = function() { frappe._decode_str = function(r) { try { return decodeURIComponent(r); - } catch(e) { + } catch (e) { if (e instanceof URIError) { return r; } else { @@ -132,30 +151,37 @@ frappe._decode_str = function(r) { } frappe.get_raw_route_str = function(route) { - if(!route) + if (!route) { route = window.location.hash; + } - if(route.substr(0,1)=='#') route = route.substr(1); - if(route.substr(0,1)=='!') route = route.substr(1); + if (!route && window.location.pathname.startsWith('/desk')) { + // route is without hash + route = window.location.pathname.substr(5); + } + + if (route.substr(0, 1)=='/') route = route.substr(1); + if (route.substr(0, 1)=='#') route = route.substr(1); + if (route.substr(0, 1)=='!') route = route.substr(1); return route; -} +}; -frappe.get_route_str = function(route) { +frappe.get_sub_path = frappe.get_route_str = function(route) { var rawRoute = frappe.get_raw_route_str(route); route = $.map(rawRoute.split('/'), frappe._decode_str).join('/'); return route; -} +}; frappe.set_route = function() { return new Promise(resolve => { var params = arguments; - if(params.length===1 && $.isArray(params[0])) { + if (params.length===1 && $.isArray(params[0])) { params = params[0]; } var route = $.map(params, function(a) { - if($.isPlainObject(a)) { + if ($.isPlainObject(a)) { frappe.route_options = a; return null; } else { @@ -174,18 +200,28 @@ frappe.set_route = function() { url.hash = route; window.location.replace(url); } else { - window.location.hash = route; + // + // window.location.hash = route; + + // routing v2 + frappe.push_state(route); } // Set favicon (app.js) frappe.provide('frappe.app'); frappe.app.set_favicon && frappe.app.set_favicon(); + setTimeout(() => { frappe.after_ajax && frappe.after_ajax(() => { resolve(); }); }, 100); }); +}; + +frappe.push_state = function (route) { + history.pushState(null, null, `/desk/${route}`); + frappe.route(); } frappe.set_re_route = function() { @@ -196,29 +232,6 @@ frappe.set_re_route = function() { frappe.has_route_options = function() { return Boolean(Object.keys(frappe.route_options || {}).length); -} - -frappe._cur_route = null; - -$(window).on('hashchange', function() { - // save the title - frappe.route_titles[frappe._cur_route] = frappe._original_title || document.title; - - if(window.location.hash==frappe._cur_route) - return; - - // hide open dialog - if(window.cur_dialog) { - if (!cur_dialog.minimizable) { - cur_dialog.hide(); - } else if (!cur_dialog.is_minimized) { - cur_dialog.toggle_minimize(); - } - } - - frappe.route(); - - frappe.route.trigger('change'); -}); +}; frappe.utils.make_event_emitter(frappe.route); diff --git a/frappe/public/js/frappe/ui/dialog.js b/frappe/public/js/frappe/ui/dialog.js index c8cef56420..bbed14730b 100644 --- a/frappe/public/js/frappe/ui/dialog.js +++ b/frappe/public/js/frappe/ui/dialog.js @@ -229,3 +229,14 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup { this.header.find('.modal-title').toggleClass('cursor-pointer'); } }; + +frappe.ui.hide_open_dialog = () => { + // hide open dialog + if (window.cur_dialog) { + if (!cur_dialog.minimizable) { + cur_dialog.hide(); + } else if (!cur_dialog.is_minimized) { + cur_dialog.toggle_minimize(); + } + } +} diff --git a/frappe/public/js/frappe/ui/toolbar/awesome_bar.js b/frappe/public/js/frappe/ui/toolbar/awesome_bar.js index e11adcfb66..556e1fbb97 100644 --- a/frappe/public/js/frappe/ui/toolbar/awesome_bar.js +++ b/frappe/public/js/frappe/ui/toolbar/awesome_bar.js @@ -107,13 +107,7 @@ frappe.search.AwesomeBar = Class.extend({ if(item.onclick) { item.onclick(item.match); } else { - var previous_hash = window.location.hash; frappe.set_route(item.route); - - // hashchange didn't fire! - if (window.location.hash == previous_hash) { - frappe.route(); - } } $input.val(""); }); diff --git a/frappe/public/js/frappe/views/container.js b/frappe/public/js/frappe/views/container.js index a5227b4271..ae4aaba570 100644 --- a/frappe/public/js/frappe/views/container.js +++ b/frappe/public/js/frappe/views/container.js @@ -82,7 +82,7 @@ frappe.views.Container = Class.extend({ $(document).trigger("page-change"); - this.page._route = window.location.hash; + this.page._route = frappe.get_sub_path(); $(this.page).trigger('show'); !this.page.disable_scroll_to_top && frappe.utils.scroll_to(0); frappe.breadcrumbs.update(); From 029e757cc67b1fc5e01e7d7a1c2724029c02b1f8 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 11 Nov 2020 10:59:50 +0530 Subject: [PATCH 05/10] fix(minor): routing in various places to support v2 routing --- frappe/hooks.py | 18 +++++---- frappe/public/js/frappe/desk.js | 29 +++++++------- frappe/public/js/frappe/router.js | 65 ++++++++++++++++++++++--------- frappe/website/render.py | 4 ++ frappe/www/desk.html | 1 + frappe/www/desk.py | 1 + 6 files changed, 79 insertions(+), 39 deletions(-) diff --git a/frappe/hooks.py b/frappe/hooks.py index 17d022465e..b3d7623e0e 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -29,16 +29,18 @@ page_js = { # website app_include_js = [ - "assets/js/libs.min.js", - "assets/js/desk.min.js", - "assets/js/list.min.js", - "assets/js/form.min.js", - "assets/js/control.min.js", - "assets/js/report.min.js", + "/assets/js/libs.min.js", + "/assets/js/desk.min.js", + "/assets/js/list.min.js", + "/assets/js/form.min.js", + "/assets/js/control.min.js", + "/assets/js/report.min.js", ] app_include_css = [ - "assets/css/desk.min.css", - "assets/css/report.min.css", + "/assets/css/desk.min.css", + "/assets/css/list.min.css", + "/assets/css/form.min.css", + "/assets/css/report.min.css", ] doctype_js = { diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 153beabf3d..78c367c8e9 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -73,19 +73,9 @@ frappe.Application = Class.extend({ this.set_rtl(); - if (frappe.boot) { - if (localStorage.getItem("session_last_route")) { - frappe.set_route(localStorage.getItem("session_last_route")); - localStorage.removeItem("session_last_route"); - } - - } - // page container this.make_page_container(); - - // route to home page - frappe.route(); + this.set_route(); // trigger app startup $(document).trigger('startup'); @@ -155,7 +145,7 @@ frappe.Application = Class.extend({ }); }, 300000); // check every 5 minutes - if(frappe.user.has_role("System Manager")){ + if (frappe.user.has_role("System Manager")) { setInterval(function() { frappe.call({ method: 'frappe.core.doctype.log_settings.log_settings.has_unseen_error_log', @@ -164,7 +154,7 @@ frappe.Application = Class.extend({ }, callback: function(r) { console.log(r); - if(r.message.show_alert){ + if (r.message.show_alert) { frappe.show_alert({ indicator: 'red', message: r.message.message @@ -179,6 +169,19 @@ frappe.Application = Class.extend({ this.fetch_tags(); }, + set_route() { + if (frappe.boot && localStorage.getItem("session_last_route")) { + frappe.set_route(localStorage.getItem("session_last_route")); + localStorage.removeItem("session_last_route"); + } else if (frappe._cur_route) { + // go to the appropriate sub-path + frappe.set_route(frappe._cur_route); + } else { + // route to home page + frappe.route(); + } + }, + setup_frappe_vue() { Vue.prototype.__ = window.__; Vue.prototype.frappe = window.frappe; diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index a4315ec359..78d1b7e1d1 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -16,22 +16,39 @@ frappe.route_hooks = {}; frappe._cur_route = null; $(window).on('hashchange', function() { - // save the title - if (window.location.hash) { - console.log(window.location.hash); let sub_path = frappe.get_sub_path(window.location.hash); window.location.hash = ''; frappe.push_state(sub_path); } }); +// routing v2, capture all clicks so that the target is managed with push-state +$('body').on('click', 'a', function(e) { + let override = (e, route) => { + e.preventDefault(); + frappe.push_state(frappe.get_sub_path(route)); + return false; + }; + + // target has "#" ,this is a v1 style route, so remake it. + if (e.target.hash) { + return override(e, e.target.hash); + } + + // target has "/desk, this is a v2 style route. + if (e.target.pathname && + (e.target.pathname.startWith('/desk') || e.target.pathname.startWith('desk'))) { + return override(e, e.target.pathname); + } +}); + frappe.route = function() { // Application is not yet initiated if (!frappe.app) return; - let sub_path = frappe.get_route_str(); + let sub_path = frappe.get_sub_path(); if (frappe.re_route[sub_path] !== undefined) { // after saving a doc, for example, @@ -40,7 +57,7 @@ frappe.route = function() { // it doesn't allow us to go back to the one prior to "New DocType 1" // Hence if this check is true, instead of changing location hash, // we just do a back to go to the doc previous to the "New DocType 1" - var re_route_val = frappe.get_route_str(frappe.re_route[sub_path]); + var re_route_val = frappe.get_sub_path(frappe.re_route[sub_path]); if (decodeURIComponent(re_route_val) === decodeURIComponent(sub_path)) { window.history.back(); return; @@ -105,7 +122,7 @@ frappe.route = function() { frappe.get_route = function(route) { // for app - route = frappe.get_raw_route_str(route).split('/'); + route = frappe.get_sub_path_string(route).split('/'); route = $.map(route, frappe._decode_str); var parts = null; var doc_name = route[route.length - 1]; @@ -150,16 +167,19 @@ frappe._decode_str = function(r) { } } -frappe.get_raw_route_str = function(route) { +frappe.get_sub_path_string = function(route) { + // return clean sub_path from hash or url + // supports both v1 and v2 routing + if (!route) { route = window.location.hash; } - - if (!route && window.location.pathname.startsWith('/desk')) { - // route is without hash - route = window.location.pathname.substr(5); + if (!route) { + route = window.location.pathname; } + if (route.substr(0, 1)=='/') route = route.substr(1); + if (route.startsWith('desk')) route = route.substr(4); if (route.substr(0, 1)=='/') route = route.substr(1); if (route.substr(0, 1)=='#') route = route.substr(1); if (route.substr(0, 1)=='!') route = route.substr(1); @@ -168,8 +188,8 @@ frappe.get_raw_route_str = function(route) { }; frappe.get_sub_path = frappe.get_route_str = function(route) { - var rawRoute = frappe.get_raw_route_str(route); - route = $.map(rawRoute.split('/'), frappe._decode_str).join('/'); + var sub_path = frappe.get_sub_path_string(route); + route = $.map(sub_path.split('/'), frappe._decode_str).join('/'); return route; }; @@ -220,14 +240,23 @@ frappe.set_route = function() { }; frappe.push_state = function (route) { - history.pushState(null, null, `/desk/${route}`); - frappe.route(); -} + let url = `/desk/${route}`; + if (window.location.pathname !== url) { + // cleanup any remenants of v1 routing + window.location.hash = ''; + + // push state so the browser looks fine + history.pushState(null, null, url); + + // now process the route + frappe.route(); + } +}; frappe.set_re_route = function() { - var tmp = window.location.hash; + var tmp = frappe.get_sub_path(); frappe.set_route.apply(null, arguments); - frappe.re_route[tmp] = window.location.hash; + frappe.re_route[tmp] = frappe.get_sub_path(); }; frappe.has_route_options = function() { diff --git a/frappe/website/render.py b/frappe/website/render.py index af3b18b233..e83357f8be 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -252,6 +252,10 @@ def resolve_path(path): if path != "index": path = resolve_from_map(path) + if path.startswith("desk"): + path = "desk" + frappe.flags.desk_route = path[5:] + return path def resolve_from_map(path): diff --git a/frappe/www/desk.html b/frappe/www/desk.html index c39933eb57..0f0aa0f34e 100644 --- a/frappe/www/desk.html +++ b/frappe/www/desk.html @@ -47,6 +47,7 @@ if(!window.frappe) window.frappe = {}; frappe.boot = {{ boot }}; + frappe._cur_route = "{{ desk_route }}"; frappe.csrf_token = "{{ csrf_token }}"; diff --git a/frappe/www/desk.py b/frappe/www/desk.py index 764235f583..dd0a684fae 100644 --- a/frappe/www/desk.py +++ b/frappe/www/desk.py @@ -37,6 +37,7 @@ def get_context(context): boot_json = re.sub("\[^<]*\", "", boot_json) context.update({ + "desk_route": frappe.flags.desk_route or "", "no_cache": 1, "build_version": get_build_version(), "include_js": hooks["app_include_js"], From 984123c3eb67cf4de93f42e2b8571379246e2ee9 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 11 Nov 2020 11:45:15 +0530 Subject: [PATCH 06/10] fix(routing-v2): handle pop state and use currentTarget --- frappe/public/js/frappe/router.js | 30 ++++++++++------------- frappe/public/js/frappe/views/pageview.js | 28 ++++++++++++++++----- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 78d1b7e1d1..637def1bc3 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -23,6 +23,11 @@ $(window).on('hashchange', function() { } }); +window.addEventListener('popstate', (event) => { + // forward-back button, just re-render based on current route + frappe.route(); +}); + // routing v2, capture all clicks so that the target is managed with push-state $('body').on('click', 'a', function(e) { let override = (e, route) => { @@ -32,14 +37,14 @@ $('body').on('click', 'a', function(e) { }; // target has "#" ,this is a v1 style route, so remake it. - if (e.target.hash) { - return override(e, e.target.hash); + if (e.currentTarget.hash) { + return override(e, e.currentTarget.hash); } // target has "/desk, this is a v2 style route. - if (e.target.pathname && - (e.target.pathname.startWith('/desk') || e.target.pathname.startWith('desk'))) { - return override(e, e.target.pathname); + if (e.currentTarget.pathname && + (e.currentTarget.pathname.startsWith('/desk') || e.currentTarget.pathname.startsWith('desk'))) { + return override(e, e.currentTarget.pathname); } }); @@ -70,9 +75,6 @@ frappe.route = function() { frappe._cur_route = sub_path; let route = frappe.get_route(); - if (route === false) { - return; - } frappe.route_history.push(route); @@ -103,7 +105,7 @@ frappe.route = function() { } } } else { - // Show desk + // Show home frappe.views.pageview.show(''); } @@ -138,12 +140,6 @@ frappe.get_route = function(route) { frappe.route_options = $.extend(frappe.route_options || {}, query_params); } - // backward compatibility - if (route && route[0]==='Module') { - frappe.set_route('modules', route[1]); - return false; - } - return route; } @@ -178,8 +174,8 @@ frappe.get_sub_path_string = function(route) { route = window.location.pathname; } - if (route.substr(0, 1)=='/') route = route.substr(1); - if (route.startsWith('desk')) route = route.substr(4); + if (route.substr(0, 1)=='/') route = route.substr(1); // for /desk/sub + if (route.startsWith('desk')) route = route.substr(4); // for desk/sub if (route.substr(0, 1)=='/') route = route.substr(1); if (route.substr(0, 1)=='#') route = route.substr(1); if (route.substr(0, 1)=='!') route = route.substr(1); diff --git a/frappe/public/js/frappe/views/pageview.js b/frappe/public/js/frappe/views/pageview.js index 90c5965b85..e2c762edd4 100644 --- a/frappe/public/js/frappe/views/pageview.js +++ b/frappe/public/js/frappe/views/pageview.js @@ -38,15 +38,31 @@ frappe.views.pageview = { }, show: function(name) { - if(!name) { + if (!name) { name = (frappe.boot ? frappe.boot.home_page : window.page_name); + + if (name === "workspace") { + if (!frappe.workspace) { + let page = frappe.container.add_page('workspace'); + let container = $('
').appendTo(page); + container = $('
').appendTo(container); + + frappe.workspace = new Desktop({ + wrapper: container + }); + } + } + frappe.container.change_to('workspace'); + frappe.workspace.route(); + frappe.utils.set_title(__('Home')); + return; } frappe.model.with_doctype("Page", function() { frappe.views.pageview.with_page(name, function(r) { - if(r && r.exc) { - if(!r['403']) + if (r && r.exc) { + if (!r['403']) frappe.show_not_found(name); - } else if(!frappe.pages[name]) { + } else if (!frappe.pages[name]) { new frappe.views.Page(name); } frappe.container.change_to(name); @@ -59,7 +75,7 @@ frappe.views.Page = class Page { constructor(name) { this.name = name; var me = this; - + // web home page if(name==window.page_name) { this.wrapper = document.getElementById('page-' + name); @@ -87,7 +103,7 @@ frappe.views.Page = class Page { } this.trigger_page_event('on_page_load'); - + // set events $(this.wrapper).on('show', function() { window.cur_frm = null; From 75fe8a615a2d28f77c78a9558debe32f364e262d Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 11 Nov 2020 16:31:47 +0530 Subject: [PATCH 07/10] feat(app): move /desk to /app --- cypress/integration/api.js | 2 +- cypress/integration/awesome_bar.js | 4 ++-- cypress/integration/control_barcode.js | 2 +- cypress/integration/control_duration.js | 2 +- cypress/integration/control_link.js | 6 +++--- cypress/integration/control_rating.js | 2 +- cypress/integration/datetime.js | 2 +- cypress/integration/depends_on.js | 2 +- cypress/integration/file_uploader.js | 2 +- cypress/integration/form.js | 16 +++++++-------- cypress/integration/grid_pagination.js | 12 +++++------ cypress/integration/list_view.js | 2 +- cypress/integration/list_view_settings.js | 6 +++--- cypress/integration/login.js | 2 +- cypress/integration/query_report.js | 4 ++-- cypress/integration/recorder.js | 16 +++++++-------- cypress/integration/relative_time_filters.js | 6 +++--- cypress/integration/report_view.js | 4 ++-- cypress/support/commands.js | 4 ++-- frappe/__init__.py | 2 +- frappe/auth.py | 2 +- .../doctype/auto_repeat/auto_repeat.js | 2 +- .../deleted_document/deleted_document_list.js | 2 +- frappe/core/doctype/doctype/doctype.js | 4 ++-- .../core/doctype/log_settings/log_settings.py | 2 +- frappe/core/doctype/user/user.py | 2 +- .../permission_manager/permission_manager.js | 2 +- .../permission_manager_help.html | 8 ++++---- .../customize_form/test_customize_form.py | 2 +- frappe/desk/doctype/dashboard/dashboard.py | 2 +- frappe/desk/moduleview.py | 8 ++++---- frappe/desk/page/leaderboard/leaderboard.js | 2 +- frappe/desk/page/setup_wizard/setup_wizard.js | 2 +- frappe/desk/report/todo/todo.py | 2 +- .../doctype/notification/notification.js | 2 +- .../google_calendar/google_calendar.js | 2 +- .../google_calendar/google_calendar.py | 2 +- .../google_contacts/google_contacts.js | 2 +- .../google_contacts/google_contacts.py | 2 +- .../doctype/google_drive/google_drive.js | 2 +- .../doctype/google_drive/google_drive.py | 2 +- frappe/model/delete_doc.py | 4 ++-- frappe/model/document.py | 4 ++-- frappe/public/js/frappe/form/form.js | 2 +- frappe/public/js/frappe/form/formatters.js | 2 +- frappe/public/js/frappe/form/linked_with.js | 2 +- .../js/frappe/form/multi_select_dialog.js | 2 +- .../frappe/form/templates/address_list.html | 2 +- .../frappe/form/templates/contact_list.html | 2 +- .../frappe/form/templates/print_layout.html | 2 +- frappe/public/js/frappe/list/list_factory.js | 8 ++++---- frappe/public/js/frappe/list/list_view.js | 2 +- frappe/public/js/frappe/recorder/recorder.js | 4 ++-- frappe/public/js/frappe/router.js | 20 ++++++++++++++----- .../frappe/ui/notifications/notifications.js | 8 ++++---- .../js/frappe/utils/energy_point_utils.js | 2 +- frappe/public/js/frappe/utils/utils.js | 2 +- frappe/public/js/frappe/views/breadcrumbs.js | 4 ++-- .../public/js/frappe/views/file/file_view.js | 6 +++--- .../views/image/image_view_item_row.html | 2 +- .../js/frappe/views/kanban/kanban_view.js | 2 +- frappe/public/js/frappe/views/pageview.js | 16 --------------- .../js/frappe/views/reports/query_report.js | 8 ++++---- .../js/frappe/views/workspace/workspace.js | 2 +- .../js/frappe/widgets/onboarding_widget.js | 2 +- frappe/public/js/frappe/widgets/utils.js | 1 - .../energy_point_log/energy_point_log.js | 2 +- .../templates/includes/comments/comments.py | 2 +- .../includes/navbar/navbar_login.html | 2 +- .../pages/integrations/gcalendar-success.html | 2 +- frappe/utils/bot.py | 6 +++--- frappe/utils/csvutils.py | 2 +- frappe/utils/data.py | 6 +++--- frappe/utils/oauth.py | 2 +- .../website_settings/google_indexing.py | 2 +- frappe/website/render.py | 5 ++--- .../workflow_action/workflow_action_list.js | 2 +- frappe/www/{desk.html => app.html} | 1 - frappe/www/{desk.py => app.py} | 3 +-- frappe/www/login.py | 2 +- 80 files changed, 147 insertions(+), 157 deletions(-) rename frappe/www/{desk.html => app.html} (98%) rename frappe/www/{desk.py => app.py} (96%) diff --git a/cypress/integration/api.js b/cypress/integration/api.js index 2279dc399d..cd91c8fae4 100644 --- a/cypress/integration/api.js +++ b/cypress/integration/api.js @@ -2,7 +2,7 @@ context('API Resources', () => { before(() => { cy.visit('/login'); cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); }); it('Creates two Comments', () => { diff --git a/cypress/integration/awesome_bar.js b/cypress/integration/awesome_bar.js index 15e85976fc..dd56ca33f7 100644 --- a/cypress/integration/awesome_bar.js +++ b/cypress/integration/awesome_bar.js @@ -2,7 +2,7 @@ context('Awesome Bar', () => { before(() => { cy.visit('/login'); cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); }); beforeEach(() => { @@ -16,7 +16,7 @@ context('Awesome Bar', () => { cy.get('h1').should('contain', 'To Do'); - cy.location('hash').should('eq', '#List/ToDo/List'); + cy.location('hash').should('eq', '/app/List/ToDo/List'); }); it('find text in doctype list', () => { diff --git a/cypress/integration/control_barcode.js b/cypress/integration/control_barcode.js index 4e05d864e6..ea26f446f4 100644 --- a/cypress/integration/control_barcode.js +++ b/cypress/integration/control_barcode.js @@ -1,7 +1,7 @@ context('Control Barcode', () => { beforeEach(() => { cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); }); function get_dialog_with_barcode() { diff --git a/cypress/integration/control_duration.js b/cypress/integration/control_duration.js index edad759216..fae207890f 100644 --- a/cypress/integration/control_duration.js +++ b/cypress/integration/control_duration.js @@ -1,7 +1,7 @@ context('Control Duration', () => { before(() => { cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); }); function get_dialog_with_duration(hide_days=0, hide_seconds=0) { diff --git a/cypress/integration/control_link.js b/cypress/integration/control_link.js index 0dc7d5b88e..248271453b 100644 --- a/cypress/integration/control_link.js +++ b/cypress/integration/control_link.js @@ -1,11 +1,11 @@ context('Control Link', () => { before(() => { cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); }); beforeEach(() => { - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); cy.create_records({ doctype: 'ToDo', description: 'this is a test todo for link' @@ -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', `#Form/ToDo/${todos[0]}`); + cy.location('hash').should('eq', `/app/Form/ToDo/${todos[0]}`); }); }); }); diff --git a/cypress/integration/control_rating.js b/cypress/integration/control_rating.js index e89ab2d3be..c1e308860e 100644 --- a/cypress/integration/control_rating.js +++ b/cypress/integration/control_rating.js @@ -1,7 +1,7 @@ context('Control Rating', () => { before(() => { cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); }); function get_dialog_with_rating() { diff --git a/cypress/integration/datetime.js b/cypress/integration/datetime.js index 9bf01632bf..7a9f51fb0d 100644 --- a/cypress/integration/datetime.js +++ b/cypress/integration/datetime.js @@ -4,7 +4,7 @@ const doctype_name = datetime_doctype.name; context('Control Date, Time and DateTime', () => { before(() => { cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); return cy.insert_doc('DocType', datetime_doctype, true); }); diff --git a/cypress/integration/depends_on.js b/cypress/integration/depends_on.js index 93417014c5..919323dcc3 100644 --- a/cypress/integration/depends_on.js +++ b/cypress/integration/depends_on.js @@ -1,7 +1,7 @@ context('Depends On', () => { before(() => { cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); return cy.window().its('frappe').then(frappe => { return frappe.call('frappe.tests.ui_test_helpers.create_doctype', { name: 'Test Depends On', diff --git a/cypress/integration/file_uploader.js b/cypress/integration/file_uploader.js index f9f44675db..4186141870 100644 --- a/cypress/integration/file_uploader.js +++ b/cypress/integration/file_uploader.js @@ -1,7 +1,7 @@ context('FileUploader', () => { before(() => { cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); }); function open_upload_dialog() { diff --git a/cypress/integration/form.js b/cypress/integration/form.js index ef89a18e7d..d804ec8d99 100644 --- a/cypress/integration/form.js +++ b/cypress/integration/form.js @@ -1,13 +1,13 @@ context('Form', () => { before(() => { cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); return cy.window().its('frappe').then(frappe => { return frappe.call("frappe.tests.ui_test_helpers.create_contact_records"); }); }); it('create a new form', () => { - cy.visit('/desk#Form/ToDo/New ToDo 1'); + cy.visit('/app/Form/ToDo/New ToDo 1'); 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,20 @@ context('Form', () => { }).as('form_save'); cy.get('.primary-action').click(); cy.wait('@form_save').its('status').should('eq', 200); - cy.visit('/desk#List/ToDo'); - cy.location('hash').should('eq', '#List/ToDo/List'); + cy.visit('/app/List/ToDo'); + cy.location('hash').should('eq', '/app/List/ToDo/List'); 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('/desk#List/Contact'); - cy.location('hash').should('eq', '#List/Contact/List'); + cy.visit('/app/List/Contact'); + cy.location('hash').should('eq', '/app/List/Contact/List'); 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('/desk#Form/Contact/Test Form Contact 3'); + cy.visit('/app/Form/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 +50,7 @@ context('Form', () => { let website_input = 'website.in'; let expectBackgroundColor = 'rgb(255, 220, 220)'; - cy.visit('/desk#Form/Contact/New Contact 1'); + cy.visit('/app/Form/Contact/New Contact 1'); 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 b383f30bb8..7142dce131 100644 --- a/cypress/integration/grid_pagination.js +++ b/cypress/integration/grid_pagination.js @@ -1,24 +1,24 @@ context('Grid Pagination', () => { beforeEach(() => { cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); }); before(() => { cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); return cy.window().its('frappe').then(frappe => { return frappe.call("frappe.tests.ui_test_helpers.create_contact_phone_nos_records"); }); }); it('creates pages for child table', () => { - cy.visit('/desk#Form/Contact/Test Contact'); + cy.visit('/app/Form/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('/desk#Form/Contact/Test Contact'); + cy.visit('/app/Form/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('/desk#Form/Contact/Test Contact'); + cy.visit('/app/Form/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('/desk#Form/Contact/Test Contact'); + // cy.visit('/app/Form/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/list_view.js b/cypress/integration/list_view.js index 0d26ca90a2..bb93d320bb 100644 --- a/cypress/integration/list_view.js +++ b/cypress/integration/list_view.js @@ -1,7 +1,7 @@ context('List View', () => { before(() => { cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); return cy.window().its('frappe').then(frappe => { return frappe.xcall("frappe.tests.ui_test_helpers.setup_workflow"); }); diff --git a/cypress/integration/list_view_settings.js b/cypress/integration/list_view_settings.js index 47f8efe94b..5979185dcf 100644 --- a/cypress/integration/list_view_settings.js +++ b/cypress/integration/list_view_settings.js @@ -1,16 +1,16 @@ context('List View Settings', () => { beforeEach(() => { cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); }); it('Default settings', () => { - cy.visit('/desk#List/DocType/List'); + cy.visit('/app/List/DocType/List'); cy.get('.list-count').should('contain', "20 of"); cy.get('.sidebar-stat').should('contain', "Tags"); }); it('disable count and sidebar stats then verify', () => { cy.wait(300); - cy.visit('/desk#List/DocType/List'); + cy.visit('/app/List/DocType/List'); cy.wait(300); cy.get('.list-count').should('contain', "20 of"); cy.get('button').contains('Menu').click(); diff --git a/cypress/integration/login.js b/cypress/integration/login.js index 861377444c..e8964768b0 100644 --- a/cypress/integration/login.js +++ b/cypress/integration/login.js @@ -35,7 +35,7 @@ context('Login', () => { cy.get('#login_password').type(Cypress.config('adminPassword')); cy.get('.btn-login').click(); - cy.location('pathname').should('eq', '/desk'); + cy.location('pathname').should('eq', '/app'); cy.window().its('frappe.session.user').should('eq', 'Administrator'); }); diff --git a/cypress/integration/query_report.js b/cypress/integration/query_report.js index 5581a20edc..b06295495a 100644 --- a/cypress/integration/query_report.js +++ b/cypress/integration/query_report.js @@ -1,11 +1,11 @@ context('Query Report', () => { before(() => { cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); }); it('add custom column in report', () => { - cy.visit('/desk#query-report/Permitted Documents For User'); + cy.visit('/app#query-report/Permitted Documents For User'); cy.get('div[class="page-form flex"]', {timeout: 60000}).should('have.length', 1).then(()=>{ cy.get('#page-query-report input[data-fieldname="user"]').as('input'); diff --git a/cypress/integration/recorder.js b/cypress/integration/recorder.js index a0f8cc3621..e89621d936 100644 --- a/cypress/integration/recorder.js +++ b/cypress/integration/recorder.js @@ -4,14 +4,14 @@ context('Recorder', () => { }); it('Navigate to Recorder', () => { - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); cy.awesomebar('recorder'); cy.get('h1').should('contain', 'Recorder'); cy.location('hash').should('eq', '#recorder'); }); it('Recorder Empty State', () => { - cy.visit('/desk#recorder'); + cy.visit('/app#recorder'); cy.get('.title-text').should('contain', 'Recorder'); cy.get('.indicator').should('contain', 'Inactive').should('have.class', 'red'); @@ -24,21 +24,21 @@ context('Recorder', () => { }); it('Recorder Start', () => { - cy.visit('/desk#recorder'); + cy.visit('/app#recorder'); cy.get('.primary-action').should('contain', 'Start').click(); cy.get('.indicator').should('contain', 'Active').should('have.class', 'green'); cy.get('.msg-box').should('contain', 'No Requests'); cy.server(); - cy.visit('/desk#List/DocType/List'); + cy.visit('/app/List/DocType/List'); cy.route('POST', '/api/method/frappe.desk.reportview.get').as('list_refresh'); cy.wait('@list_refresh'); cy.get('.title-text').should('contain', 'DocType'); cy.get('.list-count').should('contain', '20 of '); - cy.visit('/desk#recorder'); + cy.visit('/app#recorder'); cy.get('.title-text').should('contain', 'Recorder'); cy.get('.result-list').should('contain', '/api/method/frappe.desk.reportview.get'); @@ -48,11 +48,11 @@ context('Recorder', () => { }); it('Recorder View Request', () => { - cy.visit('/desk#recorder'); + cy.visit('/app#recorder'); cy.get('.primary-action').should('contain', 'Start').click(); cy.server(); - cy.visit('/desk#List/DocType/List'); + cy.visit('/app/List/DocType/List'); cy.route('POST', '/api/method/frappe.desk.reportview.get').as('list_refresh'); cy.wait('@list_refresh'); @@ -62,7 +62,7 @@ context('Recorder', () => { // temporarily commenting out theses tests as they seem to be // randomly failing maybe due a backround event - // cy.visit('/desk#recorder'); + // cy.visit('/app#recorder'); // cy.get('.list-row-container span').contains('/api/method/frappe').click(); diff --git a/cypress/integration/relative_time_filters.js b/cypress/integration/relative_time_filters.js index ac70c44345..c13c2bbb5e 100644 --- a/cypress/integration/relative_time_filters.js +++ b/cypress/integration/relative_time_filters.js @@ -4,13 +4,13 @@ context('Relative Timeframe', () => { }); before(() => { cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); cy.window().its('frappe').then(frappe => { frappe.call("frappe.tests.ui_test_helpers.create_todo_records"); }); }); it('sets relative timespan filter for last week and filters list', () => { - cy.visit('/desk#List/ToDo/List'); + cy.visit('/app/List/ToDo/List'); cy.get('.list-row:contains("this is fourth todo")').should('exist'); cy.get('.tag-filters-area .btn:contains("Add Filter")').click(); cy.get('.fieldname-select-area').should('exist'); @@ -29,7 +29,7 @@ context('Relative Timeframe', () => { cy.wait('@save_user_settings'); }); it('sets relative timespan filter for next week and filters list', () => { - cy.visit('/desk#List/ToDo/List'); + cy.visit('/app/List/ToDo/List'); cy.get('.list-row:contains("this is fourth todo")').should('exist'); cy.get('.tag-filters-area .btn:contains("Add Filter")').click(); cy.get('.fieldname-select-area input').type("Due Date{enter}", { delay: 100 }); diff --git a/cypress/integration/report_view.js b/cypress/integration/report_view.js index deeef6bdd5..af84c44cdc 100644 --- a/cypress/integration/report_view.js +++ b/cypress/integration/report_view.js @@ -4,7 +4,7 @@ const doctype_name = custom_submittable_doctype.name; context('Report View', () => { before(() => { cy.login(); - cy.visit('/desk#workspace/Website'); + cy.visit('/app#workspace/Website'); cy.insert_doc('DocType', custom_submittable_doctype, true); cy.clear_cache(); cy.insert_doc(doctype_name, { @@ -18,7 +18,7 @@ context('Report View', () => { it('Field with enabled allow_on_submit should be editable.', () => { cy.server(); cy.route('POST', 'api/method/frappe.client.set_value').as('value-update'); - cy.visit(`/desk#List/${doctype_name}/Report`); + cy.visit(`/app/List/${doctype_name}/Report`); // check status column added from docstatus cy.get('.dt-row-0 > .dt-cell--col-3').should('contain', 'Submitted'); let cell = cy.get('.dt-row-0 > .dt-cell--col-4'); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 7816d5526f..c3f2deed20 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -210,13 +210,13 @@ Cypress.Commands.add('awesomebar', text => { Cypress.Commands.add('new_form', doctype => { let route = `Form/${doctype}/New ${doctype} 1`; - cy.visit(`/desk#${route}`); + cy.visit(`/app#${route}`); cy.get('body').should('have.attr', 'data-route', route); cy.get('body').should('have.attr', 'data-ajax-state', 'complete'); }); Cypress.Commands.add('go_to_list', doctype => { - cy.visit(`/desk#List/${doctype}/List`); + cy.visit(`/app/List/${doctype}/List`); }); Cypress.Commands.add('clear_cache', () => { diff --git a/frappe/__init__.py b/frappe/__init__.py index fac0927428..52869be3dc 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -1611,7 +1611,7 @@ def log_error(message=None, title=_("Error")): method=title)).insert(ignore_permissions=True) def get_desk_link(doctype, name): - html = '{doctype_local} {name}' + html = '{doctype_local} {name}' return html.format( doctype=doctype, name=name, diff --git a/frappe/auth.py b/frappe/auth.py index 3b18c8adb9..3f2e12b416 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -167,7 +167,7 @@ class LoginManager: frappe.local.cookie_manager.set_cookie("system_user", "yes") if not resume: frappe.local.response['message'] = 'Logged In' - frappe.local.response["home_page"] = "/desk" + frappe.local.response["home_page"] = "/app" if not resume: frappe.response["full_name"] = self.full_name diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.js b/frappe/automation/doctype/auto_repeat/auto_repeat.js index 456061ee0b..7d6bf69e3d 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 f5e1147dfb..81a8992a19 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 e236464b95..d4fe53aea3 100644 --- a/frappe/core/doctype/doctype/doctype.js +++ b/frappe/core/doctype/doctype/doctype.js @@ -24,12 +24,12 @@ 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(`/desk#Form/${frm.doc.name}`); + window.open(`/app/Form/${frm.doc.name}`); // frappe.set_route('Form', frm.doc.name); }); } else { frm.add_custom_button(__('Go to {0} List', [frm.doc.name]), () => { - window.open(`/desk#List/${frm.doc.name}/List`); + window.open(`/app/List/${frm.doc.name}/List`); }); } } diff --git a/frappe/core/doctype/log_settings/log_settings.py b/frappe/core/doctype/log_settings/log_settings.py index 6d59cdeb29..08e61d3289 100644 --- a/frappe/core/doctype/log_settings/log_settings.py +++ b/frappe/core/doctype/log_settings/log_settings.py @@ -36,7 +36,7 @@ def has_unseen_error_log(user): def _get_response(show_alert=True): return { 'show_alert': True, - 'message': _("You have unseen {0}").format(' Error Logs ') + 'message': _("You have unseen {0}").format(' Error Logs ') } if frappe.db.sql_list("select name from `tabError Log` where seen = 0 limit 1"): diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 9fc6658f29..d27107213b 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -577,7 +577,7 @@ def update_password(new_password, logout_all_sessions=0, key=None, old_password= frappe.db.set_value("User", user, "reset_password_key", "") if user_doc.user_type == "System User": - return "/desk" + return "/app" else: return redirect_url if redirect_url else "/" diff --git a/frappe/core/page/permission_manager/permission_manager.js b/frappe/core/page/permission_manager/permission_manager.js index 0038975162..8d00952e8d 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/core/page/permission_manager/permission_manager_help.html b/frappe/core/page/permission_manager/permission_manager_help.html index 30ccbed92f..0613713e81 100644 --- a/frappe/core/page/permission_manager/permission_manager_help.html +++ b/frappe/core/page/permission_manager/permission_manager_help.html @@ -5,8 +5,8 @@
    • {%= __("Permissions are set on Roles and Document Types (called DocTypes) by setting rights like Read, Write, Create, Delete, Submit, Cancel, Amend, Report, Import, Export, Print, Email and Set User Permissions.") %}
    • {%= __("Permissions get applied on Users based on what Roles they are assigned.") %}
    • {%= __("Roles can be set for users from their User page.") %} - {%= __("Setup > User") %}
    • -
    • {%= __("The system provides many pre-defined roles. You can add new roles to set finer permissions.") %} {%= __("Add a New Role") %}
    • + {%= __("Setup > User") %} +
    • {%= __("The system provides many pre-defined roles. You can add new roles to set finer permissions.") %} {%= __("Add a New Role") %}
    • {%= __("Permissions are automatically applied to Standard Reports and searches.") %}
    • {%= __("As a best practice, do not assign the same set of permission rule to different Roles. Instead, set multiple Roles to the same User.") %}
    • @@ -24,13 +24,13 @@
    • {%= __("Permissions at level 0 are Document Level permissions, i.e. they are primary for access to the document.") %}
    • {%= __("If a Role does not have access at Level 0, then higher levels are meaningless.") %}
    • {%= __("Permissions at higher levels are Field Level permissions. All Fields have a Permission Level set against them and the rules defined at that permissions apply to the field. This is useful in case you want to hide or make certain field read-only for certain Roles.") %}
    • -
    • {%= __("You can use Customize Form to set levels on fields.") %} {%= __("Setup > Customize Form") %}
    • +
    • {%= __("You can use Customize Form to set levels on fields.") %} {%= __("Setup > Customize Form") %}

    • {%= __("User Permissions") %}:

      1. {%= __("User Permissions are used to limit users to specific records.") %} - {%= __("Setup > User Permissions") %}
      2. + {%= __("Setup > User Permissions") %}
      3. {%= __("Select Document Types to set which User Permissions are used to limit access.") %}
      4. {%= __("Once you have set this, the users will only be able access documents (eg. Blog Post) where the link exists (eg. Blogger).") %}
      5. {%= __("Apart from System Manager, roles with Set User Permissions right can set permissions for other users for that Document Type.") %}
      6. diff --git a/frappe/custom/doctype/customize_form/test_customize_form.py b/frappe/custom/doctype/customize_form/test_customize_form.py index 46a2f2f9df..f5e0371c1f 100644 --- a/frappe/custom/doctype/customize_form/test_customize_form.py +++ b/frappe/custom/doctype/customize_form/test_customize_form.py @@ -234,7 +234,7 @@ class TestCustomizeForm(unittest.TestCase): testdt1.delete() def test_custom_action(self): - test_route = '#List/DocType' + test_route = '/app/List/DocType' # create a dummy action (route) d = self.get_customize_form("Event") diff --git a/frappe/desk/doctype/dashboard/dashboard.py b/frappe/desk/doctype/dashboard/dashboard.py index b12bcfe27d..54bbc61e25 100644 --- a/frappe/desk/doctype/dashboard/dashboard.py +++ b/frappe/desk/doctype/dashboard/dashboard.py @@ -80,7 +80,7 @@ def get_non_standard_warning_message(non_standard_docs_map): def get_html(docs, doctype): html = '

        {}

        '.format(frappe.bold(doctype)) for doc in docs: - html += ''.format(doctype=doctype, doc=doc) + html += ''.format(doctype=doctype, doc=doc) html += '
        ' return html diff --git a/frappe/desk/moduleview.py b/frappe/desk/moduleview.py index ad696520f8..94882d86b5 100644 --- a/frappe/desk/moduleview.py +++ b/frappe/desk/moduleview.py @@ -12,7 +12,7 @@ from frappe.cache_manager import build_domain_restriced_doctype_cache, build_dom @frappe.whitelist() def get(module): """Returns data (sections, list of reports, counts) to render module view in desk: - `/desk/#Module/[name]`.""" + `/app/#Module/[name]`.""" data = get_data(module) out = { @@ -90,7 +90,7 @@ def get_data(module, build=True): return data def build_config_from_file(module): - """Build module info from `app/config/desktop.py` files.""" + """Build module info from `app/config/apptop.py` files.""" data = [] module = frappe.scrub(module) @@ -150,7 +150,7 @@ def add_section(data, label, icon, items): def add_custom_doctypes(data, doctype_info): - """Adds Custom DocTypes to modules setup via `config/desktop.py`.""" + """Adds Custom DocTypes to modules setup via `config/apptop.py`.""" add_section(data, _("Documents"), "fa fa-star", [d for d in doctype_info if (d.custom and d.document_type in ("Document", "Transaction"))]) @@ -258,7 +258,7 @@ def config_exists(app, module): return False def add_setup_section(config, app, module, label, icon): - """Add common sections to `/desk#Module/Setup`""" + """Add common sections to `/app#Module/Setup`""" try: setup_section = get_setup_section(app, module, label, icon) if setup_section: diff --git a/frappe/desk/page/leaderboard/leaderboard.js b/frappe/desk/page/leaderboard/leaderboard.js index f49078401a..5209dc0fab 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 = `#Form/${this.options.selected_doctype}/${item.name}`; + const link = `/app/Form/${this.options.selected_doctype}/${item.name}`; const name_html = item.formatted_name ? `${item.formatted_name}` : ` ${item.name} `; diff --git a/frappe/desk/page/setup_wizard/setup_wizard.js b/frappe/desk/page/setup_wizard/setup_wizard.js index eca21e8cab..9bdeecb51d 100644 --- a/frappe/desk/page/setup_wizard/setup_wizard.js +++ b/frappe/desk/page/setup_wizard/setup_wizard.js @@ -212,7 +212,7 @@ frappe.setup.SetupWizard = class SetupWizard extends frappe.ui.Slides { } setTimeout(function () { // Reload - window.location.href = '/desk'; + window.location.href = '/app'; }, 2000); } diff --git a/frappe/desk/report/todo/todo.py b/frappe/desk/report/todo/todo.py index a51d44fe08..f4fe2dc805 100644 --- a/frappe/desk/report/todo/todo.py +++ b/frappe/desk/report/todo/todo.py @@ -24,7 +24,7 @@ def execute(filters=None): for todo in todo_list: if todo.owner==frappe.session.user or todo.assigned_by==frappe.session.user: if todo.reference_type: - todo.reference = """%s: %s""" % (todo.reference_type, + todo.reference = """%s: %s""" % (todo.reference_type, todo.reference_name, todo.reference_type, todo.reference_name) else: todo.reference = None diff --git a/frappe/email/doctype/notification/notification.js b/frappe/email/doctype/notification/notification.js index 2cc027acd6..e5346da755 100644 --- a/frappe/email/doctype/notification/notification.js +++ b/frappe/email/doctype/notification/notification.js @@ -203,7 +203,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 f941cf0575..198c8e2706 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_calendar/google_calendar.py b/frappe/integrations/doctype/google_calendar/google_calendar.py index 4a9acd9e84..fbedd75029 100644 --- a/frappe/integrations/doctype/google_calendar/google_calendar.py +++ b/frappe/integrations/doctype/google_calendar/google_calendar.py @@ -120,7 +120,7 @@ def authorize_access(g_calendar, reauthorize=None): frappe.db.commit() frappe.local.response["type"] = "redirect" - frappe.local.response["location"] = "/desk#Form/{0}/{1}".format(quote("Google Calendar"), quote(google_calendar.name)) + frappe.local.response["location"] = "/app/Form/{0}/{1}".format(quote("Google Calendar"), quote(google_calendar.name)) frappe.msgprint(_("Google Calendar has been configured.")) except Exception as e: diff --git a/frappe/integrations/doctype/google_contacts/google_contacts.js b/frappe/integrations/doctype/google_contacts/google_contacts.js index af194d4978..029821b3bb 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_contacts/google_contacts.py b/frappe/integrations/doctype/google_contacts/google_contacts.py index 6455623281..4c8c3b67f6 100644 --- a/frappe/integrations/doctype/google_contacts/google_contacts.py +++ b/frappe/integrations/doctype/google_contacts/google_contacts.py @@ -79,7 +79,7 @@ def authorize_access(g_contact, reauthorize=None): frappe.db.commit() frappe.local.response["type"] = "redirect" - frappe.local.response["location"] = "/desk#Form/Google%20Contacts/{}".format(google_contact.name) + frappe.local.response["location"] = "/app/Form/Google%20Contacts/{}".format(google_contact.name) frappe.msgprint(_("Google Contacts has been configured.")) except Exception as e: diff --git a/frappe/integrations/doctype/google_drive/google_drive.js b/frappe/integrations/doctype/google_drive/google_drive.js index f184c6d75c..1463f8725d 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/integrations/doctype/google_drive/google_drive.py b/frappe/integrations/doctype/google_drive/google_drive.py index c1c73d7726..859c769018 100644 --- a/frappe/integrations/doctype/google_drive/google_drive.py +++ b/frappe/integrations/doctype/google_drive/google_drive.py @@ -88,7 +88,7 @@ def authorize_access(reauthorize=None): frappe.db.commit() frappe.local.response["type"] = "redirect" - frappe.local.response["location"] = "/desk#Form/{0}".format(quote("Google Drive")) + frappe.local.response["location"] = "/app/Form/{0}".format(quote("Google Drive")) frappe.msgprint(_("Google Drive has been configured.")) except Exception as e: diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index a38470e3f5..688ecbe40f 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -287,8 +287,8 @@ def check_if_doc_is_dynamically_linked(doc, method="Delete"): raise_link_exists_exception(doc, reference_doctype, reference_docname, at_position) def raise_link_exists_exception(doc, reference_doctype, reference_docname, row=''): - doc_link = '{1}'.format(doc.doctype, doc.name) - reference_link = '{1}'.format(reference_doctype, reference_docname) + doc_link = '{1}'.format(doc.doctype, doc.name) + reference_link = '{1}'.format(reference_doctype, reference_docname) #hack to display Single doctype only once in message if reference_doctype == reference_docname: diff --git a/frappe/model/document.py b/frappe/model/document.py index 3789e20b19..d17025538b 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -1188,8 +1188,8 @@ class Document(BaseDocument): doc.set(fieldname, flt(doc.get(fieldname), self.precision(fieldname, doc.parentfield))) def get_url(self): - """Returns Desk URL for this document. `/desk#Form/{doctype}/{name}`""" - return "/desk#Form/{doctype}/{name}".format(doctype=self.doctype, name=self.name) + """Returns Desk URL for this document. `/app/Form/{doctype}/{name}`""" + return "/app/Form/{doctype}/{name}".format(doctype=self.doctype, name=self.name) def add_comment(self, comment_type='Comment', text=None, comment_email=None, link_doctype=None, link_name=None, comment_by=None): """Add a comment to this document. diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 837fa09b1f..26dee5c831 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -1522,7 +1522,7 @@ frappe.ui.form.Form = class FrappeForm { const escaped_name = encodeURIComponent(value); - return repl('%(label)s', { + return repl('%(label)s', { color: get_color(doc || {}), doctype: df.options, escaped_name: escaped_name, diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js index bebe4e4065..49df313679 100644 --- a/frappe/public/js/frappe/form/formatters.js +++ b/frappe/public/js/frappe/form/formatters.js @@ -121,7 +121,7 @@ frappe.form.formatters = { {onclick: docfield.link_onclick.replace(/"/g, '"'), value:value}); } else if(docfield && doctype) { return ` ${__(options && options.label || value)}` diff --git a/frappe/public/js/frappe/form/linked_with.js b/frappe/public/js/frappe/form/linked_with.js index d8e07f8303..c5ed00add4 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 a0bb927563..a9e8c2b0d2 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/form/templates/address_list.html b/frappe/public/js/frappe/form/templates/address_list.html index 0f967b67a0..a6b8bf1377 100644 --- a/frappe/public/js/frappe/form/templates/address_list.html +++ b/frappe/public/js/frappe/form/templates/address_list.html @@ -9,7 +9,7 @@ {% if(addr_list[i].is_shipping_address) { %} ({%= __("Shipping") %}){% } %} - {%= __("Edit") %}

        diff --git a/frappe/public/js/frappe/form/templates/contact_list.html b/frappe/public/js/frappe/form/templates/contact_list.html index a967498d0f..e5fdc3f88e 100644 --- a/frappe/public/js/frappe/form/templates/contact_list.html +++ b/frappe/public/js/frappe/form/templates/contact_list.html @@ -9,7 +9,7 @@ {% if(contact_list[i].designation){ %} – {%= contact_list[i].designation %} {% } %} - {%= __("Edit") %} diff --git a/frappe/public/js/frappe/form/templates/print_layout.html b/frappe/public/js/frappe/form/templates/print_layout.html index 5ee9f572f3..366a771218 100644 --- a/frappe/public/js/frappe/form/templates/print_layout.html +++ b/frappe/public/js/frappe/form/templates/print_layout.html @@ -31,7 +31,7 @@ {% endif %} diff --git a/frappe/templates/pages/integrations/gcalendar-success.html b/frappe/templates/pages/integrations/gcalendar-success.html index 1ce9ba5bf3..4138644c17 100644 --- a/frappe/templates/pages/integrations/gcalendar-success.html +++ b/frappe/templates/pages/integrations/gcalendar-success.html @@ -9,7 +9,7 @@ {{ _("Success") }}

        {{ _("Your connection request to Google Calendar was successfully accepted") }}

        -