diff --git a/frappe/__init__.py b/frappe/__init__.py index d3d9c31d43..743533c0ac 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -15,7 +15,7 @@ from past.builtins import cmp from .exceptions import * from .utils.jinja import get_jenv, get_template, render_template, get_email_from_template -__version__ = '10.1.6' +__version__ = '10.1.10' __title__ = "Frappe Framework" local = Local() diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index 00b9918933..8acececa03 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -88,7 +88,7 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = frappe.db.commit() if cint(send_email): - frappe.flags.print_letterhead = print_letterhead + frappe.flags.print_letterhead = cint(print_letterhead) comm.send(print_html, print_format, attachments, send_me_a_copy=send_me_a_copy) return { diff --git a/frappe/core/doctype/data_export/exporter.py b/frappe/core/doctype/data_export/exporter.py index 98b657d71c..ca1fc6cd3f 100644 --- a/frappe/core/doctype/data_export/exporter.py +++ b/frappe/core/doctype/data_export/exporter.py @@ -29,7 +29,7 @@ def export_data(doctype=None, parent_doctype=None, all_doctypes=True, with_data= select_columns=select_columns, file_type=file_type, template=template, filters=filters) exporter.build_response() -class DataExporter(): +class DataExporter: def __init__(self, doctype=None, parent_doctype=None, all_doctypes=True, with_data=False, select_columns=None, file_type='CSV', template=False, filters=None): self.doctype = doctype @@ -98,10 +98,9 @@ class DataExporter(): self.add_data() if self.with_data and not self.data: frappe.respond_as_web_page(_('No Data'), _('There is no data to be exported'), indicator_color='orange') - return if self.file_type == 'Excel': - return self.build_response_as_excel() + self.build_response_as_excel() else: # write out response as a type csv frappe.response['result'] = cstr(self.writer.getvalue()) @@ -323,7 +322,7 @@ class DataExporter(): def build_response_as_excel(self): filename = frappe.generate_hash("", 10) with open(filename, 'wb') as f: - f.write(cstr(self.writer.getvalue()).encode("utf-8")) + f.write(cstr(self.writer.getvalue()).encode('utf-8')) f = open(filename) reader = csv.reader(f) diff --git a/frappe/core/doctype/transaction_log/test_transaction_log.py b/frappe/core/doctype/transaction_log/test_transaction_log.py index 43327b55f8..164a683c38 100644 --- a/frappe/core/doctype/transaction_log/test_transaction_log.py +++ b/frappe/core/doctype/transaction_log/test_transaction_log.py @@ -34,6 +34,9 @@ class TestTransactionLog(unittest.TestCase): sha = hashlib.sha256() - sha.update(str(third_log.transaction_hash) + str(second_log.chaining_hash)) + sha.update( + frappe.safe_encode(str(third_log.transaction_hash)) + + frappe.safe_encode(str(second_log.chaining_hash)) + ) self.assertEqual(sha.hexdigest(), third_log.chaining_hash) diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 3a620dae44..8f528edcec 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -68,6 +68,7 @@ class User(Document): self.validate_user_email_inbox() ask_pass_update() self.validate_roles() + self.validate_user_image() if self.language == "Loading...": self.language = None @@ -81,6 +82,10 @@ class User(Document): self.set('roles', []) self.append_roles(*[role.role for role in role_profile.roles]) + def validate_user_image(self): + if self.user_image and len(self.user_image) > 2000: + frappe.throw(_("Not a valid User Image.")) + def on_update(self): # clear new password self.validate_user_limit() diff --git a/frappe/database.py b/frappe/database.py index 07ff5cb32f..939a82fea7 100644 --- a/frappe/database.py +++ b/frappe/database.py @@ -17,6 +17,7 @@ from frappe import _ from frappe.model.utils.link_count import flush_local_link_count from frappe.utils.background_jobs import execute_job, get_queue from frappe import as_unicode +import six # imports - compatibility imports from six import ( @@ -81,10 +82,14 @@ class Database: conversions.update({ FIELD_TYPE.NEWDECIMAL: float, FIELD_TYPE.DATETIME: get_datetime, - TimeDelta: conversions[binary_type], UnicodeWithAttrs: conversions[text_type] }) + if six.PY2: + conversions.update({ + TimeDelta: conversions[binary_type] + }) + if usessl: self._conn = pymysql.connect(self.host, self.user or '', self.password or '', charset='utf8mb4', use_unicode = True, ssl=self.ssl, conv = conversions, local_infile = self.local_infile) diff --git a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.js b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.js index 2e3c707a5e..c264d12dec 100644 --- a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.js +++ b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.js @@ -3,6 +3,7 @@ frappe.ui.form.on('Dropbox Settings', { refresh: function(frm) { + frm.toggle_display(["app_access_key", "app_secret_key"], !(frm.doc.__onload && frm.doc.__onload.dropbox_setup_via_site_config)); frm.clear_custom_buttons(); frm.events.take_backup(frm); }, @@ -19,7 +20,7 @@ frappe.ui.form.on('Dropbox Settings', { } }) } - else if (frm.doc.dropbox_setup_via_site_config) { + else if (frm.doc.__onload && frm.doc.__onload.dropbox_setup_via_site_config) { frappe.call({ method: "frappe.integrations.doctype.dropbox_settings.dropbox_settings.get_redirect_url", freeze: true, @@ -36,7 +37,7 @@ frappe.ui.form.on('Dropbox Settings', { }, take_backup: function(frm) { - if ((frm.doc.app_access_key && frm.doc.app_secret_key) || frm.doc.dropbox_setup_via_site_config){ + if ((frm.doc.app_access_key && frm.doc.app_secret_key) || (frm.doc.__onload && frm.doc.__onload.dropbox_setup_via_site_config)){ frm.add_custom_button(__("Take Backup Now"), function(frm){ frappe.call({ method: "frappe.integrations.doctype.dropbox_settings.dropbox_settings.take_backup", diff --git a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.json b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.json index cbe1fe8b1a..a8ab581bf7 100644 --- a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.json +++ b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.json @@ -54,7 +54,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Send Notifications To", "length": 0, @@ -84,7 +84,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Backup Frequency", "length": 0, @@ -108,7 +108,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "eval:!doc.dropbox_setup_via_site_config", + "depends_on": "", "fieldname": "app_access_key", "fieldtype": "Data", "hidden": 0, @@ -139,7 +139,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "eval:!doc.dropbox_setup_via_site_config", + "depends_on": "", "fieldname": "app_secret_key", "fieldtype": "Password", "hidden": 0, @@ -283,36 +283,6 @@ "search_index": 0, "set_only_once": 0, "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "dropbox_setup_via_site_config", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Dropbox Setup via Site Config", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 } ], "has_web_view": 0, @@ -325,7 +295,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2017-06-20 15:45:33.683827", + "modified": "2018-03-22 16:02:00.597029", "modified_by": "Administrator", "module": "Integrations", "name": "Dropbox Settings", diff --git a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py index 35027b90c9..283546320f 100644 --- a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py +++ b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py @@ -20,7 +20,8 @@ ignore_list = [".DS_Store"] class DropboxSettings(Document): def onload(self): if not self.app_access_key and frappe.conf.dropbox_access_key: - self.dropbox_setup_via_site_config = 1 + self.set_onload("dropbox_setup_via_site_config", 1) + @frappe.whitelist() def take_backup(): @@ -171,7 +172,7 @@ def upload_file_to_dropbox(filename, folder, dropbox_client): cursor.offset = f.tell() except dropbox.exceptions.ApiError as e: if isinstance(e.error, dropbox.files.UploadError): - error = "File Path: {path}\n".foramt(path=path) + error = "File Path: {path}\n".format(path=path) error += frappe.get_traceback() frappe.log_error(error) else: @@ -201,7 +202,7 @@ def get_dropbox_settings(redirect_uri=False): if redirect_uri: app_details.update({ - 'rediret_uri': get_request_site_address(True) \ + 'redirect_uri': get_request_site_address(True) \ + '/api/method/frappe.integrations.doctype.dropbox_settings.dropbox_settings.dropbox_auth_finish' \ if settings.app_secret_key else frappe.conf.dropbox_broker_site\ + '/api/method/dropbox_erpnext_broker.www.setup_dropbox.generate_dropbox_access_token', @@ -233,7 +234,7 @@ def get_dropbox_authorize_url(): dropbox_oauth_flow = dropbox.DropboxOAuth2Flow( app_details["app_key"], app_details["app_secret"], - app_details["rediret_uri"], + app_details["redirect_uri"], {}, "dropbox-auth-csrf-token" ) @@ -254,7 +255,7 @@ def dropbox_auth_finish(return_access_token=False): dropbox_oauth_flow = dropbox.DropboxOAuth2Flow( app_details["app_key"], app_details["app_secret"], - app_details["rediret_uri"], + app_details["redirect_uri"], { 'dropbox-auth-csrf-token': callback.state }, diff --git a/frappe/integrations/doctype/stripe_settings/stripe_settings.py b/frappe/integrations/doctype/stripe_settings/stripe_settings.py index c96b508743..62d90cbe53 100644 --- a/frappe/integrations/doctype/stripe_settings/stripe_settings.py +++ b/frappe/integrations/doctype/stripe_settings/stripe_settings.py @@ -23,12 +23,18 @@ class StripeSettings(Document): "XAF", "XOF", "XPF", "YER", "ZAR" ] + currency_wise_minimum_charge_amount = { + 'JPY': 50, 'MXN': 10, 'DKK': 2.50, 'HKD': 4.00, 'NOK': 3.00, 'SEK': 3.00, + 'USD': 0.50, 'AUD': 0.50, 'BRL': 0.50, 'CAD': 0.50, 'CHF': 0.50, 'EUR': 0.50, + 'GBP': 0.30, 'NZD': 0.50, 'SGD': 0.50 + } + def validate(self): create_payment_gateway('Stripe') call_hook_method('payment_gateway_enabled', gateway='Stripe') if not self.flags.ignore_mandatory: self.validate_stripe_credentails() - + def validate_stripe_credentails(self): if self.publishable_key and self.secret_key: header = {"Authorization": "Bearer {0}".format(self.get_password(fieldname="secret_key", raise_exception=False))} @@ -36,11 +42,17 @@ class StripeSettings(Document): make_get_request(url="https://api.stripe.com/v1/charges", headers=header) except Exception: frappe.throw(_("Seems Publishable Key or Secret Key is wrong !!!")) - + def validate_transaction_currency(self, currency): if currency not in self.supported_currencies: frappe.throw(_("Please select another payment method. Stripe does not support transactions in currency '{0}'").format(currency)) + def validate_minimum_transaction_amount(self, currency, amount): + if currency in self.currency_wise_minimum_charge_amount: + if flt(amount) < self.currency_wise_minimum_charge_amount.get(currency, 0.0): + frappe.throw(_("For currency {0}, the minimum transaction amount should be {1}").format(currency, + self.currency_wise_minimum_charge_amount.get(currency, 0.0))) + def get_payment_url(self, **kwargs): return get_url("./integrations/stripe_checkout?{0}".format(urlencode(kwargs))) diff --git a/frappe/model/naming.py b/frappe/model/naming.py index f6f7fd57a2..70eb44abcd 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -47,7 +47,7 @@ def set_new_name(doc): if autoname.startswith("naming_series:"): set_name_by_naming_series(doc) elif "#" in autoname: - doc.name = make_autoname(autoname) + doc.name = make_autoname(autoname, doc=doc) elif autoname.lower()=='prompt': # set from __newname in save.py if not doc.name: diff --git a/frappe/public/js/frappe/dom.js b/frappe/public/js/frappe/dom.js index 584c441db8..be9356175f 100644 --- a/frappe/public/js/frappe/dom.js +++ b/frappe/public/js/frappe/dom.js @@ -33,12 +33,19 @@ frappe.dom = { document.getElementsByTagName('head')[0].appendChild(el); }, remove_script_and_style: function(txt) { + const evil_tags = ["script", "style", "noscript", "title", "meta", "base", "head"]; + const regex = new RegExp(evil_tags.map(tag => `<${tag}>.*<\\/${tag}>`).join('|')); + if (!regex.test(txt)) { + // no evil tags found, skip the DOM method entirely! + return txt; + } + var div = document.createElement('div'); div.innerHTML = txt; var found = false; - ["script", "style", "noscript", "title", "meta", "base", "head"].forEach(function(e, i) { + evil_tags.forEach(function(e) { var elements = div.getElementsByTagName(e); - var i = elements.length; + i = elements.length; while (i--) { found = true; elements[i].parentNode.removeChild(elements[i]); diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js index 39131e8406..23a8b41129 100644 --- a/frappe/public/js/frappe/form/save.js +++ b/frappe/public/js/frappe/form/save.js @@ -19,6 +19,7 @@ frappe.ui.form.save = function (frm, action, callback, btn) { var save = function () { remove_empty_rows(); + $(frm.wrapper).addClass('validated-form'); if (check_mandatory()) { _call({ @@ -127,9 +128,8 @@ frappe.ui.form.save = function (frm, action, callback, btn) { if (df.reqd && !frappe.model.has_value(doc.doctype, doc.name, df.fieldname)) { has_errors = true; error_fields[error_fields.length] = __(df.label); - // scroll to field - if (!me.scroll_set) { + if (!frm.scroll_set) { scroll_to(doc.parentfield || df.fieldname); } @@ -141,6 +141,7 @@ frappe.ui.form.save = function (frm, action, callback, btn) { } }); + if (error_fields.length) { if (doc.parenttype) { var message = __('Mandatory fields required in table {0}, Row {1}', diff --git a/frappe/public/js/frappe/list/base_list.js b/frappe/public/js/frappe/list/base_list.js index a924522052..d842885bac 100644 --- a/frappe/public/js/frappe/list/base_list.js +++ b/frappe/public/js/frappe/list/base_list.js @@ -225,7 +225,7 @@ frappe.views.BaseList = class BaseList { } setup_result_area() { - this.$result = $(`
{{ col.formatter
diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js
index d9789abac7..626de0c5e1 100644
--- a/frappe/public/js/frappe/views/reports/query_report.js
+++ b/frappe/public/js/frappe/views/reports/query_report.js
@@ -2,10 +2,10 @@
// MIT License. See license.txt
import DataTable from 'frappe-datatable';
-frappe.provide("frappe.views");
-frappe.provide("frappe.query_reports");
+frappe.provide('frappe.views');
+frappe.provide('frappe.query_reports');
-frappe.standard_pages["query-report"] = function() {
+frappe.standard_pages['query-report'] = function() {
var wrapper = frappe.container.add_page('query-report');
frappe.ui.make_app_page({
@@ -18,7 +18,7 @@ frappe.standard_pages["query-report"] = function() {
parent: wrapper,
});
- $(wrapper).bind("show", function() {
+ $(wrapper).bind('show', function() {
frappe.query_report.show();
});
};
@@ -86,7 +86,6 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
frappe.run_serially([
() => this.get_report_doc(),
() => this.get_report_settings(),
- () => this.report_settings.onload && this.report_settings.onload(this),
() => this.setup_page_head(),
() => this.refresh_report()
]);
@@ -98,6 +97,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
return frappe.run_serially([
() => this.setup_filters(),
() => this.set_route_filters(),
+ () => this.report_settings.onload && this.report_settings.onload(this),
() => this.get_user_settings(),
() => this.refresh(),
() => this.save_user_settings()
@@ -208,8 +208,8 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
this.toggle_message(true);
const filters = this.get_filter_values(true);
return new Promise(resolve => frappe.call({
- method: "frappe.desk.query_report.run",
- type: "GET",
+ method: 'frappe.desk.query_report.run',
+ type: 'GET',
args: {
report_name: this.report_name,
filters: filters
@@ -230,22 +230,21 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
}
render_report(data) {
- this._data = data.result;
- this._columns = this.prepare_columns(data.columns);
- this.is_tree_report = this._data.some(d => 'indent' in d);
-
- const columns = this.get_columns_for_datatable();
+ this.columns = this.prepare_columns(data.columns);
+ this.data = this.prepare_data(data.result);
+ this.tree_report = this.data.some(d => 'indent' in d);
+ const columns = this.get_visible_columns();
if (this.datatable) {
- this.datatable.refresh(this._data, columns);
+ this.datatable.refresh(this.data, columns);
return;
}
this.datatable = new DataTable(this.$report[0], {
columns: columns,
- data: this._data,
+ data: this.data,
inlineFilters: true,
- treeView: this.is_tree_report,
+ treeView: this.tree_report,
layout: 'fixed',
events: {
onRemoveColumn: () => this.save_user_settings(),
@@ -280,7 +279,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
save_user_settings(clear_settings = false) {
if (clear_settings) {
- return frappe.model.user_settings.remove(this.report_name, 'column_order');
+ return frappe.model.user_settings.save(this.report_name, 'column_order', []);
}
if (!this.datatable) return;
const column_order = this.datatable.datamanager.getColumns(true).map(col => col.id);
@@ -298,48 +297,58 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
[fieldtype, options] = fieldtype.split('/');
}
- return {
+ column = {
label,
fieldname: label,
fieldtype,
width,
options
};
+ } else {
+ column = {
+ label: column,
+ fieldname: column,
+ fieldtype: 'Data'
+ };
}
-
- return {
- label: column,
- fieldname: column,
- fieldtype: 'Data'
- };
}
- return column;
+ return Object.assign(column, {
+ id: column.fieldname,
+ name: column.label,
+ width: parseInt(column.width) || null,
+ editable: false,
+ format: (value, row, column, data) =>
+ frappe.format(value || '', column,
+ {for_print: false, always_show_decimals: true}, data)
+ });
});
}
- get_columns_for_datatable() {
- const columns = this._columns.map(df => {
- return {
- id: df.fieldname,
- name: df.label,
- width: df.width || null,
- editable: false,
- format: (value, row, column, data) =>
- frappe.format(value || '', df,
- {for_print: false, always_show_decimals: true}, data)
- };
+ prepare_data(data) {
+ return data.map(row => {
+ let row_obj = {};
+ if (Array.isArray(row)) {
+ this.columns.forEach((column, i) => {
+ row_obj[column.id] = row[i] || null;
+ });
+
+ return row_obj;
+ }
+ return row;
});
+ }
- return columns;
+ get_visible_columns() {
+ // return columns according to user_settings
- // if (this.user_settings.column_order && this.user_settings.column_order.length > 0) {
- // return this.user_settings.column_order
- // .map(id => columns.find(col => col.id === id))
- // .filter(Boolean);
- // } else {
- // return columns;
- // }
+ if (this.user_settings.column_order && this.user_settings.column_order.length > 0) {
+ return this.user_settings.column_order
+ .map(id => this.columns.find(col => col.id === id))
+ .filter(Boolean);
+ } else {
+ return this.columns;
+ }
}
get_filter_values(raise) {
@@ -348,8 +357,8 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
if (raise && missing_mandatory.length > 0) {
// this.chart_area.hide();
- // this.wrapper.find(".waiting-area").empty().toggle(false);
- // this.$no_result.html(__("Please set filters")).show();
+ // this.wrapper.find('.waiting-area').empty().toggle(false);
+ // this.$no_result.html(__('Please set filters')).show();
if (raise) {
frappe.throw(__('Filter missing: {0}', [missing_mandatory.map(f => f.df.label).join(', ')]));
}
@@ -381,14 +390,17 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
}
print_report(print_settings) {
- const columns = this.get_columns_for_print();
+ const custom_format = this.report_settings.html_format || null;
+ const filters_html = this.get_filters_html_for_print();
+
frappe.render_grid({
- template: this.report_settings.html_format || null,
+ template: custom_format,
title: __(this.report_name),
+ subtitle: filters_html,
print_settings: print_settings,
filters: this.get_filter_values(),
- data: this.get_data_for_print(),
- columns: columns,
+ data: custom_format ? this.data : this.get_data_for_print(),
+ columns: custom_format ? this.columns: this.get_visible_columns(),
report: this
});
}
@@ -396,49 +408,48 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
pdf_report(print_settings) {
const base_url = frappe.urllib.get_base_url();
const print_css = frappe.boot.print_css;
- const landscape = print_settings.orientation == "Landscape";
- const columns = this.columns;
+ const landscape = print_settings.orientation == 'Landscape';
- let html;
- if (this.report_settings.html_format) {
- const content = frappe.render(this.report_settings.html_format, {
- data: this.get_data_for_print(),
- filters: this.get_filter_values(),
- report: this,
- });
+ const custom_format = this.report_settings.html_format || null;
+ const columns = custom_format ? this.columns : this.get_visible_columns();
+ const data = custom_format ? this.data : this.get_data_for_print();
+ const applied_filters = this.get_filter_values();
- //Render Report in HTML
- html = frappe.render_template("print_template", {
- title:__(this.report_name),
- content: content,
- base_url: base_url,
- print_css: print_css,
- print_settings: print_settings,
- landscape: landscape,
- columns: columns
- });
- } else {
- const content = frappe.render_template("print_grid", {
- title: __(this.report_name),
- data: this.get_data_for_print(),
- columns: columns
- });
+ const filters_html = this.get_filters_html_for_print();
+ const content = frappe.render_template(custom_format || 'print_grid', {
+ title: __(this.report_name),
+ subtitle: filters_html,
+ filters: applied_filters,
+ data: data,
+ columns: columns,
+ report: this
+ });
- //Render Report in HTML
- html = frappe.render_template("print_template", {
- content: content,
- title: __(this.report_name),
- base_url: base_url,
- print_css: print_css,
- print_settings: print_settings,
- landscape: landscape,
- columns: columns
- });
- }
+ // Render Report in HTML
+ const html = frappe.render_template('print_template', {
+ title: __(this.report_name),
+ content: content,
+ base_url: base_url,
+ print_css: print_css,
+ print_settings: print_settings,
+ landscape: landscape,
+ columns: columns
+ });
frappe.render_pdf(html, print_settings);
}
+ get_filters_html_for_print() {
+ const applied_filters = this.get_filter_values();
+ return Object.keys(applied_filters)
+ .map(filter_name => {
+ const label = frappe.query_report_filters_by_name[filter_name].df.label;
+ const value = applied_filters[filter_name];
+ return `${__(label)}: ${value}`; + }) + .join(''); + } + export_report() { if (this.export_dialog) { this.export_dialog.clear(); @@ -455,8 +466,8 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { reqd: 1 }, ({ file_format }) => { if (file_format === 'CSV') { - const column_row = this._columns.map(col => col.label); - const data = this.get_data_for_print(); + const column_row = this.columns.map(col => col.label); + const data = this.get_data_for_csv(); const out = [column_row].concat(data); frappe.tools.downloadify(out, null, this.report_name); @@ -473,17 +484,22 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { open_url_post(frappe.request.url, args); } - }, __("Export Report: "+ this.report_name), __("Download")); + }, __('Export Report: '+ this.report_name), __('Download')); } - get_data_for_print() { + get_data_for_csv() { const indices = this.datatable.datamanager.getFilteredRowIndices(); const out = indices.map(i => this.datatable.datamanager.getRow(i).map(c => c.content)); return out.map(row => row.slice(1)); } + get_data_for_print() { + const indices = this.datatable.datamanager.getFilteredRowIndices(); + return indices.map(i => this.data[i]); + } + get_columns_for_print() { - return this._columns || []; + return this.columns || []; } get_menu_items() { @@ -495,7 +511,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { }, { label: __('Edit'), - action: () => frappe.set_route("Form", "Report", this.report_name), + action: () => frappe.set_route('Form', 'Report', this.report_name), condition: () => frappe.user.is_report_manager(), standard: true }, @@ -536,7 +552,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { { label: __('User Permissions'), action: () => frappe.set_route('List', 'User Permission', { - doctype: "Report", + doctype: 'Report', name: this.report_name }), condition: () => frappe.model.can_set_user_permissions('Report'), @@ -557,7 +573,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { setup_page_head() { super.setup_page_head(); - this.page.set_title_sub(``); + this.page.set_title_sub(``); } setup_report_wrapper() { @@ -568,7 +584,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { } message_div(message) { - return `
+ return `
@@ -26,7 +27,7 @@
- {% if ldap_settings.enabled %}
+ {% if ldap_settings and ldap_settings.enabled %}
{% endif %}
diff --git a/frappe/www/printview.html b/frappe/www/printview.html
index 6136426593..ecefab57ad 100644
--- a/frappe/www/printview.html
+++ b/frappe/www/printview.html
@@ -9,6 +9,9 @@
href="/assets/frappe/css/bootstrap.css">
+ {%- if has_rtl -%}
+
+ {%- endif -%}
diff --git a/frappe/www/printview.py b/frappe/www/printview.py
index c0fff90a90..7a40b96c49 100644
--- a/frappe/www/printview.py
+++ b/frappe/www/printview.py
@@ -41,7 +41,8 @@ def get_context(context):
no_letterhead=frappe.form_dict.no_letterhead),
"css": get_print_style(frappe.form_dict.style, print_format),
"comment": frappe.session.user,
- "title": doc.get(meta.title_field) if meta.title_field else doc.name
+ "title": doc.get(meta.title_field) if meta.title_field else doc.name,
+ "has_rtl": True if frappe.local.lang in ["ar", "he", "fa"] else False
}
def get_print_format_doc(print_format_name, meta):
diff --git a/frappe/www/search.py b/frappe/www/search.py
index baf2be4e63..c55970c1fc 100644
--- a/frappe/www/search.py
+++ b/frappe/www/search.py
@@ -4,13 +4,14 @@ from frappe.utils.global_search import web_search
from html2text import html2text
from frappe import _
from jinja2 import utils
+from frappe.utils import sanitize_html
def get_context(context):
context.no_cache = 1
if frappe.form_dict.q:
- frappe.form_dict.q = str(utils.escape(frappe.form_dict.q))
- context.title = _('Search Results for "{0}"').format(frappe.form_dict.q)
- context.update(get_search_results(frappe.form_dict.q))
+ query = str(utils.escape(sanitize_html(frappe.form_dict.q)))
+ context.title = _('Search Results for "{0}"').format(query)
+ context.update(get_search_results(query))
else:
context.title = _('Search')
diff --git a/socketio.js b/socketio.js
index 0764ef131b..6301535fc9 100644
--- a/socketio.js
+++ b/socketio.js
@@ -63,6 +63,7 @@ io.on('connection', function (socket) {
socket.join(room);
}
});
+
socket.on("frappe.chat.message:typing", function (data) {
const user = data.user;
const room = get_chat_room(socket, data.room);
@@ -92,9 +93,7 @@ io.on('connection', function (socket) {
socket.join(get_site_room(socket));
}
});
-
-
-
+
socket.on('disconnect', function () {
delete socket.files;
})
${message}
|