diff --git a/frappe/__version__.py b/frappe/__version__.py index e5436f3a26..2ae0efac5c 100644 --- a/frappe/__version__.py +++ b/frappe/__version__.py @@ -1,2 +1,2 @@ from __future__ import unicode_literals -__version__ = "5.0.34" +__version__ = "5.0.35" diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index 9c91c6a0f7..234dea8971 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -102,7 +102,7 @@ class CustomizeForm(Document): meta = frappe.get_meta(self.doc_type) # doctype property setters for property in self.doctype_properties: - if property != "idx" and self.get(property) != meta.get(property): + if self.get(property) != meta.get(property): self.make_property_setter(property=property, value=self.get(property), property_type=self.doctype_properties[property]) @@ -117,7 +117,7 @@ class CustomizeForm(Document): continue for property in self.docfield_properties: - if df.get(property) != meta_df[0].get(property): + if property != "idx" and df.get(property) != meta_df[0].get(property): if property == "fieldtype": self.validate_fieldtype_change(df, meta_df[0].get(property), df.get(property)) @@ -260,4 +260,3 @@ class CustomizeForm(Document): and ifnull(field_name, '')!='naming_series'""", self.doc_type) frappe.clear_cache(doctype=self.doc_type) self.fetch_to_customize() - diff --git a/frappe/data/conf.py b/frappe/data/conf.py deleted file mode 100644 index bce5d009a9..0000000000 --- a/frappe/data/conf.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -# DEPRECATED only for reference - -from __future__ import unicode_literals -# app configuration - -# database config -db_name = '%(db_name)s' -db_password = '%(db_password)s' - -# user attachments stored in -files_path = 'public/files' -public_path = 'public' - -# max file attachment size (default 1MB) -max_file_size = 1000000 - -# max email size in bytes -max_email_size = 0 - -# total pop session timeout in seconds -pop_timeout = 0 - -# generate schema (.txt files) -developer_mode = 0 - -# clear cache on refresh -auto_cache_clear = 0 - -# email logs to admin (beta) -admin_email_notification = 0 - -# user timezone -user_timezone = 'Asia/Calcutta' - -# outgoing mail settings -mail_server = None -mail_login = None -mail_password = None -mail_port = None -use_ssl = None -auto_email_id = None - -# logging settings -log_file_name = 'logs/error_log.txt' -debug_log_dbs = [] -log_level = 'logging.INFO' -log_file_size = 5000 -log_file_backup_count = 5 - diff --git a/frappe/data/languages.txt b/frappe/data/languages.txt index 616c444e77..638dcd44cc 100644 --- a/frappe/data/languages.txt +++ b/frappe/data/languages.txt @@ -2,7 +2,7 @@ ar العربية bg bǎlgarski bs bosanski ca català -cz česky +cs česky da dansk de deutsch el ελληνικά diff --git a/frappe/email/doctype/email_alert/email_alert.json b/frappe/email/doctype/email_alert/email_alert.json index bc60787f95..ed9cc746bc 100644 --- a/frappe/email/doctype/email_alert/email_alert.json +++ b/frappe/email/doctype/email_alert/email_alert.json @@ -129,7 +129,7 @@ "fieldname": "message_examples", "fieldtype": "HTML", "label": "Message Examples", - "options": "
Message Example (Markdown)
\n
Transaction {{ doc.name }} has exceeded Due Date. Please take relevant action\n\n#### Details\n\nCustomer: {{ doc.customer }}\nAmount: {{ doc.total_amount }}
", + "options": "
Message Example
\n\n
\n

Order Overdue

\n\n

Transaction {{ doc.name }} has exceeded Due Date. Please take necessary action.

\n\n

Details

