diff --git a/frappe/__version__.py b/frappe/__version__.py index 8420fa90a2..9363cb8d6f 100644 --- a/frappe/__version__.py +++ b/frappe/__version__.py @@ -1,2 +1,2 @@ from __future__ import unicode_literals -__version__ = "6.10.0" +__version__ = "6.10.1" diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 9411ab3cae..a8db66c7e2 100644 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -25,7 +25,6 @@ import mimetypes, imghdr from frappe.utils import get_files_path class FolderNotEmpty(frappe.ValidationError): pass -class ThumbnailError(frappe.ValidationError): pass exclude_from_linked_with = True @@ -154,13 +153,16 @@ class File(NestedSet): def make_thumbnail(self): if self.file_url: if self.file_url.startswith("/files"): - image, filename, extn = get_local_image(self.file_url) + try: + image, filename, extn = get_local_image(self.file_url) + except IOError: + return else: try: image, filename, extn = get_web_image(self.file_url) - except ThumbnailError: - frappe.msgprint("Unable to write file format for {0}".format(self.file_url)) + except requests.exceptions.HTTPError: + return thumbnail = ImageOps.fit( image, @@ -177,6 +179,7 @@ class File(NestedSet): self.db_set("thumbnail_url", thumbnail_url) except IOError: frappe.msgprint("Unable to write file format for {0}".format(path)) + return return thumbnail_url @@ -289,7 +292,7 @@ def get_local_image(file_url): image = Image.open(file_path) except IOError: frappe.msgprint("Unable to read file format for {0}".format(file_url)) - return + raise content = None @@ -315,9 +318,11 @@ def get_web_image(file_url): r.raise_for_status() except requests.exceptions.HTTPError, e: if "404" in e.args[0]: - frappe.throw(_("File '{0}' not found").format(file_url)) + frappe.msgprint(_("File '{0}' not found").format(file_url)) else: - raise ThumbnailError + frappe.msgprint("Unable to read file format for {0}".format(file_url)) + + raise image = Image.open(StringIO.StringIO(r.content)) diff --git a/frappe/desk/form/assign_to.py b/frappe/desk/form/assign_to.py index c20483919b..543a1c1d48 100644 --- a/frappe/desk/form/assign_to.py +++ b/frappe/desk/form/assign_to.py @@ -38,13 +38,13 @@ def add(args=None): and owner=%(assign_to)s""", args): frappe.msgprint(_("Already in user's To Do list"), raise_exception=True) return - + else: from frappe.utils import nowdate - + if args.get("re_assign"): remove_from_todo_if_already_assigned(args['doctype'], args['name']) - + d = frappe.get_doc({ "doctype":"ToDo", "owner": args['assign_to'], @@ -56,39 +56,38 @@ def add(args=None): "date": args.get('date', nowdate()), "assigned_by": args.get('assigned_by', frappe.session.user), }).insert(ignore_permissions=True) - + # set assigned_to if field exists if frappe.get_meta(args['doctype']).get_field("assigned_to"): frappe.db.set_value(args['doctype'], args['name'], "assigned_to", args['assign_to']) # notify - if not args.get("no_notification"): - notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN',\ - description=args.get("description"), notify=args.get('notify')) - + notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN',\ + description=args.get("description"), notify=args.get('notify')) + if not args.get("bulk_assign"): return get(args) else: return {} - + @frappe.whitelist() def add_multiple(args=None): import json - + if not args: args = frappe.local.form_dict - + docname_list = json.loads(args['name']) - + for docname in docname_list: args.update({"name": docname}) add(args) - + def remove_from_todo_if_already_assigned(doctype, docname): owner = frappe.db.get_value("ToDo", {"reference_type": doctype, "reference_name": docname, "status":"Open"}, "owner") if owner: remove(doctype, docname, owner) - + @frappe.whitelist() def remove(doctype, name, assign_to): """remove from todo""" @@ -156,5 +155,6 @@ def notify_assignment(assigned_by, owner, doc_type, doc_name, action='CLOSE', } arg["parenttype"] = "Assignment" + from frappe.desk.page.messages import messages messages.post(**arg) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 7eb3e48793..0c7583874b 100644 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -14,6 +14,9 @@ from poplib import error_proto import re from dateutil.relativedelta import relativedelta from datetime import datetime, timedelta +from frappe.desk.form import assign_to +from frappe.utils.user import get_system_managers +import socket class SentEmailInInbox(Exception): pass @@ -96,7 +99,7 @@ class EmailAccount(Document): ) server.sess - def get_pop3(self): + def get_pop3(self, in_receive=False): """Returns logged in POP3 connection object.""" args = { "host": self.pop3_server, @@ -111,18 +114,56 @@ class EmailAccount(Document): pop3 = POP3Server(frappe._dict(args)) try: pop3.connect() + except error_proto, e: - frappe.throw(e.message) + if in_receive and e.message=="-ERR authentication failed": + # if called via self.receive and it leads to authentication error, disable incoming + # and send email to system manager + self.handle_incoming_connect_error( + description=_('Authentication failed while receiving emails from Email Account {0}'.format(self.name)) + ) + + return None + + else: + frappe.throw(e.message) + + except socket.error: + if in_receive: + # timeout while connecting, see receive.py connect method + description = frappe.message_log.pop() if frappe.message_log else "Socket Error" + self.handle_incoming_connect_error(description=description) + + return None + + else: + raise return pop3 + def handle_incoming_connect_error(self, description): + self.db_set("enable_incoming", 0) + + for user in get_system_managers(only_name=True): + assign_to.add({ + 'assign_to': user, + 'doctype': self.doctype, + 'name': self.name, + 'description': description, + 'priority': 'High', + 'notify': 1 + }) + def receive(self, test_mails=None): """Called by scheduler to receive emails from this EMail account using POP3.""" if self.enable_incoming: if frappe.local.flags.in_test: incoming_mails = test_mails else: - pop3 = self.get_pop3() + pop3 = self.get_pop3(in_receive=True) + if not pop3: + return + incoming_mails = pop3.get_messages() exceptions = [] diff --git a/frappe/email/doctype/email_account/test_email_account.py b/frappe/email/doctype/email_account/test_email_account.py index 94cc1f6156..87627bb69c 100644 --- a/frappe/email/doctype/email_account/test_email_account.py +++ b/frappe/email/doctype/email_account/test_email_account.py @@ -13,6 +13,14 @@ from frappe.email.doctype.email_account.email_account import notify_unreplied from datetime import datetime, timedelta class TestEmailAccount(unittest.TestCase): + def setUp(self): + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + email_account.db_set("enable_incoming", 1) + + def tearDown(self): + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + email_account.db_set("enable_incoming", 0) + def test_incoming(self): frappe.db.sql("delete from tabCommunication where sender='test_sender@example.com'") diff --git a/frappe/email/doctype/email_alert/email_alert.json b/frappe/email/doctype/email_alert/email_alert.json index 8e5d0d0281..b329c4ccae 100644 --- a/frappe/email/doctype/email_alert/email_alert.json +++ b/frappe/email/doctype/email_alert/email_alert.json @@ -25,6 +25,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -47,6 +48,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -70,6 +72,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 1, @@ -93,6 +96,7 @@ "options": "DocType", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 1, @@ -116,6 +120,7 @@ "options": "\nNew\nSave\nSubmit\nCancel\nDays After\nDays Before\nValue Change", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 1, @@ -140,6 +145,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -165,6 +171,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -189,6 +196,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -213,6 +221,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -234,6 +243,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -256,6 +266,7 @@ "options": "
Condition Examples:
\ndoc.status==\"Open\"\ndoc.due_date==nowdate()\ndoc.total > 40000\n\n
Hints:
\n\nOrder Overdue
\n\nTransaction {{ doc.name }} has exceeded Due Date. Please take necessary action.
\n\nDetails
\n\n
\n<h3>Order Overdue</h3>\n\n<p>Transaction {{ doc.name }} has exceeded Due Date. Please take necessary action.</p>\n\n<!-- show last comment -->\n{% if comments %}\nLast comment: {{ comments[-1].comment }} by {{ comments[-1].by }}\n{% endif %}\n\n<h4>Details</h4>\n\n<ul>\n<li>Customer: {{ doc.customer }}\n<li>Amount: {{ doc.total_amount }}\n</ul>\n",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -414,6 +431,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -425,13 +443,15 @@
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "icon-envelope",
+ "idx": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2015-11-16 06:29:46.014034",
+ "menu_index": 0,
+ "modified": "2015-11-26 02:14:59.637519",
"modified_by": "Administrator",
"module": "Email",
"name": "Email Alert",
diff --git a/frappe/email/doctype/email_alert/email_alert.py b/frappe/email/doctype/email_alert/email_alert.py
index 2366652de3..5da08920c8 100644
--- a/frappe/email/doctype/email_alert/email_alert.py
+++ b/frappe/email/doctype/email_alert/email_alert.py
@@ -3,6 +3,7 @@
from __future__ import unicode_literals
import frappe
+import json
from frappe import _
from frappe.model.document import Document
from frappe.utils import validate_email_add, nowdate
@@ -95,10 +96,21 @@ def evaluate_alert(doc, alert, event):
return
subject = alert.subject
+
+ if event != "Value Change" and not doc.is_new():
+ # reload the doc for the latest values & comments,
+ # except for validate type event.
+ doc = frappe.get_doc(doc.doctype, doc.name)
+
+ context = {"doc": doc, "alert": alert, "comments": None}
+
+ if doc.get("_comments"):
+ context["comments"] = json.loads(doc.get("_comments"))
+
if "{" in subject:
- subject = frappe.render_template(alert.subject, {"doc": doc, "alert": alert})
+ subject = frappe.render_template(alert.subject, context)
frappe.sendmail(recipients=recipients, subject=subject,
- message= frappe.render_template(alert.message, {"doc": doc, "alert":alert}),
+ message= frappe.render_template(alert.message, context),
bulk=True, reference_doctype = doc.doctype, reference_name = doc.name,
attachments = [frappe.attach_print(doc.doctype, doc.name)] if alert.attach_print else None)
diff --git a/frappe/email/receive.py b/frappe/email/receive.py
index 1a65b8c74f..7b5711d17a 100644
--- a/frappe/email/receive.py
+++ b/frappe/email/receive.py
@@ -3,7 +3,7 @@
from __future__ import unicode_literals
import time
-import _socket, poplib
+import socket, poplib
import frappe
from frappe import _
from frappe.utils import extract_email_id, convert_utc_to_user_timezone, now, cint, cstr, strip
@@ -48,7 +48,7 @@ class POP3Server:
# connection established!
return True
- except _socket.error:
+ except socket.error:
# Invalid mail server -- due to refusing connection
frappe.msgprint(_('Invalid Mail Server. Please rectify and try again.'))
raise
diff --git a/frappe/hooks.py b/frappe/hooks.py
index a4f8a710a9..253794fc44 100644
--- a/frappe/hooks.py
+++ b/frappe/hooks.py
@@ -5,7 +5,7 @@ app_publisher = "Frappe Technologies Pvt. Ltd."
app_description = "Full stack web framework with Python, Javascript, MariaDB, Redis, Node"
app_icon = "octicon octicon-circuit-board"
-app_version = "6.10.0"
+app_version = "6.10.1"
app_color = "orange"
source_link = "https://github.com/frappe/frappe"
app_license = "MIT"
diff --git a/frappe/public/css/desk.css b/frappe/public/css/desk.css
index 26da6972ba..e19fbbb788 100644
--- a/frappe/public/css/desk.css
+++ b/frappe/public/css/desk.css
@@ -326,10 +326,26 @@ ul.linked-with-list li {
border-bottom: 1px solid #d1d8dd;
}
/* jquery ui */
+.ui-datepicker .ui-datepicker-header {
+ border-radius: 0px !important;
+}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year {
display: inline;
}
+.ui-datepicker select.ui-datepicker-month {
+ margin-right: 3px;
+}
+.ui-datepicker .ui-datepicker-title {
+ line-height: inherit !important;
+ padding-bottom: 3px;
+}
+.ui-datepicker-today .ui-state-default {
+ background-color: #EBEFF2 !important;
+}
+.ui-state-default {
+ box-shadow: none !important;
+}
.hidden-xs-inline,
.hidden-xs-inline-block {
display: none;
diff --git a/frappe/public/js/frappe/form/control.js b/frappe/public/js/frappe/form/control.js
index 726e511e46..05ea0cb6fc 100644
--- a/frappe/public/js/frappe/form/control.js
+++ b/frappe/public/js/frappe/form/control.js
@@ -523,10 +523,12 @@ frappe.ui.form.ControlDate = frappe.ui.form.ControlData.extend({
datepicker_options: {
altFormat:'yy-mm-dd',
changeYear: true,
+ changeMonth: true,
yearRange: "-70Y:+10Y",
},
make_input: function() {
this._super();
+ this.set_t_for_today();
this.set_datepicker();
},
set_datepicker: function() {
@@ -534,6 +536,15 @@ frappe.ui.form.ControlDate = frappe.ui.form.ControlData.extend({
(frappe.boot.sysdefaults.date_format || 'yyyy-mm-dd').replace("yyyy", "yy")
this.$input.datepicker(this.datepicker_options);
},
+ set_t_for_today: function() {
+ var me = this;
+ this.$input.on("keydown", function(e) {
+ if(e.which===84) { // 84 === t
+ me.set_value(frappe.datetime.str_to_user(frappe.datetime.nowdate()));
+ return false;
+ }
+ });
+ },
parse: function(value) {
if(value) {
value = dateutil.user_to_str(value);
@@ -878,7 +889,7 @@ frappe.ui.form.ControlAttachImage = frappe.ui.form.ControlAttach.extend({
this.$wrapper.on("refresh", function() {
me.set_image();
});
-
+
this.set_image();
},
set_image: function() {
diff --git a/frappe/public/less/desk.less b/frappe/public/less/desk.less
index 915b112ed0..8ba7abf010 100644
--- a/frappe/public/less/desk.less
+++ b/frappe/public/less/desk.less
@@ -134,10 +134,30 @@ ul.linked-with-list li {
/* jquery ui */
+.ui-datepicker .ui-datepicker-header {
+ border-radius: 0px !important;
+}
+
.ui-datepicker select.ui-datepicker-month, .ui-datepicker select.ui-datepicker-year {
display: inline;
}
+.ui-datepicker select.ui-datepicker-month {
+ margin-right: 3px;
+}
+.ui-datepicker .ui-datepicker-title {
+ line-height: inherit !important;
+ padding-bottom: 3px;
+}
+
+.ui-datepicker-today .ui-state-default {
+ background-color: @light-border-color !important;
+}
+
+.ui-state-default {
+ box-shadow: none !important;
+}
+
.hidden-xs-inline, .hidden-xs-inline-block {
display: none;
}
diff --git a/frappe/website/js/website.js b/frappe/website/js/website.js
index b1de0405ae..acbf1ef5ea 100644
--- a/frappe/website/js/website.js
+++ b/frappe/website/js/website.js
@@ -371,12 +371,6 @@ $.extend(frappe, {
// blur
if(!$('#freeze').length) {
var freeze = $('')
- .on("click", function() {
- if (cur_frm && cur_frm.cur_grid) {
- cur_frm.cur_grid.toggle_view();
- return false;
- }
- })
.appendTo("body");
freeze.html(repl('',
diff --git a/setup.py b/setup.py
index 28ff6e633c..31ffacb7e2 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,6 @@
from setuptools import setup, find_packages
-version = "6.10.0"
+version = "6.10.1"
with open("requirements.txt", "r") as f:
install_requires = f.readlines()