diff --git a/.travis.yml b/.travis.yml
index 36379390b8..0b9f8293df 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,6 @@
language: python
dist: trusty
+group: deprecated-2017Q2
python:
- "2.7"
diff --git a/frappe/__init__.py b/frappe/__init__.py
index d147be36be..ee8f544f18 100644
--- a/frappe/__init__.py
+++ b/frappe/__init__.py
@@ -14,7 +14,7 @@ import os, sys, importlib, inspect, json
from .exceptions import *
from .utils.jinja import get_jenv, get_template, render_template
-__version__ = '8.1.1'
+__version__ = '8.1.2'
__title__ = "Frappe Framework"
local = Local()
@@ -379,7 +379,8 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message
unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None,
attachments=None, content=None, doctype=None, name=None, reply_to=None,
cc=[], message_id=None, in_reply_to=None, send_after=None, expose_recipients=None,
- send_priority=1, communication=None, retry=1, now=None, read_receipt=None, is_notification=False):
+ send_priority=1, communication=None, retry=1, now=None, read_receipt=None, is_notification=False,
+ inline_images=None):
"""Send email using user's default **Email Account** or global default **Email Account**.
@@ -401,6 +402,7 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message
:param send_after: Send after the given datetime.
:param expose_recipients: Display all recipients in the footer message - "This email was sent to"
:param communication: Communication link to be set in Email Queue record
+ :param inline_images: List of inline images as {"filename", "filecontent"}. All src properties will be replaced with random Content-Id
"""
message = content or message
@@ -418,7 +420,8 @@ def sendmail(recipients=[], sender="", subject="No Subject", message="No Message
unsubscribe_method=unsubscribe_method, unsubscribe_params=unsubscribe_params, unsubscribe_message=unsubscribe_message,
attachments=attachments, reply_to=reply_to, cc=cc, message_id=message_id, in_reply_to=in_reply_to,
send_after=send_after, expose_recipients=expose_recipients, send_priority=send_priority,
- communication=communication, now=now, read_receipt=read_receipt, is_notification=is_notification)
+ communication=communication, now=now, read_receipt=read_receipt, is_notification=is_notification,
+ inline_images=inline_images)
whitelisted = []
guest_methods = []
diff --git a/frappe/contacts/doctype/contact/contact.js b/frappe/contacts/doctype/contact/contact.js
index cfecddffb6..d44904d9d5 100644
--- a/frappe/contacts/doctype/contact/contact.js
+++ b/frappe/contacts/doctype/contact/contact.js
@@ -19,7 +19,7 @@ frappe.ui.form.on("Contact", {
if(!frm.doc.user && !frm.is_new() && frm.perm[0].write) {
frm.add_custom_button(__("Invite as User"), function() {
frappe.call({
- method: "frappe.email.doctype.contact.contact.invite_user",
+ method: "frappe.contacts.doctype.contact.contact.invite_user",
args: {
contact: frm.doc.name
},
diff --git a/frappe/core/doctype/file/file_list.js b/frappe/core/doctype/file/file_list.js
index c2ae9af747..61b4a58a0c 100644
--- a/frappe/core/doctype/file/file_list.js
+++ b/frappe/core/doctype/file/file_list.js
@@ -17,20 +17,20 @@ frappe.listview_settings['File'] = {
},
prepare_data: function(data) {
// set image icons
- var icon = ""
+ var icon = "";
if(data.is_folder) {
icon += ' ';
} else if(frappe.utils.is_image_file(data.file_name)) {
icon += ' ';
} else {
- icon += ' '
+ icon += ' ';
}
- data._title = icon + (data.file_name ? data.file_name : data.file_url)
+ data._title = icon + (data.file_name ? data.file_name : data.file_url);
if (data.is_private) {
- data._title += ' '
+ data._title += ' ';
}
},
onload: function(doclist) {
@@ -43,7 +43,7 @@ frappe.listview_settings['File'] = {
doclist.$page.on("click", ".list-row-checkbox", function(event) {
doclist.list_renderer.settings.add_menu_item_copy(doclist);
- })
+ });
},
list_view_doc:function(doclist){
$(doclist.wrapper).on("click", 'button[list_view_doc="'+doclist.doctype+'"]', function() {
@@ -73,7 +73,7 @@ frappe.listview_settings['File'] = {
method: "frappe.core.doctype.file.file.create_new_folder",
args: data,
callback: function(r) { }
- })
+ });
}, __('Enter folder name'), __("Create"));
});
@@ -134,7 +134,7 @@ frappe.listview_settings['File'] = {
else{
frappe.throw(__("Please select file to copy"));
}
- })
+ });
doclist.copy = true;
}
},
@@ -235,5 +235,5 @@ frappe.listview_settings['File'] = {
.appendTo(doclist.breadcrumb);
}
});
- };
+ }
};
diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py
index 13a8659d4a..cecd1bdbd9 100644
--- a/frappe/desk/query_report.py
+++ b/frappe/desk/query_report.py
@@ -120,6 +120,8 @@ def export_query():
data = frappe._dict(frappe.local.form_dict)
del data["cmd"]
+ if "csrf_token" in data:
+ del data["csrf_token"]
if isinstance(data.get("filters"), basestring):
filters = json.loads(data["filters"])
diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py
index d8929ef055..920fb7f36b 100644
--- a/frappe/desk/reportview.py
+++ b/frappe/desk/reportview.py
@@ -27,6 +27,8 @@ def get_form_params():
data = frappe._dict(frappe.local.form_dict)
del data["cmd"]
+ if "csrf_token" in data:
+ del data["csrf_token"]
if isinstance(data.get("filters"), basestring):
data["filters"] = json.loads(data["filters"])
diff --git a/frappe/email/email_body.py b/frappe/email/email_body.py
index 6d7dee38aa..1c450d522f 100755
--- a/frappe/email/email_body.py
+++ b/frappe/email/email_body.py
@@ -2,19 +2,20 @@
# MIT License. See license.txt
from __future__ import unicode_literals
-import frappe
+import frappe, re
from frappe.utils.pdf import get_pdf
from frappe.email.smtp import get_outgoing_email_account
from frappe.utils import (get_url, scrub_urls, strip, expand_relative_urls, cint,
- split_emails, to_markdown, markdown, encode, random_string)
+ split_emails, to_markdown, markdown, encode, random_string, parse_addr)
import email.utils
-from frappe.utils import parse_addr
from six import iteritems
+from email.mime.multipart import MIMEMultipart
def get_email(recipients, sender='', msg='', subject='[No Subject]',
text_content = None, footer=None, print_html=None, formatted=None, attachments=None,
- content=None, reply_to=None, cc=[], email_account=None, expose_recipients=None):
+ content=None, reply_to=None, cc=[], email_account=None, expose_recipients=None,
+ inline_images=[]):
"""send an html email as multipart with attachments and all"""
content = content or msg
emailobj = EMail(sender, recipients, subject, reply_to=reply_to, cc=cc, email_account=email_account, expose_recipients=expose_recipients)
@@ -22,7 +23,8 @@ def get_email(recipients, sender='', msg='', subject='[No Subject]',
if not content.strip().startswith("<"):
content = markdown(content)
- emailobj.set_html(content, text_content, footer=footer, print_html=print_html, formatted=formatted)
+ emailobj.set_html(content, text_content, footer=footer,
+ print_html=print_html, formatted=formatted, inline_images=inline_images)
if isinstance(attachments, dict):
attachments = [attachments]
@@ -39,7 +41,6 @@ class EMail:
Also sets all messages as multipart/alternative for cleaner reading in text-only clients
"""
def __init__(self, sender='', recipients=(), subject='', alternative=0, reply_to=None, cc=(), email_account=None, expose_recipients=None):
- from email.mime.multipart import MIMEMultipart
from email import Charset
Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8')
@@ -64,7 +65,8 @@ class EMail:
self.email_account = email_account or get_outgoing_email_account()
- def set_html(self, message, text_content = None, footer=None, print_html=None, formatted=None):
+ def set_html(self, message, text_content = None, footer=None, print_html=None,
+ formatted=None, inline_images=None):
"""Attach message in the html portion of multipart/alternative"""
if not formatted:
formatted = get_formatted_html(self.subject, message, footer, print_html, email_account=self.email_account)
@@ -77,7 +79,7 @@ class EMail:
else:
self.set_html_as_text(expand_relative_urls(formatted))
- self.set_part_html(formatted)
+ self.set_part_html(formatted, inline_images)
self.html_set = True
def set_text(self, message):
@@ -88,10 +90,28 @@ class EMail:
part = MIMEText(message, 'plain', 'utf-8')
self.msg_multipart.attach(part)
- def set_part_html(self, message):
+ def set_part_html(self, message, inline_images):
from email.mime.text import MIMEText
- part = MIMEText(message, 'html', 'utf-8')
- self.msg_multipart.attach(part)
+ if inline_images:
+ related = MIMEMultipart('related')
+
+ for image in inline_images:
+ # images in dict like {filename:'', filecontent:'raw'}
+ content_id = random_string(10)
+
+ # replace filename in message with CID
+ message = re.sub('''src=['"]{0}['"]'''.format(image.get('filename')),
+ 'src="cid:{0}"'.format(content_id), message)
+
+ self.add_attachment(image.get('filename'), image.get('filecontent'),
+ None, content_id=content_id, parent=related)
+
+ html_part = MIMEText(message, 'html', 'utf-8')
+ related.attach(html_part)
+
+ self.msg_multipart.attach(related)
+ else:
+ self.msg_multipart.attach(MIMEText(message, 'html', 'utf-8'))
def set_html_as_text(self, html):
"""return html2text"""
@@ -118,7 +138,8 @@ class EMail:
self.add_attachment(res[0], res[1])
- def add_attachment(self, fname, fcontent, content_type=None):
+ def add_attachment(self, fname, fcontent, content_type=None,
+ parent=None, content_id=None):
"""add attachment"""
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
@@ -155,8 +176,13 @@ class EMail:
if fname:
part.add_header(b'Content-Disposition',
("attachment; filename=\"%s\"" % fname).encode('utf-8'))
+ if content_id:
+ part.add_header(b'Content-ID', '<{0}>'.format(content_id))
- self.msg_root.attach(part)
+ if not parent:
+ parent = self.msg_root
+
+ parent.attach(part)
def add_pdf_attachment(self, name, html, options=None):
self.add_attachment(name, get_pdf(html, options), 'application/octet-stream')
diff --git a/frappe/email/queue.py b/frappe/email/queue.py
index 2fe1e34486..cbcaffe738 100755
--- a/frappe/email/queue.py
+++ b/frappe/email/queue.py
@@ -21,7 +21,7 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc
reference_name=None, unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None,
attachments=None, reply_to=None, cc=[], message_id=None, in_reply_to=None, send_after=None,
expose_recipients=None, send_priority=1, communication=None, now=False, read_receipt=None,
- queue_separately=False, is_notification=False, add_unsubscribe_link=1):
+ queue_separately=False, is_notification=False, add_unsubscribe_link=1, inline_images=None):
"""Add email to sending queue (Email Queue)
:param recipients: List of recipients.
@@ -42,6 +42,7 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc
:param queue_separately: Queue each email separately
:param is_notification: Marks email as notification so will not trigger notifications from system
:param add_unsubscribe_link: Send unsubscribe link in the footer of the Email, default 1.
+ :param inline_images: List of inline images as {"filename", "filecontent"}. All src properties will be replaced with random Content-Id
"""
if not unsubscribe_method:
unsubscribe_method = "/api/method/frappe.email.queue.unsubscribe"
@@ -112,6 +113,7 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc
read_receipt=read_receipt,
queue_separately=queue_separately,
is_notification = is_notification,
+ inline_images = inline_images,
now=now)
@@ -152,7 +154,8 @@ def get_email_queue(recipients, sender, subject, **kwargs):
reply_to=kwargs.get('reply_to'),
cc=kwargs.get('cc'),
email_account=kwargs.get('email_account'),
- expose_recipients=kwargs.get('expose_recipients'))
+ expose_recipients=kwargs.get('expose_recipients'),
+ inline_images=kwargs.get('inline_images'))
mail.set_message_id(kwargs.get('message_id'),kwargs.get('is_notification'))
if kwargs.get('read_receipt'):
@@ -431,7 +434,7 @@ def prepare_message(email, recipient, recipients_list):
message = email.message
if not message:
return ""
-
+
if email.add_unsubscribe_link and email.reference_doctype: # is missing the check for unsubscribe message but will not add as there will be no unsubscribe url
unsubscribe_url = get_unsubcribed_url(email.reference_doctype, email.reference_name, recipient,
email.unsubscribe_method, email.unsubscribe_params)
@@ -461,19 +464,19 @@ def clear_outbox():
Called daily via scheduler.
Note: Used separate query to avoid deadlock
"""
-
- email_queues = frappe.db.sql_list("""select name from `tabEmail Queue`
+
+ email_queues = frappe.db.sql_list("""select name from `tabEmail Queue`
where priority=0 and datediff(now(), modified) > 31""")
-
+
if email_queues:
- frappe.db.sql("""delete from `tabEmail Queue` where name in (%s)"""
+ frappe.db.sql("""delete from `tabEmail Queue` where name in (%s)"""
% ','.join(['%s']*len(email_queues)), tuple(email_queues))
-
- frappe.db.sql("""delete from `tabEmail Queue Recipient` where parent in (%s)"""
+
+ frappe.db.sql("""delete from `tabEmail Queue Recipient` where parent in (%s)"""
% ','.join(['%s']*len(email_queues)), tuple(email_queues))
-
+
for dt in ("Email Queue", "Email Queue Recipient"):
frappe.db.sql("""
- update `tab{0}`
+ update `tab{0}`
set status='Expired'
where datediff(curdate(), modified) > 7 and status='Not Sent'""".format(dt))
\ No newline at end of file
diff --git a/frappe/public/css/avatar.css b/frappe/public/css/avatar.css
index 725bef2f9a..b4dad4a52b 100644
--- a/frappe/public/css/avatar.css
+++ b/frappe/public/css/avatar.css
@@ -50,6 +50,14 @@
.avatar-large .standard-image {
font-size: 36px;
}
+.avatar-xl {
+ margin-right: 10px;
+ width: 108px;
+ height: 108px;
+}
+.avatar-xl .standard-image {
+ font-size: 72px;
+}
.avatar-xs {
margin-right: 3px;
margin-top: -2px;
diff --git a/frappe/public/js/frappe/form/control.js b/frappe/public/js/frappe/form/control.js
index be94b5b1a7..d25b4616ca 100755
--- a/frappe/public/js/frappe/form/control.js
+++ b/frappe/public/js/frappe/form/control.js
@@ -100,7 +100,7 @@ frappe.ui.form.Control = Class.extend({
set_value: function(value) {
this.parse_validate_and_set_in_model(value);
},
- parse_validate_and_set_in_model: function(value) {
+ parse_validate_and_set_in_model: function(value, e) {
var me = this;
if(this.inside_change_event) return;
this.inside_change_event = true;
@@ -110,6 +110,11 @@ frappe.ui.form.Control = Class.extend({
me.set_model_value(value);
me.inside_change_event = false;
me.set_mandatory && me.set_mandatory(value);
+
+ if(me.df.change || me.df.onchange) {
+ // onchange event specified in df
+ (me.df.change || me.df.onchange).apply(me, [e]);
+ }
}
this.validate ? this.validate(value, set) : set(value);
@@ -333,39 +338,8 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
bind_change_event: function() {
var me = this;
-
this.$input && this.$input.on("change", this.change || function(e) {
- if(me.df.change || me.df.onchange) {
- // onchange event specified in df
- (me.df.change || me.df.onchange).apply(this, [e]);
- return;
- }
- if(me.doctype && me.docname && me.get_value) {
- me.parse_validate_and_set_in_model(me.get_value());
- } else {
- // inline
- var value = me.get_value();
- var parsed = me.parse ? me.parse(value) : value;
- var set_input = function(before, after) {
- if(before !== after) {
- me.set_input(after);
- }
- if(me.doc) {
- me.doc[me.df.fieldname] = value;
- }
- me.set_mandatory && me.set_mandatory(after);
- if(me.after_validate) {
- me.after_validate(after, me.$input);
- }
- }
- if(me.validate) {
- me.validate(parsed, function(validated) {
- set_input(value, validated);
- });
- } else {
- set_input(value, parsed);
- }
- }
+ me.parse_validate_and_set_in_model(me.get_value(), e);
});
},
bind_focusout: function() {
@@ -1430,22 +1404,8 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({
return;
}
var value = me.get_value();
- if(me.doctype && me.docname) {
- if(value!==me.last_value) {
- me.parse_validate_and_set_in_model(value);
- }
- } else {
- var cache_list = me.$input.cache[me.get_options()];
- if (cache_list && cache_list[""]) {
- var docs = cache_list[""].map(item => item.label);
- if(docs.includes(value)) {
- me.set_mandatory(value);
- } else {
- me.$input.val("");
- }
- } else {
- me.$input.val(value);
- }
+ if(value!==me.last_value) {
+ me.parse_validate_and_set_in_model(value);
}
});
@@ -1487,17 +1447,7 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({
frappe.boot.user.last_selected_values[me.df.options] = item.value;
}
- if(me.frm && me.frm.doc) {
- me.selected = true;
- me.parse_validate_and_set_in_model(item.value);
- setTimeout(function() {
- me.selected = false;
- }, 100);
- } else {
- me.$input.val(item.value);
- me.$input.trigger("change");
- me.set_mandatory(item.value);
- }
+ me.parse_validate_and_set_in_model(item.value);
});
this.$input.on("awesomplete-selectcomplete", function(e) {
@@ -1585,11 +1535,49 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({
return;
}
- if(this.frm) {
- this.frm.script_manager.validate_link_and_fetch(this.df, this.get_options(),
- this.docname, value, callback);
+ this.validate_link_and_fetch(this.df, this.get_options(),
+ this.docname, value, callback);
+ },
+ validate_link_and_fetch: function(df, doctype, docname, value, callback) {
+ var me = this;
+
+ if(value) {
+ var fetch = '';
+
+ if(this.frm && this.frm.fetch_dict[df.fieldname]) {
+ fetch = this.frm.fetch_dict[df.fieldname].columns.join(', ');
+ }
+
+ return frappe.call({
+ method:'frappe.desk.form.utils.validate_link',
+ type: "GET",
+ args: {
+ 'value': value,
+ 'options': doctype,
+ 'fetch': fetch
+ },
+ no_spinner: true,
+ callback: function(r) {
+ if(r.message=='Ok') {
+ if(r.fetch_values && docname) {
+ me.set_fetch_values(df, docname, r.fetch_values);
+ }
+ if(callback) callback(r.valid_value);
+ } else {
+ if(callback) callback("");
+ }
+ }
+ });
+ } else if(callback) {
+ callback(value);
}
},
+ set_fetch_values: function(df, docname, fetch_values) {
+ var fl = this.frm.fetch_dict[df.fieldname].fields;
+ for(var i=0; i < fl.length; i++) {
+ frappe.model.set_value(df.parent, docname, fl[i], fetch_values[i], df.fieldtype);
+ }
+ }
});
if(Awesomplete) {
diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js
index cc9de2587c..8aa7abfef1 100644
--- a/frappe/public/js/frappe/form/grid.js
+++ b/frappe/public/js/frappe/form/grid.js
@@ -169,7 +169,7 @@ frappe.ui.form.Grid = Class.extend({
this.truncate_rows(data);
this.grid_rows_by_docname = {};
- for(var ri=0;ri < data.length; ri++) {
+ for(var ri=0; ri < data.length; ri++) {
var d = data[ri];
if(d.idx===undefined) {
@@ -951,7 +951,6 @@ frappe.ui.form.GridRow = Class.extend({
parent = column.field_area,
df = column.df;
-
// no text editor in grid
if (df.fieldtype=='Text Editor') {
df.fieldtype = 'Text';
@@ -966,6 +965,8 @@ frappe.ui.form.GridRow = Class.extend({
doctype: this.doc.doctype,
docname: this.doc.name,
frm: this.grid.frm,
+ grid: this.grid,
+ grid_row: this,
value: this.doc[df.fieldname]
});
diff --git a/frappe/public/js/frappe/form/linked_with.js b/frappe/public/js/frappe/form/linked_with.js
index 7c9b5a3619..4ca2ca568d 100644
--- a/frappe/public/js/frappe/form/linked_with.js
+++ b/frappe/public/js/frappe/form/linked_with.js
@@ -103,9 +103,11 @@ frappe.ui.form.LinkedWith = class LinkedWith {
var me = this;
let links = null;
- links =
- Object.keys(this.frm.__linked_doctypes)
- .filter(frappe.model.can_get_report);
+ if (this.frm.__linked_doctypes) {
+ links =
+ Object.keys(this.frm.__linked_doctypes)
+ .filter(frappe.model.can_get_report);
+ }
let flag;
if(!links) {
diff --git a/frappe/public/js/frappe/form/script_manager.js b/frappe/public/js/frappe/form/script_manager.js
index ad34dcc6a7..be79a38c88 100644
--- a/frappe/public/js/frappe/form/script_manager.js
+++ b/frappe/public/js/frappe/form/script_manager.js
@@ -150,44 +150,6 @@ frappe.ui.form.ScriptManager = Class.extend({
console.log("----- end of error message -----");
console.group && console.groupEnd();
},
- validate_link_and_fetch: function(df, doctype, docname, value, callback) {
- var me = this;
-
- if(value) {
- var fetch = '';
-
- if(this.frm && this.frm.fetch_dict[df.fieldname])
- fetch = this.frm.fetch_dict[df.fieldname].columns.join(', ');
-
- return frappe.call({
- method:'frappe.desk.form.utils.validate_link',
- type: "GET",
- args: {
- 'value': value,
- 'options': doctype,
- 'fetch': fetch
- },
- no_spinner: true,
- callback: function(r) {
- if(r.message=='Ok') {
- if(r.fetch_values)
- me.set_fetch_values(df, docname, r.fetch_values);
- if(callback) callback(r.valid_value);
- } else {
- if(callback) callback("");
- }
- }
- });
- } else if(callback) {
- callback(value);
- }
- },
- set_fetch_values: function(df, docname, fetch_values) {
- var fl = this.frm.fetch_dict[df.fieldname].fields;
- for(var i=0; i < fl.length; i++) {
- frappe.model.set_value(df.parent, docname, fl[i], fetch_values[i], df.fieldtype);
- }
- },
copy_from_first_row: function(parentfield, current_row, fieldnames) {
var data = this.frm.doc[parentfield];
if(data.length===1 || data[0]===current_row) return;
diff --git a/frappe/public/js/frappe/views/calendar/calendar.js b/frappe/public/js/frappe/views/calendar/calendar.js
index 2212870480..b89a5b32ad 100644
--- a/frappe/public/js/frappe/views/calendar/calendar.js
+++ b/frappe/public/js/frappe/views/calendar/calendar.js
@@ -252,6 +252,8 @@ frappe.views.Calendar = Class.extend({
// see event_calendar.js
color = d.color;
}
+
+ if(!color) color = "blue";
d.className = "fc-bg-" + color;
return d;
});
diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js
index 31374cbb09..2d7a1e7a3e 100644
--- a/frappe/public/js/frappe/views/reports/query_report.js
+++ b/frappe/public/js/frappe/views/reports/query_report.js
@@ -315,21 +315,18 @@ frappe.views.QueryReport = Class.extend({
if(df.get_query) f.get_query = df.get_query;
if(df.on_change) f.on_change = df.on_change;
-
- // run report on change
- f.$input.on("change", function() {
+ df.onchange = () => {
if(!me.flags.filters_set) {
// don't trigger change while setting filters
return;
}
- f.$input.blur();
if (f.on_change) {
f.on_change(me);
} else {
me.trigger_refresh();
}
f.set_mandatory && f.set_mandatory(f.$input.val());
- });
+ }
}
});
@@ -416,6 +413,7 @@ frappe.views.QueryReport = Class.extend({
return;
}
});
+
if (!missing) {
me.refresh();
}
diff --git a/frappe/public/less/avatar.less b/frappe/public/less/avatar.less
index 363fd52767..a3e4925bd0 100644
--- a/frappe/public/less/avatar.less
+++ b/frappe/public/less/avatar.less
@@ -63,6 +63,16 @@
}
}
+.avatar-xl {
+ margin-right: 10px;
+ width: 108px;
+ height: 108px;
+
+ .standard-image {
+ font-size: 72px;
+ }
+}
+
.avatar-xs {
margin-right: 3px;
margin-top: -2px;
diff --git a/requirements.txt b/requirements.txt
index dfcb0cb574..2e39e22286 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,7 +6,7 @@ httplib2
jinja2
markdown2
markupsafe
-mysqlclient==1.3.8
+mysqlclient==1.3.10
python-geoip
python-geoip-geolite2
python-dateutil
@@ -41,4 +41,4 @@ xlwt
oauthlib
PyJWT
pypdf
-openpyxl
\ No newline at end of file
+openpyxl