\n\n\n
", "permlevel": 0 }, { @@ -141,7 +141,7 @@ } ], "icon": "icon-envelope", - "modified": "2015-03-25 06:20:07.472953", + "modified": "2015-07-09 00:27:00.169741", "modified_by": "Administrator", "module": "Email", "name": "Email Alert", diff --git a/frappe/hooks.py b/frappe/hooks.py index a3a207f39e..5dc3ec1b60 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -4,7 +4,7 @@ app_title = "Frappe Framework" app_publisher = "Frappe Technologies Pvt. Ltd." app_description = "Full Stack Web Application Framework in Python" app_icon = "octicon octicon-circuit-board" -app_version = "5.0.34" +app_version = "5.0.35" app_color = "orange" app_email = "support@frappe.io" diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index ab3630ec59..0560043894 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -72,111 +72,3 @@ def delete_fields(args_dict, delete=0): ", ".join(["DROP COLUMN `%s`" % f for f in fields if f in existing_fields]) frappe.db.commit() frappe.db.sql(query) - -def rename_field(doctype, old_fieldname, new_fieldname): - """This functions assumes that doctype is already synced""" - - meta = frappe.get_meta(doctype, cached=False) - new_field = meta.get_field(new_fieldname) - if not new_field: - print "rename_field: " + (new_fieldname) + " not found in " + doctype - return - - if new_field.fieldtype == "Table": - # change parentfield of table mentioned in options - frappe.db.sql("""update `tab%s` set parentfield=%s - where parentfield=%s""" % (new_field.options.split("\n")[0], "%s", "%s"), - (new_fieldname, old_fieldname)) - elif new_field.fieldtype not in no_value_fields: - if meta.issingle: - frappe.db.sql("""update `tabSingles` set field=%s - where doctype=%s and field=%s""", - (new_fieldname, doctype, old_fieldname)) - else: - # copy field value - frappe.db.sql("""update `tab%s` set `%s`=`%s`""" % \ - (doctype, new_fieldname, old_fieldname)) - - update_reports(doctype, old_fieldname, new_fieldname) - update_users_report_view_settings(doctype, old_fieldname, new_fieldname) - - # update in property setter - frappe.db.sql("""update `tabProperty Setter` set field_name = %s - where doc_type=%s and field_name=%s""", (new_fieldname, doctype, old_fieldname)) - -def update_reports(doctype, old_fieldname, new_fieldname): - def _get_new_sort_by(report_dict, report, key): - sort_by = report_dict.get(key) or "" - if sort_by: - sort_by = sort_by.split(".") - if len(sort_by) > 1: - if sort_by[0]==doctype and sort_by[1]==old_fieldname: - sort_by = doctype + "." + new_fieldname - report_dict["updated"] = True - elif report.ref_doctype == doctype and sort_by[0]==old_fieldname: - sort_by = doctype + "." + new_fieldname - report_dict["updated"] = True - - if isinstance(sort_by, list): - sort_by = '.'.join(sort_by) - - return sort_by - - reports = frappe.db.sql("""select name, ref_doctype, json from tabReport - where report_type = 'Report Builder' and ifnull(is_standard, 'No') = 'No' - and json like %s and json like %s""", - ('%%%s%%' % old_fieldname , '%%%s%%' % doctype), as_dict=True) - - for r in reports: - report_dict = json.loads(r.json) - - # update filters - new_filters = [] - for f in report_dict.get("filters"): - if f and len(f) > 1 and f[0] == doctype and f[1] == old_fieldname: - new_filters.append([doctype, new_fieldname, f[2], f[3]]) - report_dict["updated"] = True - else: - new_filters.append(f) - - # update columns - new_columns = [] - for c in report_dict.get("columns"): - if c and len(c) > 1 and c[0] == old_fieldname and c[1] == doctype: - new_columns.append([new_fieldname, doctype]) - report_dict["updated"] = True - else: - new_columns.append(c) - - # update sort by - new_sort_by = _get_new_sort_by(report_dict, r, "sort_by") - new_sort_by_next = _get_new_sort_by(report_dict, r, "sort_by_next") - - if report_dict.get("updated"): - new_val = json.dumps({ - "filters": new_filters, - "columns": new_columns, - "sort_by": new_sort_by, - "sort_order": report_dict.get("sort_order"), - "sort_by_next": new_sort_by_next, - "sort_order_next": report_dict.get("sort_order_next") - }) - - frappe.db.sql("""update `tabReport` set `json`=%s where name=%s""", (new_val, r.name)) - -def update_users_report_view_settings(doctype, ref_fieldname, new_fieldname): - user_report_cols = frappe.db.sql("""select defkey, defvalue from `tabDefaultValue` where - defkey like '_list_settings:%'""") - for key, value in user_report_cols: - new_columns = [] - columns_modified = False - for field, field_doctype in json.loads(value): - if field == ref_fieldname and field_doctype == doctype: - new_columns.append([new_fieldname, field_doctype]) - columns_modified=True - else: - new_columns.append([field, field_doctype]) - - if columns_modified: - frappe.db.sql("""update `tabDefaultValue` set defvalue=%s - where defkey=%s""" % ('%s', '%s'), (json.dumps(new_columns), key)) diff --git a/frappe/model/utils/__init__.py b/frappe/model/utils/__init__.py new file mode 100644 index 0000000000..907fcc932b --- /dev/null +++ b/frappe/model/utils/__init__.py @@ -0,0 +1,18 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe, json +from frappe import _ +""" +Model utilities, unclassified functions +""" + +def set_default(doc, key): + """Set is_default property of given doc and unset all others filtered by given key.""" + if not doc.is_default: + frappe.db.set(doc, "is_default", 1) + + frappe.db.sql("""update `tab%s` set `is_default`=0 + where `%s`=%s and name!=%s""" % (doc.doctype, key, "%s", "%s"), + (doc.get(key), doc.name)) diff --git a/frappe/model/utils/rename_field.py b/frappe/model/utils/rename_field.py new file mode 100644 index 0000000000..0fb2e29f34 --- /dev/null +++ b/frappe/model/utils/rename_field.py @@ -0,0 +1,115 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals + +import frappe +import json + +def rename_field(doctype, old_fieldname, new_fieldname): + """This functions assumes that doctype is already synced""" + + meta = frappe.get_meta(doctype, cached=False) + new_field = meta.get_field(new_fieldname) + if not new_field: + print "rename_field: " + (new_fieldname) + " not found in " + doctype + return + + if new_field.fieldtype == "Table": + # change parentfield of table mentioned in options + frappe.db.sql("""update `tab%s` set parentfield=%s + where parentfield=%s""" % (new_field.options.split("\n")[0], "%s", "%s"), + (new_fieldname, old_fieldname)) + elif new_field.fieldtype not in no_value_fields: + if meta.issingle: + frappe.db.sql("""update `tabSingles` set field=%s + where doctype=%s and field=%s""", + (new_fieldname, doctype, old_fieldname)) + else: + # copy field value + frappe.db.sql("""update `tab%s` set `%s`=`%s`""" % \ + (doctype, new_fieldname, old_fieldname)) + + update_reports(doctype, old_fieldname, new_fieldname) + update_users_report_view_settings(doctype, old_fieldname, new_fieldname) + + # update in property setter + frappe.db.sql("""update `tabProperty Setter` set field_name = %s + where doc_type=%s and field_name=%s""", (new_fieldname, doctype, old_fieldname)) + +def update_reports(doctype, old_fieldname, new_fieldname): + def _get_new_sort_by(report_dict, report, key): + sort_by = report_dict.get(key) or "" + if sort_by: + sort_by = sort_by.split(".") + if len(sort_by) > 1: + if sort_by[0]==doctype and sort_by[1]==old_fieldname: + sort_by = doctype + "." + new_fieldname + report_dict["updated"] = True + elif report.ref_doctype == doctype and sort_by[0]==old_fieldname: + sort_by = doctype + "." + new_fieldname + report_dict["updated"] = True + + if isinstance(sort_by, list): + sort_by = '.'.join(sort_by) + + return sort_by + + reports = frappe.db.sql("""select name, ref_doctype, json from tabReport + where report_type = 'Report Builder' and ifnull(is_standard, 'No') = 'No' + and json like %s and json like %s""", + ('%%%s%%' % old_fieldname , '%%%s%%' % doctype), as_dict=True) + + for r in reports: + report_dict = json.loads(r.json) + + # update filters + new_filters = [] + for f in report_dict.get("filters"): + if f and len(f) > 1 and f[0] == doctype and f[1] == old_fieldname: + new_filters.append([doctype, new_fieldname, f[2], f[3]]) + report_dict["updated"] = True + else: + new_filters.append(f) + + # update columns + new_columns = [] + for c in report_dict.get("columns"): + if c and len(c) > 1 and c[0] == old_fieldname and c[1] == doctype: + new_columns.append([new_fieldname, doctype]) + report_dict["updated"] = True + else: + new_columns.append(c) + + # update sort by + new_sort_by = _get_new_sort_by(report_dict, r, "sort_by") + new_sort_by_next = _get_new_sort_by(report_dict, r, "sort_by_next") + + if report_dict.get("updated"): + new_val = json.dumps({ + "filters": new_filters, + "columns": new_columns, + "sort_by": new_sort_by, + "sort_order": report_dict.get("sort_order"), + "sort_by_next": new_sort_by_next, + "sort_order_next": report_dict.get("sort_order_next") + }) + + frappe.db.sql("""update `tabReport` set `json`=%s where name=%s""", (new_val, r.name)) + +def update_users_report_view_settings(doctype, ref_fieldname, new_fieldname): + user_report_cols = frappe.db.sql("""select defkey, defvalue from `tabDefaultValue` where + defkey like '_list_settings:%'""") + for key, value in user_report_cols: + new_columns = [] + columns_modified = False + for field, field_doctype in json.loads(value): + if field == ref_fieldname and field_doctype == doctype: + new_columns.append([new_fieldname, field_doctype]) + columns_modified=True + else: + new_columns.append([field, field_doctype]) + + if columns_modified: + frappe.db.sql("""update `tabDefaultValue` set defvalue=%s + where defkey=%s""" % ('%s', '%s'), (json.dumps(new_columns), key)) diff --git a/frappe/patches.txt b/frappe/patches.txt index c306188ee8..90ecf19872 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -80,3 +80,5 @@ frappe.patches.v5_0.fix_text_editor_file_urls execute:frappe.db.sql("update `tabComment` set comment_type='Comment' where comment_doctype='Blog Post' and ifnull(comment_type, '')=''") frappe.patches.v5_0.modify_session frappe.patches.v5_0.expire_old_scheduler_logs +execute:frappe.permissions.reset_perms("DocType") +execute:frappe.db.sql("delete from `tabProperty Setter` where `property` = 'idx'") diff --git a/frappe/patches/v4_0/rename_profile_to_user.py b/frappe/patches/v4_0/rename_profile_to_user.py index 85b8f53c0b..9223d217c5 100644 --- a/frappe/patches/v4_0/rename_profile_to_user.py +++ b/frappe/patches/v4_0/rename_profile_to_user.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import frappe -from frappe.model import rename_field +from frappe.model.utils import rename_field from frappe.model.meta import get_table_columns def execute(): diff --git a/frappe/patches/v4_0/rename_sitemap_to_route.py b/frappe/patches/v4_0/rename_sitemap_to_route.py index af75763343..26d9792504 100644 --- a/frappe/patches/v4_0/rename_sitemap_to_route.py +++ b/frappe/patches/v4_0/rename_sitemap_to_route.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import frappe -from frappe.model import rename_field +from frappe.model.utils import rename_field def execute(): tables = frappe.db.sql_list("show tables") diff --git a/frappe/patches/v5_0/rename_table_fieldnames.py b/frappe/patches/v5_0/rename_table_fieldnames.py index ad426ef2f6..8d68134915 100644 --- a/frappe/patches/v5_0/rename_table_fieldnames.py +++ b/frappe/patches/v5_0/rename_table_fieldnames.py @@ -2,7 +2,7 @@ # License: GNU General Public License v3. See license.txt import frappe -from frappe.model import rename_field +from frappe.model.utils import rename_field from frappe.modules import scrub, get_doctype_module rename_map = { diff --git a/frappe/public/js/frappe/form/control.js b/frappe/public/js/frappe/form/control.js index e489c96e25..ea3a14cbe6 100644 --- a/frappe/public/js/frappe/form/control.js +++ b/frappe/public/js/frappe/form/control.js @@ -964,6 +964,11 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ var me = this; this.setup_buttons(); this.setup_autocomplete(); + if(this.df.change) { + this.$input.on("change", function() { + me.df.change.apply(this); + }); + } }, get_options: function() { return this.df.options; @@ -1024,6 +1029,7 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ this.$input.cache = {}; this.$input.autocomplete({ minLength: 0, + autoFocus: true, source: function(request, response) { var doctype = me.get_options(); if(!doctype) return; diff --git a/frappe/public/js/frappe/misc/pretty_date.js b/frappe/public/js/frappe/misc/pretty_date.js index 1d0a8bee43..d766c5a784 100644 --- a/frappe/public/js/frappe/misc/pretty_date.js +++ b/frappe/public/js/frappe/misc/pretty_date.js @@ -1,7 +1,7 @@ function prettyDate(time, mini){ if(moment) { - if(sys_defaults.time_zone) { + if(window.sys_defaults && sys_defaults.time_zone) { var ret = moment.tz(time, sys_defaults.time_zone).fromNow(mini); } else { var ret = moment(time).fromNow(mini); diff --git a/frappe/public/js/frappe/ui/field_group.js b/frappe/public/js/frappe/ui/field_group.js index dfe47b5ae0..f7b1198b67 100644 --- a/frappe/public/js/frappe/ui/field_group.js +++ b/frappe/public/js/frappe/ui/field_group.js @@ -32,9 +32,9 @@ frappe.ui.FieldGroup = frappe.ui.form.Layout.extend({ var me = this; $(this.body).find('input[type="text"], input[type="password"]').keypress(function(e) { if(e.which==13) { - if(this.has_primary_action) { + if(me.has_primary_action) { e.preventDefault(); - this.get_primary_btn().trigger("click"); + me.get_primary_btn().trigger("click"); } } }); diff --git a/frappe/public/js/frappe/ui/filters/filters.js b/frappe/public/js/frappe/ui/filters/filters.js index 0c3f22f333..c414140a7f 100644 --- a/frappe/public/js/frappe/ui/filters/filters.js +++ b/frappe/public/js/frappe/ui/filters/filters.js @@ -272,7 +272,8 @@ frappe.ui.Filter = Class.extend({ } else if(df.fieldtype=='Check') { df.fieldtype='Select'; df.options='No\nYes'; - } else if(['Text','Small Text','Text Editor','Code','Tag','Comments','Dynamic Link', 'Read Only'].indexOf(df.fieldtype)!=-1) { + } else if(['Text','Small Text','Text Editor','Code','Tag','Comments', + 'Dynamic Link','Read Only','Assign'].indexOf(df.fieldtype)!=-1) { df.fieldtype = 'Data'; } else if(df.fieldtype=='Link' && this.$w.find('.condition').val()!="=") { df.fieldtype = 'Data'; @@ -306,13 +307,20 @@ frappe.ui.Filter = Class.extend({ } if(this.get_condition()==='like') { - // add % only if not there at the end - if ((val.length === 0) || (val.lastIndexOf("%") !== (val.length - 1))) { - val = (val || "") + '%'; + // automatically append wildcards + if(val) { + if(val.slice(0,1) !== "%") { + val = "%" + val; + } + if(val.slice(-1) !== "%") { + val = val + "%"; + } } } else if(in_list(["in", "not in"], this.get_condition())) { val = $.map(val.split(","), function(v) { return strip(v); }); - } if(val === '%') val = ""; + } if(val === '%') { + val = ""; + } return val; }, @@ -392,6 +400,7 @@ frappe.ui.FieldSelect = Class.extend({ .autocomplete({ source: me.options, minLength: 0, + autoFocus: true, focus: function(event, ui) { ui.item && me.$select.val(ui.item.label); return false; diff --git a/frappe/public/js/frappe/ui/messages.js b/frappe/public/js/frappe/ui/messages.js index 7769a0010d..bb6a114a48 100644 --- a/frappe/public/js/frappe/ui/messages.js +++ b/frappe/public/js/frappe/ui/messages.js @@ -48,7 +48,7 @@ frappe.prompt = function(fields, callback, title, primary_label) { var d = new frappe.ui.Dialog({ fields: fields, title: title || __("Enter Value"), - }) + }); d.set_primary_action(primary_label || __("Submit"), function() { var values = d.get_values(); if(!values) { @@ -56,7 +56,7 @@ frappe.prompt = function(fields, callback, title, primary_label) { } d.hide(); callback(values); - }) + }); d.show(); return d; } diff --git a/frappe/public/js/frappe/upload.js b/frappe/public/js/frappe/upload.js index 6cf7b9616a..481ffda426 100644 --- a/frappe/public/js/frappe/upload.js +++ b/frappe/public/js/frappe/upload.js @@ -137,6 +137,7 @@ frappe.upload = { var a = parts[1]; } - return atob(a); + return decodeURIComponent(escape(atob(a))); + } } diff --git a/frappe/templates/generators/web_form.html b/frappe/templates/generators/web_form.html index ee3ecaa839..48fe7c02af 100644 --- a/frappe/templates/generators/web_form.html +++ b/frappe/templates/generators/web_form.html @@ -5,25 +5,25 @@ {% endblock %} {% block header_actions %} - {% if params.name or params.new %} - - - {{ _("Cancel") }} - {% elif is_list %} - - {{ _("New {0}").format(_(doc_type)) }} - - {% endif %} + {% if params.name or params.new %} + + + {{ _("Cancel") }} + {% elif is_list %} + + {{ _("New {0}").format(_(doc_type)) }} + + {% endif %} {% endblock %} {% block content %}
- {% if introduction_text %} -

{{ introduction_text }}

-
- {% endif %} + {% if introduction_text %} +

{{ introduction_text }}

+
+ {% endif %}
{% if login_required and frappe.user=="Guest" %}
@@ -37,31 +37,31 @@

{% elif is_list %} - {% include "templates/includes/list/list.html" %} - + {% include "templates/includes/list/list.html" %} + {% else %}
{%- macro properties(field) %} - name="{{ field.fieldname }}" id="{{ field.fieldname }}" - {% if field.placeholder %}placeholder="{{ field.placeholder }}"{% endif %} - data-label="{{ field.label }}" data-fieldtype="{{ field.fieldtype }}" - {{ (field.reqd and field.fieldtype!="Attach") and "required" or "" }} - {{ field.read_only and "disabled" or "" }} + name="{{ field.fieldname }}" id="{{ field.fieldname }}" + {% if field.placeholder %}placeholder="{{ field.placeholder }}"{% endif %} + data-label="{{ field.label }}" data-fieldtype="{{ field.fieldtype }}" + {{ (field.reqd and field.fieldtype!="Attach") and "required" or "" }} + {{ field.read_only and "disabled" or "" }} {% endmacro -%} {%- macro value(field) -%}{% if doc %}{{ doc.get(field.fieldname) or field.default or "" }}{% else %}{{ getCookie(field.options) or field.default or "" }}{% endif %}{%- endmacro -%} {%- macro help(field) -%} - {% if field.description -%} - {{ field.description }} - {%- endif -%} + {% if field.description -%} + {{ field.description }} + {%- endif -%} {%- endmacro %} {% macro label(field) %} - + {% endmacro %} {% macro render_field(field) %} @@ -138,7 +138,7 @@
@@ -150,17 +150,17 @@ {%- endif %} - {% for section in layout %} -
- {% for column in section %} -
- {% for field in column %} - {{ render_field(field) }} - {% endfor %} -
- {% endfor %} -
- {% endfor %} + {% for section in layout %} +
+ {% for column in section %} +
+ {% for field in column %} + {{ render_field(field) }} + {% endfor %} +
+ {% endfor %} +
+ {% endfor %} {% if allow_comments and not params.new -%}
@@ -176,7 +176,7 @@