diff --git a/frappe/__init__.py b/frappe/__init__.py index 96aa2b1b5f..08c0f794b3 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -740,17 +740,26 @@ def has_permission(doctype=None, ptype="read", doc=None, user=None, verbose=Fals :param doc: [optional] Checks User permissions for given doc. :param user: [optional] Check for given user. Default: current user. :param parent_doctype: Required when checking permission for a child DocType (unless doc is specified).""" + import frappe.permissions + if not doctype and doc: doctype = doc.doctype - import frappe.permissions out = frappe.permissions.has_permission(doctype, ptype, doc=doc, verbose=verbose, user=user, raise_exception=throw, parent_doctype=parent_doctype) + if throw and not out: - if doc: - frappe.throw(_("No permission for {0}").format(doc.doctype + " " + doc.name)) - else: - frappe.throw(_("No permission for {0}").format(doctype)) + # mimics frappe.throw + document_label = f"{doc.doctype} {doc.name}" if doc else doctype + msgprint( + _("No permission for {0}").format(document_label), + raise_exception=ValidationError, + title=None, + indicator='red', + is_minimizable=None, + wide=None, + as_list=False + ) return out diff --git a/frappe/client.py b/frappe/client.py index 6641e471af..e835e7fee7 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -32,6 +32,7 @@ def get_list(doctype, fields=None, filters=None, order_by=None, args = frappe._dict( doctype=doctype, + parent_doctype=parent, fields=fields, filters=filters, or_filters=or_filters, diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 91090bdd77..adf10b9a03 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -29,6 +29,7 @@ from frappe import _, conf, safe_decode from frappe.model.document import Document from frappe.utils import call_hook_method, cint, cstr, encode, get_files_path, get_hook_method, random_string, strip from frappe.utils.image import strip_exif_data, optimize_image +from frappe.utils.file_manager import safe_b64decode class MaxFileSizeReachedError(frappe.ValidationError): pass @@ -436,7 +437,7 @@ class File(Document): if b"," in self.content: self.content = self.content.split(b",")[1] - self.content = base64.b64decode(self.content) + self.content = safe_b64decode(self.content) if not self.is_private: self.is_private = 0 @@ -852,7 +853,7 @@ def extract_images_from_html(doc, content, is_private=False): content = content.encode("utf-8") if b"," in content: content = content.split(b",")[1] - content = base64.b64decode(content) + content = safe_b64decode(content) content = optimize_image(content, mtype) diff --git a/frappe/core/doctype/report/boilerplate/controller.js b/frappe/core/doctype/report/boilerplate/controller.js index 5148f34462..9cf71a8c09 100644 --- a/frappe/core/doctype/report/boilerplate/controller.js +++ b/frappe/core/doctype/report/boilerplate/controller.js @@ -1,4 +1,4 @@ -// Copyright (c) 2016, {app_publisher} and contributors +// Copyright (c) {year}, {app_publisher} and contributors // For license information, please see license.txt /* eslint-disable */ diff --git a/frappe/core/doctype/report/boilerplate/controller.py b/frappe/core/doctype/report/boilerplate/controller.py index ccf732a405..72da0c7ce5 100644 --- a/frappe/core/doctype/report/boilerplate/controller.py +++ b/frappe/core/doctype/report/boilerplate/controller.py @@ -1,5 +1,5 @@ -# Copyright (c) 2013, {app_publisher} and contributors -# License: MIT. See LICENSE +# Copyright (c) {year}, {app_publisher} and contributors +# For license information, please see license.txt # import frappe diff --git a/frappe/database/mariadb/database.py b/frappe/database/mariadb/database.py index afd912bc6b..6b827a4e89 100644 --- a/frappe/database/mariadb/database.py +++ b/frappe/database/mariadb/database.py @@ -135,9 +135,10 @@ class MariaDBDatabase(Database): table_name = get_table_name(doctype) return self.sql(f"DESC `{table_name}`") - def change_column_type(self, doctype: str, column: str, type: str) -> Union[List, Tuple]: + def change_column_type(self, doctype: str, column: str, type: str, nullable: bool = False) -> Union[List, Tuple]: table_name = get_table_name(doctype) - return self.sql(f"ALTER TABLE `{table_name}` MODIFY `{column}` {type} NOT NULL") + null_constraint = "NOT NULL" if not nullable else "" + return self.sql(f"ALTER TABLE `{table_name}` MODIFY `{column}` {type} {null_constraint}") # exception types @staticmethod diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py index 3cea1440cf..008635b1b3 100644 --- a/frappe/database/postgres/database.py +++ b/frappe/database/postgres/database.py @@ -183,9 +183,12 @@ class PostgresDatabase(Database): table_name = get_table_name(doctype) return self.sql(f"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME = '{table_name}'") - def change_column_type(self, doctype: str, column: str, type: str) -> Union[List, Tuple]: + def change_column_type(self, doctype: str, column: str, type: str, nullable: bool = False) -> Union[List, Tuple]: table_name = get_table_name(doctype) - return self.sql(f'ALTER TABLE "{table_name}" ALTER COLUMN "{column}" TYPE {type}') + null_constraint = "SET NOT NULL" if not nullable else "DROP NOT NULL" + return self.sql(f"""ALTER TABLE "{table_name}" + ALTER COLUMN "{column}" TYPE {type}, + ALTER COLUMN "{column}" {null_constraint}""") def create_auth_table(self): self.sql_ddl("""create table if not exists "__Auth" ( diff --git a/frappe/desk/page/leaderboard/leaderboard.js b/frappe/desk/page/leaderboard/leaderboard.js index 076d672db5..aa1678af37 100644 --- a/frappe/desk/page/leaderboard/leaderboard.js +++ b/frappe/desk/page/leaderboard/leaderboard.js @@ -382,10 +382,10 @@ class Leaderboard { let timespan = this.options.selected_timespan.toLowerCase(); let current_date = frappe.datetime.now_date(); let date_range_map = { - "this week": [frappe.datetime.week_start(), current_date], - "this month": [frappe.datetime.month_start(), current_date], - "this quarter": [frappe.datetime.quarter_start(), current_date], - "this year": [frappe.datetime.year_start(), current_date], + "this week": [frappe.datetime.week_start(), frappe.datetime.week_end()], + "this month": [frappe.datetime.month_start(), frappe.datetime.month_end()], + "this quarter": [frappe.datetime.quarter_start(), frappe.datetime.quarter_end()], + "this year": [frappe.datetime.year_start(), frappe.datetime.year_end()], "last week": [frappe.datetime.add_days(current_date, -7), current_date], "last month": [frappe.datetime.add_months(current_date, -1), current_date], "last quarter": [frappe.datetime.add_months(current_date, -3), current_date], diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 16c0d18d9f..cb2c2af898 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -36,10 +36,12 @@ class DatabaseQuery(object): ignore_ifnull=False, save_user_settings=False, save_user_settings_fields=False, update=None, add_total_row=None, user_settings=None, reference_doctype=None, run=True, strict=True, pluck=None, ignore_ddl=False, parent_doctype=None) -> List: - if not ignore_permissions and \ - not frappe.has_permission(self.doctype, "select", user=user, parent_doctype=parent_doctype) and \ - not frappe.has_permission(self.doctype, "read", user=user, parent_doctype=parent_doctype): + if ( + not ignore_permissions + and not frappe.has_permission(self.doctype, "select", user=user, parent_doctype=parent_doctype) + and not frappe.has_permission(self.doctype, "read", user=user, parent_doctype=parent_doctype) + ): frappe.flags.error_message = _('Insufficient Permission for {0}').format(frappe.bold(self.doctype)) raise frappe.PermissionError(self.doctype) @@ -787,12 +789,15 @@ class DatabaseQuery(object): def check_parent_permission(parent, child_doctype): if parent: # User may pass fake parent and get the information from the child table - if child_doctype and not frappe.db.exists('DocField', - {'parent': parent, 'options': child_doctype}): + if child_doctype and not ( + frappe.db.exists('DocField', {'parent': parent, 'options': child_doctype}) + or frappe.db.exists('Custom Field', {'dt': parent, 'options': child_doctype}) + ): raise frappe.PermissionError if frappe.permissions.has_permission(parent): return + # Either parent not passed or the user doesn't have permission on parent doctype of child table! raise frappe.PermissionError diff --git a/frappe/patches.txt b/frappe/patches.txt index 1760a04f35..27ba1a145d 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -188,5 +188,5 @@ frappe.patches.v14_0.copy_mail_data #08.03.21 frappe.patches.v14_0.update_workspace2 # 20.09.2021 frappe.patches.v14_0.update_github_endpoints #08-11-2021 frappe.patches.v14_0.remove_db_aggregation -frappe.patches.v14_0.save_ratings_in_fraction +frappe.patches.v14_0.save_ratings_in_fraction #23-12-2021 frappe.patches.v14_0.update_color_names_in_kanban_board_column diff --git a/frappe/patches/v14_0/save_ratings_in_fraction.py b/frappe/patches/v14_0/save_ratings_in_fraction.py index bdc5dfee3d..c933179b44 100644 --- a/frappe/patches/v14_0/save_ratings_in_fraction.py +++ b/frappe/patches/v14_0/save_ratings_in_fraction.py @@ -1,12 +1,39 @@ import frappe +from frappe.query_builder import DocType + def execute(): - rating_fields = frappe.get_all("DocField", fields=["parent", "fieldname"], filters={"fieldtype": "Rating"}) + RATING_FIELD_TYPE = "decimal(3,2)" + rating_fields = frappe.get_all( + "DocField", fields=["parent", "fieldname"], filters={"fieldtype": "Rating"} + ) - custom_rating_fields = frappe.get_all("Custom Field", fields=["dt", "fieldname"], filters={"fieldtype": "Rating"}) + custom_rating_fields = frappe.get_all( + "Custom Field", fields=["dt", "fieldname"], filters={"fieldtype": "Rating"} + ) - for field in rating_fields + custom_rating_fields: - doctype_name = field.get("parent") or field.get("dt") - doctype = frappe.qb.DocType(doctype_name) - field = field.fieldname - (frappe.qb.update(doctype_name).set(doctype[field], doctype[field]/5)).run() + for _field in rating_fields + custom_rating_fields: + doctype_name = _field.get("parent") or _field.get("dt") + doctype = DocType(doctype_name) + field = _field.fieldname + + # TODO: Add postgres support (for the check) + if ( + frappe.conf.db_type == "mariadb" + and frappe.db.get_column_type(doctype_name, field) == RATING_FIELD_TYPE + ): + continue + + # commit any changes so far for upcoming DDL + frappe.db.commit() + + # alter column types for rating fieldtype + frappe.db.change_column_type(doctype_name, column=field, type=RATING_FIELD_TYPE, nullable=True) + + # update data: int => decimal + frappe.qb.update(doctype).set( + doctype[field], doctype[field] / 5 + ).run() + + # commit to flush updated rows + frappe.db.commit() diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 8af1631b48..9a75e510da 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -190,7 +190,7 @@ frappe.ui.form.Form = class FrappeForm { setup_std_layout() { this.form_wrapper = $('
').appendTo(this.layout_main); - this.body = $('').appendTo(this.form_wrapper); + this.body = $('').appendTo(this.form_wrapper); // only tray this.meta.section_style='Simple'; // always simple! @@ -211,12 +211,24 @@ frappe.ui.form.Form = class FrappeForm { this.fields = this.layout.fields_list; let dashboard_parent = $('