[enhancement] desktop icons DocType

This commit is contained in:
Rushabh Mehta 2016-02-23 18:13:13 +05:30
parent 01e585482f
commit 28f038aa90
33 changed files with 918 additions and 182 deletions

View file

@ -34,22 +34,9 @@ def get_bootinfo():
bootinfo.modules = {}
bootinfo.module_list = []
for app in frappe.get_installed_apps(frappe_last=True):
try:
modules = frappe.get_attr(app + ".config.desktop.get_data")() or {}
if isinstance(modules, dict):
bootinfo.modules.update(modules)
else:
for m in modules:
bootinfo.modules[m['module_name']] = m
bootinfo.module_list.append(m['module_name'])
except ImportError:
pass
except AttributeError:
pass
load_desktop_icons(bootinfo)
bootinfo.module_app = frappe.local.module_app
bootinfo.hidden_modules = frappe.db.get_global("hidden_modules")
bootinfo.doctype_icons = dict(frappe.db.sql("""select name, icon from
tabDocType where ifnull(icon,'')!=''"""))
bootinfo.single_types = frappe.db.sql_list("""select name from tabDocType where issingle=1""")
@ -88,6 +75,10 @@ def load_conf_settings(bootinfo):
for key in ['developer_mode']:
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()
def get_allowed_pages():
roles = frappe.get_roles()
page_info = {}

View file

@ -41,5 +41,13 @@ def get_data():
"icon": "octicon octicon-circuit-board",
"type": "module",
"system_manager": 1
}
},
{
"module_name": "All Applications",
"label": _("All Applications"),
"color": "#4aa3df",
"icon": "octicon octicon-three-bars",
"type": "module",
"link": "javascript:frappe.desktop.all_applications.show()"
},
]

View file

