Merge branch 'develop' of https://github.com/frappe/frappe into website-chat
This commit is contained in:
commit
5ed33335de
30 changed files with 381 additions and 248 deletions
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ 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')
|
||||
|
|
@ -41,6 +47,12 @@ class StripeSettings(Document):
|
|||
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)))
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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}',
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ frappe.views.BaseList = class BaseList {
|
|||
}
|
||||
|
||||
setup_result_area() {
|
||||
this.$result = $(`<div class="result">`).hide();
|
||||
this.$result = $(`<div class="result">`);
|
||||
this.$frappe_list.append(this.$result);
|
||||
}
|
||||
|
||||
|
|
@ -531,19 +531,6 @@ class FilterArea {
|
|||
}
|
||||
|
||||
make_standard_filters() {
|
||||
$(
|
||||
`<div class="flex justify-center align-center">
|
||||
<span class="octicon octicon-search text-muted small"></span>
|
||||
</div>`
|
||||
)
|
||||
.css({
|
||||
height: '30px',
|
||||
width: '20px',
|
||||
marginRight: '-2px',
|
||||
marginLeft: '10px'
|
||||
})
|
||||
.prependTo(this.standard_filters_wrapper);
|
||||
|
||||
let fields = [
|
||||
{
|
||||
fieldtype: 'Data',
|
||||
|
|
@ -602,6 +589,21 @@ class FilterArea {
|
|||
}
|
||||
|
||||
fields.map(df => this.list_view.page.add_field(df));
|
||||
|
||||
// search icon in name filter
|
||||
$('<span class="octicon octicon-search text-muted small"></span>')
|
||||
.appendTo(this.list_view.page.fields_dict.name.$wrapper)
|
||||
.css({
|
||||
'position': 'absolute',
|
||||
'z-index': '1',
|
||||
'right': '7px',
|
||||
'top': '9px',
|
||||
'font-size': '90%'
|
||||
});
|
||||
|
||||
this.list_view.page.fields_dict.name.$wrapper
|
||||
.find('.form-control')
|
||||
.css('padding-right', '2em');
|
||||
}
|
||||
|
||||
get_standard_filters() {
|
||||
|
|
|
|||
|
|
@ -145,6 +145,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
|
||||
setup_view() {
|
||||
this.setup_columns();
|
||||
this.render_header();
|
||||
this.render_skeleton();
|
||||
this.setup_events();
|
||||
this.settings.onload && this.settings.onload(this);
|
||||
}
|
||||
|
|
@ -233,10 +235,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
</div>`;
|
||||
}
|
||||
|
||||
freeze(show) {
|
||||
this.$result.find('.list-header-meta').html(__('Refreshing') + '...');
|
||||
this.$result.find('.checkbox-actions').toggle(show);
|
||||
this.$result.find('.list-header-subject').toggle(!show);
|
||||
freeze() {
|
||||
this.$result.find('.list-count').html(`<span>${__('Refreshing')}...</span>`);
|
||||
}
|
||||
|
||||
get_args() {
|
||||
|
|
@ -264,6 +264,18 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
}
|
||||
}
|
||||
|
||||
render_header() {
|
||||
if (this.$result.find('.list-row-head').length === 0) {
|
||||
// append header once
|
||||
this.$result.prepend(this.get_header_html());
|
||||
}
|
||||
}
|
||||
|
||||
render_skeleton() {
|
||||
const $row = this.get_list_row_html_skeleton('<div><input type="checkbox" /></div>');
|
||||
this.$result.append($row.repeat(3));
|
||||
}
|
||||
|
||||
before_render() {
|
||||
this.settings.before_render && this.settings.before_render();
|
||||
frappe.model.user_settings.save(this.doctype, 'last_view', this.view_name);
|
||||
|
|
@ -274,12 +286,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
}
|
||||
|
||||
render() {
|
||||
if (this.data.length > 0) {
|
||||
this.$result.find('.list-row-container').remove();
|
||||
if (this.$result.find('.list-row-head').length === 0) {
|
||||
// append header once
|
||||
this.$result.prepend(this.get_header_html());
|
||||
}
|
||||
if (this.data.length > 0) {
|
||||
// append rows
|
||||
this.$result.append(
|
||||
this.data.map(doc => this.get_list_row_html(doc)).join('')
|
||||
|
|
|
|||
|
|
@ -19,7 +19,12 @@ $.extend(frappe.model, {
|
|||
for(var i=0, l=r.docs.length; i<l; i++) {
|
||||
var d = r.docs[i];
|
||||
|
||||
if (locals[d.doctype] && locals[d.doctype][d.name]) {
|
||||
// update values
|
||||
frappe.model.update_in_locals(d);
|
||||
} else {
|
||||
frappe.model.add_to_locals(d);
|
||||
}
|
||||
|
||||
d.__last_sync_on = new Date();
|
||||
|
||||
|
|
@ -27,10 +32,6 @@ $.extend(frappe.model, {
|
|||
frappe.meta.sync(d);
|
||||
}
|
||||
|
||||
if(cur_frm && cur_frm.doctype==d.doctype && cur_frm.docname==d.name) {
|
||||
cur_frm.doc = d;
|
||||
}
|
||||
|
||||
if(d.localname) {
|
||||
frappe.model.new_names[d.localname] = d.name;
|
||||
$(document).trigger('rename', [d.doctype, d.localname, d.name]);
|
||||
|
|
@ -96,6 +97,54 @@ $.extend(frappe.model, {
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
update_in_locals: function(doc) {
|
||||
// update values in the existing local doc instead of replacing
|
||||
let local_doc = locals[doc.doctype][doc.name];
|
||||
for (let fieldname in doc) {
|
||||
let df = frappe.meta.get_field(doc.doctype, fieldname);
|
||||
if (df && df.fieldtype === 'Table') {
|
||||
// table
|
||||
if (!(doc[fieldname] instanceof Array)) {
|
||||
doc[fieldname] = [];
|
||||
}
|
||||
|
||||
if (!(local_doc[fieldname] instanceof Array)) {
|
||||
local_doc[fieldname] = [];
|
||||
}
|
||||
|
||||
// child table, override each row and append new rows if required
|
||||
for (let i=0; i < doc[fieldname].length; i++ ) {
|
||||
let d = doc[fieldname][i];
|
||||
if (local_doc[fieldname][i]) {
|
||||
// row exists, just copy the values
|
||||
Object.assign(local_doc[fieldname][i], d);
|
||||
} else {
|
||||
local_doc[fieldname].push(d);
|
||||
if (!d.parent) d.parent = doc.name;
|
||||
frappe.model.add_to_locals(d);
|
||||
}
|
||||
}
|
||||
|
||||
// remove extra rows
|
||||
if (local_doc[fieldname].length > doc[fieldname].length) {
|
||||
for (let i = doc[fieldname].length; i < local_doc[fieldname].length; i++) {
|
||||
|
||||
// clear from local
|
||||
let d = local_doc[fieldname][i];
|
||||
if (locals[d.doctype] && locals[d.doctype][d.name]) {
|
||||
delete locals[d.doctype][d.name];
|
||||
}
|
||||
}
|
||||
local_doc[fieldname].length = doc[fieldname].length;
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
// literal
|
||||
local_doc[fieldname] = doc[fieldname];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -313,8 +313,19 @@ frappe.upload = {
|
|||
if (opts.no_socketio || frappe.flags.no_socketio || file_not_big_enough) {
|
||||
upload_with_filedata();
|
||||
return;
|
||||
} else {
|
||||
args.file_size = fileobj.size;
|
||||
frappe.call({
|
||||
method: 'frappe.utils.file_manager.validate_filename',
|
||||
args: {"filename": args.filename},
|
||||
callback: function(r) {
|
||||
args.filename = r.message;
|
||||
upload_through_socketio();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var upload_through_socketio = function() {
|
||||
frappe.socketio.uploader.start({
|
||||
file: fileobj,
|
||||
filename: args.filename,
|
||||
|
|
@ -333,6 +344,7 @@ frappe.upload = {
|
|||
start_complete + increment);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
upload_to_server: function(file, args, opts) {
|
||||
|
|
|
|||
|
|
@ -555,10 +555,10 @@ frappe.views.CommunicationComposer = Class.extend({
|
|||
|
||||
is_print_letterhead_checked: function() {
|
||||
if (this.frm && $(this.frm.wrapper).find('.form-print-wrapper').is(':visible')){
|
||||
return $(this.frm.wrapper).find('.print-letterhead').prop('checked');
|
||||
return $(this.frm.wrapper).find('.print-letterhead').prop('checked') ? 1 : 0;
|
||||
} else {
|
||||
return (frappe.model.get_doc(":Print Settings", "Print Settings") ||
|
||||
{ with_letterhead: 1 }).with_letterhead ? true : false;
|
||||
{ with_letterhead: 1 }).with_letterhead ? 1 : 0;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@
|
|||
<h2>{{ __(title) }}</h2>
|
||||
<hr>
|
||||
{% endif %}
|
||||
{% if subtitle %}
|
||||
{{ subtitle }}
|
||||
<hr>
|
||||
{% endif %}
|
||||
<table class="table table-bordered">
|
||||
<!-- heading -->
|
||||
<thead>
|
||||
|
|
@ -24,7 +28,7 @@
|
|||
{% for col in columns %}
|
||||
{% if col.name && col._id !== "_check" %}
|
||||
|
||||
{% var value = col.fieldname ? row[col.fieldname] : row[col.field]; %}
|
||||
{% var value = col.fieldname ? row[col.fieldname] : row[col.id]; %}
|
||||
|
||||
<td>
|
||||
{{ col.formatter
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
} else {
|
||||
column = {
|
||||
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 columns;
|
||||
return row_obj;
|
||||
}
|
||||
return row;
|
||||
});
|
||||
}
|
||||
|
||||
// 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;
|
||||
// }
|
||||
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 => 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,19 +408,25 @@ 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();
|
||||
|
||||
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", {
|
||||
const html = frappe.render_template('print_template', {
|
||||
title: __(this.report_name),
|
||||
content: content,
|
||||
base_url: base_url,
|
||||
|
|
@ -417,28 +435,21 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
|
|||
landscape: landscape,
|
||||
columns: columns
|
||||
});
|
||||
} else {
|
||||
const content = frappe.render_template("print_grid", {
|
||||
title: __(this.report_name),
|
||||
data: this.get_data_for_print(),
|
||||
columns: columns
|
||||
});
|
||||
|
||||
//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
|
||||
});
|
||||
}
|
||||
|
||||
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 `<h6>${__(label)}: ${value}</h6>`;
|
||||
})
|
||||
.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(`<label class="label label-warning text-color">${__('Beta')}</label>`);
|
||||
this.page.set_title_sub(`<label class='label label-warning text-color'>${__('Beta')}</label>`);
|
||||
}
|
||||
|
||||
setup_report_wrapper() {
|
||||
|
|
@ -568,7 +584,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
|
|||
}
|
||||
|
||||
message_div(message) {
|
||||
return `<div class="flex justify-center align-center text-muted" style="height: 50vh;">
|
||||
return `<div class='flex justify-center align-center text-muted' style='height: 50vh;'>
|
||||
<div>${message}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
|
@ -596,12 +612,4 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
|
|||
get get_values() {
|
||||
return this.get_filter_values;
|
||||
}
|
||||
|
||||
get data() {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
get columns() {
|
||||
return this._columns;
|
||||
}
|
||||
};
|
||||
|
|
@ -197,13 +197,15 @@ _f.Frm.prototype.watch_model_updates = function() {
|
|||
frappe.model.on(me.doctype, "*", function(fieldname, value, doc) {
|
||||
// set input
|
||||
if(doc.name===me.docname) {
|
||||
if ((value==='' || value===null) && !doc[value]) {
|
||||
if ((value==='' || value===null) && !doc[fieldname]) {
|
||||
// both the incoming and outgoing values are falsy
|
||||
// the texteditor, summernote, changes nulls to empty strings on render,
|
||||
// so ignore those changes
|
||||
} else {
|
||||
if (value != doc[fieldname]) {
|
||||
me.dirty();
|
||||
}
|
||||
}
|
||||
me.fields_dict[fieldname]
|
||||
&& me.fields_dict[fieldname].refresh(fieldname);
|
||||
|
||||
|
|
@ -764,53 +766,60 @@ _f.Frm.prototype._save = function(save_action, callback, btn, on_error, resolve)
|
|||
_f.Frm.prototype.savesubmit = function(btn, callback, on_error) {
|
||||
var me = this;
|
||||
|
||||
let handle_fail = () => {
|
||||
$(btn).prop('disabled', false);
|
||||
if (on_error) {
|
||||
on_error();
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
this.validate_form_action("Submit");
|
||||
frappe.confirm(__("Permanently Submit {0}?", [this.docname]), function() {
|
||||
frappe.validated = true;
|
||||
me.script_manager.trigger("before_submit").then(function() {
|
||||
if(!frappe.validated) {
|
||||
if(on_error) {
|
||||
on_error();
|
||||
}
|
||||
handle_fail();
|
||||
return;
|
||||
}
|
||||
|
||||
me.save('Submit', function(r) {
|
||||
if(r.exc) {
|
||||
if (on_error) {
|
||||
on_error();
|
||||
}
|
||||
handle_fail();
|
||||
} else {
|
||||
frappe.utils.play_sound("submit");
|
||||
callback && callback();
|
||||
me.script_manager.trigger("on_submit")
|
||||
.then(() => resolve(me));
|
||||
}
|
||||
}, btn, on_error, resolve);
|
||||
}, btn, () => handle_fail(), resolve);
|
||||
});
|
||||
}, on_error);
|
||||
}, () => handle_fail() );
|
||||
});
|
||||
};
|
||||
|
||||
_f.Frm.prototype.savecancel = function(btn, callback, on_error) {
|
||||
var me = this;
|
||||
|
||||
let handle_fail = () => {
|
||||
$(btn).prop('disabled', false);
|
||||
if (on_error) {
|
||||
on_error();
|
||||
}
|
||||
}
|
||||
|
||||
this.validate_form_action('Cancel');
|
||||
frappe.confirm(__("Permanently Cancel {0}?", [this.docname]), function() {
|
||||
frappe.validated = true;
|
||||
me.script_manager.trigger("before_cancel").then(function() {
|
||||
if(!frappe.validated) {
|
||||
if(on_error) {
|
||||
on_error();
|
||||
}
|
||||
handle_fail();
|
||||
return;
|
||||
}
|
||||
|
||||
var after_cancel = function(r) {
|
||||
if(r.exc) {
|
||||
if (on_error) {
|
||||
on_error();
|
||||
}
|
||||
handle_fail();
|
||||
} else {
|
||||
frappe.utils.play_sound("cancel");
|
||||
me.refresh();
|
||||
|
|
@ -820,7 +829,7 @@ _f.Frm.prototype.savecancel = function(btn, callback, on_error) {
|
|||
};
|
||||
frappe.ui.form.save(me, "cancel", after_cancel, btn);
|
||||
});
|
||||
}, on_error);
|
||||
}, () => handle_fail());
|
||||
};
|
||||
|
||||
// delete the record
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ login.bind_events = function() {
|
|||
return false;
|
||||
});
|
||||
|
||||
{% if ldap_settings %}
|
||||
$(".btn-ldap-login").on("click", function(){
|
||||
var args = {};
|
||||
args.cmd = "{{ ldap_settings.method }}";
|
||||
|
|
@ -69,6 +70,7 @@ login.bind_events = function() {
|
|||
login.call(args);
|
||||
return false;
|
||||
});
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class TestOAuth20(unittest.TestCase):
|
|||
frappe_login_key = frappe.new_doc("Social Login Key")
|
||||
frappe_login_key.get_social_login_provider("Frappe", initialize=True)
|
||||
frappe_login_key.base_url = "http://localhost:8000"
|
||||
frappe_login_key.enable_social_login = 0
|
||||
frappe_login_key.save()
|
||||
|
||||
def test_invalid_login(self):
|
||||
|
|
@ -88,6 +89,26 @@ class TestOAuth20(unittest.TestCase):
|
|||
# Check revoked token
|
||||
self.assertFalse(check_valid_openid_response(bearer_token.get("access_token")))
|
||||
|
||||
def test_resource_owner_password_credentials_grant(self):
|
||||
# Set payload
|
||||
payload = "grant_type=password"
|
||||
payload += "&username=test@example.com"
|
||||
payload += "&password=Eastern_43A1W"
|
||||
payload += "&client_id=" + self.client_id
|
||||
payload += "&scope=openid%20all"
|
||||
|
||||
headers = {'content-type':'application/x-www-form-urlencoded'}
|
||||
|
||||
# Request for bearer token
|
||||
token_response = requests.post( frappe.get_site_config().host_name +
|
||||
"/api/method/frappe.integrations.oauth2.get_token", data=payload, headers=headers)
|
||||
|
||||
# Parse bearer token json
|
||||
bearer_token = token_response.json()
|
||||
|
||||
# Check token for valid response
|
||||
self.assertTrue(check_valid_openid_response(bearer_token.get("access_token")))
|
||||
|
||||
def test_login_using_implicit_token(self):
|
||||
|
||||
oauth_client = frappe.get_doc("OAuth Client", self.client_id)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import cgitb
|
|||
import types
|
||||
import datetime
|
||||
import json
|
||||
import six
|
||||
|
||||
def make_error_snapshot(exception):
|
||||
if frappe.conf.disable_error_snapshot:
|
||||
|
|
@ -49,7 +50,7 @@ def get_snapshot(exception, context=10):
|
|||
"""
|
||||
|
||||
etype, evalue, etb = sys.exc_info()
|
||||
if isinstance(etype, types.ClassType):
|
||||
if isinstance(etype, six.class_types):
|
||||
etype = etype.__name__
|
||||
|
||||
# creates a snapshot dict with some basic information
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ def save_url(file_url, filename, dt, dn, folder, is_private, df=None):
|
|||
# return None, None
|
||||
|
||||
file_url = unquote(file_url)
|
||||
file_size = frappe.form_dict.file_size
|
||||
|
||||
f = frappe.get_doc({
|
||||
"doctype": "File",
|
||||
|
|
@ -97,6 +98,7 @@ def save_url(file_url, filename, dt, dn, folder, is_private, df=None):
|
|||
"attached_to_name": dn,
|
||||
"attached_to_field": df,
|
||||
"folder": folder,
|
||||
"file_size": file_size,
|
||||
"is_private": is_private
|
||||
})
|
||||
f.flags.ignore_permissions = True
|
||||
|
|
@ -416,3 +418,9 @@ def get_random_filename(extn=None, content_type=None):
|
|||
extn = mimetypes.guess_extension(content_type)
|
||||
|
||||
return random_string(7) + (extn or "")
|
||||
|
||||
@frappe.whitelist()
|
||||
def validate_filename(filename):
|
||||
hash_ = get_content_hash(filename)
|
||||
fname = get_file_name(filename, hash_[-6:])
|
||||
return fname
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@
|
|||
</div>
|
||||
|
||||
<input type="text" id="login_email"
|
||||
class="form-control" placeholder="{{ login_name_placeholder }}"
|
||||
class="form-control"
|
||||
placeholder="{% if login_name_placeholder %}{{ login_name_placeholder }}{% else %}{{ _("Email address") }}{% endif %}"
|
||||
required autofocus>
|
||||
|
||||
|
||||
|
|
@ -26,7 +27,7 @@
|
|||
<button class="btn btn-sm btn-primary btn-block btn-login" type="submit">
|
||||
{{ _("Sign in") }}</button>
|
||||
|
||||
{% if ldap_settings.enabled %}
|
||||
{% if ldap_settings and ldap_settings.enabled %}
|
||||
<button class="btn btn-sm btn-default btn-block btn-login btn-ldap-login">
|
||||
{{ _("Login with LDAP") }}</button>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
href="/assets/frappe/css/bootstrap.css">
|
||||
<link type="text/css" rel="stylesheet"
|
||||
href="/assets/frappe/css/font-awesome.css">
|
||||
{%- if has_rtl -%}
|
||||
<link type="text/css" rel="stylesheet" href="assets/css/frappe-rtl.css">
|
||||
{%- endif -%}
|
||||
<style>
|
||||
{{ css }}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -93,8 +94,6 @@ io.on('connection', function (socket) {
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
socket.on('disconnect', function () {
|
||||
delete socket.files;
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue