diff --git a/frappe/__init__.py b/frappe/__init__.py index 65a7fc818c..913f6440ff 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -24,7 +24,7 @@ if sys.version[0] == '2': reload(sys) sys.setdefaultencoding("utf-8") -__version__ = '11.1.18' +__version__ = '11.1.19' __title__ = "Frappe Framework" local = Local() @@ -188,6 +188,9 @@ def connect(site=None, db_name=None): local.db = Database(user=db_name or local.conf.db_name) set_user("Administrator") + for hook in get_hooks("connect") or []: + get_attr(hook)() + def connect_read_only(): from frappe.database import Database diff --git a/frappe/core/doctype/data_import/data_import.py b/frappe/core/doctype/data_import/data_import.py index 3ca154c384..dc2f9194b9 100644 --- a/frappe/core/doctype/data_import/data_import.py +++ b/frappe/core/doctype/data_import/data_import.py @@ -71,7 +71,7 @@ def import_file_by_path(path, ignore_links=False, overwrite=False, submit=False, def export_json(doctype, path, filters=None, or_filters=None, name=None, order_by="creation asc"): def post_process(out): - del_keys = ('parent', 'parentfield', 'parenttype', 'modified_by', 'creation', 'owner', 'idx') + del_keys = ('modified_by', 'creation', 'owner', 'idx') for doc in out: for key in del_keys: if key in doc: diff --git a/frappe/core/page/usage_info/usage_info.html b/frappe/core/page/usage_info/usage_info.html index 40efd7a062..e4851d8f63 100644 --- a/frappe/core/page/usage_info/usage_info.html +++ b/frappe/core/page/usage_info/usage_info.html @@ -46,10 +46,6 @@

{{ __("Space") }}

- {% var database_percent = ((limits.space_usage.database_size / limits.space) * 100); %} - {% var files_percent = ((limits.space_usage.files_size / limits.space) * 100); %} - {% var backup_percent = ((limits.space_usage.backup_size / limits.space) * 100); %} -
@@ -66,12 +62,7 @@ {{ __("Backup Size:") }} {%= limits.space_usage.backup_size %} MB -

- - {%= flt(limits.space - limits.space_usage.total, 2) %} MB - available out of - {%= limits.space %} MB -

+

{{ usage_message }}

{% endif %}
diff --git a/frappe/core/page/usage_info/usage_info.js b/frappe/core/page/usage_info/usage_info.js index 5137eeac6a..eec5411ca4 100644 --- a/frappe/core/page/usage_info/usage_info.js +++ b/frappe/core/page/usage_info/usage_info.js @@ -15,7 +15,33 @@ frappe.pages['usage-info'].on_page_load = function(wrapper) { return; } - $(frappe.render_template("usage_info", usage_info)).appendTo(page.main); + let limits = usage_info.limits; + let database_percent = (limits.space_usage.database_size / limits.space) * 100; + let files_percent = (limits.space_usage.files_size / limits.space) * 100; + let backup_percent = (limits.space_usage.backup_size / limits.space) * 100; + + let total_consumed = database_percent + files_percent + backup_percent; + + let last_part = backup_percent; + if (total_consumed > 100) { + last_part = backup_percent - (total_consumed - 100); + } + backup_percent = last_part; + + let usage_message = ''; + if (limits.space_usage.total > limits.space) { + usage_message = __('You have used up all of the space allotted to you. Please buy more space in your subscription.'); + } else { + let available = flt(limits.space - limits.space_usage.total, 2); + usage_message = __('{0} available out of {1}', [(available + ' MB').bold(), (limits.space + ' MB').bold()]); + } + + $(frappe.render_template("usage_info", Object.assign(usage_info, { + database_percent, + files_percent, + backup_percent, + usage_message + }))).appendTo(page.main); var btn_text = usage_info.limits.users == 1 ? __("Upgrade") : __("Renew / Upgrade"); $(page.main).find('.btn-primary').html(btn_text).on('click', () => { diff --git a/frappe/exceptions.py b/frappe/exceptions.py index 3a8548a6e0..a1b4ef2024 100644 --- a/frappe/exceptions.py +++ b/frappe/exceptions.py @@ -84,3 +84,4 @@ class RetryBackgroundJobError(Exception): pass class DocumentLockedError(ValidationError): pass class CircularLinkingError(ValidationError): pass class SecurityException(Exception): pass +class IncompatibleApp(ValidationError): pass diff --git a/frappe/model/create_new.py b/frappe/model/create_new.py index 8bb1356bd9..b274cfadad 100644 --- a/frappe/model/create_new.py +++ b/frappe/model/create_new.py @@ -83,7 +83,8 @@ def get_user_default_value(df, defaults, doctype_user_permissions, allowed_recor if df.fieldtype == "Link" and df.options != "User": # 1 - look in user permissions only for document_type==Setup # We don't want to include permissions of transactions to be used for defaults. - if frappe.get_meta(df.options).document_type=="Setup" and len(allowed_records)==1: + if (frappe.get_meta(df.options).document_type=="Setup" + and len(allowed_records)==1 and not df.ignore_user_permissions): return allowed_records[0] # 2 - Look in user defaults diff --git a/frappe/modules/utils.py b/frappe/modules/utils.py index 94d726c16b..a45f414ddb 100644 --- a/frappe/modules/utils.py +++ b/frappe/modules/utils.py @@ -8,6 +8,7 @@ from __future__ import unicode_literals, print_function import frappe, os, json import frappe.utils from frappe import _ +from frappe.utils import cint def export_module_json(doc, is_standard, module): """Make a folder for the given doc and add its json file (make it a standard @@ -39,11 +40,15 @@ def get_doc_module(module, doctype, name): def export_customizations(module, doctype, sync_on_migrate=0, with_permissions=0): """Export Custom Field and Property Setter for the current document to the app folder. This will be synced with bench migrate""" + + sync_on_migrate = cint(sync_on_migrate) + with_permissions = cint(with_permissions) + if not frappe.get_conf().developer_mode: raise Exception('Not developer mode') custom = {'custom_fields': [], 'property_setters': [], 'custom_perms': [], - 'doctype': doctype, 'sync_on_migrate': 1} + 'doctype': doctype, 'sync_on_migrate': sync_on_migrate} def add(_doctype): custom['custom_fields'] += frappe.get_all('Custom Field', diff --git a/frappe/permissions.py b/frappe/permissions.py index 46924331f4..3097919723 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -28,7 +28,7 @@ def print_has_permission_check_logs(func): # print only if access denied # and if user is checking his own permission if not result and self_perm_check: - msgprint(('
').join(frappe.flags['has_permission_check_logs'])) + msgprint(('
').join(frappe.flags.get('has_permission_check_logs'))) frappe.flags.pop('has_permission_check_logs', None) return result return inner diff --git a/frappe/public/js/frappe/form/workflow.js b/frappe/public/js/frappe/form/workflow.js index 044d5b53ac..115ad5e702 100644 --- a/frappe/public/js/frappe/form/workflow.js +++ b/frappe/public/js/frappe/form/workflow.js @@ -68,8 +68,8 @@ frappe.ui.form.States = Class.extend({ }, show_actions: function() { - var added = false, - me = this; + var added = false; + var me = this; this.frm.page.clear_actions_menu(); @@ -103,10 +103,15 @@ frappe.ui.form.States = Class.extend({ }); } }); + this.setup_btn(added); }); - if(added) { + }, + + setup_btn: function(action_added) { + if(action_added) { this.frm.page.btn_primary.addClass("hide"); + this.frm.page.btn_secondary.addClass("hide"); this.frm.toolbar.current_status = ""; this.setup_help(); } diff --git a/frappe/public/js/frappe/list/base_list.js b/frappe/public/js/frappe/list/base_list.js index 00d8d1686f..d702020c52 100644 --- a/frappe/public/js/frappe/list/base_list.js +++ b/frappe/public/js/frappe/list/base_list.js @@ -365,6 +365,7 @@ frappe.views.BaseList = class BaseList { this.toggle_result_area(); this.before_render(); this.render(); + this.after_render(); this.freeze(false); }); } @@ -390,6 +391,10 @@ frappe.views.BaseList = class BaseList { } + after_render() { + + } + render() { // for child classes } diff --git a/frappe/public/js/frappe/misc/utils.js b/frappe/public/js/frappe/misc/utils.js index e038552fe4..7eef74509b 100644 --- a/frappe/public/js/frappe/misc/utils.js +++ b/frappe/public/js/frappe/misc/utils.js @@ -1,6 +1,7 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // MIT License. See license.txt +import deep_equal from "fast-deep-equal"; frappe.provide('frappe.utils'); Object.assign(frappe.utils, { @@ -668,6 +669,10 @@ Object.assign(frappe.utils, { } else { return null; } + }, + + deep_equal(a, b) { + return deep_equal(a, b); } }); diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 953c690904..e6423bf3c9 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -92,7 +92,8 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { this.load_report(); } else { // same report - this.refresh_report(); + // don't do anything to preserve state + // like filters and datatable column widths } } diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index e259309b10..9799a7e934 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -76,7 +76,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { return super.before_refresh(); } - before_render() { + after_render() { if (this.report_doc) { this.set_dirty_state_for_custom_report(); } else { @@ -85,23 +85,25 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { } set_dirty_state_for_custom_report() { - const json = JSON.stringify({ + let current_settings = { filters: this.filter_area.get(), fields: this.fields, order_by: this.sort_selector.get_sql_string(), add_totals_row: this.add_totals_row, - page_length: this.page_length - }); + page_length: this.page_length, + column_widths: this.get_column_widths() + } - const report_json = JSON.stringify({ + let report_settings = { filters: this.report_doc.json.filters, fields: this.report_doc.json.fields, order_by: this.report_doc.json.order_by, add_totals_row: this.report_doc.json.add_totals_row, - page_length: this.report_doc.json.page_length - }); + page_length: this.report_doc.json.page_length, + column_widths: this.report_doc.json.column_widths + } - if (json != report_json) { + if (!frappe.utils.deep_equal(current_settings, report_settings)) { this.page.set_indicator(__('Not Saved'), 'orange'); } else { this.page.clear_indicator(); @@ -830,11 +832,16 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { return docfield.fieldtype === 'Date' ? 'right' : 'left'; })(); - const width = (docfield ? cint(docfield.width) : null) || null; - // child table column const id = doctype !== this.doctype ? `${doctype}:${fieldname}` : fieldname; + let width = (docfield ? cint(docfield.width) : null) || null; + if (this.report_doc) { + // load the user saved column width + let saved_column_widths = this.report_doc.json.column_widths || {}; + width = saved_column_widths[id] || width; + } + let compareFn = null; if (docfield.fieldtype === 'Date') { compareFn = (cell, keyword) => { @@ -950,7 +957,8 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { fields: this.fields, order_by: this.sort_selector.get_sql_string(), add_totals_row: this.add_totals_row, - page_length: this.page_length + page_length: this.page_length, + column_widths: this.get_column_widths() } return frappe.call({ @@ -975,6 +983,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { }; this.list_sidebar.setup_reports(); frappe.set_route('List', this.doctype, 'Report', r.message); + return; } // update state @@ -994,6 +1003,20 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { } } + get_column_widths() { + if (this.datatable) { + return this.datatable + .datamanager + .getColumns(true) + .reduce((acc, curr) => { + acc[curr.id] = parseInt(curr.width); + return acc; + }, {}); + } + + return {}; + } + get_report_doc() { return new Promise(resolve => { frappe.model.with_doc('Report', this.report_name, () => { diff --git a/frappe/sessions.py b/frappe/sessions.py index aaf40f30e7..81ced8ce95 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -226,12 +226,11 @@ class Session: self.insert_session_record() # update user - frappe.db.sql("""UPDATE tabUser SET last_login = %(now)s, last_ip = %(ip)s, last_active = %(now)s - where name=%(name)s""", { - "now": frappe.utils.now(), - "ip": frappe.local.request_ip, - "name": self.data['user'] - }) + user = frappe.get_doc('User', self.data['user']) + user.last_login = frappe.utils.now() + user.last_ip = frappe.local.request_ip + user.last_active = frappe.utils.now() + user.save() frappe.db.commit() diff --git a/frappe/website/doctype/blog_post/blog_post.py b/frappe/website/doctype/blog_post/blog_post.py index a20e9fa128..e4c757e64b 100644 --- a/frappe/website/doctype/blog_post/blog_post.py +++ b/frappe/website/doctype/blog_post/blog_post.py @@ -7,7 +7,7 @@ import frappe from frappe import _ from frappe.website.website_generator import WebsiteGenerator from frappe.website.render import clear_cache -from frappe.utils import today, cint, global_date_format, get_fullname, strip_html_tags, markdown +from frappe.utils import today, cint, global_date_format, get_fullname, strip_html_tags, markdown, sanitize_html from frappe.website.utils import find_first_image, get_comment_list class BlogPost(WebsiteGenerator): @@ -95,7 +95,7 @@ def get_list_context(context=None): title = _('Blog') ) - category = frappe.local.form_dict.blog_category or frappe.local.form_dict.category + category = sanitize_html(frappe.local.form_dict.blog_category or frappe.local.form_dict.category) if category: category_title = get_blog_category(category) list_context.sub_title = _("Posts filed under {0}").format(category_title) @@ -107,7 +107,7 @@ def get_list_context(context=None): list_context.title = blogger elif frappe.local.form_dict.txt: - list_context.sub_title = _('Filtered by "{0}"').format(frappe.local.form_dict.txt) + list_context.sub_title = _('Filtered by "{0}"').format(sanitize_html(frappe.local.form_dict.txt)) if list_context.sub_title: list_context.parents = [{"name": _("Home"), "route": "/"}, diff --git a/package.json b/package.json index 1914cb0180..21a2ce0571 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "awesomplete": "^1.1.2", "cookie": "^0.3.1", "express": "^4.16.2", - "frappe-datatable": "^1.8.0", + "fast-deep-equal": "^2.0.1", + "frappe-datatable": "^1.11.0", "frappe-gantt": "^0.1.0", "fuse.js": "^3.2.0", "highlight.js": "^9.12.0", diff --git a/yarn.lock b/yarn.lock index 8ebc20911b..b7dbbcb81a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1110,6 +1110,11 @@ fast-deep-equal@^1.0.0: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + fast-diff@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154" @@ -1219,10 +1224,10 @@ forwarded@~0.1.2: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= -frappe-datatable@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/frappe-datatable/-/frappe-datatable-1.8.0.tgz#7f789ed77bdf9800143fffb1bb28a24d5dbdc27c" - integrity sha512-j3DdmYtTjhcVXCVkYjKHdZOc8tSwZapanlujdx1xzXcL7Ueo+BFiPR5WptWRfH43K3nboh3m7clcAIX7LdQR4g== +frappe-datatable@^1.11.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/frappe-datatable/-/frappe-datatable-1.11.0.tgz#e0f23659e2bb2e635ecd4f3eccadf1ed762a0629" + integrity sha512-tXjpNOywq3o6nMru47xhyzrH/sZ3PmrsLLOS+6+VJmqFsVC6TmlJDP1LRxSC+xCSDpumO8jjxfdwcwWWXKfDdw== dependencies: hyperlist "^1.0.0-beta" lodash "^4.17.5"