@ -98,7 +98,7 @@ frappe.ModuleEditor = Class.extend({
},
make: function() {
var me = this;
$.each(keys(frappe.boot.modules), function(i, m) {
this.frm.doc.__onload.all_modules.forEach(function(m) {
// TODO: add checkbox
$(repl('<div class="col-sm-6"><div class="checkbox">\
<label><input type="checkbox" class="block-module-check" data-module="%(module)s">\

View file

@ -24,6 +24,11 @@ class User(Document):
self.email = self.email.strip()
self.name = self.email
def onload(self):
self.set_onload('all_modules',
[m.module_name for m in frappe.db.get_all('Desktop Icon',
fields=['module_name'], filters={'standard': 1})])
def validate(self):
self.in_insert = self.get("__islocal")
@ -343,6 +348,10 @@ class User(Document):
def username_exists(self, username=None):
return frappe.db.get_value("User", {"username": username or self.username, "name": ("!=", self.name)})
def get_blocked_modules(self):
"""Returns list of modules blocked for that user"""
return [d.module for d in self.block_modules] if self.block_modules else []
@frappe.whitelist()
def get_timezones():
import pytz

View file

@ -32,18 +32,15 @@ $.extend(frappe.desktop, {
var template = frappe.list_desktop ? "desktop_list_view" : "desktop_icon_grid";
this.wrapper.html(frappe.render_template(template, {
frappe.desktop.wrapper.html(frappe.render_template(template, {
// all visible icons
desktop_items: this.get_desktop_items(),
// user visible icons
user_desktop_items: this.get_user_desktop_items(),
desktop_items: frappe.desktop.get_desktop_items(),
}));
this.setup_module_click();
frappe.desktop.setup_module_click();
// notifications
this.show_pending_notifications();
frappe.desktop.show_pending_notifications();
$(document).on("notification-update", function() {
me.show_pending_notifications();
});
@ -51,76 +48,48 @@ $.extend(frappe.desktop, {
$(document).trigger("desktop-render");
},
get_desktop_items: function(global) {
var me = this;
frappe.modules["All Applications"] = {
icon: "octicon octicon-three-bars",
label: "All Applications",
_label: __("All Applications"),
_id: "all_applications",
color: "#4aa3df",
link: "",
force_show: true,
onclick: function() {
me.all_applications.show();
}
}
var desktop_items = [].concat(frappe.user.get_desktop_items(global));
remove_from_list(desktop_items, "Setup");
if(user_roles.indexOf('System Manager')!=-1) {
desktop_items.push('Setup');
}
remove_from_list(desktop_items, "Core");
if(user_roles.indexOf('Administrator')!=-1) {
desktop_items.push('Core');
}
remove_from_list(desktop_items, "All Applications");
desktop_items.push('All Applications');
return desktop_items;
},
get_user_desktop_items: function() {
var me = this;
var user_desktop_items = [].concat(frappe.user.get_user_desktop_items());
for (var m in frappe.modules) {
var module = frappe.modules[m];
if (module.force_show && user_desktop_items.indexOf(m)===-1) {
user_desktop_items.push(m);
}
}
get_desktop_items: function() {
// filter valid icons
var out = [];
for (var i=0, l=user_desktop_items.length; i < l; i++) {
var m = user_desktop_items[i];
var module = frappe.get_module(m);
if (module) {
module.app_icon = frappe.ui.app_icon.get_html(m);
out.push(m);
var add_to_out = function(module_name) {
var module = frappe.get_module(module_name);
if(module) {
module.app_icon = frappe.ui.app_icon.get_html(module);
out.push(module);
}
}
for (var i=0, l=frappe.boot.desktop_icons.length; i < l; i++) {
var m = frappe.boot.desktop_icons[i];
if ((['Setup', 'Core', 'All Applications'].indexOf(m.module_name) === -1)
&& frappe.boot.user.allow_modules.indexOf(m.module_name) !== -1
&& !m.hidden) {
add_to_out(m.module_name)
}
}
if(user_roles.indexOf('System Manager')!=-1) {
add_to_out('Setup')
}
if(user_roles.indexOf('Administrator')!=-1) {
add_to_out('Core')
}
add_to_out('All Applications');
return out;
},
setup_module_click: function() {
var me = this;
if(frappe.list_desktop) {
this.wrapper.on("click", ".desktop-list-item", function() {
me.open_module($(this));
frappe.desktop.wrapper.on("click", ".desktop-list-item", function() {
frappe.desktop.open_module($(this));
});
} else {
this.wrapper.on("click", ".app-icon", function() {
me.open_module($(this).parent());
frappe.desktop.wrapper.on("click", ".app-icon", function() {
frappe.desktop.open_module($(this).parent());
});
}
},
@ -128,7 +97,9 @@ $.extend(frappe.desktop, {
open_module: function(parent) {
var link = parent.attr("data-link");
if(link) {
if(link.substr(0, 1)==="/" || link.substr(0, 4)==="http") {
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);
@ -154,7 +125,14 @@ $.extend(frappe.desktop, {
$("#icon-grid .case-wrapper").each(function(i, e) {
new_order.push($(this).attr("data-name"));
});
frappe.defaults.set_default("_desktop_items", new_order);
frappe.call({
method: 'frappe.desk.doctype.desktop_icon.desktop_icon.set_order',
args: {
'new_order': new_order
},
quiet: true
});
}
});
},
@ -183,8 +161,8 @@ $.extend(frappe.desktop, {
$(frappe.render_template("all_applications_dialog", {
all_modules: keys(frappe.modules).sort(),
desktop_items: frappe.desktop.get_desktop_items(true),
user_desktop_items: frappe.desktop.get_user_desktop_items()
desktop_items: frappe.desktop.get_desktop_items(),
user_desktop_items: frappe.desktop.get_desktop_items()
})).appendTo(this.dialog_body);
this.bind_events();
@ -203,9 +181,6 @@ $.extend(frappe.desktop, {
this.dialog_body.find('input[type="checkbox"]').on("click", function() {
me.save_user_desktop_items();
frappe.user.modules = null;
frappe.after_ajax(function() {
frappe.desktop.refresh();
});

View file

@ -1,11 +1,7 @@
<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++) {
var module = frappe.get_module(desktop_items[i]);
if (!module || (user_desktop_items.indexOf(module.name)===-1 && !module.force_show)
|| frappe.user.is_module_blocked(module.name)) { continue; }
%}
{%= frappe.render_template("desktop_module_icon", module) %}
{% for (var i=0, l=desktop_items.length; i < l; i++) { %}
{{ frappe.render_template("desktop_module_icon", desktop_items[i]) }}
{% } %}
</div>
</div>

View file

@ -4,8 +4,7 @@
<div class="page-content desktop-list" style="margin-top: 40px;">
{% for (var i=0, l=desktop_items.length; i < l; i++) {
var module = frappe.get_module(desktop_items[i]);
if (!module || (user_desktop_items.indexOf(module.name)===-1 && !module.force_show)
|| frappe.user.is_module_blocked(module.name)) { continue; }
if (module) {
%}
<div class="desktop-list-item" id="module-icon-{%= module._id %}"
data-name="{%= module.name %}" data-link="{%= module.link %}"

View file

@ -1,11 +1,11 @@
<div class="case-wrapper"
data-name="{%= name %}" data-link="{%= link %}" title="{%= _label %}">
{%= app_icon %}
data-name="{{ module_name }}" data-link="{{ link }}" title="{{ _label }}">
{{ app_icon }}
<div class="case-label text-ellipsis">
<div class="circle module-count-{%= _id %}" style="display: none;">
<div class="circle module-count-{{ _id }}" 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>
<!-- <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

@ -171,6 +171,7 @@ def clear_default(key=None, value=None, parent=None, name=None, parenttype=None)
def get_defaults_for(parent="__default"):
"""get all defaults"""
defaults = frappe.cache().hget("defaults", parent)
if defaults==None:
# sort descending because first default must get precedence
res = frappe.db.sql("""select defkey, defvalue from `tabDefaultValue`

View file

@ -0,0 +1,8 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Desktop Icon', {
refresh: function(frm) {
}
});

View file

@ -0,0 +1,461 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"creation": "2016-02-22 03:47:45.387068",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "module_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Module Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "label",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Label",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "standard",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Standard",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "app",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "App",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "hidden",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Hidden",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "blocked",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Blocked",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "force_show",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Force Show",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "section_break_7",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "type",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Type",
"length": 0,
"no_copy": 0,
"options": "module\nlist\nlink",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "_doctype",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "_doctype",
"length": 0,
"no_copy": 0,
"options": "DocType",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "link",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Link",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_10",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "color",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Color",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "icon",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Icon",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "reverse",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "White Icon",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "idx",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Idx",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"in_create": 1,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-02-23 03:51:48.206216",
"modified_by": "Administrator",
"module": "Desk",
"name": "Desktop Icon",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"read_only": 1,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "module_name"
}

View file

@ -0,0 +1,158 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
import json
from frappe.model.document import Document
class DesktopIcon(Document):
def validate(self):
if not self.label:
self.label = self.module_name
def get_desktop_icons(user=None):
'''Return desktop icons for user'''
if not user:
user = frappe.session.user
user_icons = frappe.cache().hget('desktop_icons', user)
if not user_icons:
fields = ['module_name', 'hidden', 'label', 'link', 'type', 'icon', 'color',
'_doctype', 'idx', 'force_show', 'reverse']
standard_icons = frappe.db.get_all('Desktop Icon',
fields=fields, filters={'standard': 1})
standard_map = {}
for icon in standard_icons:
standard_map[icon.module_name] = icon
user_icons = frappe.db.get_all('Desktop Icon', fields=fields,
filters={'standard': 0, 'owner': user})
user_blocked_modules = frappe.get_doc('User', user).get_blocked_modules()
# update hidden property
for icon in user_icons:
standard_icon = standard_map[icon.module_name]
# override properties from standard icon
for key in ('hidden', 'route', 'label', 'color', 'icon'):
if standard_icon.get(key):
icon[key] = standard_icon.get(key)
if icon.module_name in user_blocked_modules:
icon.hidden = 1
if standard_icon.force_show:
icon.hidden = 0
# add missing standard icons (added via new install apps?)
user_icon_names = [icon.module_name for icon in user_icons]
for standard_icon in standard_icons:
if standard_icon.module_name not in user_icon_names:
user_icons.append(standard_icon)
# sort by idx
user_icons.sort(lambda a, b: 1 if a.idx > b.idx else -1)
frappe.cache().hset('desktop_icons', user, user_icons)
return user_icons
def after_doctype_insert():
frappe.db.add_unique('Desktop Icon', ('module_name', 'owner', 'standard'))
@frappe.whitelist()
def set_order(new_order):
'''set new order by duplicating user icons'''
new_order = json.loads(new_order)
for i, module_name in enumerate(new_order):
icon = get_user_copy(module_name, frappe.session.user)
icon.db_set('idx', i)
clear_desktop_icons_cache()
def clear_desktop_icons_cache():
frappe.cache().hdel('desktop_icons', frappe.session.user)
frappe.cache().hdel('bootinfo', frappe.session.user)
def get_user_copy(module_name, app=None, user=None):
'''Return user copy (Desktop Icon) of the given module_name. If user copy does not exist, create one.
:param module_name: Name of the module
:param user: User for which the copy is required (optional)
'''
if not user:
user = frappe.session.user
original_name = frappe.db.get_value('Desktop Icon', {'module_name': module_name, 'standard': 1})
if not original_name:
frappe.throw('{0} not found'.format(module_name))
original = frappe.get_doc('Desktop Icon', original_name)
existing_copy = frappe.db.get_value('Desktop Icon',
{'module_name': module_name, 'owner': user, 'standard': 0})
if existing_copy:
desktop_icon = frappe.get_doc('Desktop Icon', existing_copy)
else:
desktop_icon = frappe.get_doc({
'doctype': 'Desktop Icon',
'standard': 0,
'owner': user,
'module_name': module_name
})
for key in ('app', 'label', 'route', 'type', '_doctype', 'idx', 'reverse', 'force_show'):
if original.get(key):
desktop_icon.set(key, original.get(key))
desktop_icon.insert()
return desktop_icon
def sync_from_app(app):
'''Sync desktop icons from app. To be called during install'''
try:
modules = frappe.get_attr(app + '.config.desktop.get_data')() or {}
except ImportError:
return []
if isinstance(modules, dict):
modules_list = []
for m, desktop_icon in modules.iteritems():
desktop_icon['module_name'] = m
modules_list.append(desktop_icon)
else:
modules_list = modules
for i, m in enumerate(modules_list):
desktop_icon_name = frappe.db.get_value('Desktop Icon',
{'module_name': m['module_name'], 'app': app, 'standard': 1})
if desktop_icon_name:
desktop_icon = frappe.get_doc('Desktop Icon', desktop_icon_name)
else:
# new icon
desktop_icon = frappe.get_doc({
'doctype': 'Desktop Icon',
'idx': i,
'standard': 1,
'app': app,
'owner': 'Administrator'
})
if 'doctype' in m:
m['_doctype'] = m.pop('doctype')
desktop_icon.update(m)
desktop_icon.save()
return modules_list

View file

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
# test_records = frappe.get_test_records('Desktop Icon')
class TestDesktopIcon(unittest.TestCase):
pass

View file

@ -138,7 +138,7 @@ frappe.applications.Installer = Class.extend({
is_app: true
};
app.app_icon = frappe.ui.app_icon.get_html(app_key, null, modules);
app.app_icon = frappe.ui.app_icon.get_html(modules[app_key]);
$(frappe.render_template("application_row", {app: app})).appendTo(me.wrapper);
});

View file

@ -0,0 +1,7 @@
frappe.pages['configure-desktop'].on_page_load = function(wrapper) {
var page = frappe.ui.make_app_page({
parent: wrapper,
title: 'Configure Desktop',
single_column: true
});
}

View file

@ -0,0 +1,18 @@
{
"content": null,
"creation": "2016-02-23 01:51:24.715828",
"docstatus": 0,
"doctype": "Page",
"idx": 0,
"modified": "2016-02-23 01:51:24.715828",
"modified_by": "Administrator",
"module": "Desk",
"name": "configure-desktop",
"owner": "Administrator",
"page_name": "configure-desktop",
"roles": [],
"script": null,
"standard": "Yes",
"style": null,
"title": "Configure Desktop"
}

View file

View file

@ -0,0 +1,39 @@
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;
page.wrapper.find('.page-head').toggle(false);
page.wrapper.find('.page-content').css({'margin-top': '0px'});
// render sidebar
page.sidebar.html(frappe.render_template('modules_sidebar', {modules: frappe.get_desktop_icons()}));
page.wrapper.find('.module-link').on('click', function() {
render_section($(this).attr('data-name'));
});
var render_section = function(module_name) {
return frappe.call({
method: "frappe.desk.moduleview.get",
args: {
module: module_name
},
callback: function(r) {
m = frappe.get_module(module_name);
m.data = r.message.data;
console.log(m);
page.main.html(frappe.render_template('modules_section', m));
},
freeze: true,
});
}
}

View file

@ -0,0 +1,18 @@
{
"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

@ -0,0 +1,36 @@
<div class="container">
<div class="row">
<div class="col-md-12">
<h2>{{ label }}</h2>
{% for (var i=0; i < data.length; i++) { var section = data[i]; %}
<div class="module-section">
<h4>{{ section.label }}</h4>
{% for (var j=0; j < section.items.length; j++) {
var item = section.items[j];
if(item.type==="doctype") {
item.doctype = item.name;
}
if(item.doctype && frappe.model.can_read(item.doctype)) {
if(item.link) {
item.route=strip(item.link, "#")
}
else if(item.type==="doctype") {
item.route="List/" + item.doctype
}
else if(item.type==="report" && item.is_query_report) {
item.route="query-report/" + item.name
}
else if(item.type==="report") {
item.route="Report/" + item.doctype + "/" + item.name
}
else if(item.type==="page") {
item.route=item.name;
}
%}
<p><a href="#{{ item.route }}">{{ item.label }}</a></p>
{% } %}
{% } %}
{% } %}
</div>
</div>
</div>

View file

@ -0,0 +1,10 @@
<ul class="module-sidebar-nav nav nav-pills nav-stacked">
{% for (var i=0, l= modules.length; i < l; i++) { var item = modules[i]; %}
<li data-label="{{ item.label }}" class="strong module-sidebar-item">
<a class="module-link" data-name="{{ item.module_name }}">
<i class="icon icon-chevron-right pull-right"
style="display: none;"></i>
<span>{{ item.label }}</span></a>
</li>
{% } %}
</ul>

View file

@ -15,6 +15,7 @@ from frappe.model.db_schema import DbManager
from frappe.model.sync import sync_for
from frappe.utils.fixtures import sync_fixtures
from frappe.website import render, statics
from frappe.desk.doctype.desktop_icon.desktop_icon import sync_from_app
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):
@ -123,6 +124,7 @@ def install_app(name, verbose=False, set_as_patched=True):
sync_for(name, force=True, sync_everything=True, verbose=verbose)
sync_from_app(name)
add_to_installed_apps(name)
if set_as_patched:
@ -186,12 +188,17 @@ def remove_app(app_name, dry_run=False):
remove_from_installed_apps(app_name)
<<<<<<< HEAD
if not dry_run:
# drop tables after a commit
frappe.db.commit()
for doctype in set(drop_doctypes):
frappe.db.sql("drop table `tab{0}`".format(doctype))
=======
# delete desktop icons
frappe.db.sql('delete from `tabDesktop Icon` where app=%s', app_name)
>>>>>>> [enhancement] desktop icons DocType
def post_install(rebuild_website=False):
if rebuild_website:

View file

@ -119,3 +119,4 @@ 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
frappe.patches.v6_24.sync_desktop_icons

View file

@ -0,0 +1,35 @@
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):
desktop_icon = frappe.get_doc('Desktop Icon', {'module_name': m, 'standard': 1, 'app': app})
desktop_icon.db_set('hidden', 1)
# 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):
desktop_icon = get_user_copy(module_name, user=user.name)
desktop_icon.db_set('idx', i)
# set remaining icons as hidden
for module_name in list(set([m['module_name'] for m in modules_list]) - set(user_list)):
print user, module_name
desktop_icon = get_user_copy(module_name, user=user.name)
desktop_icon.db_set('hidden', 1)

View file

@ -82,7 +82,8 @@ frappe.Application = Class.extend({
load_bootinfo: function() {
if(frappe.boot) {
frappe.modules = frappe.boot.modules;
frappe.modules = {};
frappe.boot.desktop_icons.forEach(function(m) { frappe.modules[m.module_name]=m; });
frappe.model.sync(frappe.boot.docs);
$.extend(frappe._messages, frappe.boot.__messages);
this.check_metadata_cache_status();
@ -96,8 +97,6 @@ frappe.Application = Class.extend({
if(frappe.boot.print_css) {
frappe.dom.set_style(frappe.boot.print_css)
}
// setup valid modules
frappe.user.get_user_desktop_items()
} else {
this.set_as_guest();
}
@ -322,10 +321,12 @@ frappe.get_module = function(m) {
return;
}
module.name = m;
if(module.type==="module" && !module.link) {
module.link = "Module/" + m;
module.link = "Module/" + module.module_name;
}
if(module.type==="list" && !module.link) {
module.link = "List/" + module._doctype;
}
if (!module.link) module.link = "";
@ -340,7 +341,7 @@ frappe.get_module = function(m) {
}
if(!module._label) {
module._label = __(module.label || module.name);
module._label = __(module.label);
}
return module;

View file

@ -101,43 +101,14 @@ $.extend(frappe.user, {
return true;
}
},
get_desktop_items: function(global) {
// get user sequence preference
var modules_list = [];
if(!global) {
var user_list = frappe.defaults.get_default("_desktop_items");
if(user_list && user_list.length)
var modules_list = user_list;
}
frappe.boot.module_list.forEach(function(i, m) {
if(modules_list.indexOf(m)===-1) {
modules_list.push(m);
}
});
if(modules_list) {
// add missing modules - they will be hidden anyways by the view
$.each(frappe.modules, function(m, module) {
var module = frappe.get_module(m);
if(modules_list.indexOf(m)===-1) {
modules_list.push(m);
}
});
}
// filter hidden modules
if(frappe.boot.hidden_modules && modules_list) {
var hidden_list = JSON.parse(frappe.boot.hidden_modules);
var modules_list = $.map(modules_list, function(m) {
if(hidden_list.indexOf(m)==-1 || (frappe.modules[m] && frappe.modules[m].force_show)) return m; else return null;
});
}
get_desktop_items: function() {
// hide based on permission
modules_list = $.map(modules_list, function(m) {
modules_list = $.map(frappe.boot.desktop_icons, function(icon) {
var m = icon.module_name;
var type = frappe.modules[m] && frappe.modules[m].type;
if(frappe.boot.user.allow_modules.indexOf(m) === -1) return null;
var ret = null;
switch(type) {
case "module":
@ -149,7 +120,7 @@ $.extend(frappe.user, {
ret = m;
break;
case "list":
if(frappe.model.can_read(frappe.modules[m].doctype))
if(frappe.model.can_read(frappe.modules[m]._doctype))
ret = m;
break;
case "view":
@ -168,29 +139,14 @@ $.extend(frappe.user, {
return modules_list;
},
get_user_desktop_items: function() {
if(!frappe.user.modules) {
var user_list = frappe.defaults.get_default("_user_desktop_items");
if(!user_list) {
user_list = frappe.user.get_desktop_items();
}
// filter_blocked_modules
user_list = $.map(user_list, function(m) {
if(frappe.user.is_module_blocked(m)) {
return null;
} else {
return m;
}
});
is_module: function(m) {
frappe.boot.desktop_icons.forEach(function(module) {
if(m===module.module_name) return true;
});
return false;
},
frappe.user.modules = user_list;
}
return frappe.user.modules;
},
is_module_blocked: function(m) {
return frappe.boot.user.block_modules && frappe.boot.user.block_modules.indexOf(m)!==-1;
},
is_report_manager: function() {
return frappe.user.has_role(['Administrator', 'System Manager', 'Report Manager']);
},

View file

@ -4,12 +4,7 @@
frappe.provide("frappe.ui")
frappe.ui.app_icon = {
get_html: function(app, small, modules) {
if(!modules) {
modules = frappe.modules;
}
var module = modules[app]
get_html: function(module, small) {
var icon = module.icon;
var color = module.color;
var icon_style = "";

View file

@ -44,7 +44,7 @@ frappe.breadcrumbs = {
if(in_list(["Core", "Email", "Custom", "Workflow", "Print"], breadcrumbs.module))
breadcrumbs.module = "Setup";
if(frappe.user.modules.indexOf(breadcrumbs.module)!==-1) {
if(frappe.user.is_module(breadcrumbs.module)) {
// if module access exists
var module_info = frappe.get_module(breadcrumbs.module),
icon = module_info && module_info.icon,
@ -59,7 +59,7 @@ frappe.breadcrumbs = {
}
if(breadcrumbs.doctype && frappe.get_route()[0]==="Form") {
if(breadcrumbs.doctype==="User" && frappe.user.modules.indexOf("Setup")===-1) {
if(breadcrumbs.doctype==="User" && frappe.user.is_module("Setup")===-1) {
// no user listview for non-system managers
} else {
route = (cur_frm && cur_frm.list_route) || ("List/" + breadcrumbs.doctype)

View file

@ -31,7 +31,7 @@ def clear_cache(user=None):
cache = frappe.cache()
groups = ("bootinfo", "user_recent", "user_roles", "user_doc", "lang",
"defaults", "user_permissions", "roles", "home_page", "linked_with")
"defaults", "user_permissions", "roles", "home_page", "linked_with", "desktop_icons")
if user:
for name in groups:

View file

@ -2,7 +2,7 @@
# MIT License. See license.txt
from __future__ import unicode_literals
import redis, frappe, re
import redis, frappe, re, copy
import cPickle as pickle
from frappe.utils import cstr
@ -130,7 +130,7 @@ class RedisWrapper(redis.Redis):
if not name in frappe.local.cache:
frappe.local.cache[name] = {}
if key in frappe.local.cache[name]:
return frappe.local.cache[name][key]
return copy.deepcopy(frappe.local.cache[name][key])
value = None
try:

View file

@ -57,10 +57,6 @@ class User:
self.roles = get_roles(self.name)
return self.roles
def get_block_modules(self):
"""Returns list of blocked modules"""
return [d.module for d in self.doc.block_modules] if self.doc.block_modules else []
def build_doctype_map(self):
"""build map of special doctype properties"""
@ -200,7 +196,6 @@ class User:
d.roles = self.get_roles()
d.defaults = self.get_defaults()
d.block_modules = self.get_block_modules()
for key in ("can_create", "can_write", "can_read", "can_cancel", "can_delete",
"can_get_report", "allow_modules", "all_read", "can_search",