Merge pull request #6821 from pratu16x7/explore-2.0

feat(explore_page): Modules and Module Detail views
This commit is contained in:
Rushabh Mehta 2019-02-07 12:20:12 +05:30 committed by GitHub
commit 056daa8a73
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
60 changed files with 854 additions and 1256 deletions

View file

@ -120,6 +120,7 @@
"md5": true,
"$": true,
"jQuery": true,
"Vue": true,
"moment": true,
"hljs": true,
"Awesomplete": true,

View file

@ -8,13 +8,6 @@ context('Awesome Bar', () => {
cy.get('.navbar-home').click();
});
it('navigates to modules', () => {
cy.get('#navbar-search')
.type('modules{downarrow}{enter}', { delay: 100 });
cy.location('hash').should('eq', '#modules');
});
it('navigates to doctype list', () => {
cy.get('#navbar-search')
.type('todo{downarrow}{enter}', { delay: 100 });

View file

@ -96,8 +96,8 @@ def load_conf_settings(bootinfo):
if key in conf: bootinfo[key] = conf.get(key)
def load_desktop_icons(bootinfo):
from frappe.desk.doctype.desktop_icon.desktop_icon import get_desktop_icons
bootinfo.desktop_icons = get_desktop_icons()
from frappe.config import get_modules_from_all_apps_for_user
bootinfo.allowed_modules = get_modules_from_all_apps_for_user()
def get_allowed_pages():
return get_user_pages_or_reports('Page')

View file

@ -0,0 +1,85 @@
from __future__ import unicode_literals
from frappe import _
import frappe
from frappe.desk.moduleview import get_data
from six import iteritems
def get_modules_from_all_apps_for_user(user=None):
if not user:
user = frappe.session.user
all_modules = get_modules_from_all_apps()
user_blocked_modules = frappe.get_doc('User', user).get_blocked_modules()
allowed_modules_list = [m for m in all_modules if m.get("module_name") not in user_blocked_modules]
return allowed_modules_list
def get_modules_from_all_apps():
modules_list = []
for app in frappe.get_installed_apps():
modules_list += get_modules_from_app(app)
return modules_list
def get_modules_from_app(app):
try:
modules = frappe.get_attr(app + '.config.desktop.get_data')() or {}
except ImportError:
return []
# Only newly formatted modules that have a category to be shown on desk
modules = [m for m in modules if m.get("category")]
active_domains = frappe.get_active_domains()
if isinstance(modules, dict):
active_modules_list = []
for m, module in iteritems(modules):
module['module_name'] = m
active_modules_list.append(module)
else:
active_modules_list = []
for m in modules:
to_add = True
module_name = m.get("module_name")
# Check Domain
if is_domain(m):
if module_name not in active_domains:
to_add = False
if "condition" in m and not m["condition"]:
to_add = False
if to_add:
onboard_present = is_onboard_present(m) if show_onboard(m) else False
m["onboard_present"] = onboard_present
active_modules_list.append(m)
return active_modules_list
def show_onboard(module):
return module.get("type") == "module" and module.get("category") in ["Modules", "Domains"]
def is_onboard_present(module):
print(module["module_name"])
exists_cache = {}
def exists(name, link_type):
exists = exists_cache.get(name)
if not exists:
if link_type == "doctype" and not frappe.db.get_value('DocType', name, 'issingle'):
exists = frappe.db.count(name)
else:
exists = True
exists_cache[name] = exists
return exists
sections = get_data(module["module_name"], False)
for section in sections:
for item in section["items"]:
if exists(item.get("name"), item.get("type")):
return True
return False
def is_domain(module):
return module.get("category") == "Domains"

View file

@ -12,11 +12,7 @@ def get_data():
"name": "ToDo",
"label": _("To Do"),
"description": _("Documents assigned to you and by you."),
},
{
"type": "doctype",
"name": "File",
"label": _("Files"),
"onboard": 1,
},
{
"type": "doctype",
@ -24,6 +20,18 @@ def get_data():
"label": _("Calendar"),
"link": "List/Event/Calendar",
"description": _("Event and other calendars."),
"onboard": 1,
},
{
"type": "doctype",
"name": "Note",
"description": _("Private and public Notes."),
"onboard": 1,
},
{
"type": "doctype",
"name": "File",
"label": _("Files"),
},
{
"type": "page",
@ -32,11 +40,6 @@ def get_data():
"description": _("Chat messages and other notifications."),
"data_doctype": "Communication"
},
{
"type": "doctype",
"name": "Note",
"description": _("Private and public Notes."),
},
{
"type": "page",
"label": _("Activity"),
@ -52,6 +55,7 @@ def get_data():
"type": "doctype",
"name": "Newsletter",
"description": _("Newsletters to contacts, leads."),
"onboard": 1,
},
{
"type": "doctype",

View file

@ -1,91 +1,87 @@
from __future__ import unicode_literals
import frappe
from frappe import _
def get_data():
return [
# Administration
{
"module_name": "Desk",
"category": "Administration",
"label": _("Tools"),
"color": "#FFF5A7",
"reverse": 1,
"icon": "octicon octicon-calendar",
"type": "module"
},
{
"module_name": "File Manager",
"color": "#AA784D",
"doctype": "File",
"icon": "octicon octicon-file-directory",
"label": _("File Manager"),
"link": "List/File",
"type": "list",
"hidden": 1
},
{
"module_name": "Website",
"color": "#16a085",
"icon": "octicon octicon-globe",
"type": "module",
"hidden": 1
"description": "Todos, Notes and other basic tools to help you track your work."
},
{
"module_name": "Integrations",
"color": "#16a085",
"icon": "octicon octicon-globe",
"type": "module",
"hidden": 1
},
{
"module_name": "Setup",
"module_name": "Settings",
"category": "Administration",
"label": _("Settings"),
"color": "#bdc3c7",
"reverse": 1,
"icon": "octicon octicon-settings",
"type": "module",
"hidden": 1
"hidden": 1,
"description": "Configure your ERPNext account."
},
{
"module_name": 'Email Inbox',
"type": 'list',
"label": 'Email Inbox',
"_label": _('Email Inbox'),
"_id": 'Email Inbox',
"_doctype": 'Communication',
"icon": 'fa fa-envelope-o',
"color": '#589494',
"link": 'List/Communication/Inbox'
"module_name": "Integrations",
"category": "Administration",
"label": _("Integrations"),
"color": "#16a085",
"icon": "octicon octicon-globe",
"type": "module",
"hidden": 1,
"description": "DropBox, Woocomerce, AWS, Shopify and GoCardless."
},
{
"module_name": 'Contacts',
"category": "Administration",
"label": _("Contacts"),
"type": 'module',
"icon": "octicon octicon-book",
"color": '#ffaedb',
"hidden": 1,
"description": "People Contacts and Address Book."
},
{
"module_name": "Core",
"label": "Developer",
"category": "Administration",
"_label": _("Developer"),
"label": "Developer",
"color": "#589494",
"icon": "octicon octicon-circuit-board",
"type": "module",
"system_manager": 1,
"hidden": 1
},
{
"module_name": 'Contacts',
"type": 'module',
"icon": "octicon octicon-book",
"color": '#FFAEDB',
"condition": getattr(frappe.local.conf, 'developer_mode', 0),
"hidden": 1,
"description": "The Frappe innards of ERPNext. (Only active when developer mode is enabled)"
},
# Places
{
"module_name": "Website",
"category": "Places",
"label": _("Website"),
"_label": _("Website"),
"color": "#16a085",
"icon": "octicon octicon-globe",
"type": "module",
"hidden": 1,
"description": "Webpages and the Portal Side of Things."
},
{
"module_name": 'Social',
"category": "Places",
"label": _('Social'),
"icon": "octicon octicon-heart",
"type": 'link',
"link": 'social/home',
"link": '#social/home',
"color": '#FF4136',
'standard': 1,
'idx': 15
'idx': 15,
"description": "Build your profile and share posts on the feed with other users."
},
{
"module_name": 'Settings',
"color": "#bdc3c7",
"reverse": 1,
"icon": "octicon octicon-settings",
"type": "module"
}
]

View file

@ -1,51 +0,0 @@
from frappe import _
def get_data():
return [{
"label": _("Settings"),
"icon": "fa fa-wrench",
"items": [
{
"type": "doctype",
"name": "System Settings",
"label": _("System Settings"),
"description": _("Language, Date and Time settings"),
"hide_count": True
},
{
"type": "doctype",
"name": "Domain Settings",
"label": _("Domain Settings"),
"description": _("Enable / Disable Domains"),
"hide_count": True
},
{
"type": "doctype",
"name": "Print Settings",
"label": _("Print Settings"),
"description": _("Print Style, PDF Size"),
"hide_count": True
},
{
"type": "doctype",
"name": "Website Settings",
"label": _("Website Settings"),
"description": _("Landing Page, Website Theme, Brand Setup and more"),
"hide_count": True
},
{
"type": "doctype",
"name": "S3 Backup Settings",
"label": _("S3 Backup Settings"),
"description": _("Enable / Disable Backup, Backup Frequency"),
"hide_count": True
},
{
"type": "doctype",
"name": "SMS Settings",
"label": _("SMS Settings"),
"description": _("SMS Gateway URL, Message & Receiver Parameter"),
"hide_count": True
}
]
}]

View file

@ -11,11 +11,13 @@ def get_data():
"type": "doctype",
"name": "Web Page",
"description": _("Content web page."),
"onboard": 1,
},
{
"type": "doctype",
"name": "Web Form",
"description": _("User editable form on Website."),
"onboard": 1,
},
{
"type": "doctype",
@ -35,6 +37,7 @@ def get_data():
"type": "doctype",
"name": "Blog Post",
"description": _("Single Post (article)."),
"onboard": 1,
},
{
"type": "doctype",
@ -56,11 +59,13 @@ def get_data():
"type": "doctype",
"name": "Website Settings",
"description": _("Setup of top navigation bar, footer and logo."),
"onboard": 1,
},
{
"type": "doctype",
"name": "Website Theme",
"description": _("List of themes for Website."),
"onboard": 1,
},
{
"type": "doctype",
@ -86,6 +91,7 @@ def get_data():
"type": "doctype",
"name": "Portal Settings",
"label": _("Portal Settings"),
"onboard": 1,
}
]
},

