@@ -460,7 +460,7 @@ def has_website_permission(doctype, ptype="read", doc=None, user=None, verbose=F | |||||
doc = get_doc(doctype, doc) | doc = get_doc(doctype, doc) | ||||
for method in hooks: | for method in hooks: | ||||
result = call(get_attr(method), doc=doc, ptype=ptype, user=user, verbose=verbose) | |||||
result = call(method, doc=doc, ptype=ptype, user=user, verbose=verbose) | |||||
# if even a single permission check is Falsy | # if even a single permission check is Falsy | ||||
if not result: | if not result: | ||||
return False | return False | ||||
@@ -789,6 +789,9 @@ def get_attr(method_string): | |||||
def call(fn, *args, **kwargs): | def call(fn, *args, **kwargs): | ||||
"""Call a function and match arguments.""" | """Call a function and match arguments.""" | ||||
if isinstance(fn, basestring): | |||||
fn = get_attr(fn) | |||||
if hasattr(fn, 'fnargs'): | if hasattr(fn, 'fnargs'): | ||||
fnargs = fn.fnargs | fnargs = fn.fnargs | ||||
else: | else: | ||||
@@ -7,6 +7,7 @@ | |||||
"custom": 0, | "custom": 0, | ||||
"docstatus": 0, | "docstatus": 0, | ||||
"doctype": "DocType", | "doctype": "DocType", | ||||
"document_type": "Setup", | |||||
"fields": [ | "fields": [ | ||||
{ | { | ||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
@@ -19,7 +20,7 @@ | |||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"label": "Description and Status", | |||||
"label": "", | |||||
"length": 0, | "length": 0, | ||||
"no_copy": 0, | "no_copy": 0, | ||||
"permlevel": 0, | "permlevel": 0, | ||||
@@ -36,29 +37,55 @@ | |||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
"bold": 0, | "bold": 0, | ||||
"collapsible": 0, | "collapsible": 0, | ||||
"fieldname": "description", | |||||
"fieldtype": "Text", | |||||
"default": "Open", | |||||
"fieldname": "status", | |||||
"fieldtype": "Select", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 1, | |||||
"label": "Status", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "Open\nClosed", | |||||
"permlevel": 0, | |||||
"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, | |||||
"default": "Medium", | |||||
"fieldname": "priority", | |||||
"fieldtype": "Select", | |||||
"hidden": 0, | "hidden": 0, | ||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"label": "Description", | |||||
"label": "Priority", | |||||
"length": 0, | "length": 0, | ||||
"no_copy": 0, | "no_copy": 0, | ||||
"oldfieldname": "description", | |||||
"oldfieldtype": "Text", | |||||
"oldfieldname": "priority", | |||||
"oldfieldtype": "Data", | |||||
"options": "High\nMedium\nLow", | |||||
"permlevel": 0, | "permlevel": 0, | ||||
"print_hide": 0, | "print_hide": 0, | ||||
"print_hide_if_no_value": 0, | "print_hide_if_no_value": 0, | ||||
"print_width": "300px", | |||||
"read_only": 0, | "read_only": 0, | ||||
"report_hide": 0, | "report_hide": 0, | ||||
"reqd": 1, | |||||
"reqd": 0, | |||||
"search_index": 0, | "search_index": 0, | ||||
"set_only_once": 0, | "set_only_once": 0, | ||||
"unique": 0, | |||||
"width": "300px" | |||||
"unique": 0 | |||||
}, | }, | ||||
{ | { | ||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
@@ -87,18 +114,18 @@ | |||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
"bold": 0, | "bold": 0, | ||||
"collapsible": 0, | "collapsible": 0, | ||||
"default": "Open", | |||||
"fieldname": "status", | |||||
"fieldtype": "Select", | |||||
"fieldname": "date", | |||||
"fieldtype": "Date", | |||||
"hidden": 0, | "hidden": 0, | ||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_list_view": 1, | |||||
"label": "Status", | |||||
"in_list_view": 0, | |||||
"label": "Due Date", | |||||
"length": 0, | "length": 0, | ||||
"no_copy": 0, | "no_copy": 0, | ||||
"options": "Open\nClosed", | |||||
"oldfieldname": "date", | |||||
"oldfieldtype": "Date", | |||||
"permlevel": 0, | "permlevel": 0, | ||||
"print_hide": 0, | "print_hide": 0, | ||||
"print_hide_if_no_value": 0, | "print_hide_if_no_value": 0, | ||||
@@ -113,20 +140,17 @@ | |||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
"bold": 0, | "bold": 0, | ||||
"collapsible": 0, | "collapsible": 0, | ||||
"default": "Medium", | |||||
"fieldname": "priority", | |||||
"fieldtype": "Select", | |||||
"fieldname": "owner", | |||||
"fieldtype": "Link", | |||||
"hidden": 0, | "hidden": 0, | ||||
"ignore_user_permissions": 0, | |||||
"ignore_user_permissions": 1, | |||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"label": "Priority", | |||||
"label": "Allocated To", | |||||
"length": 0, | "length": 0, | ||||
"no_copy": 0, | "no_copy": 0, | ||||
"oldfieldname": "priority", | |||||
"oldfieldtype": "Data", | |||||
"options": "High\nMedium\nLow", | |||||
"options": "User", | |||||
"permlevel": 0, | "permlevel": 0, | ||||
"print_hide": 0, | "print_hide": 0, | ||||
"print_hide_if_no_value": 0, | "print_hide_if_no_value": 0, | ||||
@@ -141,19 +165,18 @@ | |||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
"bold": 0, | "bold": 0, | ||||
"collapsible": 0, | "collapsible": 0, | ||||
"fieldname": "date", | |||||
"fieldtype": "Date", | |||||
"fieldname": "description_section", | |||||
"fieldtype": "Section Break", | |||||
"hidden": 0, | "hidden": 0, | ||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"label": "Due Date", | |||||
"label": "", | |||||
"length": 0, | "length": 0, | ||||
"no_copy": 0, | "no_copy": 0, | ||||
"oldfieldname": "date", | |||||
"oldfieldtype": "Date", | |||||
"permlevel": 0, | "permlevel": 0, | ||||
"precision": "", | |||||
"print_hide": 0, | "print_hide": 0, | ||||
"print_hide_if_no_value": 0, | "print_hide_if_no_value": 0, | ||||
"read_only": 0, | "read_only": 0, | ||||
@@ -167,26 +190,29 @@ | |||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
"bold": 0, | "bold": 0, | ||||
"collapsible": 0, | "collapsible": 0, | ||||
"fieldname": "owner", | |||||
"fieldtype": "Link", | |||||
"fieldname": "description", | |||||
"fieldtype": "Text Editor", | |||||
"hidden": 0, | "hidden": 0, | ||||
"ignore_user_permissions": 1, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"label": "Allocated To", | |||||
"label": "Description", | |||||
"length": 0, | "length": 0, | ||||
"no_copy": 0, | "no_copy": 0, | ||||
"options": "User", | |||||
"oldfieldname": "description", | |||||
"oldfieldtype": "Text", | |||||
"permlevel": 0, | "permlevel": 0, | ||||
"print_hide": 0, | "print_hide": 0, | ||||
"print_hide_if_no_value": 0, | "print_hide_if_no_value": 0, | ||||
"print_width": "300px", | |||||
"read_only": 0, | "read_only": 0, | ||||
"report_hide": 0, | "report_hide": 0, | ||||
"reqd": 0, | |||||
"reqd": 1, | |||||
"search_index": 0, | "search_index": 0, | ||||
"set_only_once": 0, | "set_only_once": 0, | ||||
"unique": 0 | |||||
"unique": 0, | |||||
"width": "300px" | |||||
}, | }, | ||||
{ | { | ||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
@@ -370,14 +396,14 @@ | |||||
"hide_heading": 0, | "hide_heading": 0, | ||||
"hide_toolbar": 0, | "hide_toolbar": 0, | ||||
"icon": "icon-check", | "icon": "icon-check", | ||||
"idx": 0, | |||||
"idx": 2, | |||||
"in_create": 0, | "in_create": 0, | ||||
"in_dialog": 0, | "in_dialog": 0, | ||||
"is_submittable": 0, | "is_submittable": 0, | ||||
"issingle": 0, | "issingle": 0, | ||||
"istable": 0, | "istable": 0, | ||||
"max_attachments": 0, | "max_attachments": 0, | ||||
"modified": "2016-04-01 06:08:48.898505", | |||||
"modified": "2016-04-07 01:35:53.935024", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Desk", | "module": "Desk", | ||||
"name": "ToDo", | "name": "ToDo", | ||||
@@ -17,7 +17,7 @@ def search_link(doctype, txt, query=None, filters=None, page_len=20, searchfield | |||||
# this is called by the search box | # this is called by the search box | ||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def search_widget(doctype, txt, query=None, searchfield=None, start=0, | def search_widget(doctype, txt, query=None, searchfield=None, start=0, | ||||
page_len=10, filters=None): | |||||
page_len=10, filters=None, as_dict=False): | |||||
if isinstance(filters, basestring): | if isinstance(filters, basestring): | ||||
import json | import json | ||||
filters = json.loads(filters) | filters = json.loads(filters) | ||||
@@ -31,8 +31,8 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0, | |||||
if query and query.split()[0].lower()!="select": | if query and query.split()[0].lower()!="select": | ||||
# by method | # by method | ||||
frappe.response["values"] = frappe.get_attr(query)(doctype, txt, | |||||
searchfield, start, page_len, filters) | |||||
frappe.response["values"] = frappe.call(query, doctype, txt, | |||||
searchfield, start, page_len, filters, as_dict=as_dict) | |||||
elif not query and doctype in standard_queries: | elif not query and doctype in standard_queries: | ||||
# from standard queries | # from standard queries | ||||
search_widget(doctype, txt, standard_queries[doctype][0], | search_widget(doctype, txt, standard_queries[doctype][0], | ||||
@@ -89,7 +89,8 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0, | |||||
limit_page_length=page_len, | limit_page_length=page_len, | ||||
order_by="if(_relevance, _relevance, 99999), idx desc, modified desc".format(doctype), | order_by="if(_relevance, _relevance, 99999), idx desc, modified desc".format(doctype), | ||||
ignore_permissions = True if doctype == "DocType" else False, # for dynamic links | ignore_permissions = True if doctype == "DocType" else False, # for dynamic links | ||||
as_list=True) | |||||
as_dict=as_dict, | |||||
as_list=not as_dict) | |||||
# remove _relevance from results | # remove _relevance from results | ||||
frappe.response["values"] = [r[:-1] for r in values] | frappe.response["values"] = [r[:-1] for r in values] | ||||
@@ -31,11 +31,14 @@ frappe.ui.form.Grid = Class.extend({ | |||||
.appendTo(this.parent) | .appendTo(this.parent) | ||||
.attr("data-fieldname", this.df.fieldname); | .attr("data-fieldname", this.df.fieldname); | ||||
$(this.wrapper).find(".grid-add-row").click(function() { | |||||
this.wrapper.find(".grid-add-row").click(function() { | |||||
me.add_new_row(null, null, true); | me.add_new_row(null, null, true); | ||||
return false; | return false; | ||||
}); | }); | ||||
this.custom_buttons = {}; | |||||
this.grid_buttons = this.wrapper.find('.grid-buttons'); | |||||
this.setup_allow_bulk_edit(); | this.setup_allow_bulk_edit(); | ||||
}, | }, | ||||
@@ -344,7 +347,23 @@ frappe.ui.form.Grid = Class.extend({ | |||||
frappe.tools.downloadify(data, null, me.df.label); | frappe.tools.downloadify(data, null, me.df.label); | ||||
return false; | return false; | ||||
}); | }); | ||||
}, | |||||
add_custom_button: function(label, click) { | |||||
// add / unhide a custom button | |||||
var btn = this.custom_buttons[label]; | |||||
if(!btn) { | |||||
btn = $('<button class="btn btn-default btn-xs btn-custom">' + label + '</button>') | |||||
.css('margin-right', '10px') | |||||
.prependTo(this.grid_buttons) | |||||
.on('click', click); | |||||
this.custom_buttons[label] = btn; | |||||
} else { | |||||
btn.removeClass('hidden'); | |||||
} | |||||
}, | |||||
clear_custom_buttons: function() { | |||||
// hide all custom buttons | |||||
this.grid_buttons.find('.btn-custom').addClass('hidden'); | |||||
} | } | ||||
}); | }); | ||||
@@ -6,7 +6,7 @@ | |||||
<div class="grid-empty text-center hide">{%= __("No Data") %}</div> | <div class="grid-empty text-center hide">{%= __("No Data") %}</div> | ||||
<div class="small form-clickable-section grid-footer"> | <div class="small form-clickable-section grid-footer"> | ||||
<div class="row"> | <div class="row"> | ||||
<div class="col-sm-6"> | |||||
<div class="col-sm-6 grid-buttons"> | |||||
<a href="#" class="grid-add-multiple-rows btn btn-xs btn-default hide" | <a href="#" class="grid-add-multiple-rows btn btn-xs btn-default hide" | ||||
style="margin-right: 10px;"> | style="margin-right: 10px;"> | ||||
{%= __("Add multiple rows") %}</a> | {%= __("Add multiple rows") %}</a> | ||||
@@ -498,7 +498,7 @@ frappe.ui.form.Section = Class.extend({ | |||||
var missing_mandatory = false; | var missing_mandatory = false; | ||||
for (var j=0, l=this.fields_list.length; j < l; j++) { | for (var j=0, l=this.fields_list.length; j < l; j++) { | ||||
var section_df = this.fields_list[j].df; | var section_df = this.fields_list[j].df; | ||||
if (section_df.reqd && this.layout.frm.doc[section_df.fieldname]==null) { | |||||
if (section_df.reqd && this.layout.doc[section_df.fieldname]==null) { | |||||
missing_mandatory = true; | missing_mandatory = true; | ||||
break; | break; | ||||
} | } | ||||
@@ -2,7 +2,6 @@ | |||||
// MIT License. See license.txt | // MIT License. See license.txt | ||||
frappe.ui.form.LinkSelector = Class.extend({ | frappe.ui.form.LinkSelector = Class.extend({ | ||||
_help: __("Dialog box to select a Link Value"), | |||||
init: function(opts) { | init: function(opts) { | ||||
/* help: Options: doctype, get_query, target */ | /* help: Options: doctype, get_query, target */ | ||||
$.extend(this, opts); | $.extend(this, opts); | ||||
@@ -49,7 +48,6 @@ frappe.ui.form.LinkSelector = Class.extend({ | |||||
search: function() { | search: function() { | ||||
var args = { | var args = { | ||||
txt: this.dialog.fields_dict.txt.get_value(), | txt: this.dialog.fields_dict.txt.get_value(), | ||||
doctype: this.doctype, | |||||
searchfield: "name" | searchfield: "name" | ||||
}, | }, | ||||
me = this; | me = this; | ||||
@@ -65,55 +63,50 @@ frappe.ui.form.LinkSelector = Class.extend({ | |||||
this.target.fieldinfo[this.fieldname].get_query(cur_frm.doc)); | this.target.fieldinfo[this.fieldname].get_query(cur_frm.doc)); | ||||
} | } | ||||
return frappe.call({ | |||||
method: "frappe.desk.search.search_widget", | |||||
type: "GET", | |||||
args: args, | |||||
callback: function(r) { | |||||
var parent = me.dialog.fields_dict.results.$wrapper; | |||||
parent.empty(); | |||||
if(r.values.length) { | |||||
$.each(r.values, function(i, v) { | |||||
var row = $(repl('<div class="row link-select-row">\ | |||||
<div class="col-xs-4">\ | |||||
<b><a href="#" data-value="%(name)s">%(name)s</a></b></div>\ | |||||
<div class="col-xs-8">\ | |||||
<span class="text-muted">%(values)s</span></div>\ | |||||
</div>', { | |||||
name: v[0], | |||||
values: v.splice(1).join(", ") | |||||
})).appendTo(parent); | |||||
frappe.link_search(this.doctype, args, function(r) { | |||||
var parent = me.dialog.fields_dict.results.$wrapper; | |||||
parent.empty(); | |||||
if(r.values.length) { | |||||
$.each(r.values, function(i, v) { | |||||
var row = $(repl('<div class="row link-select-row">\ | |||||
<div class="col-xs-4">\ | |||||
<b><a href="#" data-value="%(name)s">%(name)s</a></b></div>\ | |||||
<div class="col-xs-8">\ | |||||
<span class="text-muted">%(values)s</span></div>\ | |||||
</div>', { | |||||
name: v[0], | |||||
values: v.splice(1).join(", ") | |||||
})).appendTo(parent); | |||||
row.find("a").click(function() { | |||||
var value = $(this).attr("data-value"); | |||||
var $link = this; | |||||
if(me.target.is_grid) { | |||||
// set in grid | |||||
me.set_in_grid(value); | |||||
} else { | |||||
if(me.target.doctype) | |||||
me.target.parse_validate_and_set_in_model(value); | |||||
else { | |||||
me.target.set_input(value); | |||||
me.target.$input.trigger("change"); | |||||
} | |||||
me.dialog.hide(); | |||||
row.find("a").click(function() { | |||||
var value = $(this).attr("data-value"); | |||||
var $link = this; | |||||
if(me.target.is_grid) { | |||||
// set in grid | |||||
me.set_in_grid(value); | |||||
} else { | |||||
if(me.target.doctype) | |||||
me.target.parse_validate_and_set_in_model(value); | |||||
else { | |||||
me.target.set_input(value); | |||||
me.target.$input.trigger("change"); | |||||
} | } | ||||
return false; | |||||
}) | |||||
me.dialog.hide(); | |||||
} | |||||
return false; | |||||
}) | }) | ||||
} else { | |||||
$('<div class="alert alert-info">' + __("No Results") | |||||
+ (frappe.model.can_read(me.doctype) ? | |||||
('. <a class="new-doc">' | |||||
+ __("Make a new") + " " + __(me.doctype) + "</a>") : '') | |||||
+ '</div>').appendTo(parent).find(".new-doc").click(function() { | |||||
me.target.new_doc(); | |||||
}); | |||||
} | |||||
}, | |||||
btn: this.dialog.get_primary_btn() | |||||
}); | |||||
}) | |||||
} else { | |||||
$('<div class="alert alert-info">' + __("No Results") | |||||
+ (frappe.model.can_read(me.doctype) ? | |||||
('. <a class="new-doc">' | |||||
+ __("Make a new") + " " + __(me.doctype) + "</a>") : '') | |||||
+ '</div>').appendTo(parent).find(".new-doc").click(function() { | |||||
me.target.new_doc(); | |||||
}); | |||||
} | |||||
}, this.dialog.get_primary_btn()); | |||||
}, | }, | ||||
set_in_grid: function(value) { | set_in_grid: function(value) { | ||||
var me = this, updated = false; | var me = this, updated = false; | ||||
@@ -145,4 +138,27 @@ frappe.ui.form.LinkSelector = Class.extend({ | |||||
show_alert(__("{0} added", [value])); | show_alert(__("{0} added", [value])); | ||||
} | } | ||||
} | } | ||||
}) | |||||
}); | |||||
frappe.link_search = function(doctype, args, callback, btn) { | |||||
if(!args) { | |||||
args: { | |||||
txt: '' | |||||
} | |||||
} | |||||
args.doctype = doctype; | |||||
if(!args.searchfield) { | |||||
args.searchfield = 'name'; | |||||
} | |||||
frappe.call({ | |||||
method: "frappe.desk.search.search_widget", | |||||
type: "GET", | |||||
args: args, | |||||
callback: function(r) { | |||||
callback && callback(r); | |||||
}, | |||||
btn: btn | |||||
}); | |||||
} | |||||
@@ -288,9 +288,11 @@ frappe.ui.Page = Class.extend({ | |||||
set_title: function(txt, icon) { | set_title: function(txt, icon) { | ||||
if(!txt) txt = ""; | if(!txt) txt = ""; | ||||
// strip icon | |||||
// strip html | |||||
txt = txt.replace(/<[^>]*>/g, ""); | |||||
this.title = txt; | this.title = txt; | ||||
frappe.utils.set_title(txt.replace(/<[^>]*>/g, "")); | |||||
frappe.utils.set_title(txt); | |||||
if(icon) { | if(icon) { | ||||
txt = '<span class="'+ icon +' text-muted" style="font-size: inherit;"></span> ' + txt; | txt = '<span class="'+ icon +' text-muted" style="font-size: inherit;"></span> ' + txt; | ||||
} | } | ||||
@@ -427,7 +427,7 @@ _f.Frm.prototype.refresh = function(docname) { | |||||
this.print_preview.preview(); | this.print_preview.preview(); | ||||
} | } | ||||
if(is_a_different_doc && this.doc.docstatus===1) { | |||||
if(this.show_print_first && is_a_different_doc && this.doc.docstatus===1) { | |||||
// show print view | // show print view | ||||
this.print_doc(); | this.print_doc(); | ||||
} | } | ||||