From 7faf26c29ffb93f8dffe54cf3504e3901c3d7df3 Mon Sep 17 00:00:00 2001 From: Maxwell Date: Thu, 19 Jun 2014 23:46:43 -0300 Subject: [PATCH 01/16] Fix app doesnt showed in desktop Related to #607, #608 and this discussion on GGroup https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/erpnext-developer-forum/cQMhAZAFiF4/7B8GHkH4RZ0J --- frappe/utils/boilerplate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index 21c2f06e93..80e7bd57ca 100644 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -194,7 +194,7 @@ desktop_template = """from frappe import _ def get_data(): return {{ - "{app_title}": {{ + "{app_name}": {{ "color": "{app_color}", "icon": "{app_icon}", "type": "module", From 5b15e85ada4d66bb2926ab1ab8d63c604d88daa8 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 20 Jun 2014 11:30:52 +0530 Subject: [PATCH 02/16] Do not validate links, selects, mandatory, etc. on cancel --- frappe/model/document.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/model/document.py b/frappe/model/document.py index 1ccc9d2aa9..c5e18e60f7 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -159,7 +159,10 @@ class Document(BaseDocument): self.check_if_latest() self.set_parent_in_children() self.run_before_save_methods() - self._validate() + + if self._action != "cancel": + self._validate() + if self._action == "update_after_submit": self.validate_update_after_submit() From 3fb36672042dc5754f0af24eb156ea34206d0a9e Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 20 Jun 2014 16:47:34 +0530 Subject: [PATCH 03/16] new feature: dynamic links :boom: --- .../core/doctype/custom_field/custom_field.js | 15 ++++- .../doctype/custom_field/custom_field.json | 4 +- frappe/core/doctype/docfield/docfield.json | 4 +- frappe/core/doctype/doctype/doctype.py | 9 +++ frappe/core/doctype/event/event.json | 10 +-- frappe/core/doctype/todo/todo.json | 8 ++- frappe/model/base_document.py | 14 ++-- frappe/model/db_schema.py | 1 + frappe/model/delete_doc.py | 20 ++++++ frappe/model/document.py | 2 +- frappe/model/rename_doc.py | 26 ++++++++ frappe/public/js/frappe/form/control.js | 64 ++++++++++++------- frappe/public/js/frappe/form/formatters.js | 20 ++++-- .../public/js/frappe/form/script_manager.js | 4 +- frappe/public/js/frappe/ui/filters.js | 2 +- frappe/public/js/frappe/views/doclistview.js | 8 ++- 16 files changed, 159 insertions(+), 52 deletions(-) diff --git a/frappe/core/doctype/custom_field/custom_field.js b/frappe/core/doctype/custom_field/custom_field.js index 040cb10616..ec4eea4809 100644 --- a/frappe/core/doctype/custom_field/custom_field.js +++ b/frappe/core/doctype/custom_field/custom_field.js @@ -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 Options.
Eg.: Customer'; - else if(doc.fieldtype == 'Select') cur_frm.fields_dict['options_help'].disp_area.innerHTML = 'Please enter values in Options, with each option on a new line.
Eg.: Field: Country
Options:
China
India
United States

