').insertAfter(this.layout.wrapper.find('.form-message'))
});
// workflow state
@@ -222,6 +213,13 @@ frappe.ui.form.Form = class FrappeForm {
});
}
+ setup_notify_on_rename() {
+ $(document).on('rename', (ev, dt, old_name, new_name) => {
+ if(dt==this.doctype)
+ this.rename_notify(dt, old_name, new_name);
+ });
+ }
+
setup_file_drop() {
var me = this;
this.$wrapper.on('dragenter dragover', false)
@@ -445,11 +443,13 @@ frappe.ui.form.Form = class FrappeForm {
this.layout.doc = this.doc;
this.layout.attach_doc_and_docfields();
- this.sidebar = new frappe.ui.form.Sidebar({
- frm: this,
- page: this.page
- });
- this.sidebar.make();
+ if (frappe.boot.desk_settings.form_sidebar) {
+ this.sidebar = new frappe.ui.form.Sidebar({
+ frm: this,
+ page: this.page
+ });
+ this.sidebar.make();
+ }
// clear layout message
this.layout.show_message();
@@ -1665,7 +1665,7 @@ frappe.ui.form.Form = class FrappeForm {
});
driver.defineSteps(steps);
- frappe.route.on('change', () => driver.reset());
+ frappe.router.on('change', () => driver.reset());
driver.start();
}
diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js
index 075d1cec84..27b3b869c5 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 `
`
diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js
index ce149e4371..1d0f1f8ffd 100644
--- a/frappe/public/js/frappe/form/layout.js
+++ b/frappe/public/js/frappe/form/layout.js
@@ -127,14 +127,6 @@ frappe.ui.form.Layout = Class.extend({
if (this.no_opening_section()) {
this.fields.unshift({fieldtype: 'Section Break'});
}
-
- this.fields.unshift({
- fieldtype: 'Section Break',
- fieldname: '_form_dashboard',
- cssClass: 'form-dashboard',
- collapsible: 1,
- // hidden: 1
- });
},
replace_field: function(fieldname, df, render) {
@@ -312,10 +304,6 @@ frappe.ui.form.Layout = Class.extend({
collapse = false;
}
- if (df.fieldname === '_form_dashboard') {
- collapse = localStorage.getItem('collapseFormDashboard')==='yes' ? true : false;
- }
-
section.collapse(collapse);
}
}
@@ -587,17 +575,13 @@ frappe.ui.form.Section = Class.extend({
wrapper: this.wrapper
};
- if (this.df.collapsible && this.df.fieldname !== '_form_dashboard') {
- this.collapse(true);
- }
-
this.refresh();
},
make: function() {
if (!this.layout.page) {
this.layout.page = $('
').appendTo(this.layout.wrapper);
}
- let make_card = this.layout.card_layout && this.df.fieldname !== '_form_dashboard';
+ let make_card = this.layout.card_layout;
this.wrapper = $(`
`)
.appendTo(this.layout.page);
this.layout.sections.push(this);
@@ -664,18 +648,12 @@ frappe.ui.form.Section = Class.extend({
hide = !this.body.hasClass("hide");
}
- if (this.df.fieldname==='_form_dashboard') {
- localStorage.setItem('collapseFormDashboard', hide ? 'yes' : 'no');
- }
-
this.body.toggleClass("hide", hide);
this.head.toggleClass("collapsed", hide);
let indicator_icon = hide ? 'down' : 'up-line';
this.indicator & this.indicator.html(frappe.utils.icon(indicator_icon, 'sm', 'mb-1'));
- // this.indicator && this.indicator.toggleClass("octicon-chevron-down", hide);
- // this.indicator && this.indicator.toggleClass("octicon-chevron-up", !hide);
// refresh signature fields
this.fields_list.forEach((f) => {
diff --git a/frappe/public/js/frappe/form/toolbar.js b/frappe/public/js/frappe/form/toolbar.js
index 28cc9bb28b..bdbf0b687d 100644
--- a/frappe/public/js/frappe/form/toolbar.js
+++ b/frappe/public/js/frappe/form/toolbar.js
@@ -29,7 +29,9 @@ frappe.ui.form.Toolbar = Class.extend({
}
},
set_title: function() {
- if(this.frm.meta.title_field) {
+ if (this.frm.is_new()) {
+ var title = __('New {0}', [this.frm.meta.name]);
+ } else if (this.frm.meta.title_field) {
let title_field = (this.frm.doc[this.frm.meta.title_field] || "").toString().trim();
var title = strip_html(title_field || this.frm.docname);
if(this.frm.doc.__islocal || title === this.frm.docname || this.frm.meta.autoname==="hash") {
@@ -198,30 +200,40 @@ frappe.ui.form.Toolbar = Class.extend({
make_menu: function() {
this.page.clear_icons();
this.page.clear_menu();
- var me = this;
- var p = this.frm.perm[0];
- var docstatus = cint(this.frm.doc.docstatus);
- var is_submittable = frappe.model.is_submittable(this.frm.doc.doctype)
- var issingle = this.frm.meta.issingle;
- var print_settings = frappe.model.get_doc(":Print Settings", "Print Settings")
- var allow_print_for_draft = cint(print_settings.allow_print_for_draft);
- var allow_print_for_cancelled = cint(print_settings.allow_print_for_cancelled);
- // Navigate
- if (!this.frm.is_new() && !issingle) {
- this.page.add_action_icon("left", function() {
- me.frm.navigate_records(1);
- }, 'prev-doc', __("Previous Document"));
- this.page.add_action_icon("right", function() {
- me.frm.navigate_records(0);
- }, 'next-doc', __("Next Document"));
+ if (frappe.boot.desk_settings.form_sidebar) {
+ this.make_navigation();
+ this.make_menu_items();
}
+ },
+ make_navigation() {
+ // Navigate
+ if (!this.frm.is_new() && !this.frm.meta.issingle) {
+ this.page.add_action_icon("left", () => {
+ this.frm.navigate_records(1);
+ }, 'prev-doc');
+ this.page.add_action_icon("right", ()=> {
+ this.frm.navigate_records(0);
+ }, 'next-doc');
+ }
+ },
+
+ make_menu_items() {
// Print
+ const me = this;
+ const p = this.frm.perm[0];
+ const docstatus = cint(this.frm.doc.docstatus);
+ const is_submittable = frappe.model.is_submittable(this.frm.doc.doctype)
+
+ const print_settings = frappe.model.get_doc(":Print Settings", "Print Settings")
+ const allow_print_for_draft = cint(print_settings.allow_print_for_draft);
+ const allow_print_for_cancelled = cint(print_settings.allow_print_for_cancelled);
+
if (!is_submittable || docstatus == 1 ||
(allow_print_for_cancelled && docstatus == 2)||
(allow_print_for_draft && docstatus == 0)) {
- if (frappe.model.can_print(null, me.frm) && !issingle) {
+ if (frappe.model.can_print(null, me.frm) && !this.frm.meta.issingle) {
this.page.add_menu_item(__("Print"), function() {
me.frm.print_doc();
}, true);
@@ -283,30 +295,7 @@ frappe.ui.form.Toolbar = Class.extend({
});
}
- if (frappe.user_roles.includes("System Manager")) {
- let is_doctype_form = me.frm.doctype === 'DocType';
- let doctype = is_doctype_form ? me.frm.docname : me.frm.doctype;
- let is_doctype_custom = is_doctype_form ? me.frm.doc.custom : false;
-
- if (doctype != 'DocType' && !is_doctype_custom && me.frm.meta.issingle === 0) {
- this.page.add_menu_item(__("Customize"), function() {
- if (me.frm.meta && me.frm.meta.custom) {
- frappe.set_route('Form', 'DocType', doctype);
- } else {
- frappe.set_route('Form', 'Customize Form', {
- doc_type: doctype
- });
- }
- }, true);
- }
-
- if (frappe.boot.developer_mode===1 && !is_doctype_form) {
- // edit doctype
- this.page.add_menu_item(__("Edit DocType"), function() {
- frappe.set_route('Form', 'DocType', me.frm.doctype);
- }, true);
- }
- }
+ this.make_customize_buttons();
// Auto Repeat
if(this.can_repeat()) {
@@ -325,6 +314,35 @@ frappe.ui.form.Toolbar = Class.extend({
});
}
},
+
+ make_customize_buttons() {
+ if (frappe.user_roles.includes("System Manager")) {
+ let is_doctype_form = this.frm.doctype === 'DocType';
+ let doctype = is_doctype_form ? this.frm.docname : this.frm.doctype;
+ let is_doctype_custom = is_doctype_form ? this.frm.doc.custom : false;
+
+ if (doctype != 'DocType' && !is_doctype_custom && this.frm.meta.issingle === 0) {
+ this.page.add_menu_item(__("Customize"), () => {
+ if (this.frm.meta && this.frm.meta.custom) {
+ frappe.set_route('Form', 'DocType', doctype);
+ } else {
+ frappe.set_route('Form', 'Customize Form', {
+ doc_type: doctype
+ });
+ }
+ }, true);
+ }
+
+ if (frappe.boot.developer_mode===1 && !is_doctype_form) {
+ // edit doctype
+ this.page.add_menu_item(__("Edit DocType"), () => {
+ frappe.set_route('Form', 'DocType', this.frm.doctype);
+ }, true);
+ }
+ }
+
+ },
+
can_repeat: function() {
return this.frm.meta.allow_auto_repeat
&& !this.frm.is_new()
diff --git a/frappe/public/js/frappe/list/base_list.js b/frappe/public/js/frappe/list/base_list.js
index c67de6afc9..96b06aac95 100644
--- a/frappe/public/js/frappe/list/base_list.js
+++ b/frappe/public/js/frappe/list/base_list.js
@@ -182,15 +182,17 @@ frappe.views.BaseList = class BaseList {
'Dashboard': 'dashboard'
}
- this.views_menu = this.page.add_custom_button_group(__(`{0} View`, [this.view_name]), icon_map[this.view_name] || 'list');
- this.views_list = new frappe.views.Views({
- doctype: this.doctype,
- parent: this.views_menu,
- page: this.page,
- list_view: this,
- sidebar: this.list_sidebar,
- icon_map: icon_map
- });
+ if (frappe.boot.desk_settings.view_switcher) {
+ this.views_menu = this.page.add_custom_button_group(__(`{0} View`, [this.view_name]), icon_map[this.view_name] || 'list');
+ this.views_list = new frappe.views.Views({
+ doctype: this.doctype,
+ parent: this.views_menu,
+ page: this.page,
+ list_view: this,
+ sidebar: this.list_sidebar,
+ icon_map: icon_map
+ });
+ }
}
set_default_secondary_action() {
@@ -236,7 +238,7 @@ frappe.views.BaseList = class BaseList {
}
setup_side_bar() {
- if (this.hide_sidebar) return;
+ if (this.hide_sidebar || !frappe.boot.desk_settings.list_sidebar) return;
this.list_sidebar = new frappe.views.ListSidebar({
doctype: this.doctype,
stats: this.stats,
diff --git a/frappe/public/js/frappe/list/list_filter.js b/frappe/public/js/frappe/list/list_filter.js
index fbbf5e583f..c02755d50c 100644
--- a/frappe/public/js/frappe/list/list_filter.js
+++ b/frappe/public/js/frappe/list/list_filter.js
@@ -167,6 +167,7 @@ export default class ListFilter {
}
get_list_filters() {
+ if (frappe.session.user === 'Guest') return Promise.resolve();
return frappe.db
.get_list('List Filter', {
fields: ['name', 'filter_name', 'for_user', 'filters'],
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index 4ba2f76b67..8a005ffdfb 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -9,7 +9,6 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
const doctype = route[1];
if (route.length === 2) {
- // List/{doctype} => List/{doctype}/{last_view} or List
const user_settings = frappe.get_user_settings(doctype);
const last_view = user_settings.last_view;
frappe.set_route(
@@ -871,7 +870,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
? encodeURIComponent(doc.name)
: doc.name;
- return "/app/form/" + frappe.router.slug(frappe.router.doctype_layout || this.doctype) + "/" + docname;
+ return `/app/${frappe.router.slug(frappe.router.doctype_layout || this.doctype)}/${docname}`;
}
get_seen_class(doc) {
@@ -905,6 +904,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
let escaped_subject = frappe.utils.escape_html(subject);
const seen = this.get_seen_class(doc);
+ console.log(this.get_form_link(doc));
let subject_html = `
${item.name}`;
+ html += `
${item.name}`;
});
}
@@ -181,7 +181,7 @@ frappe.views.Views = class Views {
reports.map((r) => {
if (!r.ref_doctype || r.ref_doctype == this.doctype) {
const report_type = r.report_type === 'Report Builder' ?
- `list/${r.ref_doctype}/report` : 'query-report';
+ `/app/list/${r.ref_doctype}/report` : 'query-report';
const route = r.route || report_type + '/' + (r.title || r.name);
@@ -233,11 +233,11 @@ frappe.views.Views = class Views {
// has standard calendar view
calendars.push({
name: 'Default',
- route: `list/${this.doctype}/calendar/default`
+ route: `/app/${this.get_doctype_route()}/view/calendar/default`
});
}
result.map(calendar => {
- calendars.push({name: calendar.name, route: `list/${doctype}/calendar/${calendar.name}`});
+ calendars.push({name: calendar.name, route: `/app/${this.get_doctype_route()}/view/calendar/${calendar.name}`});
});
return calendars;
@@ -249,7 +249,7 @@ frappe.views.Views = class Views {
let accounts = frappe.boot.email_accounts;
accounts.forEach(account => {
let email_account = (account.email_id == "All Accounts") ? "All Accounts" : account.email_account;
- let route = ["List", "Communication", "Inbox", email_account].join('/');
+ let route = `/app/communication/inbox/${email_account}`;
let display_name = ["All Accounts", "Sent Mail", "Spam", "Trash"].includes(account.email_id)
? __(account.email_id)
: account.email_account;
@@ -262,4 +262,8 @@ frappe.views.Views = class Views {
return accounts_to_add;
}
+
+ get_doctype_route() {
+ return frappe.router.slug(frappe.router.doctype_layout || this.doctype);
+ }
}
\ No newline at end of file
diff --git a/frappe/public/js/frappe/model/create_new.js b/frappe/public/js/frappe/model/create_new.js
index 09cb55d8fc..210a4ae5d3 100644
--- a/frappe/public/js/frappe/model/create_new.js
+++ b/frappe/public/js/frappe/model/create_new.js
@@ -126,6 +126,7 @@ $.extend(frappe.model, {
var user_permissions = frappe.defaults.get_user_permissions();
let allowed_records = [];
let default_doc = null;
+ let value = null;
if(user_permissions) {
({allowed_records, default_doc} = frappe.perm.filter_allowed_docs_for_doctype(user_permissions[df.options], doc.doctype));
}
@@ -139,71 +140,79 @@ $.extend(frappe.model, {
if (df.fieldtype==="Link" && df.options!=="User") {
// If user permission has Is Default enabled or single-user permission has found against respective doctype.
if (has_user_permissions && default_doc) {
- return default_doc;
- }
-
- if(!df.ignore_user_permissions) {
+ value = default_doc;
+ } else {
// 2 - look in user defaults
- var user_defaults = frappe.defaults.get_user_defaults(df.options);
- if (user_defaults && user_defaults.length===1) {
- // Use User Permission value when only when it has a single value
- user_default = user_defaults[0];
+
+ if(!df.ignore_user_permissions) {
+ var user_defaults = frappe.defaults.get_user_defaults(df.options);
+ if (user_defaults && user_defaults.length===1) {
+ // Use User Permission value when only when it has a single value
+ user_default = user_defaults[0];
+ }
+ }
+
+ else if (!user_default) {
+ user_default = frappe.defaults.get_user_default(df.fieldname);
+ }
+
+ else if(!user_default && df.remember_last_selected_value && frappe.boot.user.last_selected_values) {
+ user_default = frappe.boot.user.last_selected_values[df.options];
+ }
+
+ var is_allowed_user_default = user_default &&
+ (!has_user_permissions || allowed_records.includes(user_default));
+
+ // is this user default also allowed as per user permissions?
+ if (is_allowed_user_default) {
+ value = user_default;
}
}
- if (!user_default) {
- user_default = frappe.defaults.get_user_default(df.fieldname);
- }
-
- if(!user_default && df.remember_last_selected_value && frappe.boot.user.last_selected_values) {
- user_default = frappe.boot.user.last_selected_values[df.options];
- }
-
- var is_allowed_user_default = user_default &&
- (!has_user_permissions || allowed_records.includes(user_default));
-
- // is this user default also allowed as per user permissions?
- if (is_allowed_user_default) {
- return user_default;
- }
}
// 3 - look in default of docfield
- if (df['default']) {
+ if (!value || df['default']) {
const default_val = String(df['default']);
if (default_val == "__user" || default_val.toLowerCase() == "user") {
- return frappe.session.user;
+ value = frappe.session.user;
} else if (default_val == "user_fullname") {
- return frappe.session.user_fullname;
+ value = frappe.session.user_fullname;
} else if (default_val == "Today") {
- return frappe.datetime.get_today();
+ value = frappe.datetime.get_today();
} else if ((default_val || "").toLowerCase() === "now") {
- return frappe.datetime.now_datetime();
+ value = frappe.datetime.now_datetime();
} else if (default_val[0]===":") {
var boot_doc = frappe.model.get_default_from_boot_docs(df, doc, parent_doc);
var is_allowed_boot_doc = !has_user_permissions || allowed_records.includes(boot_doc);
if (is_allowed_boot_doc) {
- return boot_doc;
+ value = boot_doc;
}
} else if (df.fieldname===meta.title_field) {
// ignore defaults for title field
- return "";
+ value = "";
+ } else {
+ // is this default value is also allowed as per user permissions?
+ var is_allowed_default = !has_user_permissions || allowed_records.includes(df.default);
+ if (df.fieldtype!=="Link" || df.options==="User" || is_allowed_default) {
+ value = df["default"];
+ }
}
- // is this default value is also allowed as per user permissions?
- var is_allowed_default = !has_user_permissions || allowed_records.includes(df.default);
- if (df.fieldtype!=="Link" || df.options==="User" || is_allowed_default) {
- return df["default"];
- }
} else if (df.fieldtype=="Time") {
- return frappe.datetime.now_time();
+ value = frappe.datetime.now_time();
}
+
+ // set it here so we know it was set as a default
+ df.__default_value = value;
+
+ return value;
},
get_default_from_boot_docs: function(df, doc, parent_doc) {
diff --git a/frappe/public/js/frappe/model/user_settings.js b/frappe/public/js/frappe/model/user_settings.js
index 30bd98008d..4d11429ce4 100644
--- a/frappe/public/js/frappe/model/user_settings.js
+++ b/frappe/public/js/frappe/model/user_settings.js
@@ -6,6 +6,8 @@ $.extend(frappe.model.user_settings, {
.then(r => JSON.parse(r.message || '{}'));
},
save: function(doctype, key, value) {
+ if (frappe.session.user === 'Guest') return Promise.resolve();
+
const old_user_settings = frappe.model.user_settings[doctype] || {};
const new_user_settings = $.extend(true, {}, old_user_settings); // deep copy
@@ -31,6 +33,7 @@ $.extend(frappe.model.user_settings, {
return this.update(doctype, user_settings);
},
update: function(doctype, user_settings) {
+ if (frappe.session.user === 'Guest') return Promise.resolve();
return frappe.call({
method: 'frappe.model.utils.user_settings.save',
args: {
diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js
index a49b916f6e..b3a28999f4 100644
--- a/frappe/public/js/frappe/request.js
+++ b/frappe/public/js/frappe/request.js
@@ -8,6 +8,7 @@ frappe.provide('frappe.request.error_handlers');
frappe.request.url = '/';
frappe.request.ajax_count = 0;
frappe.request.waiting_for_ajax = [];
+frappe.request.logs = {}
frappe.xcall = function(method, params) {
return new Promise((resolve, reject) => {
@@ -89,6 +90,11 @@ frappe.call = function(opts) {
delete args.cmd;
}
+ // debouce if required
+ if (opts.debounce && frappe.request.is_fresh(args, opts.debounce)) {
+ return Promise.resolve();
+ }
+
return frappe.request.call({
type: opts.type || "POST",
args: args,
@@ -127,7 +133,7 @@ frappe.request.call = function(opts) {
message: __('The resource you are looking for is not available')});
},
403: function(xhr) {
- if (frappe.session.user === 'Guest') {
+ if (frappe.session.logged_in_user !== 'Guest') {
// session expired
frappe.app.handle_session_expired();
}
@@ -239,7 +245,7 @@ frappe.request.call = function(opts) {
status_code_handler(data, xhr);
}
} catch(e) {
- console.log("Unable to handle success response"); // eslint-disable-line
+ console.log("Unable to handle success response", data); // eslint-disable-line
console.trace(e); // eslint-disable-line
}
@@ -278,6 +284,26 @@ frappe.request.call = function(opts) {
});
}
+frappe.request.is_fresh = function(args, threshold) {
+ // return true if a request with similar args has been sent recently
+ if (!frappe.request.logs[args.cmd]) {
+ frappe.request.logs[args.cmd] = [];
+ }
+
+ for (let past_request of frappe.request.logs[args.cmd]) {
+ // check if request has same args and was made recently
+ if ((new Date() - past_request.timestamp) < threshold
+ && frappe.utils.deep_equal(args, past_request.args)) {
+ console.log('throttled');
+ return true;
+ }
+ }
+
+ // log the request
+ frappe.request.logs[args.cmd].push({args: args, timestamp: new Date()});
+ return false;
+}
+
// call execute serverside request
frappe.request.prepare = function(opts) {
$("body").attr("data-ajax-state", "triggered");
@@ -322,7 +348,8 @@ frappe.request.cleanup = function(opts, r) {
if(r) {
// session expired? - Guest has no business here!
- if (r.session_expired || frappe.session.user === "Guest") {
+ if (r.session_expired ||
+ (frappe.session.user === 'Guest' && frappe.session.logged_in_user !== "Guest")) {
frappe.app.handle_session_expired();
return;
}
diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js
index d3fe28dd10..5421a60676 100644
--- a/frappe/public/js/frappe/router.js
+++ b/frappe/public/js/frappe/router.js
@@ -20,12 +20,13 @@ $(window).on('hashchange', function() {
let sub_path = frappe.router.get_sub_path(window.location.hash);
window.location.hash = '';
frappe.router.push_state(sub_path);
+ return false;
}
});
window.addEventListener('popstate', () => {
// forward-back button, just re-render based on current route
- frappe.route();
+ frappe.router.route();
});
// routing v2, capture all clicks so that the target is managed with push-state
@@ -52,18 +53,40 @@ $('body').on('click', 'a', function(e) {
}
// target has "/app, this is a v2 style route.
- if (e.currentTarget.pathname &&
- (e.currentTarget.pathname.startsWith('/app') || e.currentTarget.pathname.startsWith('app'))) {
+ if (e.currentTarget.pathname && frappe.router.is_app_route()) {
return override(e, e.currentTarget.pathname);
}
});
frappe.router = {
current_route: null,
- doctype_names: {},
- factory_views: ['form', 'list', 'report', 'tree', 'print'],
+ routes: {},
+ factory_views: ['form', 'list', 'report', 'tree', 'print', 'dashboard'],
+ list_views: ['list', 'kanban', 'report', 'calendar', 'tree', 'gantt', 'dashboard', 'image', 'inbox'],
layout_mapped: {},
+ is_app_route() {
+ // desk paths must begin with /app or doctype route
+ let path = window.location.pathname;
+ if (path.substr(0, 1) === '/') path = path.substr(1);
+ path = path.split('/');
+ if (path[0]) {
+ return path[0]==='app';
+ }
+ },
+
+ setup() {
+ // setup the route names by forming slugs of the given doctypes
+ for(let doctype of frappe.boot.user.can_read) {
+ this.routes[this.slug(doctype)] = {doctype: doctype};
+ }
+ if (frappe.boot.doctype_layouts) {
+ for (let doctype_layout of frappe.boot.doctype_layouts) {
+ this.routes[this.slug(doctype_layout.name)] = {doctype: doctype_layout.document_type, doctype_layout: doctype_layout.name };
+ }
+ }
+ },
+
route() {
// resolve the route from the URL or hash
// translate it so the objects are well defined
@@ -71,64 +94,81 @@ frappe.router = {
if (!frappe.app) return;
- let sub_path = frappe.router.get_sub_path();
- if (frappe.router.re_route(sub_path)) return;
+ let sub_path = this.get_sub_path();
+ if (this.re_route(sub_path)) return;
- frappe.router.translate_doctype_name().then(() => {
- frappe.router.set_history(sub_path);
-
- if (frappe.router.current_route[0]) {
- frappe.router.render_page();
- } else {
- // Show home
- frappe.views.pageview.show('');
- }
-
- frappe.router.set_title();
- frappe.route.trigger('change');
- });
+ this.current_route = this.parse();
+ this.set_history(sub_path);
+ this.render();
+ this.set_title();
+ this.trigger('change');
},
- translate_doctype_name() {
- return new Promise((resolve) => {
- const route = frappe.router.current_route = frappe.router.parse();
- const factory = route[0].toLowerCase();
- const set_name = () => {
- const d = frappe.router.doctype_names[route[1]];
- route[1] = d.doctype;
- frappe.router.doctype_layout = d.doctype_layout;
- resolve();
- };
+ parse(route) {
+ route = this.get_sub_path_string(route).split('/');
+ route = $.map(route, this.decode_component);
+ this.set_route_options_from_url(route);
+ return this.convert_to_standard_route(route);
+ },
- if (frappe.router.factory_views.includes(factory)) {
- // translate the doctype to its original name
- if (frappe.router.doctype_names[route[1]]) {
- set_name();
+ convert_to_standard_route(route) {
+ // /app/user = ["List", "User"]
+ // /app/user/view/report = ["List", "User", "Report"]
+ // /app/user/view/tree = ["Tree", "User"]
+ // /app/user/user-001 = ["Form", "User", "user-001"]
+ // /app/user/user-001 = ["Form", "User", "user-001"]
+ let standard_route = route;
+ let doctype_route = this.routes[route[0]];
+
+ if (doctype_route) {
+ // doctype route
+ if (route[1]) {
+ if (route[2] && route[1]==='view') {
+ if (route[2].toLowerCase()==='tree') {
+ standard_route = ['Tree', doctype_route.doctype];
+ } else {
+ standard_route = ['List', doctype_route.doctype, frappe.utils.to_title_case(route[2])];
+ }
} else {
- frappe.xcall('frappe.desk.utils.get_doctype_name', {name: route[1]}).then((data) => {
- frappe.router.doctype_names[route[1]] = data.name_map;
- set_name();
- });
+ standard_route = ['Form', doctype_route.doctype, route[1]];
}
+ } else if (frappe.model.is_single(doctype_route.doctype)) {
+ standard_route = ['Form', doctype_route.doctype, doctype_route.doctype];
} else {
- resolve();
+ standard_route = ['List', doctype_route.doctype, 'List'];
}
- });
+
+ if (doctype_route.doctype_layout) {
+ // set the layout
+ this.doctype_layout = doctype_route.doctype_layout;
+ }
+ }
+
+ return standard_route;
},
set_history(sub_path) {
- frappe.route_history.push(frappe.router.current_route);
+ frappe.route_history.push(this.current_route);
frappe.route_titles[sub_path] = frappe._original_title || document.title;
frappe.ui.hide_open_dialog();
},
+ render() {
+ if (this.current_route[0]) {
+ this.render_page();
+ } else {
+ // Show home
+ frappe.views.pageview.show('');
+ }
+ },
+
render_page() {
// create the page generator (factory) object and call `show`
// if there is no generator, render the `Page` object
// first the router needs to know if its a "page", "doctype", "workspace"
- const route = frappe.router.current_route;
+ const route = this.current_route;
const factory = frappe.utils.to_title_case(route[0]);
if (factory === 'Workspace') {
frappe.views.pageview.show('');
@@ -155,12 +195,12 @@ frappe.router = {
re_route(sub_path) {
if (frappe.re_route[sub_path] !== undefined) {
// after saving a doc, for example,
- // "New DocType 1" and the renamed "TestDocType", both exist in history
+ // "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"
+ // 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.router.get_sub_path(frappe.re_route[sub_path]);
+ // we just do a back to go to the doc previous to the "new-doctype-1"
+ var re_route_val = this.get_sub_path(frappe.re_route[sub_path]);
if (decodeURIComponent(re_route_val) === decodeURIComponent(sub_path)) {
window.history.back();
return true;
@@ -185,32 +225,14 @@ frappe.router = {
// set the route (push state) with given arguments
// example 1: frappe.set_route('a', 'b', 'c');
// example 2: frappe.set_route(['a', 'b', 'c']);
- // example 3: frappe.set_route('a/b/c');
+ // example 3: frappe.set_route('a/b/c');
+ let route = arguments;
return new Promise(resolve => {
- var route = arguments;
- if (route.length===1 && $.isArray(route[0])) {
- // called as frappe.set_route(['a', 'b', 'c']);
- route = route[0];
- }
-
- if (route.length===1 && route[0].includes('/')) {
- // called as frappe.set_route('a/b/c')
- route = $.map(route[0].split('/'), frappe.router.decode_component);
- }
-
- if (route && route[0] == '') {
- route.shift();
- }
-
- if (route && ['desk', 'app'].includes(route[0])) {
- // we only need subpath, remove "app" (or "desk")
- route.shift();
- }
-
- frappe.router.slug_parts(route);
- const sub_path = frappe.router.make_url_from_list(route);
- frappe.router.push_state(sub_path);
+ route = this.get_route_from_arguments(route);
+ route = this.convert_from_standard_route(route);
+ const sub_path = this.make_url(route);
+ this.push_state(sub_path);
setTimeout(() => {
frappe.after_ajax && frappe.after_ajax(() => {
@@ -220,19 +242,67 @@ frappe.router = {
});
},
- slug_parts(route) {
- // slug doctype
+ get_route_from_arguments(route) {
+ if (route.length===1 && $.isArray(route[0])) {
+ // called as frappe.set_route(['a', 'b', 'c']);
+ route = route[0];
+ }
- // if app is part of the route, then first 2 elements are "" and "app"
- if (route[0] && frappe.router.factory_views.includes(route[0].toLowerCase())) {
- route[0] = route[0].toLowerCase();
- route[1] = frappe.router.slug(route[1]);
+ if (route.length===1 && route[0].includes('/')) {
+ // called as frappe.set_route('a/b/c')
+ route = $.map(route[0].split('/'), this.decode_component);
+ }
+
+ if (route && route[0] == '') {
+ route.shift();
+ }
+
+ if (route && ['desk', 'app'].includes(route[0])) {
+ // we only need subpath, remove "app" (or "desk")
+ route.shift();
+ }
+
+ return route;
+
+ },
+
+ convert_from_standard_route(route) {
+ // ["List", "Sales Order"] => /sales-order
+ // ["Form", "Sales Order", "SO-0001"] => /sales-order/SO-0001
+ // ["Tree", "Account"] = /account/view/tree
+
+ const view = route[0].toLowerCase();
+ if (view === 'list') {
+ if (route[2] && route[2] !== 'list') {
+ const new_route = [this.slug(route[1]), 'view', route[2].toLowerCase()];
+
+ // calendar / inbox
+ if (route[3]) new_route.push(route[3]);
+ return new_route;
+ } else {
+ return [this.slug(route[1])]
+ }
+ } else if (view === 'form') {
+ return [this.slug(route[1]), route[2]]
+ } else if (view === 'tree') {
+ return [this.slug(route[1]), 'view', 'tree'];
}
return route;
},
- make_url_from_list(params) {
- return $.map(params, function(a) {
+ slug_parts(route) {
+ // slug doctype
+
+ // if app is part of the route, then first 2 elements are "" and "app"
+ if (route[0] && this.factory_views.includes(route[0].toLowerCase())) {
+ route[0] = route[0].toLowerCase();
+ route[1] = this.slug(route[1]);
+ }
+ return route;
+ },
+
+ make_url(params) {
+ return '/app/' + $.map(params, function(a) {
if ($.isPlainObject(a)) {
frappe.route_options = a;
return null;
@@ -247,10 +317,8 @@ frappe.router = {
}).join('/');
},
- push_state(sub_path) {
+ push_state(url) {
// change the URL and call the router
- const url = `/app/${sub_path}`;
-
if (window.location.pathname !== url) {
// cleanup any remenants of v1 routing
window.location.hash = '';
@@ -259,19 +327,10 @@ frappe.router = {
history.pushState(null, null, url);
// now process the route
- frappe.router.route();
+ this.route();
}
},
- parse(route) {
- route = frappe.router.get_sub_path_string(route).split('/');
- route = $.map(route, frappe.router.decode_component);
-
- frappe.router.set_route_options_from_url(route);
-
- return route;
- },
-
get_sub_path_string(route) {
// return clean sub_path from hash or url
// supports both v1 and v2 routing
@@ -279,7 +338,7 @@ frappe.router = {
route = window.location.hash || window.location.pathname;
}
- return frappe.router.strip_prefix(route);
+ return this.strip_prefix(route);
},
strip_prefix(route) {
@@ -292,8 +351,8 @@ frappe.router = {
},
get_sub_path(route) {
- var sub_path = frappe.router.get_sub_path_string(route);
- route = $.map(sub_path.split('/'), frappe.router.decode_component).join('/');
+ var sub_path = this.get_sub_path_string(route);
+ route = $.map(sub_path.split('/'), this.decode_component).join('/');
return route;
},
@@ -333,10 +392,9 @@ frappe.router = {
};
// global functions for backward compatibility
-frappe.route = frappe.router.route;
frappe.get_route = () => frappe.router.current_route;
frappe.get_route_str = () => frappe.router.current_route.join('/');
-frappe.set_route = frappe.router.set_route;
+frappe.set_route = function() { return frappe.router.set_route.apply(frappe.router, arguments) };
frappe.get_prev_route = function() {
if (frappe.route_history && frappe.route_history.length > 1) {
@@ -356,4 +414,4 @@ frappe.has_route_options = function() {
return Boolean(Object.keys(frappe.route_options || {}).length);
};
-frappe.utils.make_event_emitter(frappe.route);
+frappe.utils.make_event_emitter(frappe.router);
diff --git a/frappe/public/js/frappe/router_history.js b/frappe/public/js/frappe/router_history.js
index 61fc4d6b13..c64c3fc9f2 100644
--- a/frappe/public/js/frappe/router_history.js
+++ b/frappe/public/js/frappe/router_history.js
@@ -1,19 +1,21 @@
-frappe.provide('frappe.route');
frappe.route_history_queue = [];
const routes_to_skip = ['Form', 'social', 'setup-wizard', 'recorder'];
const save_routes = frappe.utils.debounce(() => {
+ if (frappe.session.user === 'Guest') return;
const routes = frappe.route_history_queue;
frappe.route_history_queue = [];
+
frappe.xcall('frappe.deferred_insert.deferred_insert', {
'doctype': 'Route History',
'records': routes
}).catch(() => {
frappe.route_history_queue.concat(routes);
- });
+ });
+
}, 10000);
-frappe.route.on('change', () => {
+frappe.router.on('change', () => {
const route = frappe.get_route();
if (is_route_useful(route)) {
frappe.route_history_queue.push({
diff --git a/frappe/public/js/frappe/ui/notifications/notifications.js b/frappe/public/js/frappe/ui/notifications/notifications.js
index afdb40118a..998ddbedc9 100644
--- a/frappe/public/js/frappe/ui/notifications/notifications.js
+++ b/frappe/public/js/frappe/ui/notifications/notifications.js
@@ -12,8 +12,7 @@ frappe.ui.Notifications = class Notifications {
}
make() {
- this.dropdown = $('.navbar').find('.dropdown-notifications');
- this.dropdown.removeClass("hidden")
+ this.dropdown = $('.navbar').find('.dropdown-notifications').removeClass('hidden');
this.dropdown_list = this.dropdown.find('.notifications-list');
this.header_items = this.dropdown_list.find('.header-items');
this.header_actions = this.dropdown_list.find('.header-actions');
diff --git a/frappe/public/js/frappe/ui/tags.js b/frappe/public/js/frappe/ui/tags.js
index c7c0d6c4bb..e609dc6bc2 100644
--- a/frappe/public/js/frappe/ui/tags.js
+++ b/frappe/public/js/frappe/ui/tags.js
@@ -20,7 +20,7 @@ frappe.ui.Tags = class {
setup(parent, placeholder) {
this.$ul = parent;
- this.$input = $(`
`);
+ this.$input = $(`
`);
this.$inputWrapper = this.get_list_element(this.$input);
this.$placeholder = this.get_list_element($(`
${placeholder}`));
diff --git a/frappe/public/js/frappe/ui/toolbar/awesome_bar.js b/frappe/public/js/frappe/ui/toolbar/awesome_bar.js
index 577b3d3f14..f9ec4c789d 100644
--- a/frappe/public/js/frappe/ui/toolbar/awesome_bar.js
+++ b/frappe/public/js/frappe/ui/toolbar/awesome_bar.js
@@ -7,6 +7,7 @@ frappe.search.AwesomeBar = Class.extend({
setup: function(element) {
var me = this;
+ $('.search-bar').removeClass('hidden');
var $input = $(element);
var input = $input.get(0);
@@ -122,6 +123,7 @@ frappe.search.AwesomeBar = Class.extend({
$input.blur();
});
frappe.search.utils.setup_recent();
+ frappe.tags.utils.fetch_tags();
},
add_help: function() {
diff --git a/frappe/public/js/frappe/ui/toolbar/navbar.html b/frappe/public/js/frappe/ui/toolbar/navbar.html
index 5ddb5ffc83..2349b2205e 100644
--- a/frappe/public/js/frappe/ui/toolbar/navbar.html
+++ b/frappe/public/js/frappe/ui/toolbar/navbar.html
@@ -6,7 +6,7 @@