@@ -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 doc: [optional] Checks User permissions for given doc. | ||||
:param user: [optional] Check for given user. Default: current user. | :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).""" | :param parent_doctype: Required when checking permission for a child DocType (unless doc is specified).""" | ||||
import frappe.permissions | |||||
if not doctype and doc: | if not doctype and doc: | ||||
doctype = doc.doctype | doctype = doc.doctype | ||||
import frappe.permissions | |||||
out = frappe.permissions.has_permission(doctype, ptype, doc=doc, verbose=verbose, user=user, | out = frappe.permissions.has_permission(doctype, ptype, doc=doc, verbose=verbose, user=user, | ||||
raise_exception=throw, parent_doctype=parent_doctype) | raise_exception=throw, parent_doctype=parent_doctype) | ||||
if throw and not out: | 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 | return out | ||||
@@ -32,6 +32,7 @@ def get_list(doctype, fields=None, filters=None, order_by=None, | |||||
args = frappe._dict( | args = frappe._dict( | ||||
doctype=doctype, | doctype=doctype, | ||||
parent_doctype=parent, | |||||
fields=fields, | fields=fields, | ||||
filters=filters, | filters=filters, | ||||
or_filters=or_filters, | or_filters=or_filters, | ||||
@@ -29,6 +29,7 @@ from frappe import _, conf, safe_decode | |||||
from frappe.model.document import Document | 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 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.image import strip_exif_data, optimize_image | ||||
from frappe.utils.file_manager import safe_b64decode | |||||
class MaxFileSizeReachedError(frappe.ValidationError): | class MaxFileSizeReachedError(frappe.ValidationError): | ||||
pass | pass | ||||
@@ -436,7 +437,7 @@ class File(Document): | |||||
if b"," in self.content: | if b"," in self.content: | ||||
self.content = self.content.split(b",")[1] | self.content = self.content.split(b",")[1] | ||||
self.content = base64.b64decode(self.content) | |||||
self.content = safe_b64decode(self.content) | |||||
if not self.is_private: | if not self.is_private: | ||||
self.is_private = 0 | self.is_private = 0 | ||||
@@ -852,7 +853,7 @@ def extract_images_from_html(doc, content, is_private=False): | |||||
content = content.encode("utf-8") | content = content.encode("utf-8") | ||||
if b"," in content: | if b"," in content: | ||||
content = content.split(b",")[1] | content = content.split(b",")[1] | ||||
content = base64.b64decode(content) | |||||
content = safe_b64decode(content) | |||||
content = optimize_image(content, mtype) | content = optimize_image(content, mtype) | ||||
@@ -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 | // For license information, please see license.txt | ||||
/* eslint-disable */ | /* eslint-disable */ | ||||
@@ -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 | # import frappe | ||||
@@ -135,9 +135,10 @@ class MariaDBDatabase(Database): | |||||
table_name = get_table_name(doctype) | table_name = get_table_name(doctype) | ||||
return self.sql(f"DESC `{table_name}`") | 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) | 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 | # exception types | ||||
@staticmethod | @staticmethod | ||||
@@ -183,9 +183,12 @@ class PostgresDatabase(Database): | |||||
table_name = get_table_name(doctype) | table_name = get_table_name(doctype) | ||||
return self.sql(f"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME = '{table_name}'") | 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) | 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): | def create_auth_table(self): | ||||
self.sql_ddl("""create table if not exists "__Auth" ( | self.sql_ddl("""create table if not exists "__Auth" ( | ||||
@@ -382,10 +382,10 @@ class Leaderboard { | |||||
let timespan = this.options.selected_timespan.toLowerCase(); | let timespan = this.options.selected_timespan.toLowerCase(); | ||||
let current_date = frappe.datetime.now_date(); | let current_date = frappe.datetime.now_date(); | ||||
let date_range_map = { | 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 week": [frappe.datetime.add_days(current_date, -7), current_date], | ||||
"last month": [frappe.datetime.add_months(current_date, -1), current_date], | "last month": [frappe.datetime.add_months(current_date, -1), current_date], | ||||
"last quarter": [frappe.datetime.add_months(current_date, -3), current_date], | "last quarter": [frappe.datetime.add_months(current_date, -3), current_date], | ||||
@@ -36,10 +36,12 @@ class DatabaseQuery(object): | |||||
ignore_ifnull=False, save_user_settings=False, save_user_settings_fields=False, | ignore_ifnull=False, save_user_settings=False, save_user_settings_fields=False, | ||||
update=None, add_total_row=None, user_settings=None, reference_doctype=None, | 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: | 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)) | frappe.flags.error_message = _('Insufficient Permission for {0}').format(frappe.bold(self.doctype)) | ||||
raise frappe.PermissionError(self.doctype) | raise frappe.PermissionError(self.doctype) | ||||
@@ -787,12 +789,15 @@ class DatabaseQuery(object): | |||||
def check_parent_permission(parent, child_doctype): | def check_parent_permission(parent, child_doctype): | ||||
if parent: | if parent: | ||||
# User may pass fake parent and get the information from the child table | # 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 | raise frappe.PermissionError | ||||
if frappe.permissions.has_permission(parent): | if frappe.permissions.has_permission(parent): | ||||
return | return | ||||
# Either parent not passed or the user doesn't have permission on parent doctype of child table! | # Either parent not passed or the user doesn't have permission on parent doctype of child table! | ||||
raise frappe.PermissionError | raise frappe.PermissionError | ||||
@@ -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_workspace2 # 20.09.2021 | ||||
frappe.patches.v14_0.update_github_endpoints #08-11-2021 | frappe.patches.v14_0.update_github_endpoints #08-11-2021 | ||||
frappe.patches.v14_0.remove_db_aggregation | 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 | frappe.patches.v14_0.update_color_names_in_kanban_board_column |
@@ -1,12 +1,39 @@ | |||||
import frappe | import frappe | ||||
from frappe.query_builder import DocType | |||||
def execute(): | 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"} | |||||
) | |||||
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) | |||||
custom_rating_fields = frappe.get_all("Custom Field", fields=["dt", "fieldname"], filters={"fieldtype": "Rating"}) | |||||
# update data: int => decimal | |||||
frappe.qb.update(doctype).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 = frappe.qb.DocType(doctype_name) | |||||
field = field.fieldname | |||||
(frappe.qb.update(doctype_name).set(doctype[field], doctype[field]/5)).run() | |||||
# commit to flush updated rows | |||||
frappe.db.commit() |
@@ -190,7 +190,7 @@ frappe.ui.form.Form = class FrappeForm { | |||||
setup_std_layout() { | setup_std_layout() { | ||||
this.form_wrapper = $('<div></div>').appendTo(this.layout_main); | this.form_wrapper = $('<div></div>').appendTo(this.layout_main); | ||||
this.body = $('<div></div>').appendTo(this.form_wrapper); | |||||
this.body = $('<div class="std-form-layout"></div>').appendTo(this.form_wrapper); | |||||
// only tray | // only tray | ||||
this.meta.section_style='Simple'; // always simple! | this.meta.section_style='Simple'; // always simple! | ||||
@@ -211,12 +211,24 @@ frappe.ui.form.Form = class FrappeForm { | |||||
this.fields = this.layout.fields_list; | this.fields = this.layout.fields_list; | ||||
let dashboard_parent = $('<div class="form-dashboard">'); | let dashboard_parent = $('<div class="form-dashboard">'); | ||||
let dashboard_added = false; | |||||
if (this.layout.tabs.length) { | if (this.layout.tabs.length) { | ||||
this.layout.tabs[0].wrapper.prepend(dashboard_parent); | |||||
this.layout.tabs.every(tab => { | |||||
if (tab.df.options === 'Dashboard') { | |||||
tab.wrapper.prepend(dashboard_parent); | |||||
dashboard_added = true; | |||||
return false; | |||||
} | |||||
return true; | |||||
}); | |||||
if (!dashboard_added) { | |||||
this.layout.tabs[0].wrapper.prepend(dashboard_parent); | |||||
} | |||||
} else { | } else { | ||||
dashboard_parent.insertAfter(this.layout.wrapper.find('.form-message')); | |||||
this.layout.wrapper.find('.form-page').prepend(dashboard_parent); | |||||
} | } | ||||
this.dashboard = new frappe.ui.form.Dashboard(dashboard_parent, this); | this.dashboard = new frappe.ui.form.Dashboard(dashboard_parent, this); | ||||
this.tour = new frappe.ui.form.FormTour({ | this.tour = new frappe.ui.form.FormTour({ | ||||
@@ -29,7 +29,7 @@ $.extend(frappe.contacts, { | |||||
} | } | ||||
}, | }, | ||||
get_last_doc: function(frm) { | get_last_doc: function(frm) { | ||||
const reverse_routes = frappe.route_history.reverse(); | |||||
const reverse_routes = frappe.route_history.slice().reverse(); | |||||
const last_route = reverse_routes.find(route => { | const last_route = reverse_routes.find(route => { | ||||
return route[0] === 'Form' && route[1] !== frm.doctype | return route[0] === 'Form' && route[1] !== frm.doctype | ||||
}) | }) | ||||
@@ -1,4 +1,5 @@ | |||||
.form-control { | .form-control { | ||||
height: inherit; | |||||
border: none; | border: none; | ||||
font-size: var(--text-md); | font-size: var(--text-md); | ||||
position: relative; | position: relative; | ||||
@@ -13,10 +14,9 @@ | |||||
font-weight: normal; | font-weight: normal; | ||||
font-size: var(--text-sm); | font-size: var(--text-sm); | ||||
} | } | ||||
min-height: var(--input-height); | |||||
border-radius: $border-radius; | border-radius: $border-radius; | ||||
font-weight: 400; | font-weight: 400; | ||||
padding: 8px 12px; | |||||
padding: 6px 12px; | |||||
cursor: default; | cursor: default; | ||||
color: var(--disabled-text-color); | color: var(--disabled-text-color); | ||||
background-color: var(--disabled-control-bg); | background-color: var(--disabled-control-bg); | ||||
@@ -79,10 +79,9 @@ | |||||
.grid-static-col, | .grid-static-col, | ||||
.row-index { | .row-index { | ||||
height: 39px; | |||||
padding: var(--padding-sm) var(--padding-md); | |||||
height: 34px; | |||||
padding: 8px; | |||||
max-height: 200px; | max-height: 200px; | ||||
// border-right: 1px solid var(--border-color); | |||||
} | } | ||||
.grid-row-check { | .grid-row-check { | ||||
@@ -108,6 +107,7 @@ | |||||
.grid-row > .row { | .grid-row > .row { | ||||
.col:last-child { | .col:last-child { | ||||
margin-right: calc(-1 * var(--margin-sm)); | margin-right: calc(-1 * var(--margin-sm)); | ||||
border-right: none; | |||||
} | } | ||||
.col { | .col { | ||||
@@ -149,7 +149,7 @@ | |||||
} | } | ||||
textarea { | textarea { | ||||
height: 40px !important; | |||||
height: 37px !important; | |||||
} | } | ||||
.form-control { | .form-control { | ||||
@@ -157,7 +157,7 @@ | |||||
border: 0px; | border: 0px; | ||||
padding-top: 8px; | padding-top: 8px; | ||||
padding-bottom: 9px; | padding-bottom: 9px; | ||||
height: 40px; | |||||
height: 34px; | |||||
} | } | ||||
.link-btn { | .link-btn { | ||||
@@ -196,6 +196,10 @@ | |||||
} | } | ||||
} | } | ||||
.grid-static-col[data-fieldtype="Check"] .static-area { | |||||
padding-top: 2px; | |||||
} | |||||
.grid-static-col[data-fieldtype="Rating"] .field-area { | .grid-static-col[data-fieldtype="Rating"] .field-area { | ||||
margin-top: 1rem; | margin-top: 1rem; | ||||
margin-left: 1rem; | margin-left: 1rem; | ||||
@@ -1,6 +1,12 @@ | |||||
@import "../common/form.scss"; | @import "../common/form.scss"; | ||||
@import '~cropperjs/dist/cropper.min'; | @import '~cropperjs/dist/cropper.min'; | ||||
.std-form-layout > .form-layout > .form-page { | |||||
border-radius: var(--border-radius-md); | |||||
box-shadow: var(--card-shadow); | |||||
background-color: var(--card-bg); | |||||
} | |||||
.form-section, .form-dashboard-section { | .form-section, .form-dashboard-section { | ||||
margin: 0px; | margin: 0px; | ||||
@@ -12,6 +18,7 @@ | |||||
.section-head { | .section-head { | ||||
@extend .head-title; | @extend .head-title; | ||||
font-size: var(--text-base); | |||||
width: 100%; | width: 100%; | ||||
padding: var(--padding-sm) var(--padding-md); | padding: var(--padding-sm) var(--padding-md); | ||||
margin: 0; | margin: 0; | ||||
@@ -47,8 +54,12 @@ | |||||
.form-section.card-section, | .form-section.card-section, | ||||
.form-dashboard-section { | .form-dashboard-section { | ||||
margin-bottom: var(--margin-lg); | |||||
@extend .frappe-card; | |||||
border-bottom: 1px solid var(--gray-200); | |||||
padding: var(--padding-xs); | |||||
} | |||||
.row.form-section.card-section.visible-section:last-child { | |||||
border-bottom: none; | |||||
} | } | ||||
.form-dashboard-section { | .form-dashboard-section { | ||||
@@ -57,9 +68,8 @@ | |||||
} | } | ||||
.section-body { | .section-body { | ||||
display: block; | display: block; | ||||
padding-left: var(--padding-md); | |||||
padding-right: var(--padding-md); | |||||
padding-bottom: var(--padding-md); | |||||
padding: var(--padding-md); | |||||
padding-top: 0; | |||||
} | } | ||||
} | } | ||||
@@ -85,7 +95,8 @@ | |||||
.comment-box { | .comment-box { | ||||
@include card(); | @include card(); | ||||
padding: 25px var(--padding-xl); | |||||
margin-top: var(--margin-lg); | |||||
padding: var(--padding-lg); | |||||
.comment-input-header { | .comment-input-header { | ||||
@extend .head-title; | @extend .head-title; | ||||
margin-bottom: var(--margin-sm); | margin-bottom: var(--margin-sm); | ||||
@@ -304,19 +315,18 @@ | |||||
} | } | ||||
.form-tabs-list { | .form-tabs-list { | ||||
margin-bottom: var(--margin-lg); | |||||
padding-left: var(--padding-xs); | |||||
border-bottom: 1px solid var(--gray-200); | |||||
.form-tabs { | .form-tabs { | ||||
.nav-item { | .nav-item { | ||||
.nav-link { | .nav-link { | ||||
padding-bottom: var(--padding-md); | |||||
color: var(--gray-700); | color: var(--gray-700); | ||||
padding-left: 0; | |||||
padding-right: 0; | |||||
margin-right: var(--margin-xl); | |||||
padding: var(--padding-md) 0; | |||||
margin: 0 var(--margin-md); | |||||
&.active { | &.active { | ||||
font-weight: 500; | |||||
font-weight: 600; | |||||
border-bottom: 1px solid var(--primary); | border-bottom: 1px solid var(--primary); | ||||
color: var(--text-color); | color: var(--text-color); | ||||
} | } | ||||
@@ -507,10 +507,10 @@ def get_timespan_date_range(timespan): | |||||
"yesterday": lambda: (add_to_date(today, days=-1),) * 2, | "yesterday": lambda: (add_to_date(today, days=-1),) * 2, | ||||
"today": lambda: (today, today), | "today": lambda: (today, today), | ||||
"tomorrow": lambda: (add_to_date(today, days=1),) * 2, | "tomorrow": lambda: (add_to_date(today, days=1),) * 2, | ||||
"this week": lambda: (get_first_day_of_week(today), today), | |||||
"this month": lambda: (get_first_day(today), today), | |||||
"this quarter": lambda: (get_quarter_start(today), today), | |||||
"this year": lambda: (get_year_start(today), today), | |||||
"this week": lambda: (get_first_day_of_week(today), get_last_day_of_week(today)), | |||||
"this month": lambda: (get_first_day(today), get_last_day(today)), | |||||
"this quarter": lambda: (get_quarter_start(today), get_quarter_ending(today)), | |||||
"this year": lambda: (get_year_start(today), get_year_ending(today)), | |||||
"next week": lambda: (get_first_day_of_week(add_to_date(today, days=7)), get_last_day_of_week(add_to_date(today, days=7))), | "next week": lambda: (get_first_day_of_week(add_to_date(today, days=7)), get_last_day_of_week(add_to_date(today, days=7))), | ||||
"next month": lambda: (get_first_day(add_to_date(today, months=1)), get_last_day(add_to_date(today, months=1))), | "next month": lambda: (get_first_day(add_to_date(today, months=1)), get_last_day(add_to_date(today, months=1))), | ||||
"next quarter": lambda: (get_quarter_start(add_to_date(today, months=3)), get_quarter_ending(add_to_date(today, months=3))), | "next quarter": lambda: (get_quarter_start(add_to_date(today, months=3)), get_quarter_ending(add_to_date(today, months=3))), | ||||
@@ -1,4 +1,4 @@ | |||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors | |||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors | |||||
# License: MIT. See LICENSE | # License: MIT. See LICENSE | ||||
import frappe | import frappe | ||||
@@ -17,6 +17,20 @@ class MaxFileSizeReachedError(frappe.ValidationError): | |||||
pass | pass | ||||
def safe_b64decode(binary: bytes) -> bytes: | |||||
"""Adds padding if doesn't already exist before decoding. | |||||
This attempts to avoid the `binascii.Error: Incorrect padding` error raised | |||||
when the number of trailing = is simply not enough :crie:. Although, it may | |||||
be an indication of corrupted data. | |||||
Refs: | |||||
* https://en.wikipedia.org/wiki/Base64 | |||||
* https://stackoverflow.com/questions/2941995/python-ignore-incorrect-padding-error-when-base64-decoding | |||||
""" | |||||
return base64.b64decode(binary + b"===") | |||||
def get_file_url(file_data_name): | def get_file_url(file_data_name): | ||||
data = frappe.db.get_value("File", file_data_name, ["file_name", "file_url"], as_dict=True) | data = frappe.db.get_value("File", file_data_name, ["file_name", "file_url"], as_dict=True) | ||||
return data.file_url or data.file_name | return data.file_url or data.file_name | ||||
@@ -112,7 +126,7 @@ def get_uploaded_content(): | |||||
if 'filedata' in frappe.form_dict: | if 'filedata' in frappe.form_dict: | ||||
if "," in frappe.form_dict.filedata: | if "," in frappe.form_dict.filedata: | ||||
frappe.form_dict.filedata = frappe.form_dict.filedata.rsplit(",", 1)[1] | frappe.form_dict.filedata = frappe.form_dict.filedata.rsplit(",", 1)[1] | ||||
frappe.uploaded_content = base64.b64decode(frappe.form_dict.filedata) | |||||
frappe.uploaded_content = safe_b64decode(frappe.form_dict.filedata) | |||||
frappe.uploaded_filename = frappe.form_dict.filename | frappe.uploaded_filename = frappe.form_dict.filename | ||||
return frappe.uploaded_filename, frappe.uploaded_content | return frappe.uploaded_filename, frappe.uploaded_content | ||||
else: | else: | ||||
@@ -126,7 +140,7 @@ def save_file(fname, content, dt, dn, folder=None, decode=False, is_private=0, d | |||||
if b"," in content: | if b"," in content: | ||||
content = content.split(b",")[1] | content = content.split(b",")[1] | ||||
content = base64.b64decode(content) | |||||
content = safe_b64decode(content) | |||||
file_size = check_max_file_size(content) | file_size = check_max_file_size(content) | ||||
content_hash = get_content_hash(content) | content_hash = get_content_hash(content) | ||||
@@ -69,15 +69,15 @@ frappe.ready(function() { | |||||
const confirm_password = $('#confirm_password').val() | const confirm_password = $('#confirm_password').val() | ||||
if (!args.old_password && !args.key) { | if (!args.old_password && !args.key) { | ||||
frappe.msgprint({ | frappe.msgprint({ | ||||
title: "{{ _('Message') }}", | |||||
message: "{{ _('Old Password Required.') }}", | |||||
title: "{{ _('Missing Value') }}", | |||||
message: "{{ _('Please enter your old password.') }}", | |||||
clear: true | clear: true | ||||
}); | }); | ||||
} | } | ||||
if (!args.new_password) { | if (!args.new_password) { | ||||
frappe.msgprint({ | frappe.msgprint({ | ||||
title: "{{ _('Message') }}", | |||||
message: "{{ _('New Password Required.') }}", | |||||
title: "{{ _('Missing Value') }}", | |||||
message: "{{ _('Please enter your new password.') }}", | |||||
clear: true | clear: true | ||||
}); | }); | ||||
} | } | ||||
@@ -110,8 +110,8 @@ frappe.ready(function() { | |||||
.html("{{ _('Status Updated') }}"); | .html("{{ _('Status Updated') }}"); | ||||
if(r.message) { | if(r.message) { | ||||
frappe.msgprint({ | frappe.msgprint({ | ||||
title: "{{ _('Message') }}", | |||||
message: "{{ _('Password Updated') }}", | |||||
title: "{{ _('Password set') }}", | |||||
message: "{{ _('Your new password has been set successfully.') }}", | |||||
// password is updated successfully | // password is updated successfully | ||||
// clear any server message | // clear any server message | ||||
clear: true | clear: true | ||||