@@ -244,30 +244,24 @@ def validate_permissions(permissions, for_remove=False): | |||||
webnotes.msgprint(get_txt(d) + " Higher level permissions are meaningless if level 0 permission is not set.", | webnotes.msgprint(get_txt(d) + " Higher level permissions are meaningless if level 0 permission is not set.", | ||||
raise_exception=True) | raise_exception=True) | ||||
if d.create: | |||||
webnotes.msgprint("Create Permission has no meaning at level " + d.permlevel, | |||||
raise_exception=True) | |||||
if d.submit: | |||||
webnotes.msgprint("Submit Permission has no meaning at level " + d.permlevel, | |||||
raise_exception=True) | |||||
if d.cancel: | |||||
webnotes.msgprint("Cancel Permission has no meaning at level " + d.permlevel, | |||||
raise_exception=True) | |||||
if d.amend: | |||||
webnotes.msgprint("Amend Permission has no meaning at level " + d.permlevel, | |||||
raise_exception=True) | |||||
if d.match: | |||||
webnotes.msgprint("Match rules have no meaning at level " + d.permlevel, | |||||
if d.create or d.submit or d.cancel or d.amend or d.match: | |||||
webnotes.msgprint("Create, Submit, Cancel, Amend, Match has no meaning at level " + d.permlevel, | |||||
raise_exception=True) | raise_exception=True) | ||||
def check_permission_dependency(d): | |||||
if d.write and not d.read: | |||||
webnotes.msgprint(get_txt(d) + " Cannot set Write permission if Read is not set.", | |||||
raise_exception=True) | |||||
if (d.submit or d.cancel or d.amend) and not d.write: | |||||
webnotes.msgprint(get_txt(d) + " Cannot set Submit, Cancel, Amend permission if Write is not set.", | |||||
raise_exception=True) | |||||
for d in permissions: | for d in permissions: | ||||
if not d.permlevel: d.permlevel=0 | |||||
if not d.permlevel: | |||||
d.permlevel=0 | |||||
check_atleast_one_set(d) | check_atleast_one_set(d) | ||||
if not for_remove: | if not for_remove: | ||||
check_double(d) | check_double(d) | ||||
check_level_zero_is_set(d) | check_level_zero_is_set(d) | ||||
check_permission_dependency(d) | |||||
@@ -22,18 +22,22 @@ cur_frm.cscript.refresh = function(doc) { | |||||
// update display settings | // update display settings | ||||
wn.ui.set_theme(doc.theme); | wn.ui.set_theme(doc.theme); | ||||
if(doc.background_image) { | if(doc.background_image) { | ||||
wn.ui.set_user_background(doc.background_image); | |||||
wn.ui.set_user_background(doc.background_image); | |||||
} | } | ||||
if(doc.user_image) { | if(doc.user_image) { | ||||
wn.boot.user_info[user].image = wn.utils.get_file_link(doc.user_image); | wn.boot.user_info[user].image = wn.utils.get_file_link(doc.user_image); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
cur_frm.add_custom_button("Set Properties", function() { | |||||
wn.set_route("user-properties", doc.name); | |||||
}) | |||||
} | } | ||||
cur_frm.cscript.enabled = function(doc) { | cur_frm.cscript.enabled = function(doc) { | ||||
if(!doc.__islocal) { | if(!doc.__islocal) { | ||||
cur_frm.toggle_display(['sb1', 'sb2', 'sb3'], doc.enabled); | |||||
cur_frm.toggle_display(['sb1', 'sb2'], doc.enabled); | |||||
cur_frm.toggle_enable('*', doc.enabled); | cur_frm.toggle_enable('*', doc.enabled); | ||||
cur_frm.set_df_property('enabled', 'disabled', false); | cur_frm.set_df_property('enabled', 'disabled', false); | ||||
} | } | ||||
@@ -4,7 +4,7 @@ | |||||
"docstatus": 0, | "docstatus": 0, | ||||
"creation": "2012-12-07 15:15:20", | "creation": "2012-12-07 15:15:20", | ||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"modified": "2012-12-21 12:35:01" | |||||
"modified": "2013-01-02 14:47:27" | |||||
}, | }, | ||||
{ | { | ||||
"istable": 0, | "istable": 0, | ||||
@@ -111,6 +111,7 @@ | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | }, | ||||
{ | { | ||||
"print_width": "50%", | |||||
"oldfieldtype": "Column Break", | "oldfieldtype": "Column Break", | ||||
"doctype": "DocField", | "doctype": "DocField", | ||||
"width": "50%", | "width": "50%", | ||||
@@ -297,12 +298,14 @@ | |||||
}, | }, | ||||
{ | { | ||||
"description": "These values will be automatically updated in transactions and also will be useful to restrict permissions for this user on transactions containing these values.", | "description": "These values will be automatically updated in transactions and also will be useful to restrict permissions for this user on transactions containing these values.", | ||||
"print_width": "50%", | |||||
"oldfieldtype": "Column Break", | "oldfieldtype": "Column Break", | ||||
"doctype": "DocField", | "doctype": "DocField", | ||||
"label": "Defaults", | "label": "Defaults", | ||||
"width": "50%", | "width": "50%", | ||||
"fieldname": "sb2", | "fieldname": "sb2", | ||||
"fieldtype": "Section Break", | "fieldtype": "Section Break", | ||||
"hidden": 1, | |||||
"permlevel": 1 | "permlevel": 1 | ||||
}, | }, | ||||
{ | { | ||||
@@ -312,6 +315,7 @@ | |||||
"options": "DefaultValue", | "options": "DefaultValue", | ||||
"fieldname": "defaults", | "fieldname": "defaults", | ||||
"fieldtype": "Table", | "fieldtype": "Table", | ||||
"hidden": 1, | |||||
"permlevel": 0 | "permlevel": 0 | ||||
}, | }, | ||||
{ | { | ||||
@@ -347,6 +351,7 @@ | |||||
"permlevel": 1 | "permlevel": 1 | ||||
}, | }, | ||||
{ | { | ||||
"print_width": "50%", | |||||
"oldfieldtype": "Column Break", | "oldfieldtype": "Column Break", | ||||
"doctype": "DocField", | "doctype": "DocField", | ||||
"width": "50%", | "width": "50%", | ||||
@@ -409,13 +414,21 @@ | |||||
"match": "owner" | "match": "owner" | ||||
}, | }, | ||||
{ | { | ||||
"amend": 0, | |||||
"create": 0, | |||||
"doctype": "DocPerm", | "doctype": "DocPerm", | ||||
"submit": 0, | |||||
"role": "Administrator", | "role": "Administrator", | ||||
"cancel": 0, | |||||
"permlevel": 1 | "permlevel": 1 | ||||
}, | }, | ||||
{ | { | ||||
"amend": 0, | |||||
"create": 0, | |||||
"doctype": "DocPerm", | "doctype": "DocPerm", | ||||
"submit": 0, | |||||
"role": "System Manager", | "role": "System Manager", | ||||
"cancel": 0, | |||||
"permlevel": 1 | "permlevel": 1 | ||||
} | } | ||||
] | ] |
@@ -2,45 +2,41 @@ | |||||
{ | { | ||||
"owner": "Administrator", | "owner": "Administrator", | ||||
"docstatus": 0, | "docstatus": 0, | ||||
"creation": "2012-07-03 13:30:34", | |||||
"creation": "2012-07-13 16:53:46", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"modified": "2012-07-13 12:25:07" | |||||
"modified": "2013-01-02 12:46:21" | |||||
}, | }, | ||||
{ | { | ||||
"section_style": "Simple", | |||||
"istable": 0, | |||||
"istable": 1, | |||||
"allow_print": 0, | "allow_print": 0, | ||||
"module": "Core", | "module": "Core", | ||||
"server_code_error": " ", | |||||
"issingle": 0, | |||||
"autoname": "UR.#####", | |||||
"read_only": 0, | "read_only": 0, | ||||
"allow_email": 0, | "allow_email": 0, | ||||
"hide_heading": 0, | "hide_heading": 0, | ||||
"autoname": "UR.#####", | |||||
"issingle": 0, | |||||
"name": "__common__", | "name": "__common__", | ||||
"colour": "White:FFF", | |||||
"doctype": "DocType", | "doctype": "DocType", | ||||
"show_in_menu": 0, | |||||
"version": 1, | |||||
"hide_toolbar": 0, | "hide_toolbar": 0, | ||||
"allow_copy": 0 | "allow_copy": 0 | ||||
}, | }, | ||||
{ | { | ||||
"parent": "UserRole", | "parent": "UserRole", | ||||
"permlevel": 0, | |||||
"print_width": "200px", | |||||
"oldfieldtype": "Link", | "oldfieldtype": "Link", | ||||
"doctype": "DocField", | |||||
"oldfieldname": "role", | "oldfieldname": "role", | ||||
"reqd": 0, | "reqd": 0, | ||||
"name": "__common__", | "name": "__common__", | ||||
"doctype": "DocField", | |||||
"label": "Role", | "label": "Role", | ||||
"width": "200px", | "width": "200px", | ||||
"parenttype": "DocType", | "parenttype": "DocType", | ||||
"options": "Role", | |||||
"fieldname": "role", | "fieldname": "role", | ||||
"fieldtype": "Link", | "fieldtype": "Link", | ||||
"search_index": 0, | "search_index": 0, | ||||
"hidden": 0, | "hidden": 0, | ||||
"options": "Role", | |||||
"permlevel": 0, | |||||
"parentfield": "fields" | "parentfield": "fields" | ||||
}, | }, | ||||
{ | { | ||||
@@ -4,6 +4,69 @@ wn.pages['permission-manager'].onload = function(wrapper) { | |||||
title: 'Permission Manager', | title: 'Permission Manager', | ||||
single_column: true | single_column: true | ||||
}); | }); | ||||
$(wrapper).find(".layout-main").html("<div class='perm-engine'></div>\ | |||||
<table class='table table-bordered' style='background-color: #f9f9f9;'>\ | |||||
<tr><td>\ | |||||
<h4><i class='icon-question-sign'></i> Quick Help for Setting Permissions:</h4>\ | |||||
<ol>\ | |||||
<li>Permissions are set on Roles and Document Types (called DocTypes) by restricting \ | |||||
read, write, create, submit, cancel and amend rights.</li>\ | |||||
<li>Permissions translate to Users based on what Role they are assigned.</li>\ | |||||
<li>To set user roles, just go to <a href='#List/Profile'>Setup > Users</a> \ | |||||
and click on the user to assign roles.</li>\ | |||||
<li>The system provides pre-defined roles, but you can <a href='#List/Role'>add new roles</a>\ | |||||
to set finer permissions.</li>\ | |||||
<li>Permissions are automatically translated to Standard Reports and Searches.</li>\ | |||||
<li>As a best practice, do not assign the same set of permission rule to different Roles\ | |||||
instead set multiple Roles to the User.</li>\ | |||||
</ol>\ | |||||
</tr></td>\ | |||||
<tr><td>\ | |||||
<h4><i class='icon-hand-right'></i> Meaning of Submit, Cancel, Amend:</h4>\ | |||||
<ol>\ | |||||
<li>Certain documents should not be changed once final, like an\ | |||||
Invoice for example. The final state for such documents is called <b>Submitted</b>.\ | |||||
You can restrict which roles can Submit.</li>\ | |||||
<li><b>Cancel</b> allows you change Submitted documents by cancelling them and amending them.\ | |||||
Cancel permissions also allows the user to delete a document (if it is not linked to any other document).</li>\ | |||||
<li>When you <b>Amend</b> a document after cancel and save it, it will get a new number that is\ | |||||
a version of the old number. For example if you cancel and amend 'INV004' it will become a new\ | |||||
document 'INV004-1'. This helps you to keep track of each amendment.</li>\ | |||||
</ol>\ | |||||
</tr></td>\ | |||||
<tr><td>\ | |||||
<h4><i class='icon-user'></i> Restricting By User:</h4>\ | |||||
<ol>\ | |||||
<li>To restrict a User of a particular Role to documents that are only self-created,\ | |||||
Click on button in the 'Condition' column and select the option 'User is the creator of the document'.</li>\ | |||||
<li>To restrict a User of a particular Role to documents that are explicitly assigned to them,\ | |||||
create a Custom Field of type Link (Profile) and then use the 'Condition' settings\ | |||||
to map that field to the Permission rule.\ | |||||
</ol>\ | |||||
</tr></td>\ | |||||
<tr><td>\ | |||||
<h4><i class='icon-cog'></i> Advanced Settings:</h4>\ | |||||
<p>To further restrict permissions based on certain values in a document, use the\ | |||||
'Condition' settings. <br><br>For example: You want to restrict users to transactions marked\ | |||||
with a certain property called 'Territory':</p>\ | |||||
<ol>\ | |||||
<li>Make sure that the transactions you want to restrict have a Link \ | |||||
field 'territory' that maps to a 'Territory' master. If not, create a\ | |||||
<a href='#List/Custom Field'>Custom Field</a> of type Link.</li>\ | |||||
<li>In the Permission Manager, click on the button in the 'Condition' column\ | |||||
for the Role you want to restrict.</li>\ | |||||
<li>A new pop will open that will ask you to select further conditions. \ | |||||
If the 'territory' Link Field exists, it will give you an option to select \ | |||||
it.</li>\ | |||||
<li>Go to Setup > <a href='#user-properties'>User Properties</a> to set \ | |||||
'territory' for diffent Users.</li>\ | |||||
</ol>\ | |||||
<p>Once you have set this, the users will only be able access documents with that property</p>\ | |||||
<hr>\ | |||||
<p>If these instructions where not helpful, please add in your suggestions at\ | |||||
<a href='https://github.com/webnotes/wnframework/issues'>GitHub Issues</a></p>\ | |||||
</tr></td>\ | |||||
</table>"); | |||||
wrapper.permission_engine = new wn.PermissionEngine(wrapper); | wrapper.permission_engine = new wn.PermissionEngine(wrapper); | ||||
} | } | ||||
wn.pages['permission-manager'].refresh = function(wrapper) { | wn.pages['permission-manager'].refresh = function(wrapper) { | ||||
@@ -13,7 +76,7 @@ wn.pages['permission-manager'].refresh = function(wrapper) { | |||||
wn.PermissionEngine = Class.extend({ | wn.PermissionEngine = Class.extend({ | ||||
init: function(wrapper) { | init: function(wrapper) { | ||||
this.wrapper = wrapper; | this.wrapper = wrapper; | ||||
this.body = $(this.wrapper).find(".layout-main"); | |||||
this.body = $(this.wrapper).find(".perm-engine"); | |||||
this.make(); | this.make(); | ||||
this.refresh(); | this.refresh(); | ||||
this.add_check_events(); | this.add_check_events(); | ||||
@@ -68,7 +131,7 @@ wn.PermissionEngine = Class.extend({ | |||||
callback: function() { me.refresh(); } | callback: function() { me.refresh(); } | ||||
}); | }); | ||||
})); | })); | ||||
}, 'icon-retweet'); | |||||
}, 'icon-retweet').toggle(false); | |||||
}, | }, | ||||
get_doctype: function() { | get_doctype: function() { | ||||
var doctype = this.doctype_select.val(); | var doctype = this.doctype_select.val(); | ||||
@@ -112,7 +175,6 @@ wn.PermissionEngine = Class.extend({ | |||||
this.show_permission_table(perm_list); | this.show_permission_table(perm_list); | ||||
} | } | ||||
this.show_add_rule(); | this.show_add_rule(); | ||||
this.show_explain(); | |||||
}, | }, | ||||
show_permission_table: function(perm_list) { | show_permission_table: function(perm_list) { | ||||
var me = this; | var me = this; | ||||
@@ -152,7 +214,8 @@ wn.PermissionEngine = Class.extend({ | |||||
if(!d.permlevel) d.permlevel = 0; | if(!d.permlevel) d.permlevel = 0; | ||||
var row = $("<tr>").appendTo(me.table.find("tbody")); | var row = $("<tr>").appendTo(me.table.find("tbody")); | ||||
add_cell(row, d, "parent"); | add_cell(row, d, "parent"); | ||||
add_cell(row, d, "role"); | |||||
me.set_show_users(add_cell(row, d, "role"), d.role); | |||||
var cell = add_cell(row, d, "permlevel"); | var cell = add_cell(row, d, "permlevel"); | ||||
if(d.permlevel==0) { | if(d.permlevel==0) { | ||||
cell.css("font-weight", "bold"); | cell.css("font-weight", "bold"); | ||||
@@ -170,6 +233,30 @@ wn.PermissionEngine = Class.extend({ | |||||
me.add_delete_button(row, d); | me.add_delete_button(row, d); | ||||
}); | }); | ||||
}, | }, | ||||
set_show_users: function(cell, role) { | |||||
cell.html("<a href='#'>"+role+"</a>") | |||||
.find("a") | |||||
.attr("data-role", role) | |||||
.click(function() { | |||||
var role = $(this).attr("data-role"); | |||||
wn.call({ | |||||
module: "core", | |||||
page: "permission_manager", | |||||
method: "get_users_with_role", | |||||
args: { | |||||
role: role | |||||
}, | |||||
callback: function(r) { | |||||
r.message = $.map(r.message, function(p) { | |||||
return '<a href="#Form/Profile/'+p+'">'+p+'</a>'; | |||||
}) | |||||
msgprint("<h4>Users with role "+role+":</h4>" | |||||
+ r.message.join("<br>")); | |||||
} | |||||
}) | |||||
return false; | |||||
}) | |||||
}, | |||||
add_match_button: function(row, d) { | add_match_button: function(row, d) { | ||||
var me = this; | var me = this; | ||||
if(d.permlevel > 0) { | if(d.permlevel > 0) { | ||||
@@ -231,7 +318,6 @@ wn.PermissionEngine = Class.extend({ | |||||
chk.attr("checked", chk.is(":checked") ? null : "checked"); | chk.attr("checked", chk.is(":checked") ? null : "checked"); | ||||
} else { | } else { | ||||
me.get_perm(args.name)[args.ptype]=args.value; | me.get_perm(args.name)[args.ptype]=args.value; | ||||
me.show_explain(); | |||||
} | } | ||||
} | } | ||||
}) | }) | ||||
@@ -367,79 +453,5 @@ wn.PermissionEngine = Class.extend({ | |||||
get_link_fields: function(doctype) { | get_link_fields: function(doctype) { | ||||
return link_fields = wn.model.get("DocField", {parent:doctype, | return link_fields = wn.model.get("DocField", {parent:doctype, | ||||
fieldtype:"Link", options:["not in", ["Profile", '[Select]']]}); | fieldtype:"Link", options:["not in", ["Profile", '[Select]']]}); | ||||
}, | |||||
show_explain: function() { | |||||
$(".perm-explain").remove(); | |||||
if(!this.get_doctype()) return; | |||||
var wrapper = $("<div class='perm-explain well'></div>").appendTo(this.body); | |||||
var doctype = null; | |||||
var core_finished = false; | |||||
$.each(this.perm_list, function(i, p) { | |||||
if(p.parent != doctype) { | |||||
core_finished = false; | |||||
doctype = p.parent; | |||||
$('<h3>For ' + doctype + '</h3><h4>Document Permissions</h4>') | |||||
.appendTo(wrapper); | |||||
} | |||||
if(p.permlevel==0) { | |||||
var perms = $.map(["read", "write", "create", "submit", "cancel", "amend"], function(type) { | |||||
if(p[type]) return type; | |||||
}).join(", "); | |||||
if(!p.match) { | |||||
var _p = $('<p>').html("A user with role <b>"+p.role + "</b> can " | |||||
+ perms + " a document of type <b>" + doctype + "</b>.") | |||||
} else { | |||||
if(p.match=="owner") { | |||||
var _p = $('<p>').html("A user with role <b>"+p.role + "</b> can " | |||||
+ perms + " <b>" + doctype + "</b> <u>only if</u> that document is created by that user."); | |||||
} else if(p.match.indexOf(":")!=-1) { | |||||
var field = p.match.split(":")[0]; | |||||
var _p = $('<p>').html("A user with role <b>"+p.role + "</b> can " | |||||
+ perms + " <b>" + doctype + "</b> <u>only if</u> <b>"+field+"</b> equals User's Id."); | |||||
} else { | |||||
var _p = $('<p>').html("A user with role <b>"+p.role + "</b> can " | |||||
+ perms + " <b>" + doctype + "</b> <u>only for</u> records with user's <b>"+p.match+"</b> property."); | |||||
} | |||||
} | |||||
} else { | |||||
if(!core_finished) { | |||||
core_finished = true; | |||||
$("<br><h4>Field Level Permissions</h4>").appendTo(wrapper); | |||||
} | |||||
var perms = $.map(["read", "write"], function(type) { | |||||
if(p[type]) return type; | |||||
}).join(", "); | |||||
var _p = $('<p>').html("A user with role <b>"+p.role + "</b> can only <u>" | |||||
+ perms + "</u> fields at level <b>"+ p.permlevel +"</b> in a <b>" + doctype + "</b>.") | |||||
} | |||||
$("<a>Show Users</a>").appendTo(_p).attr("data-role", p.role) | |||||
.css("margin-left", "7px") | |||||
.click(function() { | |||||
var link = $(this); | |||||
wn.call({ | |||||
module: "core", | |||||
page: "permission_manager", | |||||
method: "get_users_with_role", | |||||
args: { | |||||
role: link.attr("data-role"), | |||||
}, | |||||
callback: function(r) { | |||||
$.each(r.message, function(i, uid) { | |||||
msgprint("<a href='#Form/Profile/"+uid+"'>" | |||||
+ wn.user_info(uid).fullname + "</a> ("+ | |||||
uid+")"); | |||||
}); | |||||
cur_dialog.set_title("Users with role " | |||||
+ link.attr("data-role")); | |||||
} | |||||
}); | |||||
}); | |||||
_p.appendTo(wrapper); | |||||
}) | |||||
} | } | ||||
}) | }) |
@@ -7,7 +7,7 @@ def get_roles_and_doctypes(): | |||||
"doctypes": [d[0] for d in webnotes.conn.sql("""select name from tabDocType where | "doctypes": [d[0] for d in webnotes.conn.sql("""select name from tabDocType where | ||||
ifnull(istable,0)=0 and | ifnull(istable,0)=0 and | ||||
ifnull(issingle,0)=0 and | ifnull(issingle,0)=0 and | ||||
module != 'Core' """)], | |||||
name not in ('DocType')""")], | |||||
"roles": [d[0] for d in webnotes.conn.sql("""select name from tabRole where name not in | "roles": [d[0] for d in webnotes.conn.sql("""select name from tabRole where name not in | ||||
('All', 'Guest', 'Administrator')""")] | ('All', 'Guest', 'Administrator')""")] | ||||
} | } | ||||
@@ -4,6 +4,21 @@ wn.pages['user-properties'].onload = function(wrapper) { | |||||
title: 'User Properties', | title: 'User Properties', | ||||
single_column: true | single_column: true | ||||
}); | }); | ||||
$(wrapper).find(".layout-main").html("<div class='user-settings'></div>\ | |||||
<table class='table table-bordered' style='background-color: #f9f9f9;'>\ | |||||
<tr><td>\ | |||||
<h4><i class='icon-question-sign'></i> Quick Help for User Properties:</h4>\ | |||||
<ol>\ | |||||
<li>You can set various 'properties' to Users.</li>\ | |||||
<li>These properties are Link Type fields from all Documents.</li>\ | |||||
<li>These properties will appear as values in forms that contain them.</li>\ | |||||
<li>These properties can also be used to 'assign' a particular document, \ | |||||
whose property matches with the User's property to a User. These can be set\ | |||||
using the <a href='#permission-manager'>Permission Manager</a></li>\ | |||||
<li>A user can have multiple values for a property.</li>\ | |||||
</ol>\ | |||||
</tr></td>\ | |||||
</table>"); | |||||
wrapper.user_properties = new wn.UserProperties(wrapper); | wrapper.user_properties = new wn.UserProperties(wrapper); | ||||
} | } | ||||
@@ -14,7 +29,7 @@ wn.pages['user-properties'].refresh = function(wrapper) { | |||||
wn.UserProperties = Class.extend({ | wn.UserProperties = Class.extend({ | ||||
init: function(wrapper) { | init: function(wrapper) { | ||||
this.wrapper = wrapper; | this.wrapper = wrapper; | ||||
this.body = $(this.wrapper).find(".layout-main"); | |||||
this.body = $(this.wrapper).find(".user-settings"); | |||||
this.make(); | this.make(); | ||||
this.refresh(); | this.refresh(); | ||||
}, | }, | ||||
@@ -72,11 +87,6 @@ wn.UserProperties = Class.extend({ | |||||
this.show_property_table(); | this.show_property_table(); | ||||
} | } | ||||
this.show_add_property(); | this.show_add_property(); | ||||
$("<div class='well'>User Properties appear as default values in forms.\ | |||||
<br>They are also used to restrict permissions \ | |||||
in the <a href='#permission-manager'>Permission Manager</a>\ | |||||
<br>You can also set multiple values for one property. \ | |||||
If so, the permission rules will apply if any of the values match.</div>").appendTo(this.body); | |||||
}, | }, | ||||
refresh: function() { | refresh: function() { | ||||
var me = this; | var me = this; | ||||
@@ -119,7 +129,8 @@ wn.UserProperties = Class.extend({ | |||||
$.each(this.prop_list, function(i, d) { | $.each(this.prop_list, function(i, d) { | ||||
var row = $("<tr>").appendTo(me.table.find("tbody")); | var row = $("<tr>").appendTo(me.table.find("tbody")); | ||||
$("<td>").html(d.parent).appendTo(row); | |||||
$("<td>").html('<a href="#Form/Profile/'+d.parent+'">' | |||||
+d.parent+'</a>').appendTo(row); | |||||
$("<td>").html(d.defkey).appendTo(row); | $("<td>").html(d.defkey).appendTo(row); | ||||
$("<td>").html(d.defvalue).appendTo(row); | $("<td>").html(d.defvalue).appendTo(row); | ||||
@@ -184,7 +195,7 @@ wn.UserProperties = Class.extend({ | |||||
if(l[0]==key) return l[1]; | if(l[0]==key) return l[1]; | ||||
})[0]; | })[0]; | ||||
return 'select name from `tab'+doctype | return 'select name from `tab'+doctype | ||||
+'` where name like "%s"' | |||||
+'` where name like "%s" limit 20' | |||||
} | } | ||||
d.get_input("add").click(function() { | d.get_input("add").click(function() { | ||||
var args = d.get_values(); | var args = d.get_values(); | ||||
@@ -3,11 +3,17 @@ import webnotes | |||||
@webnotes.whitelist(allow_roles=["System Manager", "Administrator"]) | @webnotes.whitelist(allow_roles=["System Manager", "Administrator"]) | ||||
def get_users_and_links(): | def get_users_and_links(): | ||||
links = list(set(webnotes.conn.sql("""select fieldname, options | |||||
links, all_fields = [], [] | |||||
for l in webnotes.conn.sql("""select fieldname, options | |||||
from tabDocField where fieldtype='Link' | from tabDocField where fieldtype='Link' | ||||
and parent not in ('[Select]', 'DocType', 'Module Def') | and parent not in ('[Select]', 'DocType', 'Module Def') | ||||
""") + webnotes.conn.sql("""select fieldname, options | """) + webnotes.conn.sql("""select fieldname, options | ||||
from `tabCustom Field` where fieldtype='Link'"""))) | |||||
from `tabCustom Field` where fieldtype='Link'"""): | |||||
if not l[0] in all_fields: | |||||
links.append([l[0], l[1]]) | |||||
all_fields.append(l[0]) | |||||
links.sort() | links.sort() | ||||
return { | return { | ||||
@@ -21,10 +27,11 @@ def get_users_and_links(): | |||||
def get_properties(user=None, key=None): | def get_properties(user=None, key=None): | ||||
return webnotes.conn.sql("""select name, parent, defkey, defvalue | return webnotes.conn.sql("""select name, parent, defkey, defvalue | ||||
from tabDefaultValue | from tabDefaultValue | ||||
where %s%s and parent!='Control Panel' order by parent, defkey""" % (\ | |||||
user and (" parent='%s'" % user) or "", | |||||
key and ((user and " and " or "") + " defkey='%s'" % key) or "", | |||||
), as_dict=True) | |||||
where parent!='Control Panel' | |||||
and substr(defkey,0,1)!='_' | |||||
%s%s order by parent, defkey""" % (\ | |||||
user and (" and parent='%s'" % user) or "", | |||||
key and (" and defkey='%s'" % key) or ""), as_dict=True) | |||||
@webnotes.whitelist(allow_roles=["System Manager", "Administrator"]) | @webnotes.whitelist(allow_roles=["System Manager", "Administrator"]) | ||||
def remove(user, name): | def remove(user, name): | ||||
@@ -150,10 +150,10 @@ | |||||
"lib/public/js/legacy/widgets/form/form_comments.js", | "lib/public/js/legacy/widgets/form/form_comments.js", | ||||
"lib/public/js/legacy/wn/widgets/form/sidebar.js", | "lib/public/js/legacy/wn/widgets/form/sidebar.js", | ||||
"lib/public/js/legacy/wn/widgets/form/comments.js", | "lib/public/js/legacy/wn/widgets/form/comments.js", | ||||
"lib/public/js/legacy/wn/widgets/form/assign_to.js", | |||||
"lib/public/js/wn/form/attachments.js", | "lib/public/js/wn/form/attachments.js", | ||||
"lib/public/js/wn/form/linked_with.js", | "lib/public/js/wn/form/linked_with.js", | ||||
"lib/public/js/wn/form/states.js", | "lib/public/js/wn/form/states.js", | ||||
"lib/public/js/wn/form/assign_to.js", | |||||
"lib/public/js/wn/print/print_table.js", | "lib/public/js/wn/print/print_table.js", | ||||
'lib/public/js/lib/jquery/jquery.ui.interactions.min.js', | 'lib/public/js/lib/jquery/jquery.ui.interactions.min.js', | ||||
@@ -215,7 +215,7 @@ textarea { | |||||
.input-block-level { | .input-block-level { | ||||
display: block; | display: block; | ||||
width: 100%; | width: 100%; | ||||
min-height: 28px; | |||||
min-height: 30px; | |||||
-webkit-box-sizing: border-box; | -webkit-box-sizing: border-box; | ||||
-moz-box-sizing: border-box; | -moz-box-sizing: border-box; | ||||
box-sizing: border-box; | box-sizing: border-box; | ||||
@@ -225,7 +225,7 @@ body { | |||||
margin: 0; | margin: 0; | ||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | ||||
font-size: 13px; | font-size: 13px; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
color: #333333; | color: #333333; | ||||
background-color: #ffffff; | background-color: #ffffff; | ||||
} | } | ||||
@@ -407,7 +407,7 @@ a:hover { | |||||
display: block; | display: block; | ||||
float: left; | float: left; | ||||
width: 100%; | width: 100%; | ||||
min-height: 28px; | |||||
min-height: 30px; | |||||
margin-left: 2.127659574468085%; | margin-left: 2.127659574468085%; | ||||
*margin-left: 2.074468085106383%; | *margin-left: 2.074468085106383%; | ||||
-webkit-box-sizing: border-box; | -webkit-box-sizing: border-box; | ||||
@@ -648,14 +648,14 @@ a:hover { | |||||
} | } | ||||
p { | p { | ||||
margin: 0 0 9px; | |||||
margin: 0 0 10px; | |||||
} | } | ||||
.lead { | .lead { | ||||
margin-bottom: 18px; | |||||
margin-bottom: 20px; | |||||
font-size: 19.5px; | font-size: 19.5px; | ||||
font-weight: 200; | font-weight: 200; | ||||
line-height: 27px; | |||||
line-height: 30px; | |||||
} | } | ||||
small { | small { | ||||
@@ -720,10 +720,10 @@ h3, | |||||
h4, | h4, | ||||
h5, | h5, | ||||
h6 { | h6 { | ||||
margin: 9px 0; | |||||
margin: 10px 0; | |||||
font-family: inherit; | font-family: inherit; | ||||
font-weight: bold; | font-weight: bold; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
color: inherit; | color: inherit; | ||||
text-rendering: optimizelegibility; | text-rendering: optimizelegibility; | ||||
} | } | ||||
@@ -742,7 +742,7 @@ h6 small { | |||||
h1, | h1, | ||||
h2, | h2, | ||||
h3 { | h3 { | ||||
line-height: 36px; | |||||
line-height: 40px; | |||||
} | } | ||||
h1 { | h1 { | ||||
@@ -786,15 +786,15 @@ h4 small { | |||||
} | } | ||||
.page-header { | .page-header { | ||||
padding-bottom: 8px; | |||||
margin: 18px 0 27px; | |||||
padding-bottom: 9px; | |||||
margin: 20px 0 30px; | |||||
border-bottom: 1px solid #eeeeee; | border-bottom: 1px solid #eeeeee; | ||||
} | } | ||||
ul, | ul, | ||||
ol { | ol { | ||||
padding: 0; | padding: 0; | ||||
margin: 0 0 9px 25px; | |||||
margin: 0 0 10px 25px; | |||||
} | } | ||||
ul ul, | ul ul, | ||||
@@ -805,7 +805,7 @@ ol ul { | |||||
} | } | ||||
li { | li { | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
} | } | ||||
ul.unstyled, | ul.unstyled, | ||||
@@ -828,12 +828,12 @@ ol.inline > li { | |||||
} | } | ||||
dl { | dl { | ||||
margin-bottom: 18px; | |||||
margin-bottom: 20px; | |||||
} | } | ||||
dt, | dt, | ||||
dd { | dd { | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
} | } | ||||
dt { | dt { | ||||
@@ -841,7 +841,7 @@ dt { | |||||
} | } | ||||
dd { | dd { | ||||
margin-left: 9px; | |||||
margin-left: 10px; | |||||
} | } | ||||
.dl-horizontal { | .dl-horizontal { | ||||
@@ -874,7 +874,7 @@ dd { | |||||
} | } | ||||
hr { | hr { | ||||
margin: 18px 0; | |||||
margin: 20px 0; | |||||
border: 0; | border: 0; | ||||
border-top: 1px solid #eeeeee; | border-top: 1px solid #eeeeee; | ||||
border-bottom: 1px solid #ffffff; | border-bottom: 1px solid #ffffff; | ||||
@@ -893,7 +893,7 @@ abbr.initialism { | |||||
blockquote { | blockquote { | ||||
padding: 0 0 0 15px; | padding: 0 0 0 15px; | ||||
margin: 0 0 18px; | |||||
margin: 0 0 20px; | |||||
border-left: 5px solid #eeeeee; | border-left: 5px solid #eeeeee; | ||||
} | } | ||||
@@ -901,12 +901,12 @@ blockquote p { | |||||
margin-bottom: 0; | margin-bottom: 0; | ||||
font-size: 16px; | font-size: 16px; | ||||
font-weight: 300; | font-weight: 300; | ||||
line-height: 22.5px; | |||||
line-height: 25px; | |||||
} | } | ||||
blockquote small { | blockquote small { | ||||
display: block; | display: block; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
color: #999999; | color: #999999; | ||||
} | } | ||||
@@ -944,9 +944,9 @@ blockquote:after { | |||||
address { | address { | ||||
display: block; | display: block; | ||||
margin-bottom: 18px; | |||||
margin-bottom: 20px; | |||||
font-style: normal; | font-style: normal; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
} | } | ||||
code, | code, | ||||
@@ -970,10 +970,10 @@ code { | |||||
pre { | pre { | ||||
display: block; | display: block; | ||||
padding: 8.5px; | |||||
margin: 0 0 9px; | |||||
padding: 9.5px; | |||||
margin: 0 0 10px; | |||||
font-size: 12px; | font-size: 12px; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
word-break: break-all; | word-break: break-all; | ||||
word-wrap: break-word; | word-wrap: break-word; | ||||
white-space: pre; | white-space: pre; | ||||
@@ -987,7 +987,7 @@ pre { | |||||
} | } | ||||
pre.prettyprint { | pre.prettyprint { | ||||
margin-bottom: 18px; | |||||
margin-bottom: 20px; | |||||
} | } | ||||
pre code { | pre code { | ||||
@@ -1005,7 +1005,7 @@ pre code { | |||||
} | } | ||||
form { | form { | ||||
margin: 0 0 18px; | |||||
margin: 0 0 20px; | |||||
} | } | ||||
fieldset { | fieldset { | ||||
@@ -1018,16 +1018,16 @@ legend { | |||||
display: block; | display: block; | ||||
width: 100%; | width: 100%; | ||||
padding: 0; | padding: 0; | ||||
margin-bottom: 18px; | |||||
margin-bottom: 20px; | |||||
font-size: 19.5px; | font-size: 19.5px; | ||||
line-height: 36px; | |||||
line-height: 40px; | |||||
color: #333333; | color: #333333; | ||||
border: 0; | border: 0; | ||||
border-bottom: 1px solid #e5e5e5; | border-bottom: 1px solid #e5e5e5; | ||||
} | } | ||||
legend small { | legend small { | ||||
font-size: 13.5px; | |||||
font-size: 15px; | |||||
color: #999999; | color: #999999; | ||||
} | } | ||||
@@ -1038,7 +1038,7 @@ select, | |||||
textarea { | textarea { | ||||
font-size: 13px; | font-size: 13px; | ||||
font-weight: normal; | font-weight: normal; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
} | } | ||||
input, | input, | ||||
@@ -1071,11 +1071,11 @@ input[type="tel"], | |||||
input[type="color"], | input[type="color"], | ||||
.uneditable-input { | .uneditable-input { | ||||
display: inline-block; | display: inline-block; | ||||
height: 18px; | |||||
height: 20px; | |||||
padding: 4px 6px; | padding: 4px 6px; | ||||
margin-bottom: 9px; | |||||
margin-bottom: 10px; | |||||
font-size: 13px; | font-size: 13px; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
color: #555555; | color: #555555; | ||||
vertical-align: middle; | vertical-align: middle; | ||||
-webkit-border-radius: 4px; | -webkit-border-radius: 4px; | ||||
@@ -1166,13 +1166,13 @@ input[type="checkbox"] { | |||||
select, | select, | ||||
input[type="file"] { | input[type="file"] { | ||||
height: 28px; | |||||
height: 30px; | |||||
/* In IE7, the height of the select element cannot be changed by height, only font-size */ | /* In IE7, the height of the select element cannot be changed by height, only font-size */ | ||||
*margin-top: 4px; | *margin-top: 4px; | ||||
/* For IE7, add top margin to align select with labels */ | /* For IE7, add top margin to align select with labels */ | ||||
line-height: 28px; | |||||
line-height: 30px; | |||||
} | } | ||||
select { | select { | ||||
@@ -1233,7 +1233,7 @@ textarea::-webkit-input-placeholder { | |||||
.radio, | .radio, | ||||
.checkbox { | .checkbox { | ||||
min-height: 18px; | |||||
min-height: 20px; | |||||
padding-left: 20px; | padding-left: 20px; | ||||
} | } | ||||
@@ -1607,9 +1607,9 @@ select:focus:invalid:focus { | |||||
} | } | ||||
.form-actions { | .form-actions { | ||||
padding: 17px 20px 18px; | |||||
margin-top: 18px; | |||||
margin-bottom: 18px; | |||||
padding: 19px 20px 20px; | |||||
margin-top: 20px; | |||||
margin-bottom: 20px; | |||||
background-color: #f5f5f5; | background-color: #f5f5f5; | ||||
border-top: 1px solid #e5e5e5; | border-top: 1px solid #e5e5e5; | ||||
*zoom: 1; | *zoom: 1; | ||||
@@ -1633,7 +1633,7 @@ select:focus:invalid:focus { | |||||
.help-block { | .help-block { | ||||
display: block; | display: block; | ||||
margin-bottom: 9px; | |||||
margin-bottom: 10px; | |||||
} | } | ||||
.help-inline { | .help-inline { | ||||
@@ -1690,12 +1690,12 @@ select:focus:invalid:focus { | |||||
.input-prepend .add-on { | .input-prepend .add-on { | ||||
display: inline-block; | display: inline-block; | ||||
width: auto; | width: auto; | ||||
height: 18px; | |||||
height: 20px; | |||||
min-width: 16px; | min-width: 16px; | ||||
padding: 4px 5px; | padding: 4px 5px; | ||||
font-size: 13px; | font-size: 13px; | ||||
font-weight: normal; | font-weight: normal; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
text-align: center; | text-align: center; | ||||
text-shadow: 0 1px 0 #ffffff; | text-shadow: 0 1px 0 #ffffff; | ||||
background-color: #eeeeee; | background-color: #eeeeee; | ||||
@@ -1911,16 +1911,16 @@ input.search-query { | |||||
} | } | ||||
.control-group { | .control-group { | ||||
margin-bottom: 9px; | |||||
margin-bottom: 10px; | |||||
} | } | ||||
legend + .control-group { | legend + .control-group { | ||||
margin-top: 18px; | |||||
margin-top: 20px; | |||||
-webkit-margin-top-collapse: separate; | -webkit-margin-top-collapse: separate; | ||||
} | } | ||||
.form-horizontal .control-group { | .form-horizontal .control-group { | ||||
margin-bottom: 18px; | |||||
margin-bottom: 20px; | |||||
*zoom: 1; | *zoom: 1; | ||||
} | } | ||||
@@ -1963,7 +1963,7 @@ legend + .control-group { | |||||
.form-horizontal .uneditable-input + .help-block, | .form-horizontal .uneditable-input + .help-block, | ||||
.form-horizontal .input-prepend + .help-block, | .form-horizontal .input-prepend + .help-block, | ||||
.form-horizontal .input-append + .help-block { | .form-horizontal .input-append + .help-block { | ||||
margin-top: 9px; | |||||
margin-top: 10px; | |||||
} | } | ||||
.form-horizontal .form-actions { | .form-horizontal .form-actions { | ||||
@@ -1979,13 +1979,13 @@ table { | |||||
.table { | .table { | ||||
width: 100%; | width: 100%; | ||||
margin-bottom: 18px; | |||||
margin-bottom: 20px; | |||||
} | } | ||||
.table th, | .table th, | ||||
.table td { | .table td { | ||||
padding: 8px; | padding: 8px; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
text-align: left; | text-align: left; | ||||
vertical-align: top; | vertical-align: top; | ||||
border-top: 1px solid #dddddd; | border-top: 1px solid #dddddd; | ||||
@@ -2308,7 +2308,7 @@ table th[class*="span"], | |||||
.dropdown-menu .divider { | .dropdown-menu .divider { | ||||
*width: 100%; | *width: 100%; | ||||
height: 1px; | height: 1px; | ||||
margin: 8px 1px; | |||||
margin: 9px 1px; | |||||
*margin: -5px 0 5px; | *margin: -5px 0 5px; | ||||
overflow: hidden; | overflow: hidden; | ||||
background-color: #e5e5e5; | background-color: #e5e5e5; | ||||
@@ -2320,7 +2320,7 @@ table th[class*="span"], | |||||
padding: 3px 20px; | padding: 3px 20px; | ||||
clear: both; | clear: both; | ||||
font-weight: normal; | font-weight: normal; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
color: #333333; | color: #333333; | ||||
white-space: nowrap; | white-space: nowrap; | ||||
} | } | ||||
@@ -2529,7 +2529,7 @@ table th[class*="span"], | |||||
float: right; | float: right; | ||||
font-size: 20px; | font-size: 20px; | ||||
font-weight: bold; | font-weight: bold; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
color: #000000; | color: #000000; | ||||
text-shadow: 0 1px 0 #ffffff; | text-shadow: 0 1px 0 #ffffff; | ||||
opacity: 0.2; | opacity: 0.2; | ||||
@@ -2559,7 +2559,7 @@ button.close { | |||||
margin-bottom: 0; | margin-bottom: 0; | ||||
*margin-left: .3em; | *margin-left: .3em; | ||||
font-size: 13px; | font-size: 13px; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
color: #333333; | color: #333333; | ||||
text-align: center; | text-align: center; | ||||
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); | text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); | ||||
@@ -2989,8 +2989,8 @@ input[type="submit"].btn.btn-mini { | |||||
} | } | ||||
.btn-toolbar { | .btn-toolbar { | ||||
margin-top: 9px; | |||||
margin-bottom: 9px; | |||||
margin-top: 10px; | |||||
margin-bottom: 10px; | |||||
font-size: 0; | font-size: 0; | ||||
} | } | ||||
@@ -3224,7 +3224,7 @@ input[type="submit"].btn.btn-mini { | |||||
.alert { | .alert { | ||||
padding: 8px 35px 8px 14px; | padding: 8px 35px 8px 14px; | ||||
margin-bottom: 18px; | |||||
margin-bottom: 20px; | |||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); | ||||
background-color: #fcf8e3; | background-color: #fcf8e3; | ||||
border: 1px solid #fbeed5; | border: 1px solid #fbeed5; | ||||
@@ -3246,7 +3246,7 @@ input[type="submit"].btn.btn-mini { | |||||
position: relative; | position: relative; | ||||
top: -2px; | top: -2px; | ||||
right: -21px; | right: -21px; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
} | } | ||||
.alert-success { | .alert-success { | ||||
@@ -3296,7 +3296,7 @@ input[type="submit"].btn.btn-mini { | |||||
} | } | ||||
.nav { | .nav { | ||||
margin-bottom: 18px; | |||||
margin-bottom: 20px; | |||||
margin-left: 0; | margin-left: 0; | ||||
list-style: none; | list-style: none; | ||||
} | } | ||||
@@ -3323,7 +3323,7 @@ input[type="submit"].btn.btn-mini { | |||||
padding: 3px 15px; | padding: 3px 15px; | ||||
font-size: 11px; | font-size: 11px; | ||||
font-weight: bold; | font-weight: bold; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
color: #999999; | color: #999999; | ||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); | ||||
text-transform: uppercase; | text-transform: uppercase; | ||||
@@ -3365,7 +3365,7 @@ input[type="submit"].btn.btn-mini { | |||||
.nav-list .divider { | .nav-list .divider { | ||||
*width: 100%; | *width: 100%; | ||||
height: 1px; | height: 1px; | ||||
margin: 8px 1px; | |||||
margin: 9px 1px; | |||||
*margin: -5px 0 5px; | *margin: -5px 0 5px; | ||||
overflow: hidden; | overflow: hidden; | ||||
background-color: #e5e5e5; | background-color: #e5e5e5; | ||||
@@ -3415,7 +3415,7 @@ input[type="submit"].btn.btn-mini { | |||||
.nav-tabs > li > a { | .nav-tabs > li > a { | ||||
padding-top: 8px; | padding-top: 8px; | ||||
padding-bottom: 8px; | padding-bottom: 8px; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
border: 1px solid transparent; | border: 1px solid transparent; | ||||
-webkit-border-radius: 4px 4px 0 0; | -webkit-border-radius: 4px 4px 0 0; | ||||
-moz-border-radius: 4px 4px 0 0; | -moz-border-radius: 4px 4px 0 0; | ||||
@@ -3696,7 +3696,7 @@ input[type="submit"].btn.btn-mini { | |||||
.navbar { | .navbar { | ||||
*position: relative; | *position: relative; | ||||
*z-index: 2; | *z-index: 2; | ||||
margin-bottom: 18px; | |||||
margin-bottom: 20px; | |||||
overflow: visible; | overflow: visible; | ||||
} | } | ||||
@@ -3745,7 +3745,7 @@ input[type="submit"].btn.btn-mini { | |||||
.navbar .brand { | .navbar .brand { | ||||
display: block; | display: block; | ||||
float: left; | float: left; | ||||
padding: 11px 20px 11px; | |||||
padding: 10px 20px 10px; | |||||
margin-left: -20px; | margin-left: -20px; | ||||
font-size: 20px; | font-size: 20px; | ||||
font-weight: 200; | font-weight: 200; | ||||
@@ -3939,7 +3939,7 @@ input[type="submit"].btn.btn-mini { | |||||
.navbar .nav > li > a { | .navbar .nav > li > a { | ||||
float: none; | float: none; | ||||
padding: 11px 15px 11px; | |||||
padding: 10px 15px 10px; | |||||
color: #777777; | color: #777777; | ||||
text-decoration: none; | text-decoration: none; | ||||
text-shadow: 0 1px 0 #ffffff; | text-shadow: 0 1px 0 #ffffff; | ||||
@@ -4268,7 +4268,7 @@ input[type="submit"].btn.btn-mini { | |||||
.breadcrumb { | .breadcrumb { | ||||
padding: 8px 15px; | padding: 8px 15px; | ||||
margin: 0 0 18px; | |||||
margin: 0 0 20px; | |||||
list-style: none; | list-style: none; | ||||
background-color: #f5f5f5; | background-color: #f5f5f5; | ||||
-webkit-border-radius: 4px; | -webkit-border-radius: 4px; | ||||
@@ -4293,7 +4293,7 @@ input[type="submit"].btn.btn-mini { | |||||
} | } | ||||
.pagination { | .pagination { | ||||
margin: 18px 0; | |||||
margin: 20px 0; | |||||
} | } | ||||
.pagination ul { | .pagination ul { | ||||
@@ -4318,7 +4318,7 @@ input[type="submit"].btn.btn-mini { | |||||
.pagination ul > li > span { | .pagination ul > li > span { | ||||
float: left; | float: left; | ||||
padding: 4px 12px; | padding: 4px 12px; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
text-decoration: none; | text-decoration: none; | ||||
background-color: #ffffff; | background-color: #ffffff; | ||||
border: 1px solid #dddddd; | border: 1px solid #dddddd; | ||||
@@ -4437,7 +4437,7 @@ input[type="submit"].btn.btn-mini { | |||||
} | } | ||||
.pager { | .pager { | ||||
margin: 18px 0; | |||||
margin: 20px 0; | |||||
text-align: center; | text-align: center; | ||||
list-style: none; | list-style: none; | ||||
*zoom: 1; | *zoom: 1; | ||||
@@ -4858,14 +4858,14 @@ input[type="submit"].btn.btn-mini { | |||||
.thumbnails > li { | .thumbnails > li { | ||||
float: left; | float: left; | ||||
margin-bottom: 18px; | |||||
margin-bottom: 20px; | |||||
margin-left: 20px; | margin-left: 20px; | ||||
} | } | ||||
.thumbnail { | .thumbnail { | ||||
display: block; | display: block; | ||||
padding: 4px; | padding: 4px; | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
border: 1px solid #ddd; | border: 1px solid #ddd; | ||||
-webkit-border-radius: 4px; | -webkit-border-radius: 4px; | ||||
-moz-border-radius: 4px; | -moz-border-radius: 4px; | ||||
@@ -5082,8 +5082,8 @@ a.badge:hover { | |||||
} | } | ||||
.progress { | .progress { | ||||
height: 18px; | |||||
margin-bottom: 18px; | |||||
height: 20px; | |||||
margin-bottom: 20px; | |||||
overflow: hidden; | overflow: hidden; | ||||
background-color: #f7f7f7; | background-color: #f7f7f7; | ||||
background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); | background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); | ||||
@@ -5245,7 +5245,7 @@ a.badge:hover { | |||||
} | } | ||||
.accordion { | .accordion { | ||||
margin-bottom: 18px; | |||||
margin-bottom: 20px; | |||||
} | } | ||||
.accordion-group { | .accordion-group { | ||||
@@ -5276,7 +5276,7 @@ a.badge:hover { | |||||
.carousel { | .carousel { | ||||
position: relative; | position: relative; | ||||
margin-bottom: 18px; | |||||
margin-bottom: 20px; | |||||
line-height: 1; | line-height: 1; | ||||
} | } | ||||
@@ -5383,7 +5383,7 @@ a.badge:hover { | |||||
.carousel-caption h4, | .carousel-caption h4, | ||||
.carousel-caption p { | .carousel-caption p { | ||||
line-height: 18px; | |||||
line-height: 20px; | |||||
color: #ffffff; | color: #ffffff; | ||||
} | } | ||||
@@ -5400,7 +5400,7 @@ a.badge:hover { | |||||
margin-bottom: 30px; | margin-bottom: 30px; | ||||
font-size: 18px; | font-size: 18px; | ||||
font-weight: 200; | font-weight: 200; | ||||
line-height: 27px; | |||||
line-height: 30px; | |||||
color: inherit; | color: inherit; | ||||
background-color: #eeeeee; | background-color: #eeeeee; | ||||
-webkit-border-radius: 6px; | -webkit-border-radius: 6px; | ||||
@@ -5417,7 +5417,7 @@ a.badge:hover { | |||||
} | } | ||||
.hero-unit li { | .hero-unit li { | ||||
line-height: 27px; | |||||
line-height: 30px; | |||||
} | } | ||||
.pull-right { | .pull-right { | ||||
@@ -1,117 +0,0 @@ | |||||
// Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com) | |||||
// | |||||
// MIT License (MIT) | |||||
// | |||||
// Permission is hereby granted, free of charge, to any person obtaining a | |||||
// copy of this software and associated documentation files (the "Software"), | |||||
// to deal in the Software without restriction, including without limitation | |||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense, | |||||
// and/or sell copies of the Software, and to permit persons to whom the | |||||
// Software is furnished to do so, subject to the following conditions: | |||||
// | |||||
// The above copyright notice and this permission notice shall be included in | |||||
// all copies or substantial portions of the Software. | |||||
// | |||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | |||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |||||
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | |||||
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
// | |||||
// assign to is lined to todo | |||||
// refresh - load todos | |||||
// create - new todo | |||||
// delete to do | |||||
wn.widgets.form.sidebar.AssignTo = Class.extend({ | |||||
init: function(parent, sidebar, doctype, docname) { | |||||
var me = this; | |||||
this.doctype = doctype; | |||||
this.name = docname; | |||||
this.wrapper = $a(parent, 'div', 'sidebar-comment-wrapper'); | |||||
this.body = $a(this.wrapper, 'div'); | |||||
this.add_btn = $("<button class='btn btn-small'>\ | |||||
<i class='icon-plus'></i></button>") | |||||
.click(function() { me.add(); }).appendTo(this.wrapper).get(0); | |||||
this.refresh(); | |||||
}, | |||||
refresh: function() { | |||||
var me = this; | |||||
$c('webnotes.widgets.form.assign_to.get', { | |||||
doctype: me.doctype, | |||||
name: me.name | |||||
}, function(r,rt) { | |||||
me.render(r.message) | |||||
}) | |||||
}, | |||||
render: function(d) { | |||||
var me = this; | |||||
$(this.body).empty(); | |||||
if(this.dialog) { | |||||
this.dialog.hide(); | |||||
} | |||||
for(var i=0; i<d.length; i++) { | |||||
$(this.body).append(repl('<div>%(owner)s \ | |||||
<a class="close" href="#" data-owner="%(owner)s">×</a></div>', d[i])) | |||||
} | |||||
// set remove | |||||
$(this.body).find('a.close').click(function() { | |||||
$c('webnotes.widgets.form.assign_to.remove', { | |||||
doctype: me.doctype, | |||||
name: me.name, | |||||
assign_to: $(this).attr('data-owner') | |||||
}, function(r,rt) {me.render(r.message);}); | |||||
return false; | |||||
}); | |||||
}, | |||||
add: function() { | |||||
var me = this; | |||||
if(!me.dialog) { | |||||
me.dialog = new wn.ui.Dialog({ | |||||
title: 'Add to To Do', | |||||
width: 350, | |||||
fields: [ | |||||
{fieldtype:'Link', fieldname:'assign_to', options:'Profile', | |||||
label:'Assign To', | |||||
description:'Add to To Do List of', reqd:true}, | |||||
{fieldtype:'Data', fieldname:'description', label:'Comment'}, | |||||
{fieldtype:'Date', fieldname:'date', label:'Complete By'}, | |||||
{fieldtype:'Select', fieldname:'priority', label:'Priority', | |||||
options:'Low\nMedium\nHigh', 'default':'Medium'}, | |||||
{fieldtype:'Check', fieldname:'notify', label:'Notify By Email'}, | |||||
{fieldtype:'Button', label:'Add', fieldname:'add_btn'} | |||||
] | |||||
}); | |||||
me.dialog.fields_dict.add_btn.input.onclick = function() { | |||||
var assign_to = me.dialog.fields_dict.assign_to.get_value(); | |||||
if(assign_to) { | |||||
$c('webnotes.widgets.form.assign_to.add', { | |||||
doctype: me.doctype, | |||||
name: me.name, | |||||
assign_to: assign_to, | |||||
description: me.dialog.fields_dict.description.get_value(), | |||||
priority: me.dialog.fields_dict.priority.get_value(), | |||||
date: me.dialog.fields_dict.date.get_value(), | |||||
notify: me.dialog.fields_dict.notify.get_value() | |||||
}, function(r,rt) {me.render(r.message);}); | |||||
} | |||||
}; | |||||
me.dialog.fields_dict.assign_to.get_query = function() { | |||||
return "select name, concat_ws(' ', first_name, middle_name, last_name) \ | |||||
from `tabProfile` where ifnull(enabled, 0)=1 and docstatus < 2 and \ | |||||
(%(key)s like \"%s\" or \ | |||||
concat_ws(' ', first_name, middle_name, last_name) like \"%%%s\") \ | |||||
limit 50"; | |||||
}; | |||||
} | |||||
me.dialog.clear(); | |||||
me.dialog.show(); | |||||
} | |||||
}); | |||||
@@ -115,7 +115,11 @@ wn.widgets.form.sidebar = { Sidebar: function(form) { | |||||
{ | { | ||||
title: 'Assign', | title: 'Assign', | ||||
render: function(wrapper) { | render: function(wrapper) { | ||||
me.form.assign_to = new wn.widgets.form.sidebar.AssignTo(wrapper, me, me.form.doctype, me.form.docname); | |||||
me.form.assign_to = new wn.ui.form.AssignTo({ | |||||
parent: $(wrapper), | |||||
frm: me.form | |||||
}); | |||||
me.form.assign_to.refresh(); | |||||
}, | }, | ||||
display: function() { return !me.form.doc.__islocal } | display: function() { return !me.form.doc.__islocal } | ||||
}, | }, | ||||
@@ -123,7 +127,10 @@ wn.widgets.form.sidebar = { Sidebar: function(form) { | |||||
{ | { | ||||
title: 'Attachments', | title: 'Attachments', | ||||
render: function(wrapper) { | render: function(wrapper) { | ||||
me.form.attachments = new wn.ui.form.Attachments({parent: $(wrapper), frm:me.form}); | |||||
me.form.attachments = new wn.ui.form.Attachments({ | |||||
parent: $(wrapper), | |||||
frm:me.form | |||||
}); | |||||
me.form.attachments.refresh(); | me.form.attachments.refresh(); | ||||
}, | }, | ||||
display: function() { return me.form.meta.allow_attach } | display: function() { return me.form.meta.allow_attach } | ||||
@@ -77,7 +77,7 @@ wn.ui.form.Attachments = Class.extend({ | |||||
var me = this; | var me = this; | ||||
$(repl('<div class="alert alert-info"><span style="display: inline-block; width: 90%;\ | $(repl('<div class="alert alert-info"><span style="display: inline-block; width: 90%;\ | ||||
text-overflow: ellipsis; white-space: nowrap; overflow: hidden;">\ | text-overflow: ellipsis; white-space: nowrap; overflow: hidden;">\ | ||||
<i class="icon icon-file"></i> <a href="%(href)s"\ | |||||
<i class="icon-file"></i> <a href="%(href)s"\ | |||||
target="_blank" title="%(filename)s">%(filename)s</a></span><a href="#" class="close">×</a>\ | target="_blank" title="%(filename)s">%(filename)s</a></span><a href="#" class="close">×</a>\ | ||||
</div>', { | </div>', { | ||||
filename: filename, | filename: filename, | ||||
@@ -63,9 +63,16 @@ wn.ui.Dialog = wn.ui.FieldGroup.extend({ | |||||
this.appframe.$titlebar.find('.appframe-title').html(t || ''); | this.appframe.$titlebar.find('.appframe-title').html(t || ''); | ||||
}, | }, | ||||
set_postion: function() { | set_postion: function() { | ||||
this.zindex = 1; | |||||
if(cur_dialog) { | |||||
this.zindex = cur_dialog.zindex + 1; | |||||
} | |||||
// place it at the center | // place it at the center | ||||
this.wrapper.style.left = (($(window).width() - cint(this.wrapper.style.width))/2) + 'px'; | |||||
this.wrapper.style.top = ($(window).scrollTop() + 60) + 'px'; | |||||
$(this.wrapper).css({ | |||||
left: (($(window).width() - cint(this.wrapper.style.width))/2) + 'px', | |||||
top: ($(window).scrollTop() + 60) + 'px', | |||||
"z-index": this.zindex | |||||
}) | |||||
}, | }, | ||||
show: function() { | show: function() { | ||||
// already live, do nothing | // already live, do nothing | ||||
@@ -72,53 +72,11 @@ class Database: | |||||
self._conn.select_db(db_name) | self._conn.select_db(db_name) | ||||
self.cur_db_name = db_name | self.cur_db_name = db_name | ||||
def check_transaction_status(self, query): | |||||
""" | |||||
Update *in_transaction* and check if "START TRANSACTION" is not called twice | |||||
""" | |||||
if self.in_transaction and query and query.strip().split()[0].lower() in ['start', 'alter', 'drop', 'create']: | |||||
raise Exception, 'This statement can cause implicit commit' | |||||
if query and query.strip().lower()=='start transaction': | |||||
self.in_transaction = 1 | |||||
self.transaction_writes = 0 | |||||
if query and query.strip().split()[0].lower() in ['commit', 'rollback']: | |||||
self.in_transaction = 0 | |||||
if self.in_transaction and query[:6].lower() in ['update', 'insert']: | |||||
self.transaction_writes += 1 | |||||
if self.transaction_writes > 10000: | |||||
if self.auto_commit_on_many_writes: | |||||
webnotes.conn.commit() | |||||
webnotes.conn.begin() | |||||
else: | |||||
webnotes.msgprint('A very long query was encountered. If you are trying to import data, please do so using smaller files') | |||||
raise Exception, 'Bad Query!!! Too many writes' | |||||
def fetch_as_dict(self, formatted=0, as_utf8=0): | |||||
""" | |||||
Internal - get results as dictionary | |||||
""" | |||||
result = self._cursor.fetchall() | |||||
ret = [] | |||||
for r in result: | |||||
row_dict = webnotes._dict({}) | |||||
for i in range(len(r)): | |||||
val = self.convert_to_simple_type(r[i], formatted) | |||||
if as_utf8 and type(val) is unicode: | |||||
val = val.encode('utf-8') | |||||
row_dict[self._cursor.description[i][0]] = val | |||||
ret.append(row_dict) | |||||
return ret | |||||
def validate_query(self, q): | def validate_query(self, q): | ||||
cmd = q.strip().lower().split()[0] | cmd = q.strip().lower().split()[0] | ||||
if cmd in ['alter', 'drop', 'truncate'] and webnotes.user.name != 'Administrator': | if cmd in ['alter', 'drop', 'truncate'] and webnotes.user.name != 'Administrator': | ||||
webnotes.msgprint('Not allowed to execute query') | webnotes.msgprint('Not allowed to execute query') | ||||
raise Execption | raise Execption | ||||
# ====================================================================================== | |||||
def sql(self, query, values=(), as_dict = 0, as_list = 0, formatted = 0, | def sql(self, query, values=(), as_dict = 0, as_list = 0, formatted = 0, | ||||
debug=0, ignore_ddl=0, as_utf8=0, auto_commit=0, update=None): | debug=0, ignore_ddl=0, as_utf8=0, auto_commit=0, update=None): | ||||
@@ -168,15 +126,43 @@ class Database: | |||||
else: | else: | ||||
return self._cursor.fetchall() | return self._cursor.fetchall() | ||||
def check_transaction_status(self, query): | |||||
if self.in_transaction and query and query.strip().split()[0].lower() in ['start', 'alter', 'drop', 'create']: | |||||
raise Exception, 'This statement can cause implicit commit' | |||||
if query and query.strip().lower()=='start transaction': | |||||
self.in_transaction = 1 | |||||
self.transaction_writes = 0 | |||||
if query and query.strip().split()[0].lower() in ['commit', 'rollback']: | |||||
self.in_transaction = 0 | |||||
if self.in_transaction and query[:6].lower() in ['update', 'insert']: | |||||
self.transaction_writes += 1 | |||||
if self.transaction_writes > 10000: | |||||
if self.auto_commit_on_many_writes: | |||||
webnotes.conn.commit() | |||||
webnotes.conn.begin() | |||||
else: | |||||
webnotes.msgprint('A very long query was encountered. If you are trying to import data, please do so using smaller files') | |||||
raise Exception, 'Bad Query!!! Too many writes' | |||||
def fetch_as_dict(self, formatted=0, as_utf8=0): | |||||
result = self._cursor.fetchall() | |||||
ret = [] | |||||
for r in result: | |||||
row_dict = webnotes._dict({}) | |||||
for i in range(len(r)): | |||||
val = self.convert_to_simple_type(r[i], formatted) | |||||
if as_utf8 and type(val) is unicode: | |||||
val = val.encode('utf-8') | |||||
row_dict[self._cursor.description[i][0]] = val | |||||
ret.append(row_dict) | |||||
return ret | |||||
def get_description(self): | def get_description(self): | ||||
""" | |||||
Get metadata of the last query | |||||
""" | |||||
return self._cursor.description | return self._cursor.description | ||||
# ====================================================================================== | |||||
def convert_to_simple_type(self, v, formatted=0): | def convert_to_simple_type(self, v, formatted=0): | ||||
import datetime | import datetime | ||||
from webnotes.utils import formatdate, fmt_money | from webnotes.utils import formatdate, fmt_money | ||||
@@ -211,12 +197,7 @@ class Database: | |||||
return v | return v | ||||
# ====================================================================================== | |||||
def convert_to_lists(self, res, formatted=0, as_utf8=0): | def convert_to_lists(self, res, formatted=0, as_utf8=0): | ||||
""" | |||||
Convert the given result set to a list of lists (with cleaned up dates and decimals) | |||||
""" | |||||
nres = [] | nres = [] | ||||
for r in res: | for r in res: | ||||
nr = [] | nr = [] | ||||
@@ -227,13 +208,8 @@ class Database: | |||||
nr.append(val) | nr.append(val) | ||||
nres.append(nr) | nres.append(nr) | ||||
return nres | return nres | ||||
# ====================================================================================== | |||||
def convert_to_utf8(self, res, formatted=0): | def convert_to_utf8(self, res, formatted=0): | ||||
""" | |||||
Convert the given result set to a list of lists and as utf8 (with cleaned up dates and decimals) | |||||
""" | |||||
nres = [] | nres = [] | ||||
for r in res: | for r in res: | ||||
nr = [] | nr = [] | ||||
@@ -311,8 +287,6 @@ class Database: | |||||
self.set_value(doc.doctype, doc.name, field, val, doc.modified, doc.modified_by) | self.set_value(doc.doctype, doc.name, field, val, doc.modified, doc.modified_by) | ||||
doc.fields[field] = val | doc.fields[field] = val | ||||
# ====================================================================================== | |||||
def set_global(self, key, val, user='__global'): | def set_global(self, key, val, user='__global'): | ||||
res = self.sql('select defkey from `tabDefaultValue` where defkey=%s and parent=%s', (key, user)) | res = self.sql('select defkey from `tabDefaultValue` where defkey=%s and parent=%s', (key, user)) | ||||
if res: | if res: | ||||
@@ -324,8 +298,6 @@ class Database: | |||||
g = self.sql("select defvalue from tabDefaultValue where defkey=%s and parent=%s", (key, user)) | g = self.sql("select defvalue from tabDefaultValue where defkey=%s and parent=%s", (key, user)) | ||||
return g and g[0][0] or None | return g and g[0][0] or None | ||||
# ====================================================================================== | |||||
def set_default(self, key, val, parent="Control Panel"): | def set_default(self, key, val, parent="Control Panel"): | ||||
"""set control panel default (tabDefaultVal)""" | """set control panel default (tabDefaultVal)""" | ||||
@@ -338,7 +310,6 @@ class Database: | |||||
else: | else: | ||||
self.add_default(key, val, parent) | self.add_default(key, val, parent) | ||||
def add_default(self, key, val, parent="Control Panel"): | def add_default(self, key, val, parent="Control Panel"): | ||||
d = webnotes.doc('DefaultValue') | d = webnotes.doc('DefaultValue') | ||||
d.parent = parent | d.parent = parent | ||||
@@ -374,22 +345,13 @@ class Database: | |||||
def commit(self): | def commit(self): | ||||
self.sql("commit") | self.sql("commit") | ||||
def rollback(self): | def rollback(self): | ||||
self.sql("ROLLBACK") | self.sql("ROLLBACK") | ||||
# ====================================================================================== | |||||
def field_exists(self, dt, fn): | def field_exists(self, dt, fn): | ||||
""" | |||||
Returns True if `fn` exists in `DocType` `dt` | |||||
""" | |||||
return self.sql("select name from tabDocField where fieldname=%s and parent=%s", (dt, fn)) | return self.sql("select name from tabDocField where fieldname=%s and parent=%s", (dt, fn)) | ||||
def exists(self, dt, dn=None): | def exists(self, dt, dn=None): | ||||
""" | |||||
Returns true if the record exists | |||||
""" | |||||
if isinstance(dt, basestring): | if isinstance(dt, basestring): | ||||
try: | try: | ||||
return self.sql('select name from `tab%s` where name=%s' % (dt, '%s'), dn) | return self.sql('select name from `tab%s` where name=%s' % (dt, '%s'), dn) | ||||
@@ -409,11 +371,7 @@ class Database: | |||||
def get_table_columns(self, doctype): | def get_table_columns(self, doctype): | ||||
return [r[0] for r in self.sql("DESC `tab%s`" % doctype)] | return [r[0] for r in self.sql("DESC `tab%s`" % doctype)] | ||||
# ====================================================================================== | |||||
def close(self): | def close(self): | ||||
""" | |||||
Close my connection | |||||
""" | |||||
if self._conn: | if self._conn: | ||||
self._cursor.close() | self._cursor.close() | ||||
self._conn.close() | self._conn.close() | ||||
@@ -76,7 +76,7 @@ def add(args=None): | |||||
return get(args) | return get(args) | ||||
@webnotes.whitelist() | @webnotes.whitelist() | ||||
def remove(args=None): | |||||
def remove(doctype, name, assign_to): | |||||
"""remove from todo""" | """remove from todo""" | ||||
if not args: | if not args: | ||||
args = webnotes.form_dict | args = webnotes.form_dict | ||||
@@ -84,11 +84,15 @@ def remove(args=None): | |||||
res = webnotes.conn.sql("""\ | res = webnotes.conn.sql("""\ | ||||
select assigned_by, owner, reference_type, reference_name from `tabToDo` | select assigned_by, owner, reference_type, reference_name from `tabToDo` | ||||
where reference_type=%(doctype)s and reference_name=%(name)s | where reference_type=%(doctype)s and reference_name=%(name)s | ||||
and owner=%(assign_to)s""", args) | |||||
and owner=%(assign_to)s""", locals()) | |||||
webnotes.conn.sql("""delete from `tabToDo` | webnotes.conn.sql("""delete from `tabToDo` | ||||
where reference_type=%(doctype)s and reference_name=%(name)s | where reference_type=%(doctype)s and reference_name=%(name)s | ||||
and owner=%(assign_to)s""", args) | |||||
and owner=%(assign_to)s""", locals()) | |||||
# clear assigned_to if field exists | |||||
if "assigned_to" in webnotes.conn.get_columns(doctype): | |||||
webnotes.conn.set_value(doctype, name, "assigned_to", None) | |||||
if res and res[0]: notify_assignment(res[0][0], res[0][1], res[0][2], res[0][3]) | if res and res[0]: notify_assignment(res[0][0], res[0][1], res[0][2], res[0][3]) | ||||