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"