@@ -47,9 +47,18 @@ cur_frm.fields_dict['dt'].get_query = function(doc, dt, dn) { | |||
} | |||
cur_frm.cscript.fieldtype = function(doc, dt, dn) { | |||
if(doc.fieldtype == 'Link') cur_frm.fields_dict['options_help'].disp_area.innerHTML = 'Please enter name of the document you want this field to be linked to in <b>Options</b>.<br> Eg.: Customer'; | |||
else if(doc.fieldtype == 'Select') cur_frm.fields_dict['options_help'].disp_area.innerHTML = 'Please enter values in <b>Options</b>, with each option on a new line. <br>Eg.: <b>Field:</b> Country <br><b>Options:</b><br>China<br>India<br>United States<br><br><b>'; | |||
else cur_frm.fields_dict['options_help'].disp_area.innerHTML = ''; | |||
if(doc.fieldtype == 'Link') { | |||
cur_frm.fields_dict['options_help'].disp_area.innerHTML = | |||
__('Name of the Document Type (DocType) you want this field to be linked to. e.g. Customer'); | |||
} else if(doc.fieldtype == 'Select') { | |||
cur_frm.fields_dict['options_help'].disp_area.innerHTML = | |||
__('Options for select. Each option on a new line. e.g.: <br>Option 1<br>Option 2<br>Option 3<br>'); | |||
} else if(doc.fieldtype == 'Dynamic Link') { | |||
cur_frm.fields_dict['options_help'].disp_area.innerHTML = | |||
__('Fieldname which will be the DocType for this link field.'); | |||
} else { | |||
cur_frm.fields_dict['options_help'].disp_area.innerHTML = ''; | |||
} | |||
} | |||
@@ -57,7 +57,7 @@ | |||
"no_copy": 0, | |||
"oldfieldname": "fieldtype", | |||
"oldfieldtype": "Select", | |||
"options": "Button\nCheck\nCode\nColumn Break\nCurrency\nData\nDate\nDatetime\nFloat\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime", | |||
"options": "Button\nCheck\nCode\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime", | |||
"permlevel": 0, | |||
"reqd": 1, | |||
"search_index": 0 | |||
@@ -257,7 +257,7 @@ | |||
], | |||
"icon": "icon-glass", | |||
"idx": 1, | |||
"modified": "2014-05-26 03:21:02.832530", | |||
"modified": "2014-06-20 05:54:17.225853", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "Custom Field", | |||
@@ -34,7 +34,7 @@ | |||
"label": "Type", | |||
"oldfieldname": "fieldtype", | |||
"oldfieldtype": "Select", | |||
"options": "Attach\nButton\nCheck\nCode\nColumn Break\nCurrency\nData\nDate\nDatetime\nFloat\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime", | |||
"options": "Attach\nButton\nCheck\nCode\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime", | |||
"permlevel": 0, | |||
"reqd": 1, | |||
"search_index": 1 | |||
@@ -304,7 +304,7 @@ | |||
"in_dialog": 1, | |||
"issingle": 0, | |||
"istable": 1, | |||
"modified": "2014-05-26 03:00:13.705058", | |||
"modified": "2014-06-20 05:42:29.975498", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "DocField", | |||
@@ -161,6 +161,7 @@ class DocType(Document): | |||
def validate_fields_for_doctype(doctype): | |||
validate_fields(frappe.get_meta(doctype).get("fields")) | |||
# this is separate because it is also called via custom field | |||
def validate_fields(fields): | |||
def check_illegal_characters(fieldname): | |||
for c in ['.', ',', ' ', '-', '&', '%', '=', '"', "'", '*', '$', | |||
@@ -204,6 +205,13 @@ def validate_fields(fields): | |||
if d.in_list_view and d.fieldtype!="Image" and (d.fieldtype in no_value_fields): | |||
frappe.throw(_("'In List View' not allowed for type {0} in row {1}").format(d.fieldtype, d.idx)) | |||
def check_dynamic_link_options(d): | |||
if d.fieldtype=="Dynamic Link": | |||
doctype_pointer = filter(lambda df: df.fieldname==d.options, fields) | |||
if not doctype_pointer or (doctype_pointer[0].fieldtype!="Link") \ | |||
or (doctype_pointer[0].options!="DocType"): | |||
frappe.throw(_("Options 'Dynamic Link' type of field must point to another Link Field with options as 'DocType'")) | |||
for d in fields: | |||
if not d.permlevel: d.permlevel = 0 | |||
if not d.fieldname: | |||
@@ -212,6 +220,7 @@ def validate_fields(fields): | |||
check_unique_fieldname(d.fieldname) | |||
check_illegal_mandatory(d) | |||
check_link_table_options(d) | |||
check_dynamic_link_options(d) | |||
check_hidden_and_mandatory(d) | |||
check_in_list_view(d) | |||
@@ -218,24 +218,26 @@ | |||
}, | |||
{ | |||
"fieldname": "ref_type", | |||
"fieldtype": "Data", | |||
"fieldtype": "Link", | |||
"hidden": 0, | |||
"label": "Ref Type", | |||
"no_copy": 0, | |||
"oldfieldname": "ref_type", | |||
"oldfieldtype": "Data", | |||
"options": "DocType", | |||
"permlevel": 0, | |||
"read_only": 1, | |||
"read_only": 0, | |||
"search_index": 0 | |||
}, | |||
{ | |||
"fieldname": "ref_name", | |||
"fieldtype": "Data", | |||
"fieldtype": "Dynamic Link", | |||
"hidden": 0, | |||
"label": "Ref Name", | |||
"no_copy": 0, | |||
"oldfieldname": "ref_name", | |||
"oldfieldtype": "Data", | |||
"options": "ref_type", | |||
"permlevel": 0, | |||
"read_only": 1, | |||
"search_index": 0 | |||
@@ -244,7 +246,7 @@ | |||
"icon": "icon-calendar", | |||
"idx": 1, | |||
"in_create": 1, | |||
"modified": "2014-05-27 03:49:10.612463", | |||
"modified": "2014-06-20 06:40:05.415405", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "Event", | |||
@@ -91,13 +91,14 @@ | |||
{ | |||
"allow_on_submit": 0, | |||
"fieldname": "reference_type", | |||
"fieldtype": "Data", | |||
"fieldtype": "Link", | |||
"hidden": 0, | |||
"in_filter": 0, | |||
"label": "Reference Type", | |||
"no_copy": 0, | |||
"oldfieldname": "reference_type", | |||
"oldfieldtype": "Data", | |||
"options": "DocType", | |||
"permlevel": 0, | |||
"print_hide": 0, | |||
"report_hide": 0, | |||
@@ -107,13 +108,14 @@ | |||
{ | |||
"allow_on_submit": 0, | |||
"fieldname": "reference_name", | |||
"fieldtype": "Data", | |||
"fieldtype": "Dynamic Link", | |||
"hidden": 0, | |||
"in_filter": 0, | |||
"label": "Reference Name", | |||
"no_copy": 0, | |||
"oldfieldname": "reference_name", | |||
"oldfieldtype": "Data", | |||
"options": "reference_type", | |||
"permlevel": 0, | |||
"print_hide": 0, | |||
"report_hide": 0, | |||
@@ -158,7 +160,7 @@ | |||
"in_dialog": 0, | |||
"issingle": 0, | |||
"max_attachments": 0, | |||
"modified": "2014-05-27 03:49:21.667888", | |||
"modified": "2014-06-20 06:20:11.947183", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "ToDo", | |||
@@ -264,11 +264,17 @@ class BaseDocument(object): | |||
return "{}: {}".format(_(df.label), docname) | |||
invalid_links = [] | |||
for df in self.meta.get_link_fields(): | |||
doctype = df.options | |||
for df in self.meta.get_link_fields() + self.meta.get("fields", | |||
{"fieldtype":"Dynamic Link"}): | |||
if not doctype: | |||
frappe.throw(_("Options not set for link field {0}").format(df.fieldname)) | |||
if df.fieldtype=="Link": | |||
doctype = df.options | |||
if not doctype: | |||
frappe.throw(_("Options not set for link field {0}").format(df.fieldname)) | |||
else: | |||
doctype = self.get(df.options) | |||
if not doctype: | |||
frappe.throw(_("{0} must be set first").format(self.meta.get_label(df.options))) | |||
docname = self.get(df.fieldname) | |||
if docname: | |||
@@ -29,6 +29,7 @@ type_map = { | |||
,'Text': ('text', '') | |||
,'Data': ('varchar', '255') | |||
,'Link': ('varchar', '255') | |||
,'Dynamic Link':('varchar', '255') | |||
,'Password': ('varchar', '255') | |||
,'Select': ('varchar', '255') | |||
,'Read Only': ('varchar', '255') | |||
@@ -8,6 +8,7 @@ import frappe.model.meta | |||
import frappe.defaults | |||
from frappe.utils.file_manager import remove_all | |||
from frappe import _ | |||
from rename_doc import dynamic_link_queries | |||
def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reload=False, ignore_permissions=False): | |||
""" | |||
@@ -48,6 +49,7 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa | |||
# check if links exist | |||
if not force: | |||
check_if_doc_is_linked(doc) | |||
check_if_doc_is_dynamically_linked(doc) | |||
delete_from_table(doctype, name, ignore_doctypes, doc) | |||
@@ -106,3 +108,21 @@ def check_if_doc_is_linked(doc, method="Delete"): | |||
frappe.throw(_("Cannot delete or cancel because {0} {1} is linked with {2} {3}").format(doc.doctype, | |||
doc.name, item.parent or item.name, item.parenttype if item.parent else link_dt), | |||
frappe.LinkExistsError) | |||
def check_if_doc_is_dynamically_linked(doc): | |||
for query in dynamic_link_queries: | |||
for df in frappe.db.sql(query, as_dict=True): | |||
if frappe.get_meta(df.parent).issingle: | |||
# dynamic link in single doc | |||
refdoc = frappe.get_singles_dict(df.parent) | |||
if refdoc.get(df.options)==doc.doctype and refdoc.get(df.fieldname)==doc.name: | |||
frappe.throw(_("Cannot delete or cancel because {0} {1} is linked with {2} {3}").format(doc.doctype, | |||
doc.name, df.parent, df.parent), frappe.LinkExistsError) | |||
else: | |||
# dynamic link in table | |||
for name in frappe.db.sql_list("""select name from `tab{parent}` where | |||
{options}=%s and {fieldname}=%s""".format(**df), (doc.doctype, doc.name)): | |||
frappe.throw(_("Cannot delete or cancel because {0} {1} is linked with {2} {3}").format(doc.doctype, | |||
doc.name, df.parent, name), frappe.LinkExistsError) |
@@ -494,7 +494,7 @@ class Document(BaseDocument): | |||
val1 = cint(val1) | |||
val2 = cint(val2) | |||
elif df.fieldtype in ("Data", "Text", "Small Text", "Long Text", | |||
"Text Editor", "Select", "Link"): | |||
"Text Editor", "Select", "Link", "Dynamic Link"): | |||
val1 = cstr(val1) | |||
val2 = cstr(val2) | |||
@@ -33,6 +33,8 @@ def rename_doc(doctype, old, new, force=False, merge=False, ignore_permissions=F | |||
link_fields = get_link_fields(doctype) | |||
update_link_field_values(link_fields, old, new, doctype) | |||
rename_dynamic_links(doctype, old, new) | |||
if doctype=='DocType': | |||
rename_doctype(doctype, old, new, force) | |||
@@ -274,3 +276,27 @@ def update_parenttype_values(old, new): | |||
update `tab%s` set parenttype=%s | |||
where parenttype=%s""" % (doctype, '%s', '%s'), | |||
(new, old)) | |||
dynamic_link_queries = [ | |||
"""select parent, fieldname, options from tabDocField where fieldtype='Dynamic Link'""", | |||
"""select dt as parent, fieldname, options from `tabCustom Field` where fieldtype='Dynamic Link'""", | |||
] | |||
def rename_dynamic_links(doctype, old, new): | |||
for query in dynamic_link_queries: | |||
for df in frappe.db.sql(query, as_dict=True): | |||
# dynamic link in single, just one value to check | |||
if frappe.get_meta(df.parent).issingle: | |||
refdoc = frappe.get_singles_dict(df.parent) | |||
if refdoc.get(df.options)==doctype and refdoc.get(df.fieldname)==old: | |||
frappe.db.sql("""update tabSingles set value=%s where | |||
field=%s and value=%s""", (new, df.fieldname, old)) | |||
else: | |||
# replace for each value where renamed | |||
for to_change in frappe.db.sql_list("""select name from `tab{parent}` where | |||
{options}=%s and {fieldname}=%s""".format(**df), (doctype, old)): | |||
frappe.db.sql("""update `tab{parent}` set {fieldname}=%s | |||
where name=%s""".format(**df), (new, to_change)) |
@@ -1,7 +1,7 @@ | |||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||
// MIT License. See license.txt | |||
frappe.ui.form.make_control = function(opts) { | |||
frappe.ui.form.make_control = function (opts) { | |||
var control_class_name = "Control" + opts.df.fieldtype.replace(/ /g, ""); | |||
if(frappe.ui.form[control_class_name]) { | |||
return new frappe.ui.form[control_class_name](opts); | |||
@@ -802,32 +802,36 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ | |||
this.setup_buttons(); | |||
this.setup_autocomplete(); | |||
}, | |||
get_options: function() { | |||
return this.df.options; | |||
}, | |||
setup_buttons: function() { | |||
var me = this; | |||
// magnifier - search | |||
this.$input_area.find(".btn-search").on("click", function() { | |||
var doctype = me.get_options(); | |||
if(!doctype) return; | |||
new frappe.ui.form.LinkSelector({ | |||
doctype: me.df.options, | |||
doctype: doctype, | |||
target: me, | |||
txt: me.get_value() | |||
}); | |||
}); | |||
// open | |||
if(frappe.model.can_read(me.df.options)) { | |||
this.$input_area.find(".btn-open").on("click", function() { | |||
var value = me.get_value(); | |||
if(value && me.df.options) frappe.set_route("Form", me.df.options, value); | |||
}); | |||
} else { | |||
this.$input_area.find(".btn-open").remove(); | |||
} | |||
this.$input_area.find(".btn-open").on("click", function() { | |||
var value = me.get_value(); | |||
if(value && me.get_options()) | |||
frappe.set_route("Form", me.get_options(), value); | |||
}); | |||
// new | |||
if(frappe.model.can_create(me.df.options)) { | |||
if(this.df.fieldtype==="Dynamic Link" || frappe.model.can_create(me.df.options)) { | |||
this.$input_area.find(".btn-new").on("click", function() { | |||
me.frm.new_doc(me.df.options, me); | |||
var doctype = me.get_options(); | |||
if(!doctype) return; | |||
me.frm.new_doc(doctype, me); | |||
}); | |||
} else { | |||
this.$input_area.find(".btn-new").remove(); | |||
@@ -854,18 +858,20 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ | |||
this.$input.autocomplete({ | |||
minLength: 0, | |||
source: function(request, response) { | |||
if (!me.$input.cache[me.df.options]) { | |||
me.$input.cache[me.df.options] = {}; | |||
var doctype = me.get_options(); | |||
if(!doctype) return; | |||
if (!me.$input.cache[doctype]) { | |||
me.$input.cache[doctype] = {}; | |||
} | |||
if (me.$input.cache[me.df.options][request.term]!=null) { | |||
if (me.$input.cache[doctype][request.term]!=null) { | |||
// immediately show from cache | |||
response(me.$input.cache[me.df.options][request.term]); | |||
response(me.$input.cache[doctype][request.term]); | |||
} | |||
var args = { | |||
'txt': request.term, | |||
'doctype': me.df.options, | |||
'doctype': doctype, | |||
}; | |||
me.set_custom_query(args); | |||
@@ -876,13 +882,13 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ | |||
no_spinner: true, | |||
args: args, | |||
callback: function(r) { | |||
if(frappe.model.can_create(me.df.options)) { | |||
if(frappe.model.can_create(doctype)) { | |||
r.results.push({ | |||
value: "<i class='icon-plus'></i> <em>" + __("Create a new {0}", [me.df.options]) + "</em>", | |||
make_new: true | |||
}); | |||
}; | |||
me.$input.cache[me.df.options][request.term] = r.results; | |||
me.$input.cache[doctype][request.term] = r.results; | |||
response(r.results); | |||
}, | |||
}); | |||
@@ -901,10 +907,13 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ | |||
select: function(event, ui) { | |||
me.autocomplete_open = false; | |||
if(ui.item.make_new) { | |||
var doctype = me.get_options(); | |||
if(!doctype) return; | |||
if (me.frm) { | |||
me.frm.new_doc(me.df.options, me); | |||
me.frm.new_doc(doctype, me); | |||
} else { | |||
new_doc(me.df.options); | |||
new_doc(doctype); | |||
} | |||
return false; | |||
} | |||
@@ -968,10 +977,21 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ | |||
return; | |||
} | |||
this.frm.script_manager.validate_link_and_fetch(this.df, this.docname, value, callback); | |||
this.frm.script_manager.validate_link_and_fetch(this.df, this.get_options(), | |||
this.docname, value, callback); | |||
}, | |||
}); | |||
frappe.ui.form.ControlDynamicLink = frappe.ui.form.ControlLink.extend({ | |||
get_options: function() { | |||
var options = frappe.model.get_value(this.df.parent, this.docname, this.df.options); | |||
if(!options) { | |||
msgprint(__("Please set {0} first", | |||
[frappe.meta.get_docfield(this.df.parent, this.df.options, this.docname).label])); | |||
} | |||
return options; | |||
}, | |||
}); | |||
frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({ | |||
make_input: function() { | |||
@@ -33,6 +33,7 @@ frappe.form.formatters = { | |||
return value ? "<i class='icon-check'></i>" : "<i class='icon-check-empty'></i>"; | |||
}, | |||
Link: function(value, docfield, options) { | |||
var doctype = docfield._options || docfield.options; | |||
if(options && options.for_print) | |||
return value; | |||
if(!value) | |||
@@ -40,13 +41,13 @@ frappe.form.formatters = { | |||
if(docfield && docfield.link_onclick) { | |||
return repl('<a onclick="%(onclick)s">%(value)s</a>', | |||
{onclick: docfield.link_onclick.replace(/"/g, '"'), value:value}); | |||
} else if(docfield && docfield.options) { | |||
} else if(docfield && doctype) { | |||
return repl('%(icon)s<a href="#Form/%(doctype)s/%(name)s">%(label)s</a>', { | |||
doctype: encodeURIComponent(docfield.options), | |||
doctype: encodeURIComponent(doctype), | |||
name: encodeURIComponent(value), | |||
label: value, | |||
icon: (options && options.no_icon) ? "" : | |||
('<i class="icon-fixed-width '+frappe.boot.doctype_icons[docfield.options]+'"></i> ') | |||
('<i class="icon-fixed-width '+frappe.boot.doctype_icons[doctype]+'"></i> ') | |||
}); | |||
} else { | |||
return value; | |||
@@ -119,11 +120,20 @@ frappe.form.formatters = { | |||
} | |||
frappe.form.get_formatter = function(fieldtype) { | |||
if(!fieldtype) fieldtype = "Data"; | |||
if(!fieldtype) | |||
fieldtype = "Data"; | |||
return frappe.form.formatters[fieldtype.replace(/ /g, "")] || frappe.form.formatters.Data; | |||
} | |||
frappe.format = function(value, df, options, doc) { | |||
if(!df) df = {"fieldtype":"Data"}; | |||
return frappe.form.get_formatter(df.fieldtype)(value, df, options, doc); | |||
var fieldtype = df.fieldtype || "Data"; | |||
// format Dynamic Link as a Link | |||
if(fieldtype==="Dynamic Link") { | |||
fieldtype = "Link"; | |||
df._options = doc ? doc[df.options] : null; | |||
} | |||
return frappe.form.get_formatter(fieldtype)(value, df, options, doc); | |||
} |
@@ -80,7 +80,7 @@ frappe.ui.form.ScriptManager = Class.extend({ | |||
console.log("----- end of error message -----"); | |||
console.group && console.groupEnd(); | |||
}, | |||
validate_link_and_fetch: function(df, docname, value, callback) { | |||
validate_link_and_fetch: function(df, doctype, docname, value, callback) { | |||
var me = this; | |||
if(value) { | |||
@@ -94,7 +94,7 @@ frappe.ui.form.ScriptManager = Class.extend({ | |||
type: "GET", | |||
args: { | |||
'value': value, | |||
'options': df.options, | |||
'options': doctype, | |||
'fetch': fetch | |||
}, | |||
no_spinner: true, | |||
@@ -255,7 +255,7 @@ frappe.ui.Filter = Class.extend({ | |||
if(df.fieldtype=='Check') { | |||
df.fieldtype='Select'; | |||
df.options='No\nYes'; | |||
} else if(['Text','Small Text','Text Editor','Code','Tag','Comments'].indexOf(df.fieldtype)!=-1) { | |||
} else if(['Text','Small Text','Text Editor','Code','Tag','Comments','Dynamic Link'].indexOf(df.fieldtype)!=-1) { | |||
df.fieldtype = 'Data'; | |||
} else if(df.fieldtype=='Link' && this.$w.find('.condition').val()!="=") { | |||
df.fieldtype = 'Data'; | |||
@@ -87,9 +87,11 @@ frappe.views.DocListView = frappe.ui.Listing.extend({ | |||
this.make_help(); | |||
this.$page.find(".show_filters").css({"padding":"15px", "margin":"0px -15px"}); | |||
var me = this; | |||
// this.$w.on("render-complete", function() { | |||
// me.set_sidebar_height(); | |||
// }); | |||
this.$w.on("render-complete", function() { | |||
if(me.data.length===1) { | |||
frappe.set_route("Form", me.doctype, me.data[0].name); | |||
} | |||
}); | |||
}, | |||
set_sidebar_height: function() { | |||