View file

@ -18,8 +18,7 @@ class Domain(Document):
self.setup_roles()
self.setup_properties()
self.set_values()
# always set the desktop icons while changing the domain settings
self.setup_desktop_icons()
if not int(frappe.defaults.get_defaults().setup_complete or 0):
# if setup not complete, setup desktop etc.
self.setup_sidebar_items()
@ -89,12 +88,6 @@ class Domain(Document):
frappe.db.set_value('Portal Settings', None, 'default_role',
self.data.get('default_portal_role'))
def setup_desktop_icons(self):
'''set desktop icons form `data.desktop_icons`'''
from frappe.desk.doctype.desktop_icon.desktop_icon import set_desktop_icons
if self.data.desktop_icons:
set_desktop_icons(self.data.desktop_icons)
def setup_properties(self):
if self.data.properties:
for args in self.data.properties:

View file

@ -71,10 +71,6 @@ frappe.ui.form.on('User', {
frm.toggle_display(['sb1', 'sb3', 'modules_access'], false);
if(!frm.is_new()) {
frm.add_custom_button(__("Set Desktop Icons"), function() {
frappe.frappe_toolbar.modules_select.show(doc.name);
}, null, "btn-default")
if(has_access_to_edit_user()) {
frm.add_custom_button(__("Set User Permissions"), function() {

View file

@ -1511,7 +1511,7 @@
"columns": 0,
"default": "",
"description": "",
"fieldname": "desktop_icon_access",
"fieldname": "sb_allow_modules",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
@ -1520,7 +1520,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Desktop Icon",
"label": "Allow Modules",
"length": 0,
"no_copy": 0,
"permlevel": 1,
@ -2303,7 +2303,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 5,
"modified": "2018-11-21 12:34:57.652854",
"modified": "2019-01-30 13:56:10.732154",
"modified_by": "Administrator",
"module": "Core",
"name": "User",

View file

@ -36,9 +36,9 @@ class User(Document):
self.name = self.email
def onload(self):
from frappe.config import get_modules_from_all_apps
self.set_onload('all_modules',
[m.module_name for m in frappe.db.get_all('Desktop Icon',
fields=['module_name'], filters={'standard': 1}, order_by="module_name")])
[m.get("module_name") for m in get_modules_from_all_apps()])
def before_insert(self):
self.flags.in_insert = True

View file

@ -1 +0,0 @@
First screen after login. Array of module icons based on permission.

View file

@ -1,3 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt

View file

@ -1,26 +0,0 @@
<div><input class="form-control desktop-app-search" type="text" placeholder="{%= __("Search Application") %}">
</div>
{% if (frappe.user.has_role("System Manager")) { %}
<p class="text-right"><a href="#applications" class="btn btn-sm btn-default">Install new applications</a>
</p>
{% } %}
<hr>
<p class="text-muted small">{%= __("Checked items will be shown on desktop") %}</p>
<div class="list-group all-applications-list">
{% for(var i=0, l=all_modules.length; i < l; i++) {
var module_name = all_modules[i];
var module = frappe.get_module(module_name);
if (desktop_items.indexOf(module_name)===-1
|| frappe.user.is_module_blocked(module_name)) { continue; }
%}
<div class="list-group-item" data-label="{%= module.label %}" data-name="{%= module.name %}">
<div class="checkbox">
<label>
<input type="checkbox" {% if (user_desktop_items.indexOf(module.name)!==-1) { %} checked {% } %}
data-name="{%= module.name %}"
{{ module.force_show ? "disabled" : ""}}> {%= __(module.label) %}
</label>
</div>
</div>
{% } %}
</div>

View file

@ -1,344 +1 @@
frappe.provide('frappe.desktop');
frappe.pages['desktop'].on_page_load = function(wrapper) {
// load desktop
if(!frappe.list_desktop) {
frappe.desktop.set_background();
}
frappe.desktop.refresh(wrapper);
};
frappe.pages['desktop'].on_page_show = function(wrapper) {
if(frappe.list_desktop) {
$("body").attr("data-route", "list-desktop");
}
};
$.extend(frappe.desktop, {
refresh: function(wrapper) {
if (wrapper) {
this.wrapper = $(wrapper);
}
this.render();
this.make_sortable();
},
render: function() {
var me = this;
frappe.utils.set_title(__("Desktop"));
var template = frappe.list_desktop ? "desktop_list_view" : "desktop_icon_grid";
var all_icons = frappe.get_desktop_icons();
var explore_icon = {
module_name: 'Explore',
label: 'Explore',
_label: __('Explore'),
_id: 'Explore',
_doctype: '',
icon: 'octicon octicon-telescope',
color: '#7578f6',
link: 'modules'
};
explore_icon.app_icon = frappe.ui.app_icon.get_html(explore_icon);
all_icons.push(explore_icon);
frappe.desktop.wrapper.html(frappe.render_template(template, {
// all visible icons
desktop_items: all_icons,
}));
frappe.desktop.setup_module_click();
// notifications
frappe.desktop.show_pending_notifications();
$(document).on("notification-update", function() {
me.show_pending_notifications();
});
$(document).trigger("desktop-render");
},
render_help_messages: function(help_messages) {
var wrapper = frappe.desktop.wrapper.find('.help-message-wrapper');
var $help_messages = wrapper.find('.help-messages');
var set_current_message = function(idx) {
idx = cint(idx);
wrapper.current_message_idx = idx;
wrapper.find('.left-arrow, .right-arrow').addClass('disabled');
wrapper.find('.help-message-item').addClass('hidden');
wrapper.find('[data-message-idx="'+idx+'"]').removeClass('hidden');
if(idx > 0) {
wrapper.find('.left-arrow').removeClass('disabled');
}
if(idx < help_messages.length - 1) {
wrapper.find('.right-arrow').removeClass('disabled');
}
}
if(help_messages) {
wrapper.removeClass('hidden');
help_messages.forEach(function(message, i) {
var $message = $('<div class="help-message-item hidden"></div>')
.attr('data-message-idx', i)
.html(frappe.render_template('desktop_help_message', message))
.appendTo($help_messages);
});
set_current_message(0);
wrapper.find('.close').on('click', function() {
wrapper.addClass('hidden');
});
}
wrapper.find('.left-arrow').on('click', function() {
if(wrapper.current_message_idx) {
set_current_message(wrapper.current_message_idx - 1);
}
})
wrapper.find('.right-arrow').on('click', function() {
if(help_messages.length > wrapper.current_message_idx + 1) {
set_current_message(wrapper.current_message_idx + 1);
}
});
},
setup_module_click: function() {
frappe.desktop.wiggling = false;
if(frappe.list_desktop) {
frappe.desktop.wrapper.on("click", ".desktop-list-item", function() {
frappe.desktop.open_module($(this));
});
} else {
frappe.desktop.wrapper.on("click", ".app-icon, .app-icon-svg", function() {
if ( !frappe.desktop.wiggling ) {
frappe.desktop.open_module($(this).parent());
}
});
}
frappe.desktop.wrapper.on("click", ".circle", function() {
var doctype = $(this).attr('data-doctype');
if(doctype) {
frappe.ui.notifications.show_open_count_list(doctype);
}
});
frappe.desktop.setup_wiggle();
},
setup_wiggle: () => {
// Wiggle, Wiggle, Wiggle.
const DURATION_LONG_PRESS = 1000;
var timer_id = 0;
const $cases = frappe.desktop.wrapper.find('.case-wrapper');
const $icons = frappe.desktop.wrapper.find('.app-icon');
const $notis = $(frappe.desktop.wrapper.find('.circle').toArray().filter((object) => {
// This hack is so bad, I should punch myself.
// Seriously, punch yourself.
const text = $(object).find('.circle-text').html();
return text;
}));
const clearWiggle = () => {
const $closes = $cases.find('.module-remove');
$closes.hide();
$notis.show();
$icons.removeClass('wiggle');
frappe.desktop.wiggling = false;
};
frappe.desktop.wrapper.on('mousedown', '.app-icon', () => {
timer_id = setTimeout(() => {
frappe.desktop.wiggling = true;
// hide all notifications.
$notis.hide();
$cases.each((i) => {
const $case = $($cases[i]);
const template =
`
<div class="circle module-remove" style="background-color:#E0E0E0; color:#212121">
<div class="circle-text">
<b>
&times
</b>
</div>
</div>
`;
$case.append(template);
const $close = $case.find('.module-remove');
const name = $case.attr('title');
$close.click(() => {
// good enough to create dynamic dialogs?
const dialog = new frappe.ui.Dialog({
title: __(`Hide ${name}?`)
});
dialog.set_primary_action(__('Hide'), () => {
frappe.call({
method: 'frappe.desk.doctype.desktop_icon.desktop_icon.hide',
args: { name: name },
freeze: true,
callback: (response) =>
{
if ( response.message ) {
location.reload();
}
}
})
dialog.hide();
clearWiggle();
});
// Hacks, Hacks and Hacks.
var $cancel = dialog.get_close_btn();
$cancel.click(() => {
clearWiggle();
});
$cancel.html(__(`Cancel`));
dialog.show();
});
});
$icons.addClass('wiggle');
}, DURATION_LONG_PRESS);
});
frappe.desktop.wrapper.on('mouseup mouseleave', '.app-icon', () => {
clearTimeout(timer_id);
});
// also stop wiggling if clicked elsewhere.
$('body').click((event) => {
if ( frappe.desktop.wiggling ) {
const $target = $(event.target);
// our target shouldn't be .app-icons or .close
const $parent = $target.parents('.case-wrapper');
if ( $parent.length == 0 )
clearWiggle();
}
});
// end wiggle
},
open_module: function(parent) {
var link = parent.attr("data-link");
if(link) {
if(link.indexOf('javascript:')===0) {
eval(link.substr(11));
} else if(link.substr(0, 1)==="/" || link.substr(0, 4)==="http") {
window.open(link, "_blank");
} else {
frappe.set_route(link);
}
return false;
} else {
var module = frappe.get_module(parent.attr("data-name"));
if (module && module.onclick) {
module.onclick();
return false;
}
}
},
make_sortable: function() {
if (frappe.dom.is_touchscreen() || frappe.list_desktop) {
return;
}
new Sortable($("#icon-grid").get(0), {
animation: 150,
onUpdate: function(event) {
var new_order = [];
$("#icon-grid .case-wrapper").each(function(i, e) {
new_order.push($(this).attr("data-name"));
});
frappe.call({
method: 'frappe.desk.doctype.desktop_icon.desktop_icon.set_order',
args: {
'new_order': new_order,
'user': frappe.session.user
},
quiet: true
});
}
});
},
set_background: function() {
frappe.ui.set_user_background(frappe.boot.user.background_image, null,
frappe.boot.user.background_style);
},
show_pending_notifications: function() {
var modules_list = frappe.get_desktop_icons();
for (var i=0, l=modules_list.length; i < l; i++) {
var module = modules_list[i];
var module_doctypes = frappe.boot.notification_info.module_doctypes[module.module_name];
var sum = 0;
if(module_doctypes && frappe.boot.notification_info.open_count_doctype) {
// sum all doctypes for a module
for (var j=0, k=module_doctypes.length; j < k; j++) {
var doctype = module_doctypes[j];
let count = (frappe.boot.notification_info.open_count_doctype[doctype] || 0);
count = typeof count == "string" ? parseInt(count) : count;
sum += count;
}
}
if(frappe.boot.notification_info.open_count_doctype
&& frappe.boot.notification_info.open_count_doctype[module.module_name]!=null) {
// notification count explicitly for doctype
let count = frappe.boot.notification_info.open_count_doctype[module.module_name] || 0;
count = typeof count == "string" ? parseInt(count) : count;
sum += count;
}
if(frappe.boot.notification_info.open_count_module
&& frappe.boot.notification_info.open_count_module[module.module_name]!=null) {
// notification count explicitly for module
let count = frappe.boot.notification_info.open_count_module[module.module_name] || 0;
count = typeof count == "string" ? parseInt(count) : count;
sum += count;
}
// if module found
if(module._id.indexOf('/')===-1 && !module._report) {
var notifier = $(".module-count-" + frappe.scrub(module._id));
if(notifier.length) {
notifier.toggle(sum ? true : false);
var circle = notifier.find(".circle-text");
var text = sum || '';
if(text > 99) {
text = '99+';
}
if(circle.length) {
circle.html(text);
} else {
notifier.html(text);
}
}
}
}
}
});
frappe.pages['desktop'].on_page_load = function() {};

View file

@ -1,10 +1,11 @@
{
"creation": "2013-02-14 17:37:37.000000",
"content": null,
"creation": "2019-01-29 13:11:48.872579",
"docstatus": 0,
"doctype": "Page",
"icon": "fa fa-th",
"idx": 1,
"modified": "2013-07-11 14:41:56.000000",
"icon": "icon-th",
"idx": 0,
"modified": "2019-01-29 13:11:48.872579",
"modified_by": "Administrator",
"module": "Core",
"name": "desktop",
@ -15,6 +16,9 @@
"role": "All"
}
],
"script": null,
"standard": "Yes",
"style": null,
"system_page": 0,
"title": "Desktop"
}

View file

@ -1,25 +0,0 @@
from __future__ import unicode_literals
import functools
import frappe
from past.builtins import cmp
@frappe.whitelist()
def get_help_messages():
'''Return help messages for the desktop (called via `get_help_messages` hook)
Format for message:
{
title: _('Add Employees to Manage Them'),
description: _('Add your Employees so you can manage leaves, expenses and payroll'),
action: 'Add Employee',
route: 'List/Employee'
}
'''
messages = []
for fn in frappe.get_hooks('get_help_messages'):
messages += frappe.get_attr(fn)()
return sorted(messages, key = functools.cmp_to_key(lambda a, b: cmp(a.get('count'), b.get('count'))))

View file

@ -1,8 +0,0 @@
<div><span class="indicator blue">{{ title }}</span></div>
<p>{{ description }}</p>
<div>
<a class="btn btn-sm btn-default" href="#{{ route }}">{{ action }}</a>
<span class="help-progress" title="{{ __("You have made {0} of {1}", [count, target]) }}">
<span class="help-progress-bar" style="width: {{ Math.floor(count/target*100) }}%"></span>
</span>
</div>

View file

@ -1,21 +0,0 @@
<div style="text-align: center; padding-top: calc(40px + 3%)">
<div id="icon-grid">
{% for (var i=0, l=desktop_items.length; i < l; i++) { %}
{{ frappe.render_template("desktop_module_icon", desktop_items[i]) }}
{% } %}
</div>
<div class="help-message-wrapper hidden">
<div class="help-message-container">
<a class="close pull-right" style="margin-right: -7px;">
<i class="octicon octicon-x"></i></a>
<div class="help-messages">
</div>
<a class="left-arrow octicon octicon-chevron-left">
</a>
<a class="right-arrow octicon octicon-chevron-right">
</a>
</div>
</div>
</div>
<div style="clear: both"></div>

View file

@ -1,23 +0,0 @@
<div class="container page-body">
<div class="row">
<div class="layout-main-section">
<div class="page-content desktop-list" style="margin-top: 40px;">
{% for (var i=0, l=desktop_items.length; i < l; i++) {
var module = desktop_items[i];
%}
<div class="desktop-list-item" id="module-icon-{%= module._id %}"
data-name="{%= module.name %}" data-link="{%= module.link %}"
title="{%= module._label %}">
<h4>
<i class="{{ module.icon }} text-muted"
style="font-size: 20px; margin-right: 15px;"></i>
{{ module._label }}
</h4>
<span class="open-notification global module-count-{{ module._id }}"
style="display: none;"></span>
</div>
{% } %}
</div>
</div>
</div>
</div>

View file

@ -1,11 +0,0 @@
<div class="case-wrapper"
data-name="{{ module_name }}" data-link="{{ link }}" title="{{ _label }}">
{{ app_icon }}
<div class="case-label ellipsis">
<div class="circle module-count-{{ frappe.scrub(_id) }}" data-doctype="{{ _doctype }}" style="display: none;">
<span class="circle-text"></span>
</div>
<!-- <span id="module-count-{{ _id }}" class="octicon octicon-primitive-dot circle" style="display: None"></span> -->
<span class="case-label-text">{{ _label }}</span>
</div>
</div>

View file

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@ -14,6 +15,7 @@
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -40,10 +42,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -70,10 +74,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -100,10 +106,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -130,10 +138,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -159,10 +169,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -189,10 +201,76 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "category",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Category",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -219,10 +297,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -249,10 +329,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -279,10 +361,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -308,10 +392,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -339,10 +425,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -370,10 +458,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -401,10 +491,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -431,10 +523,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -460,10 +554,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -490,10 +586,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -520,10 +618,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -550,10 +650,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -580,6 +682,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
@ -593,7 +696,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-05-08 15:41:31.121652",
"modified": "2019-01-24 04:58:58.720618",
"modified_by": "Administrator",
"module": "Desk",
"name": "Desktop Icon",
@ -602,7 +705,6 @@
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
@ -629,5 +731,6 @@
"sort_order": "DESC",
"title_field": "module_name",
"track_changes": 1,
"track_seen": 0
"track_seen": 0,
"track_views": 0
}

View file

@ -31,7 +31,7 @@ def get_desktop_icons(user=None):
user_icons = frappe.cache().hget('desktop_icons', user)
if not user_icons:
fields = ['module_name', 'hidden', 'label', 'link', 'type', 'icon', 'color',
fields = ['module_name', 'hidden', 'label', 'link', 'type', 'icon', 'color', 'description', 'category',
'_doctype', '_report', 'idx', 'force_show', 'reverse', 'custom', 'standard', 'blocked']
active_domains = frappe.get_active_domains()

View file

@ -24,7 +24,7 @@ def hide_module(module):
set_hidden(module, frappe.session.user, 1)
clear_desktop_icons_cache()
def get_data(module):
def get_data(module, build=True):
"""Get module data for the module view `desk/#Module/[name]`"""
doctype_info = get_doctype_info(module)
data = build_config_from_file(module)
@ -42,6 +42,43 @@ def get_data(module):
#set_last_modified(data)
if build:
exists_cache = {}
def exists(name):
exists = exists_cache.get(name)
if not exists:
if not frappe.db.get_value('DocType', name, 'issingle'):
exists = frappe.db.count(name)
else:
exists = True
exists_cache[name] = exists
return exists
for section in data:
for item in section["items"]:
# Onboarding
# First disable based on exists of depends_on list
doctype = item.get("doctype")
dependencies = item.get("dependencies") or None
if not dependencies and doctype:
item["dependencies"] = [doctype]
dependencies = item.get("dependencies")
if dependencies:
incomplete_dependencies = [d for d in dependencies if not exists(d)]
if len(incomplete_dependencies):
item["incomplete_dependencies"] = incomplete_dependencies
if item.get("onboard"):
# Mark Spotlights for initial
if item.get("type") == "doctype":
name = item.get("name")
count = exists(name)
item["count"] = count
return data
def build_config_from_file(module):

View file

@ -1,185 +0,0 @@
frappe.pages['modules'].on_page_load = function(wrapper) {
var page = frappe.ui.make_app_page({
parent: wrapper,
title: 'Modules',
single_column: false
});
frappe.modules_page = page;
frappe.module_links = {};
page.section_data = {};
page.wrapper.find('.page-head h1').css({'padding-left': '15px'});
// page.wrapper.find('.page-content').css({'margin-top': '0px'});
// menu
page.add_menu_item(__('Set Desktop Icons'), function() {
frappe.frappe_toolbar.modules_select
.show(frappe.session.user);
});
if(frappe.user.has_role('System Manager')) {
page.add_menu_item(__('Install Apps'), function() {
frappe.set_route("applications");
});
}
page.get_page_modules = () => {
return frappe.get_desktop_icons(true)
.filter(d => d.type==='module' && !d.blocked)
.sort((a, b) => { return (a._label > b._label) ? 1 : -1; });
};
let get_module_sidebar_item = (item) => `<li class="strong module-sidebar-item">
<a class="module-link" data-name="${item.module_name}" href="#${item.link}">
<i class="fa fa-chevron-right pull-right" style="display: none;"></i>
<span>${item._label}</span>
</a>
</li>`;
let get_sidebar_html = () => {
let sidebar_items_html = page.get_page_modules()
.map(get_module_sidebar_item.bind(this)).join("");
return `<ul class="module-sidebar-nav overlay-sidebar nav nav-pills nav-stacked">
${sidebar_items_html}
<li class="divider"></li>
</ul>`;
};
// render sidebar
page.sidebar.html(get_sidebar_html());
// help click
page.main.on("click", '.module-section-link[data-type="help"]', function() {
frappe.help.show_video($(this).attr("data-youtube-id"));
return false;
});
// notifications click
page.main.on("click", '.open-notification', function() {
var doctype = $(this).attr('data-doctype');
if(doctype) {
frappe.ui.notifications.show_open_count_list(doctype);
}
});
page.activate_link = function(link) {
page.last_link = link;
page.wrapper.find('.module-sidebar-item.active, .module-link.active').removeClass('active');
$(link).addClass('active').parent().addClass("active");
show_section($(link).attr('data-name'));
};
var show_section = function(module_name) {
if (!module_name) return;
if(module_name in page.section_data) {
render_section(page.section_data[module_name]);
} else {
page.main.empty();
return frappe.call({
method: "frappe.desk.moduleview.get",
args: {
module: module_name
},
callback: function(r) {
var m = frappe.get_module(module_name);
m.data = r.message.data;
process_data(module_name, m.data);
page.section_data[module_name] = m;
render_section(m);
},
freeze: true,
});
}
};
var render_section = function(m) {
page.set_title(__(m.label));
page.main.html(frappe.render_template('modules_section', m));
// if(frappe.utils.is_xs() || frappe.utils.is_sm()) {
// // call this after a timeout, becuase a refresh will set the page to the top
// setTimeout(function() {
// $(document).scrollTop($('.module-body').offset().top - 150);
// }, 100);
// }
//setup_section_toggle();
frappe.app.update_notification_count_in_modules();
};
var process_data = function(module_name, data) {
frappe.module_links[module_name] = [];
data.forEach(function(section) {
section.items.forEach(function(item) {
item.style = '';
if(item.type==="doctype") {
item.doctype = item.name;
// map of doctypes that belong to a module
frappe.module_links[module_name].push(item.name);
}
if(!item.route) {
if(item.link) {
item.route=strip(item.link, "#");
}
else if(item.type==="doctype") {
if(frappe.model.is_single(item.doctype)) {
item.route = 'Form/' + item.doctype;
} else {
if (item.filters) {
frappe.route_options=item.filters;
}
item.route="List/" + item.doctype;
//item.style = 'font-weight: 500;';
}
// item.style = 'font-weight: bold;';
}
else if(item.type==="report" && item.is_query_report) {
item.route="query-report/" + item.name;
}
else if(item.type==="report") {
item.route="List/" + item.doctype + "/Report/" + item.name;
}
else if(item.type==="page") {
item.route=item.name;
}
}
if(item.route_options) {
item.route += "?" + $.map(item.route_options, function(value, key) {
return encodeURIComponent(key) + "=" + encodeURIComponent(value); }).join('&');
}
if(item.type==="page" || item.type==="help" || item.type==="report" ||
(item.doctype && frappe.model.can_read(item.doctype))) {
item.shown = true;
}
});
});
};
};
frappe.pages['modules'].on_page_show = function(wrapper) {
let route = frappe.get_route();
let modules = frappe.modules_page.get_page_modules().map(d => d.module_name);
$("body").attr("data-sidebar", 1);
if(route.length > 1) {
// activate section based on route
let module_name = route[1];
if(modules.includes(module_name)) {
frappe.modules_page.activate_link(
frappe.modules_page.sidebar.find('.module-link[data-name="'+ module_name +'"]'));
} else {
frappe.throw(__(`Module ${module_name} not found.`));
}
} else if(frappe.modules_page.last_link) {
// open last link
frappe.set_route('modules', frappe.modules_page.last_link.attr('data-name'));
} else {
// first time, open the first page
frappe.modules_page.activate_link(frappe.modules_page.sidebar.find('.module-link:first'));
}
};

View file

@ -1,18 +0,0 @@
{
"content": null,
"creation": "2016-03-07 04:46:00.420330",
"docstatus": 0,
"doctype": "Page",
"idx": 0,
"modified": "2016-03-07 04:46:00.420330",
"modified_by": "Administrator",
"module": "Desk",
"name": "modules",
"owner": "Administrator",
"page_name": "modules",
"roles": [],
"script": null,
"standard": "Yes",
"style": null,
"title": "Modules"
}

View file

@ -1,30 +0,0 @@
<div class="module-body">
{% for (var i=0; i < data.length; i++) { var section = data[i]; %}
{% if ((i % 2)===0) { %}<div class="row module-section">{% } %}
<div class="col-sm-6 module-section-column">
<div class="h4 section-head">
{{ section.label }}
</div>
<div class="section-body">
{% for (var j=0; j < section.items.length; j++) {
var item = section.items[j];
if(item.shown) { %}
<div style="min-height: 22px; margin-top: 8px;">
<a class="module-section-link small" data-type="{{ item.type }}"
{% if(item.type==="help") { %}
data-youtube-id="{{ item.youtube_id }}"{% } %}
href="#{{ item.route }}" style="{{ item.style }}">
{{ item.label || __(item.name) }}
</a>
{% if(item.type==="doctype") { %}
<span class="open-notification global hide"
data-doctype="{{ item.doctype || item.name }}"></span>
{% } %}
</div>
{% } %}
{% } %}
</div>
</div>
{% if ((i % 2)===1 || i===data.length-1) { %}</div>{% } %}
{% } %}
</div>

View file

@ -23,7 +23,7 @@ from frappe.database import setup_database
def install_db(root_login="root", root_password=None, db_name=None, source_sql=None,
admin_password=None, verbose=True, force=0, site_config=None, reinstall=False,
db_type=None):
if not db_type:
db_type = frappe.conf.db_type or 'mariadb'
@ -158,9 +158,6 @@ def remove_app(app_name, dry_run=False, yes=False):
if not dry_run:
frappe.delete_doc("Module Def", module_name)
# delete desktop icons
frappe.db.sql('delete from `tabDesktop Icon` where app=%s', app_name)
remove_from_installed_apps(app_name)
if not dry_run:

View file

@ -145,11 +145,9 @@ frappe.patches.v6_16.feed_doc_owner
frappe.patches.v6_21.print_settings_repeat_header_footer
frappe.patches.v6_24.set_language_as_code
frappe.patches.v6_20x.update_insert_after
finally:frappe.patches.v6_24.sync_desktop_icons
frappe.patches.v6_20x.set_allow_draft_for_print
frappe.patches.v6_20x.remove_roles_from_website_user
frappe.patches.v7_0.set_user_fullname
frappe.patches.v7_0.desktop_icons_hidden_by_admin_as_blocked
frappe.patches.v7_0.add_communication_in_doc
frappe.patches.v7_0.update_send_after_in_bulk_email
execute:frappe.db.sql('''delete from `tabSingles` where doctype="Email Settings"''') # 2016-06-13
@ -183,15 +181,11 @@ frappe.patches.v8_0.deprecate_integration_broker
frappe.patches.v8_0.update_gender_and_salutation
frappe.patches.v8_0.setup_email_inbox #2017-03-29
frappe.patches.v8_0.newsletter_childtable_migrate
execute:frappe.db.sql("delete from `tabDesktop Icon` where module_name='Communication'")
execute:frappe.db.sql("update `tabDesktop Icon` set type='list' where _doctype='Communication'")
frappe.patches.v8_0.fix_non_english_desktop_icons # 2017-04-12
frappe.patches.v8_0.set_doctype_values_in_custom_role
frappe.patches.v8_0.install_new_build_system_requirements
frappe.patches.v8_0.set_currency_field_precision # 2017-05-09
frappe.patches.v8_0.rename_print_to_printing
frappe.patches.v7_1.disabled_print_settings_for_custom_print_format
frappe.patches.v8_0.update_desktop_icons
execute:frappe.db.sql('update tabReport set module="Desk" where name="ToDo"')
frappe.patches.v8_1.enable_allow_error_traceback_in_system_settings
frappe.patches.v8_1.update_format_options_in_auto_email_report
@ -236,3 +230,4 @@ frappe.patches.v11_0.fix_order_by_in_reports_json
execute:frappe.delete_doc('Page', 'applications', ignore_missing=True)
frappe.patches.v11_0.set_missing_creation_and_modified_value_for_user_permissions
frappe.patches.v12_0.set_primary_key_in_series
execute:frappe.delete_doc("Page", "modules", ignore_missing=True)

View file

@ -1,44 +0,0 @@
from __future__ import unicode_literals
import frappe, json
from frappe.desk.doctype.desktop_icon.desktop_icon import sync_from_app, get_user_copy
import frappe.defaults
def execute():
frappe.reload_doc('desk', 'doctype', 'desktop_icon')
frappe.db.sql('delete from `tabDesktop Icon`')
modules_list = []
for app in frappe.get_installed_apps():
modules_list += sync_from_app(app)
# sync hidden modules
hidden_modules = frappe.db.get_global('hidden_modules')
if hidden_modules:
for m in json.loads(hidden_modules):
try:
desktop_icon = frappe.get_doc('Desktop Icon', {'module_name': m, 'standard': 1, 'app': app})
desktop_icon.db_set('hidden', 1)
except frappe.DoesNotExistError:
pass
# sync user sort
for user in frappe.get_all('User', filters={'user_type': 'System User'}):
user_list = frappe.defaults.get_user_default('_user_desktop_items', user=user.name)
if user_list:
user_list = json.loads(user_list)
for i, module_name in enumerate(user_list):
try:
desktop_icon = get_user_copy(module_name, user=user.name)
desktop_icon.db_set('idx', i)
except frappe.DoesNotExistError:
pass
# set remaining icons as hidden
for module_name in list(set([m['module_name'] for m in modules_list]) - set(user_list)):
try:
desktop_icon = get_user_copy(module_name, user=user.name)
desktop_icon.db_set('hidden', 1)
except frappe.DoesNotExistError:
pass

View file

@ -1,12 +0,0 @@
from __future__ import unicode_literals
import frappe
def execute():
# all icons hidden in standard are "blocked"
# this is for the use case where the admin wants to remove icon for everyone
# in 7.0, icons may be hidden by default, but still can be shown to the user
# e.g. Accounts, Stock etc, so we need a new property for blocked
if frappe.db.table_exists('Desktop Icon'):
frappe.db.sql('update `tabDesktop Icon` set blocked = 1 where standard=1 and hidden=1')

View file

@ -11,12 +11,6 @@ def execute():
update_routes(['Help Category', 'Help Article'])
remove_from_installed_apps('knowledge_base')
# remove desktop icon
desktop_icon_name = frappe.db.get_value('Desktop Icon',
dict(module_name='Knowledge Base', type='module'))
if desktop_icon_name:
frappe.delete_doc('Desktop Icon', desktop_icon_name)
# remove module def
if frappe.db.exists('Module Def', 'Knowledge Base'):
frappe.delete_doc('Module Def', 'Knowledge Base')

View file

@ -1,14 +0,0 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import frappe
from frappe.desk.doctype.desktop_icon.desktop_icon import clear_desktop_icons_cache
def execute():
frappe.db.sql("""
update `tabDesktop Icon`
set module_name=_doctype, label=_doctype
where type = 'link' and _doctype != label and link like 'List/%'
""")

View file

@ -3,31 +3,18 @@ import frappe, json
from frappe.core.doctype.user.user import ask_pass_update, setup_user_email_inbox
def execute():
"""
"""
depricate email inbox page if exists
remove desktop icon for email inbox page if exists
patch to remove Custom DocPerm for communication
add user inbox child table entry for existing email account in not exists
"""
if frappe.db.exists("Page", "email_inbox"):
frappe.delete_doc("Page", "email_inbox")
desktop_icon = frappe.db.get_value("Desktop Icon", {
"module_name": "Email",
"type": "Page",
"link": "email_inbox"
})
if desktop_icon:
frappe.delete_doc("Desktop Icon", desktop_icon)
frappe.db.sql("""update `tabCustom DocPerm` set `write`=0, email=1 where parent='Communication'""")
setup_inbox_from_email_account()
def setup_inbox_from_email_account():
""" add user inbox child table entry for existing email account in not exists """
frappe.reload_doc("core", "doctype", "user_email")
frappe.reload_doc("email", "doctype", "email_account")
@ -36,4 +23,4 @@ def setup_inbox_from_email_account():
for email_account in email_accounts:
setup_user_email_inbox(email_account.get("name"), email_account.get("awaiting_password"),
email_account.get("email_id"), email_account.get("enabled_outgoing"))
email_account.get("email_id"), email_account.get("enabled_outgoing"))

View file

@ -1,25 +0,0 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe.utils import cstr
def execute():
""" update the desktop icons """
frappe.reload_doc('desk', 'doctype', 'desktop_icon')
icons = frappe.get_all("Desktop Icon", filters={ "type": "link" }, fields=["link", "name"])
for icon in icons:
# check if report exists
icon_link = icon.get("link", "") or ""
parts = icon_link.split("/")
if not parts:
continue
report_name = parts[-1]
if "report" in parts[0] and frappe.db.get_value("Report", report_name):
frappe.db.sql(""" update `tabDesktop Icon` set _report='{report_name}'
where name='{name}'""".format(report_name=report_name, name=icon.get("name")))

View file

@ -133,6 +133,7 @@
"public/js/lib/Sortable.min.js",
"public/js/lib/jquery/jquery.hotkeys.js",
"public/js/lib/bootstrap.min.js",
"node_modules/vue/dist/vue.js",
"node_modules/moment/min/moment-with-locales.min.js",
"node_modules/moment-timezone/builds/moment-timezone-with-data.min.js",
"public/js/lib/socket.io.min.js",
@ -229,7 +230,6 @@
"public/js/frappe/ui/toolbar/about.js",
"public/js/frappe/ui/toolbar/navbar.html",
"public/js/frappe/ui/toolbar/toolbar.js",
"public/js/frappe/ui/toolbar/modules_select.js",
"public/js/frappe/ui/toolbar/notifications.js",
"public/js/frappe/views/communication.js",
"public/js/frappe/views/translation_manager.js",
@ -381,5 +381,8 @@
],
"js/social.min.js": [
"public/js/frappe/social/social_home.js"
],
"js/modules.min.js": [
"public/js/frappe/views/modules_home.js"
]
}

View file

@ -194,7 +194,7 @@ frappe.Application = Class.extend({
load_bootinfo: function() {
if(frappe.boot) {
frappe.modules = {};
frappe.boot.desktop_icons.forEach(function(m) {
frappe.boot.allowed_modules.forEach(function(m) {
frappe.modules[m.module_name]=m;
});
frappe.model.sync(frappe.boot.docs);
@ -264,9 +264,6 @@ frappe.Application = Class.extend({
$.extend(frappe.boot.notification_info, r.message);
$(document).trigger("notification-update");
// update in module views
me.update_notification_count_in_modules();
if(frappe.get_route()[0] != "messages") {
if(r.message.new_messages.length) {
frappe.utils.set_title_prefix("(" + r.message.new_messages.length + ")");
@ -279,18 +276,6 @@ frappe.Application = Class.extend({
}
},
update_notification_count_in_modules: function() {
$.each(frappe.boot.notification_info.open_count_doctype, function(doctype, count) {
if(count) {
$('.open-notification.global[data-doctype="'+ doctype +'"]')
.removeClass("hide").html(count > 99 ? "99+" : count);
} else {
$('.open-notification.global[data-doctype="'+ doctype +'"]')
.addClass("hide");
}
});
},
set_globals: function() {
frappe.session.user = frappe.boot.user.name;
frappe.session.user_email = frappe.boot.user.email;
@ -591,86 +576,3 @@ frappe.get_module = function(m, default_module) {
return module;
};
frappe.get_desktop_icons = function(show_hidden, show_global) {
// filter valid icons
// hidden == hidden from desktop
// blocked == no view from modules either
var out = [];
var add_to_out = function(module) {
module = frappe.get_module(module.module_name, module);
module.app_icon = frappe.ui.app_icon.get_html(module);
out.push(module);
};
var show_module = function(m) {
var out = true;
if(m.type==="page") {
out = m.link in frappe.boot.page_info;
} else if(m.force_show) {
out = true;
} else if(m._report) {
out = m._report in frappe.boot.user.all_reports;
} else if(m._doctype) {
//out = frappe.model.can_read(m._doctype);
out = frappe.boot.user.can_read.includes(m._doctype);
} else {
if(['Help', 'Settings'].includes(m.module_name)) {
// no permissions necessary for learn
out = true;
} else if(m.module_name==='Setup' && frappe.user.has_role('System Manager')) {
out = true;
} else {
out = frappe.boot.user.allow_modules.indexOf(m.module_name) !== -1;
}
}
if(m.hidden && !show_hidden) {
out = false;
}
if(m.blocked && !show_global) {
out = false;
}
return out;
};
let m;
for (var i=0, l=frappe.boot.desktop_icons.length; i < l; i++) {
m = frappe.boot.desktop_icons[i];
if ((['Setup', 'Core'].indexOf(m.module_name) === -1) && show_module(m)) {
add_to_out(m);
}
}
if(frappe.user_roles.includes('System Manager')) {
m = frappe.get_module('Setup');
if(show_module(m)) add_to_out(m);
}
if(frappe.user_roles.includes('Administrator')) {
m = frappe.get_module('Core');
if(show_module(m)) add_to_out(m);
}
return out;
};
frappe.add_to_desktop = function(label, doctype, report) {
frappe.call({
method: 'frappe.desk.doctype.desktop_icon.desktop_icon.add_user_icon',
args: {
'link': frappe.get_route_str(),
'label': label,
'type': 'link',
'_doctype': doctype,
'_report': report
},
callback: function(r) {
if(r.message) {
frappe.show_alert(__("Added"));
}
}
});
};

View file

@ -153,13 +153,6 @@ frappe.ui.form.Toolbar = Class.extend({
this.page.add_menu_item(__("Reload"), function() {
me.frm.reload_doc();}, true);
// add to desktop
if(me.frm.meta.issingle) {
this.page.add_menu_item(__('Add to Desktop'), function () {
frappe.add_to_desktop(me.frm.doctype, me.frm.doctype);
}, true);
}
// delete
if((cint(me.frm.doc.docstatus) != 1) && !me.frm.doc.__islocal
&& frappe.model.can_delete(me.frm.doctype)) {

View file

@ -1008,13 +1008,6 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
standard: true
});
// add to desktop
items.push({
label: __('Add to Desktop'),
action: () => frappe.add_to_desktop(doctype, doctype),
standard: true
});
if (frappe.user.has_role('System Manager') && frappe.boot.developer_mode === 1) {
// edit doctype
items.push({

View file

@ -74,7 +74,7 @@ $.extend(frappe.user, {
},
get_desktop_items: function() {
// hide based on permission
var modules_list = $.map(frappe.boot.desktop_icons, function(icon) {
var modules_list = $.map(frappe.boot.allowed_modules, function(icon) {
var m = icon.module_name;
var type = frappe.modules[m] && frappe.modules[m].type;
@ -105,14 +105,6 @@ $.extend(frappe.user, {
return modules_list;
},
is_module: function(m) {
var icons = frappe.get_desktop_icons();
for(var i=0; i<icons.length; i++) {
if(m===icons[i].module_name) return true;
}
return false;
},
is_report_manager: function() {
return frappe.user.has_role(['Administrator', 'System Manager', 'Report Manager']);
},
@ -147,9 +139,9 @@ $.extend(frappe.user, {
/* Normally frappe.user is an object
* having properties and methods.
* But in the following case
*
*
* if (frappe.user === 'Administrator')
*
*
* frappe.user will cast to a string
* returning frappe.user.name
*/

View file

@ -21,7 +21,7 @@ $(document).on("toolbar_setup", function() {
if(limits.space || limits.users || limits.expiry || limits.emails) {
help_links = [];
help_links.push('<li><a href="#usage-info">' + frappe._('Usage Info') + '</a></li>');
help_links.push('<li class="usage-info-link"><a href="#usage-info">' + frappe._('Usage Info') + '</a></li>');
help_links.push('<li class="divider"></li>');
$(help_links.join("\n")).insertBefore($("#toolbar-user").find("li:first"));
}

View file

@ -1,108 +0,0 @@
frappe.provide('frappe.ui.toolbar');
frappe.ui.toolbar.ModulesSelect = class {
constructor() {
this.user = frappe.boot.user.name;
this.setup();
}
setup() {
this.dialog = new frappe.ui.Dialog({
title: __('Set Desktop Icons'),
fields: [
{
label: __('Setup for'),
fieldname: 'setup_for',
fieldtype: 'Select',
options: [
{label: __('User'), value: 'user'},
{label: __('Everyone'), value: 'everyone'}
],
default: 'user',
onchange: () => {
let field = this.$setup_for;
if(field.get_value() === 'everyone') {
this.$user.$wrapper.hide();
this.user = undefined;
field.set_description(__('Limit icon choices for all users.'));
} else {
this.$user.$wrapper.show();
this.user = this.$user.get_value();
field.set_description('');
}
this.$icons_list.refresh();
}
},
{ fieldtype: 'Column Break' },
{
label: __('User'),
fieldname: 'user',
fieldtype: 'Link',
options: 'User',
default: frappe.boot.user.name,
onchange: () => {
this.user = this.get_value() || frappe.boot.user.name;
this.$icons_list.refresh();
}
},
{ fieldtype: 'Section Break' },
{
// label: __('Icons'),
fieldname: 'icons',
fieldtype: 'MultiCheck',
select_all: 1,
columns: 2,
get_data: () => {
return new Promise((resolve) => {
frappe.call({
method: 'frappe.desk.doctype.desktop_icon.desktop_icon.get_module_icons',
args: {user: this.user},
freeze: true,
callback: (r) => {
const icons = r.message.icons;
const user = r.message.user;
resolve(icons
.map(icon => {
const uncheck = user ? icon.hidden : icon.blocked;
return { label: icon.label, value: icon.module_name, checked:!uncheck };
}).sort(function(a, b){
if(a.label < b.label) return -1;
if(a.label > b.label) return 1;
return 0;
})
);
}
});
});
}
}
]
});
this.dialog.set_primary_action(__('Save'), () => {
frappe.call({
method: 'frappe.desk.doctype.desktop_icon.desktop_icon.update_icons',
args: {
hidden_list: this.$icons_list.get_unchecked_options(),
user: this.user
},
freeze: true,
callback: () => {
window.location.href = '';
}
});
});
this.$icons_list = this.dialog.fields_dict.icons;
this.$setup_for = this.dialog.fields_dict.setup_for;
this.$user = this.dialog.fields_dict.user;
}
show(user) {
if(user) {
this.user = user || frappe.boot.user.name;
this.$icons_list.refresh();
}
this.dialog.show();
}
};

View file

@ -24,8 +24,6 @@
{%= frappe.user.full_name() %}</span>
<b class="caret hidden-xs hidden-sm"></b></a>
<ul class="dropdown-menu" id="toolbar-user" role="menu">
<li class="navbar-set-desktop-icons"><a>
{%= __("Set Desktop Icons") %}</a></li>
<li><a href="#Form/User/{%= encodeURIComponent(frappe.session.user) %}">
{%= __("My Settings") %}</a></li>
<li class="navbar-reload">

View file

@ -21,21 +21,12 @@ frappe.ui.toolbar.Toolbar = Class.extend({
make: function() {
this.setup_sidebar();
this.setup_help();
this.setup_modules_dialog();
this.bind_events();
$(document).trigger('toolbar_setup');
},
setup_modules_dialog() {
this.modules_select = new frappe.ui.toolbar.ModulesSelect();
$('.navbar-set-desktop-icons').on('click', () => {
this.modules_select.show();
});
},
bind_events: function() {
$(document).on("notification-update", function() {
frappe.ui.notifications.update_notifications();

View file

@ -40,7 +40,9 @@ frappe.breadcrumbs = {
var breadcrumbs = frappe.breadcrumbs.all[frappe.breadcrumbs.current_page()];
if(!frappe.visible_modules) {
frappe.visible_modules = $.map(frappe.get_desktop_icons(true), (m) => { return m.module_name; });
frappe.visible_modules = $.map(frappe.boot.allowed_modules, (m) => {
return m.module_name;
});
}
var $breadcrumbs = $("#navbar-breadcrumbs").empty();
@ -88,7 +90,6 @@ frappe.breadcrumbs = {
}
if(breadcrumbs.doctype && frappe.get_route()[0]==="Form") {
if(breadcrumbs.doctype==="User"
&& frappe.user.is_module("Setup")===-1
|| frappe.get_doc('DocType', breadcrumbs.doctype).issingle) {
// no user listview for non-system managers and single doctypes
} else {

View file

@ -0,0 +1,151 @@
<template>
<div class="modules-page-container">
<div v-for="category in module_categories"
:key="category">
<div v-if="modules.filter(m => m.category === category).length" class="module-category h6 uppercase">
{{ category }}
</div>
<div class="modules-container">
<a v-for="module in modules.filter(m => m.category === category )"
:key="module.name"
:href="module.type === 'module' ? '#modules/' + module.module_name : module.link"
class="border module-box"
>
<div class="flush-top">
<div class="module-box-content">
<h4 class="h4">
<span class="indicator" :class="module.count ? 'red' : (module.onboard_present ? 'orange' : 'grey')"></span>
{{ module.label }}
</h4>
<p class="small text-muted"> {{ module.description }} </p>
</div>
</div>
</a>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
let modules_list = frappe.boot.allowed_modules
.filter(d => (d.type==='module' || d.category==='Places') && !d.blocked);
modules_list.forEach(module => {
module.count = this.get_module_count(module.module_name);
});
return {
route_str: frappe.get_route()[1],
module_label: '',
module_categories: ["Modules", "Domains", "Places", "Administration"],
modules: modules_list
};
},
methods: {
get_module_count(module_name) {
var module_doctypes = frappe.boot.notification_info.module_doctypes[module_name];
var sum = 0;
if(module_doctypes && frappe.boot.notification_info.open_count_doctype) {
// sum all doctypes for a module
for (var j=0, k=module_doctypes.length; j < k; j++) {
var doctype = module_doctypes[j];
let count = (frappe.boot.notification_info.open_count_doctype[doctype] || 0);
count = typeof count == "string" ? parseInt(count) : count;
sum += count;
}
}
if(frappe.boot.notification_info.open_count_doctype
&& frappe.boot.notification_info.open_count_doctype[module_name]!=null) {
// notification count explicitly for doctype
let count = frappe.boot.notification_info.open_count_doctype[module_name] || 0;
count = typeof count == "string" ? parseInt(count) : count;
sum += count;
}
if(frappe.boot.notification_info.open_count_module
&& frappe.boot.notification_info.open_count_module[module_name]!=null) {
// notification count explicitly for module
let count = frappe.boot.notification_info.open_count_module[module_name] || 0;
count = typeof count == "string" ? parseInt(count) : count;
sum += count;
}
sum = sum > 99 ? "99+" : sum;
return sum;
}
}
}
</script>
<style lang="less" scoped>
.modules-page-container {
margin: 70px 0px;
}
.module-category {
margin-top: 30px;
margin-bottom: 15px;
border-bottom: 1px solid #d0d8dd;
}
.modules-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
column-gap: 15px;
row-gap: 15px;
}
.module-box {
border-radius: 4px;
cursor: pointer;
padding: 5px 15px;
padding-top: 3px;
display: block;
}
.module-box:hover {
background-color: #fafbfc;
text-decoration: none;
}
.module-box-content {
padding-right: 15px;
flex: 1;
h4 {
margin-bottom: 5px
}
p {
margin-top: 5px;
font-size: 80%;
}
}
.icon-box {
padding: 15px;
width: 54px;
display: flex;
justify-content: center;
}
.icon {
font-size: 24px;
}
.open-notification {
top: -2px;
}
</style>

View file

@ -0,0 +1,58 @@
<template>
<div>
<div v-if="sections.length" class="sections-container">
<div v-for="section in sections"
:key="section.label"
class="border section-box"
>
<h4 class="h4"> {{ section.label }} </h4>
<module-link-item v-for="item in section.items"
:key="section.label + item.label"
:data-youtube-id="item.type==='help' ? item.youtube_id : false"
v-bind="item"
:open_count="item.type==='doctype' ? frappe.boot.notification_info.open_count_doctype[item.doctype] : false"
>
</module-link-item>
</div>
</div>
<div v-else class="sections-container">
<div v-for="n in 3" :key="n" class="skeleton-section-box"></div>
</div>
</div>
</template>
<script>
import ModuleLinkItem from "./ModuleLinkItem.vue";
export default {
components: {
ModuleLinkItem
},
props: ['module_name', 'sections'],
}
</script>
<style lang="less" scoped>
.sections-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
column-gap: 15px;
row-gap: 15px;
}
.section-box {
padding: 5px 20px;
border-radius: 4px;
}
.skeleton-section-box {
background-color: #f5f7fa;
height: 250px;
border-radius: 4px;
}
.h4 {
margin-bottom: 15px;
}
</style>

View file

@ -0,0 +1,106 @@
<template>
<div class="link-item flush-top small"
:class="{'onboard-spotlight': onboard, 'disabled-link': disabled_dependent}"
@mouseover="mouseover" @mouseleave="mouseleave"
>
<span :class="['indicator', indicator_color]"></span>
<span v-if="disabled_dependent" class="link-content text-muted">{{ label || __(name) }}</span>
<a v-else class="link-content" :href="route">{{ label || __(name) }}</a>
<div v-if="disabled_dependent" v-show="popover_active"
@mouseover="popover_hover = true" @mouseleave="popover_hover = false"
class="module-link-popover popover fade top in" role="tooltip"
>
<div class="arrow"></div>
<h3 class="popover-title" style="display: none;"></h3>
<div class="popover-content">
<div class="small text-muted">{{ __("You need to create these first: ") }}</div>
<div class="small">{{ __(incomplete_dependencies.join(", ")) }}</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['label', 'name', 'dependencies', 'incomplete_dependencies', 'onboard', 'count', 'route', 'doctype', 'open_count'],
data() {
return {
hover: false,
popover_hover: false,
}
},
computed: {
disabled_dependent() {
return this.dependencies && this.incomplete_dependencies;
},
indicator_color() {
if(this.open_count) {
return 'red';
}
if(this.onboard) {
return this.count ? 'blue' : 'orange';
};
return 'grey';
},
popover_active() {
return this.popover_hover || this.hover;
}
},
methods: {
mouseover() {
$('.module-link-popover').hide();
this.hover = true;
},
mouseleave() {
setTimeout(() => {
this.hover = false;
}, 300);
}
}
}
</script>
<style lang="less" scoped>
.link-item {
position: relative;
margin: 10px 0px;
cursor: default;
}
.onboard-spotlight {
.link-content {
font-weight: 600;
}
}
a:hover, a:focus {
text-decoration: underline;
}
// Overriding indicator styles
.indicator {
margin-right: 5px;
color: inherit;
font-weight: inherit;
}
.link-content {
flex: 1;
}
.popover {
display: block;
top: -60px;
max-width: 220px;
}
.popover.top > .arrow {
left: 20%;
}
</style>

View file

@ -0,0 +1,138 @@
<template>
<div class="modules-page-container">
<module-detail v-if="this.route && modules_list.map(m => m.module_name).includes(route[1])" :module_name="route[1]" :sections="current_module_sections"></module-detail>
</div>
</template>
<script>
import ModuleDetail from './ModuleDetail.vue';
export default {
components: {
ModuleDetail
},
data() {
return {
route: frappe.get_route(),
current_module_label: '',
current_module_sections: [],
modules_data_cache: {},
modules_list: frappe.boot.allowed_modules
.filter(d => (d.type==='module' || d.category==='Places') && !d.blocked),
};
},
created() {
this.update_current_module();
},
mounted() {
frappe.module_links = {};
frappe.route.on('change', () => {
this.update_current_module();
});
},
methods: {
update_current_module() {
let route = frappe.get_route();
if(route[0] === 'modules' || !route[0]) {
this.route = route;
let module = this.modules_list.filter(m => m.module_name == route[1])[0];
let module_name = module && (module.label || module.module_name);
let title = this.current_module_label ? this.current_module_label : module_name;
frappe.modules.home && frappe.modules.home.page.set_title(title);
if(!frappe.modules.home) {
setTimeout(() => {
frappe.modules.home.page.set_title(title);
}, 200);
}
if(module_name) {
this.get_module_sections(module.module_name);
}
}
},
get_module_sections(module_name) {
let cache = this.modules_data_cache[module_name];
if(cache) {
this.current_module_sections = cache;
} else {
this.current_module_sections = [];
return frappe.call({
method: "frappe.desk.moduleview.get",
args: {
module: module_name,
},
callback: (r) => {
var m = frappe.get_module(module_name);
this.current_module_sections = r.message.data;
this.process_data(module_name, this.current_module_sections);
this.modules_data_cache[module_name] = this.current_module_sections;
},
freeze: true,
});
}
},
process_data(module_name, data) {
frappe.module_links[module_name] = [];
data.forEach(function(section) {
section.items.forEach(function(item) {
item.style = '';
if(item.type==="doctype") {
item.doctype = item.name;
// map of doctypes that belong to a module
frappe.module_links[module_name].push(item.name);
}
if(!item.route) {
if(item.link) {
item.route=strip(item.link, "#");
}
else if(item.type==="doctype") {
if(frappe.model.is_single(item.doctype)) {
item.route = 'Form/' + item.doctype;
} else {
if (item.filters) {
frappe.route_options=item.filters;
}
item.route="List/" + item.doctype;
//item.style = 'font-weight: 500;';
}
// item.style = 'font-weight: bold;';
}
else if(item.type==="report" && item.is_query_report) {
item.route="query-report/" + item.name;
}
else if(item.type==="report") {
item.route="List/" + item.doctype + "/Report/" + item.name;
}
else if(item.type==="page") {
item.route=item.name;
}
item.route = '#' + item.route;
}
if(item.route_options) {
item.route += "?" + $.map(item.route_options, function(value, key) {
return encodeURIComponent(key) + "=" + encodeURIComponent(value); }).join('&');
}
if(item.type==="page" || item.type==="help" || item.type==="report" ||
(item.doctype && frappe.model.can_read(item.doctype))) {
item.shown = true;
}
});
});
}
}
}
</script>
<style lang="less" scoped>
.modules-page-container {
margin: 15px 0px;
}
</style>

View file

@ -0,0 +1,24 @@
import Modules from './components/Modules.vue';
frappe.provide('frappe.modules');
frappe.modules.Home = class {
constructor({ parent }) {
this.$parent = $(parent);
this.page = parent.page;
this.setup_header();
this.make_body();
}
make_body() {
this.$modules_container = this.$parent.find('.layout-main');
Vue.prototype.__ = window.__;
Vue.prototype.frappe = window.frappe;
new Vue({
el: this.$modules_container[0],
render: h => h(Modules)
});
}
setup_header() {
// subtitle
}
};

View file

@ -1,6 +1,8 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt
import Desktop from './components/Desktop.vue';
frappe.provide('frappe.views.pageview');
frappe.provide("frappe.standard_pages");
@ -39,6 +41,22 @@ frappe.views.pageview = {
show: function(name) {
if(!name) {
name = (frappe.boot ? frappe.boot.home_page : window.page_name);
if(name === "desktop") {
let page = frappe.container.add_page('desktop');
frappe.container.change_to('desktop');
let container = $('<div class="container"></div>').appendTo(page);
container = $('<div></div>').appendTo(container);
Vue.prototype.__ = window.__;
Vue.prototype.frappe = window.frappe;
new Vue({
el: container[0],
render: h => h(Desktop)
});
return;
}
}
frappe.model.with_doctype("Page", function() {
frappe.views.pageview.with_page(name, function(r) {
@ -82,7 +100,6 @@ frappe.views.Page = Class.extend({
}
this.trigger_page_event('on_page_load');
// set events
$(this.wrapper).on('show', function() {
window.cur_frm = null;
@ -144,3 +161,27 @@ frappe.show_message_page = function(opts) {
frappe.container.change_to(opts.page_name);
};
frappe.views.ModulesFactory = class ModulesFactory extends frappe.views.Factory {
show() {
if (frappe.pages.modules) {
frappe.container.change_to('modules');
} else {
this.make('modules');
}
}
make(page_name) {
const assets = [
'/assets/js/modules.min.js'
];
frappe.require(assets, () => {
frappe.modules.home = new frappe.modules.Home({
parent: this.make_page(true, page_name)
});
});
}
};

View file

@ -961,12 +961,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
}),
condition: () => frappe.model.can_set_user_permissions('Report'),
standard: true
},
{
label: __('Add to Desktop'),
action: () => frappe.add_to_desktop(this.report_name, null, this.report_name),
standard: true
},
}
];
}

View file

@ -1188,17 +1188,6 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
});
}
// add to desktop
items.push({
label: __('Add to Desktop'),
action: () => {
frappe.add_to_desktop(
this.report_name || __('{0} Report', [this.doctype]),
this.doctype, this.report_name
);
}
});
return items.map(i => Object.assign(i, { standard: true }));
}

View file

@ -107,11 +107,6 @@ frappe.views.ReportView = frappe.ui.BaseList.extend({
this.set_tag_and_status_filter();
this.setup_listview_settings();
// add to desktop
this.page.add_menu_item(__("Add to Desktop"), function() {
frappe.add_to_desktop(me.docname || __('{0} Report', [me.doctype]), me.doctype, me.docname);
}, true);
},
make_new_and_refresh: function() {

View file

@ -373,14 +373,6 @@ frappe.views.TreeView = Class.extend({
me.page.add_menu_item(menu_item["label"], menu_item["action"]);
}
});
// last menu item
me.page.add_menu_item(__('Add to Desktop'), () => {
const label = me.doctype === 'Account' ?
__('Chart of Accounts') :
__(me.doctype);
frappe.add_to_desktop(label, me.doctype);
});
}
});

View file

@ -3,11 +3,6 @@
@icon-color: @light-bg;
@icon-hover: #fff;
body[data-route=""] .navbar-default, body[data-route="desktop"] .navbar-default {
background-color: rgba(255, 255, 255, 0.9);
border-color: rgba(54, 65, 76, 0.1);
}
#page-desktop {
min-width: 100%;
margin-top: 0px;

View file

@ -116,8 +116,6 @@ def get_dict(fortype, name=None):
messages += frappe.db.sql("select 'DocType:', name from tabDocType")
messages += frappe.db.sql("select 'Role:', name from tabRole")
messages += frappe.db.sql("select 'Module:', name from `tabModule Def`")
messages += frappe.db.sql("select 'Module:', label from `tabDesktop Icon` where standard=1 or owner=%s",
frappe.session.user)
message_dict = make_dict_from_messages(messages)
message_dict.update(get_dict_from_hooks(fortype, name))

View file

@ -32,7 +32,7 @@ function watch_assets() {
case 'BUNDLE_START': {
const output = event.output[0];
if (output.endsWith('.js')) {
if (output.endsWith('.js', '.vue')) {
log('Rebuilding', path.basename(event.output[0]));
}
break;