'; - 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.:
Option 1
Option 2
Option 3
'); + } 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 = ''; + } } diff --git a/frappe/core/doctype/custom_field/custom_field.json b/frappe/core/doctype/custom_field/custom_field.json index 58be09b1c4..55c404c902 100644 --- a/frappe/core/doctype/custom_field/custom_field.json +++ b/frappe/core/doctype/custom_field/custom_field.json @@ -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", diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json index b68a566d98..b1296d4056 100644 --- a/frappe/core/doctype/docfield/docfield.json +++ b/frappe/core/doctype/docfield/docfield.json @@ -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", diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 963c7dbb93..3ba9314cc0 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -157,6 +157,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 ['.', ',', ' ', '-', '&', '%', '=', '"', "'", '*', '$', @@ -200,6 +201,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: @@ -208,6 +216,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) diff --git a/frappe/core/doctype/event/event.json b/frappe/core/doctype/event/event.json index ec5e6ca3ad..de46143f73 100644 --- a/frappe/core/doctype/event/event.json +++ b/frappe/core/doctype/event/event.json @@ -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", diff --git a/frappe/core/doctype/todo/todo.json b/frappe/core/doctype/todo/todo.json index 1139bf07bb..ec9e27e2b2 100644 --- a/frappe/core/doctype/todo/todo.json +++ b/frappe/core/doctype/todo/todo.json @@ -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", diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 7c84c5e654..b0600a96d4 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -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: diff --git a/frappe/model/db_schema.py b/frappe/model/db_schema.py index 488f623cea..7f2a013d8c 100644 --- a/frappe/model/db_schema.py +++ b/frappe/model/db_schema.py @@ -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') diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 56c334f33a..06b30b2168 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -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) diff --git a/frappe/model/document.py b/frappe/model/document.py index c5e18e60f7..37265cbd7e 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -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) diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 5201158d3b..0a05809c33 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -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)) diff --git a/frappe/public/js/frappe/form/control.js b/frappe/public/js/frappe/form/control.js index 88e87f3bb8..a8c999f669 100644 --- a/frappe/public/js/frappe/form/control.js +++ b/frappe/public/js/frappe/form/control.js @@ -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: " " + __("Create a new {0}", [me.df.options]) + "", 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() { diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js index e99158a1ab..8d03823db6 100644 --- a/frappe/public/js/frappe/form/formatters.js +++ b/frappe/public/js/frappe/form/formatters.js @@ -33,6 +33,7 @@ frappe.form.formatters = { return value ? "" : ""; }, 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('%(value)s', {onclick: docfield.link_onclick.replace(/"/g, '"'), value:value}); - } else if(docfield && docfield.options) { + } else if(docfield && doctype) { return repl('%(icon)s%(label)s', { - doctype: encodeURIComponent(docfield.options), + doctype: encodeURIComponent(doctype), name: encodeURIComponent(value), label: value, icon: (options && options.no_icon) ? "" : - (' ') + (' ') }); } 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); } diff --git a/frappe/public/js/frappe/form/script_manager.js b/frappe/public/js/frappe/form/script_manager.js index 93cd5c8663..bd17d0a519 100644 --- a/frappe/public/js/frappe/form/script_manager.js +++ b/frappe/public/js/frappe/form/script_manager.js @@ -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, diff --git a/frappe/public/js/frappe/ui/filters.js b/frappe/public/js/frappe/ui/filters.js index 0e1b90752e..bb24e36412 100644 --- a/frappe/public/js/frappe/ui/filters.js +++ b/frappe/public/js/frappe/ui/filters.js @@ -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'; diff --git a/frappe/public/js/frappe/views/doclistview.js b/frappe/public/js/frappe/views/doclistview.js index b1829d25c0..7738606fda 100644 --- a/frappe/public/js/frappe/views/doclistview.js +++ b/frappe/public/js/frappe/views/doclistview.js @@ -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() { From 079b345404f0df15517d9563ccad830c402dbba5 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 20 Jun 2014 16:51:57 +0530 Subject: [PATCH 04/16] dynamic_link_fix --- frappe/model/delete_doc.py | 2 +- frappe/model/rename_doc.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 06b30b2168..e78db6515b 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -118,7 +118,7 @@ def check_if_doc_is_dynamically_linked(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) + doc.name, df.parent, ""), frappe.LinkExistsError) else: # dynamic link in table diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 0a05809c33..b8a4237da8 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -292,7 +292,7 @@ def rename_dynamic_links(doctype, old, new): 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)) + field=%s and value=%s and doctype=%s""", (new, df.fieldname, old, df.parent)) else: # replace for each value where renamed for to_change in frappe.db.sql_list("""select name from `tab{parent}` where From d6ca8baf0bcf26acbf3af358def1d7deb293792e Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 20 Jun 2014 19:09:43 +0530 Subject: [PATCH 05/16] dynamic_link_fix --- frappe/model/base_document.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index b0600a96d4..fd3d66e001 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -267,17 +267,18 @@ class BaseDocument(object): for df in self.meta.get_link_fields() + self.meta.get("fields", {"fieldtype":"Dynamic Link"}): - 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: + 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))) + # MySQL is case insensitive. Preserve case of the original docname in the Link Field. value = frappe.db.get_value(doctype, docname) setattr(self, df.fieldname, value) From cb004a168bb85621cef9c17881b2c3353c679a08 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 20 Jun 2014 20:16:27 +0530 Subject: [PATCH 06/16] Save naming series in property setter before reload --- frappe/core/doctype/doctype/doctype.py | 24 ++++++++++++++++++++++++ frappe/database.py | 3 +++ frappe/model/delete_doc.py | 11 ++++++++++- frappe/patches.txt | 1 + 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 963c7dbb93..de007164d0 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -11,6 +11,7 @@ from frappe.utils import now, cint from frappe.model import no_value_fields from frappe.model.document import Document from frappe.model.db_schema import type_map +from frappe.core.doctype.property_setter.property_setter import make_property_setter class DocType(Document): def validate(self): @@ -106,6 +107,29 @@ class DocType(Document): else: frappe.db.sql("rename table `tab%s` to `tab%s`" % (old, new)) + def before_reload(self): + if not (self.issingle and self.istable): + self.preserve_naming_series_options_in_property_setter() + + def preserve_naming_series_options_in_property_setter(self): + """preserve naming_series as property setter if it does not exist""" + naming_series = self.get("fields", {"fieldname": "naming_series"}) + + if not naming_series: + return + + # check if atleast 1 record exists + if not (frappe.db.table_exists("tab" + self.name) and frappe.db.sql("select name from `tab{}` limit 1".format(self.name))): + return + + existing_property_setter = frappe.db.get_value("Property Setter", {"doc_type": self.name, + "property": "options", "field_name": "naming_series"}) + + if not existing_property_setter: + make_property_setter(self.name, "naming_series", "options", naming_series[0].options, "Text") + if naming_series[0].default: + make_property_setter(self.name, "naming_series", "default", naming_series[0].default, "Text") + def export_doc(self): from frappe.modules.export_file import export_to_files export_to_files(record_list=[['DocType', self.name]]) diff --git a/frappe/database.py b/frappe/database.py index c082aa0a74..721de5576f 100644 --- a/frappe/database.py +++ b/frappe/database.py @@ -485,6 +485,9 @@ class Database: def table_exists(self, tablename): return tablename in [d[0] for d in self.sql("show tables")] + def a_row_exists(self, doctype): + return self.sql("select name from `tab{doctype}` limit 1".format(doctype=doctype)) + def exists(self, dt, dn=None): if isinstance(dt, basestring): if dt!="DocType" and dt==dn: diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 56c334f33a..1c1cdb8270 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -31,7 +31,16 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa remove_all(doctype, name) if doctype=="DocType": - if not for_reload: + if for_reload: + + try: + doc = frappe.get_doc(doctype, name) + except frappe.DoesNotExistError: + pass + else: + doc.run_method("before_reload") + + else: frappe.db.sql("delete from `tabCustom Field` where dt = %s", name) frappe.db.sql("delete from `tabCustom Script` where dt = %s", name) frappe.db.sql("delete from `tabProperty Setter` where doc_type = %s", name) diff --git a/frappe/patches.txt b/frappe/patches.txt index 5387aafbc2..8962d1404b 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -41,3 +41,4 @@ frappe.patches.v4_0.fix_attach_field_file_url execute:frappe.reset_perms("User") #2014-06-13 execute:frappe.db.sql("""delete from `tabUserRole` where ifnull(parentfield, '')=''""") #2014-06-17 frappe.patches.v4_0.remove_user_owner_custom_field +execute:frappe.db.sql("""update `tabProperty Setter` set property_type='Text' where property in ('options', 'default')""") #2014-06-20 From 66393fcd82dc289801e15fd595dcd2539f077651 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 23 Jun 2014 12:14:24 +0530 Subject: [PATCH 07/16] added upload / download in user permissions --- .../page/user_permissions/user_permissions.js | 56 +++++++++++++++++++ .../page/user_permissions/user_permissions.py | 27 +++++++++ frappe/defaults.py | 5 +- frappe/public/js/frappe/form/control.js | 6 +- frappe/utils/datautils.py | 1 + frappe/utils/file_manager.py | 2 + 6 files changed, 94 insertions(+), 3 deletions(-) diff --git a/frappe/core/page/user_permissions/user_permissions.js b/frappe/core/page/user_permissions/user_permissions.js index 681eea2e24..38d6c3618c 100644 --- a/frappe/core/page/user_permissions/user_permissions.js +++ b/frappe/core/page/user_permissions/user_permissions.js @@ -73,6 +73,20 @@ frappe.UserPermissions = Class.extend({ options: "[Select]" }); + me.download = me.wrapper.appframe.add_field({ + fieldname: "download", + label: __("Download"), + fieldtype: "Button", + icon: "icon-download" + }); + + me.upload = me.wrapper.appframe.add_field({ + fieldname: "upload", + label: __("Upload"), + fieldtype: "Button", + icon: "icon-upload" + }); + // bind change event $.each(me.filters, function(k, f) { f.$input.on("change", function() { @@ -86,9 +100,51 @@ frappe.UserPermissions = Class.extend({ }); me.set_from_route(); + me.setup_download_upload(); } }); }, + setup_download_upload: function() { + var me = this; + me.download.$input.on("click", function() { + window.location.href = frappe.urllib.get_base_url() + + "/api/method/frappe.core.page.user_permissions.user_permissions.get_user_permissions_csv"; + }); + + me.upload.$input.on("click", function() { + var d = new frappe.ui.Dialog({ + title: "Upload User Permissions", + fields: [ + { + fieldtype:"HTML", + options: '
    '+ + "
  1. "+__("Upload CSV file containing all user permissions in the same format as Download.")+"
  2. "+ + "
  3. "+__("Any existing permission will be deleted / overwritten.")+"
  4. "+ + '
' + }, + { + fieldtype:"Attach", fieldname:"attach", + } + ], + primary_action_label: __("Upload and Sync"), + primary_action: function() { + frappe.call({ + method:"frappe.core.page.user_permissions.user_permissions.import_user_permissions", + args: { + filedata: d.fields_dict.attach.get_value() + }, + callback: function(r) { + if(!r.exc) { + msgprint("Permissions Updated"); + d.hide(); + } + } + }); + } + }); + d.show(); + }) + }, get_link_names: function() { return this.options.link_fields; }, diff --git a/frappe/core/page/user_permissions/user_permissions.py b/frappe/core/page/user_permissions/user_permissions.py index 5ad93f7bff..e4722cc467 100644 --- a/frappe/core/page/user_permissions/user_permissions.py +++ b/frappe/core/page/user_permissions/user_permissions.py @@ -7,6 +7,8 @@ from frappe import _ import frappe.defaults import frappe.permissions from frappe.core.doctype.user.user import get_system_users +from frappe.utils.datautils import UnicodeWriter, read_csv_content_from_uploaded_file +from frappe.defaults import clear_default @frappe.whitelist() def get_users_and_links(): @@ -79,3 +81,28 @@ def get_doctypes_for_user_permissions(): return frappe.db.sql_list("""select name from tabDocType where ifnull(issingle,0)=0 and ifnull(istable,0)=0 {condition}""".format(condition=condition), tuple(values)) + +@frappe.whitelist() +def get_user_permissions_csv(): + out = [["User Permissions"], ["User", "Document Type", "Value"]] + out += [[a.parent, a.defkey, a.defvalue] for a in get_permissions()] + + csv = UnicodeWriter() + for row in out: + csv.writerow(row) + + frappe.response['result'] = str(csv.getvalue()) + frappe.response['type'] = 'csv' + frappe.response['doctype'] = "User Permissions" + +@frappe.whitelist() +def import_user_permissions(): + frappe.only_for("System Manager") + rows = read_csv_content_from_uploaded_file(ignore_encoding=True) + clear_default(parenttype="User Permission") + + if rows[0][0]!="User Permissions" and rows[1][0] != "User": + frappe.throw(frappe._("Please upload using the same template as download.")) + + for row in rows[2:]: + frappe.permissions.add_user_permission(row[1], row[2], row[0]) diff --git a/frappe/defaults.py b/frappe/defaults.py index 80997e4420..83aadd9709 100644 --- a/frappe/defaults.py +++ b/frappe/defaults.py @@ -4,7 +4,10 @@ from __future__ import unicode_literals import frappe -common_keys = ["__default", "__global"] +# Note: DefaultValue records are identified by parenttype +# __default, __global or 'User Permission' + +common_keys = ["__default", "__global", "User Permission"] def set_user_default(key, value, user=None, parenttype=None): set_default(key, value, user or frappe.session.user, parenttype) diff --git a/frappe/public/js/frappe/form/control.js b/frappe/public/js/frappe/form/control.js index a8c999f669..57ca6476e7 100644 --- a/frappe/public/js/frappe/form/control.js +++ b/frappe/public/js/frappe/form/control.js @@ -538,7 +538,8 @@ frappe.ui.form.ControlButton = frappe.ui.form.ControlData.extend({ }, set_label: function() { $(this.label_span).html(" "); - this.$input && this.$input.html(this.df.label); + this.$input && this.$input.html((this.df.icon ? + (' ') : "") + this.df.label); } }); @@ -551,7 +552,8 @@ frappe.ui.form.ControlAttach = frappe.ui.form.ControlData.extend({ .on("click", function() { me.onclick(); }); - this.$value = $('
\ + this.$value = $('
\ + \ \ ×
') .prependTo(me.input_area) diff --git a/frappe/utils/datautils.py b/frappe/utils/datautils.py index 92b0dc320e..ff8c021741 100644 --- a/frappe/utils/datautils.py +++ b/frappe/utils/datautils.py @@ -35,6 +35,7 @@ def read_csv_content_from_attached_file(doc): def read_csv_content(fcontent, ignore_encoding=False): rows = [] + print fcontent if not isinstance(fcontent, unicode): decoded = False for encoding in ["utf-8", "windows-1250", "windows-1252"]: diff --git a/frappe/utils/file_manager.py b/frappe/utils/file_manager.py index d660fa2277..b3aa94a1d9 100644 --- a/frappe/utils/file_manager.py +++ b/frappe/utils/file_manager.py @@ -68,6 +68,8 @@ def save_url(file_url, dt, dn): def get_uploaded_content(): # should not be unicode when reading a file, hence using frappe.form if 'filedata' in frappe.form_dict: + if "," in frappe.form_dict.filedata: + frappe.form_dict.filedata = frappe.form_dict.filedata.rsplit(",", 1)[1] frappe.uploaded_content = base64.b64decode(frappe.form_dict.filedata) frappe.uploaded_filename = frappe.form_dict.filename return frappe.uploaded_filename, frappe.uploaded_content From 16d091c5b74a96f5ec3576d7a3f22660f0ad54b8 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 23 Jun 2014 12:26:26 +0530 Subject: [PATCH 08/16] reverted common_keys --- frappe/defaults.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/defaults.py b/frappe/defaults.py index 83aadd9709..2077e46207 100644 --- a/frappe/defaults.py +++ b/frappe/defaults.py @@ -7,7 +7,7 @@ import frappe # Note: DefaultValue records are identified by parenttype # __default, __global or 'User Permission' -common_keys = ["__default", "__global", "User Permission"] +common_keys = ["__default", "__global"] def set_user_default(key, value, user=None, parenttype=None): set_default(key, value, user or frappe.session.user, parenttype) From fa280fec5780bf94ccb8c2b90cae52aad3348cc3 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 23 Jun 2014 12:27:39 +0530 Subject: [PATCH 09/16] removed print --- frappe/utils/datautils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/utils/datautils.py b/frappe/utils/datautils.py index ff8c021741..92b0dc320e 100644 --- a/frappe/utils/datautils.py +++ b/frappe/utils/datautils.py @@ -35,7 +35,6 @@ def read_csv_content_from_attached_file(doc): def read_csv_content(fcontent, ignore_encoding=False): rows = [] - print fcontent if not isinstance(fcontent, unicode): decoded = False for encoding in ["utf-8", "windows-1250", "windows-1252"]: From a4b8a362ae72e73e56d9a39774b83f63ecbebd7e Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 23 Jun 2014 12:31:06 +0530 Subject: [PATCH 10/16] user permissions, show buttons only for system manager --- .../page/user_permissions/user_permissions.js | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/frappe/core/page/user_permissions/user_permissions.js b/frappe/core/page/user_permissions/user_permissions.js index 38d6c3618c..7d1392736b 100644 --- a/frappe/core/page/user_permissions/user_permissions.js +++ b/frappe/core/page/user_permissions/user_permissions.js @@ -73,19 +73,21 @@ frappe.UserPermissions = Class.extend({ options: "[Select]" }); - me.download = me.wrapper.appframe.add_field({ - fieldname: "download", - label: __("Download"), - fieldtype: "Button", - icon: "icon-download" - }); + if(user_roles.indexOf("System Manager")!==-1) { + me.download = me.wrapper.appframe.add_field({ + fieldname: "download", + label: __("Download"), + fieldtype: "Button", + icon: "icon-download" + }); - me.upload = me.wrapper.appframe.add_field({ - fieldname: "upload", - label: __("Upload"), - fieldtype: "Button", - icon: "icon-upload" - }); + me.upload = me.wrapper.appframe.add_field({ + fieldname: "upload", + label: __("Upload"), + fieldtype: "Button", + icon: "icon-upload" + }); + } // bind change event $.each(me.filters, function(k, f) { From ee3705f66f235e3cde1738fd5f244ce10096b073 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 23 Jun 2014 12:39:14 +0530 Subject: [PATCH 11/16] change default max_file_size to 3MB, its 2014 --- frappe/utils/file_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/utils/file_manager.py b/frappe/utils/file_manager.py index b3aa94a1d9..de3fdd11c7 100644 --- a/frappe/utils/file_manager.py +++ b/frappe/utils/file_manager.py @@ -146,12 +146,12 @@ def save_file_on_filesystem(fname, content, content_type=None): } def check_max_file_size(content): - max_file_size = conf.get('max_file_size') or 1000000 + max_file_size = conf.get('max_file_size') or 3000000 file_size = len(content) if file_size > max_file_size: frappe.msgprint(_("File size exceeded the maximum allowed size of {0} MB").format( - max_file_size / 1000000.0), + max_file_size / 3000000.0), raise_exception=MaxFileSizeReachedError) return file_size From f0e26cac1cae895b73a4de0f408f200abc8eacd2 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 23 Jun 2014 15:47:19 +0530 Subject: [PATCH 12/16] form prev/next fix and file_order fix --- frappe/public/js/frappe/form/infobar.js | 20 ++++++++++++++++--- frappe/utils/file_manager.py | 4 ++-- frappe/widgets/form/utils.py | 26 ++++++++++++++++++++----- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/frappe/public/js/frappe/form/infobar.js b/frappe/public/js/frappe/form/infobar.js index 62ba2b4801..66f7a93133 100644 --- a/frappe/public/js/frappe/form/infobar.js +++ b/frappe/public/js/frappe/form/infobar.js @@ -116,13 +116,27 @@ frappe.ui.form.InfoBar = Class.extend({ }, go_prev_next: function(prev) { - var me = this; + var me = this, + filters = null, + order_by = "modified desc", + doclistview = frappe.pages["List/" + me.frm.doctype]; + + // filters / order defined in listview + if(doclistview) { + filters = doclistview.doclistview.filter_list.get_filters(); + if(doclistview.doclistview.listview.order_by) { + order_by = doclistview.doclistview.listview.order_by; + } + } + return frappe.call({ method: "frappe.widgets.form.utils.get_next", args: { doctype: me.frm.doctype, - name: me.frm.docname, - prev: prev ? 1 : 0 + value: me.frm.doc[order_by.split(" ")[0]], + prev: prev ? 1 : 0, + filters: filters, + order_by: order_by }, callback: function(r) { if(r.message) diff --git a/frappe/utils/file_manager.py b/frappe/utils/file_manager.py index de3fdd11c7..bde7154259 100644 --- a/frappe/utils/file_manager.py +++ b/frappe/utils/file_manager.py @@ -146,12 +146,12 @@ def save_file_on_filesystem(fname, content, content_type=None): } def check_max_file_size(content): - max_file_size = conf.get('max_file_size') or 3000000 + max_file_size = conf.get('max_file_size') or 3145728 file_size = len(content) if file_size > max_file_size: frappe.msgprint(_("File size exceeded the maximum allowed size of {0} MB").format( - max_file_size / 3000000.0), + max_file_size / 1048576), raise_exception=MaxFileSizeReachedError) return file_size diff --git a/frappe/widgets/form/utils.py b/frappe/widgets/form/utils.py index 2caa09cf2e..79c69a89aa 100644 --- a/frappe/widgets/form/utils.py +++ b/frappe/widgets/form/utils.py @@ -63,15 +63,31 @@ def add_comment(doc): return doc.as_dict() @frappe.whitelist() -def get_next(doctype, name, prev): +def get_next(doctype, value, prev, filters=None, order_by="modified desc"): import frappe.widgets.reportview prev = int(prev) - field = "`tab%s`.name" % doctype + sort_field, sort_order = order_by.split(" ") + + if not filters: filters = [] + if isinstance(filters, basestring): + filters = json.loads(filters) + + # condition based on sort order + condition = ">" if sort_order.lower()=="desc" else "<" + + # switch the condition + if prev: + condition = "<" if condition==">" else "<" + + # add condition for next or prev item + if not order_by[0] in [f[1] for f in filters]: + filters.append([doctype, sort_field, condition, value]) + res = frappe.widgets.reportview.execute(doctype, - fields = [field], - filters = [[doctype, "name", "<" if prev else ">", name]], - order_by = field + " " + ("desc" if prev else "asc"), + fields = ["name"], + filters = filters, + order_by = sort_field + " " + sort_order, limit_start=0, limit_page_length=1, as_list=True) if not res: From 8e891892ba6f6ffcd2041becd8d2b5a2fb5638db Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Tue, 24 Jun 2014 16:14:22 +0530 Subject: [PATCH 13/16] fix bad attachment urls and encode them --- frappe/public/js/frappe/form/attachments.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/attachments.js b/frappe/public/js/frappe/form/attachments.js index 5dbdb62e28..de1204aa53 100644 --- a/frappe/public/js/frappe/form/attachments.js +++ b/frappe/public/js/frappe/form/attachments.js @@ -61,7 +61,7 @@ frappe.ui.form.Attachments = Class.extend({ }, add_attachment: function(attachment) { var file_name = attachment.file_name; - var file_url = attachment.file_url; + var file_url = this.get_file_url(attachment); var fileid = attachment.name; if (!file_name) { file_name = file_url; @@ -96,6 +96,13 @@ frappe.ui.form.Attachments = Class.extend({ $close.remove(); } }, + get_file_url: function(attachment) { + var file_url = attachment.file_url; + if (!file_url) { + file_url = '/files/' + attachment.file_name; + } + return encodeURI(file_url); + }, remove_attachment_by_filename: function(filename, callback) { this.remove_attachment(this.get_attachments()[filename], callback); }, From 16073efa617c8d4a86964e6cbf05464a3f255aec Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 24 Jun 2014 16:32:25 +0530 Subject: [PATCH 14/16] hotfix in permission --- frappe/permissions.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frappe/permissions.py b/frappe/permissions.py index b5165f2024..95208d97a7 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -37,12 +37,13 @@ def has_permission(doctype, ptype="read", doc=None, verbose=True, user=None): if not role_permissions.get(ptype): return False - if doc and role_permissions["apply_user_permissions"].get(ptype): + if doc: if isinstance(doc, basestring): doc = frappe.get_doc(meta.name, doc) - if not user_has_permission(doc, verbose=verbose, user=user): - return False + if role_permissions["apply_user_permissions"].get(ptype): + if not user_has_permission(doc, verbose=verbose, user=user): + return False if not has_controller_permissions(doc, ptype, user=user): return False From 294c73f557144b65252bfef58b07b561fe2406d0 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 24 Jun 2014 16:42:50 +0530 Subject: [PATCH 15/16] Fixed comment deletion --- frappe/public/js/frappe/form/comments.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/comments.js b/frappe/public/js/frappe/form/comments.js index b981037ad0..265ce50dff 100644 --- a/frappe/public/js/frappe/form/comments.js +++ b/frappe/public/js/frappe/form/comments.js @@ -49,7 +49,7 @@ frappe.ui.form.Comments = Class.extend({ c.fullname = frappe.user_info(c.comment_by).fullname; c.comment = frappe.markdown(c.comment); - $(repl('
\ + $(repl('
\ \ \ \ From ff34bf6c77b56483e7ad00efc8d755288249d702 Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Wed, 25 Jun 2014 12:07:33 +0530 Subject: [PATCH 16/16] hotfix get_file_url --- frappe/public/js/frappe/form/attachments.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/attachments.js b/frappe/public/js/frappe/form/attachments.js index de1204aa53..c292416fa3 100644 --- a/frappe/public/js/frappe/form/attachments.js +++ b/frappe/public/js/frappe/form/attachments.js @@ -99,7 +99,12 @@ frappe.ui.form.Attachments = Class.extend({ get_file_url: function(attachment) { var file_url = attachment.file_url; if (!file_url) { - file_url = '/files/' + attachment.file_name; + if (attachment.file_name.indexOf('files/') === 0) { + file_url = '/' + attachment.file_name; + } + else { + file_url = '/files/' + attachment.file_name; + } } return encodeURI(file_url); },