From 097eb0a4b577bfd29203d209a663b5b49e41e97f Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 26 Nov 2015 13:07:32 +0530 Subject: [PATCH 1/6] [enhancement] ability to add comments in email alerts --- .../doctype/email_alert/email_alert.json | 24 +++++++++++++++++-- .../email/doctype/email_alert/email_alert.py | 16 +++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) 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:

\n
doc.status==\"Open\"\ndoc.due_date==nowdate()\ndoc.total > 40000\n
\n

Hints:

\n
    \n
  1. To check for an event every day, select \"Date Change\" in Event
  2. \n
  3. To send an alert if a particular value changes, select \"Value Change\"
  4. \n
", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -278,6 +289,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -301,6 +313,7 @@ "options": "Email Alert Recipient", "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 1, @@ -323,6 +336,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -345,6 +359,7 @@ "no_copy": 0, "permlevel": 0, "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 1, @@ -368,6 +383,7 @@ "permlevel": 0, "precision": "", "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -388,9 +404,10 @@ "label": "Message Examples", "length": 0, "no_copy": 0, - "options": "
Message Example
\n\n
\n

Order Overdue

\n\n

Transaction {{ doc.name }} has exceeded Due Date. Please take necessary action.

\n\n

Details

\n\n\n
", + "options": "
Message Example
\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) From 6177d09b187293164c8ea7624d660d761f550305 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 23 Nov 2015 15:44:08 +0530 Subject: [PATCH 2/6] [fix] In pop3 receive, catch authentication and timeout errors while connecting, disable incoming emails and assign the email account to system managers --- frappe/desk/form/assign_to.py | 28 +++++------ .../doctype/email_account/email_account.py | 47 +++++++++++++++++-- .../email_account/test_email_account.py | 8 ++++ frappe/email/receive.py | 4 +- 4 files changed, 68 insertions(+), 19 deletions(-) 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/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 From 46d9ac097790f995b1f9bce31062d77208550fc9 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 26 Nov 2015 13:30:58 +0530 Subject: [PATCH 3/6] [minor] [fix] website.js --- frappe/website/js/website.js | 6 ------ 1 file changed, 6 deletions(-) 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('
%(msg)s
', From 0917ed9cb018ad0a124ee4878b7412bdb699e173 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 26 Nov 2015 15:41:24 +0530 Subject: [PATCH 4/6] [minor] date picker fixes, t=today, fixes #3830, #4380 --- frappe/public/css/desk.css | 16 ++++++++++++++++ frappe/public/js/frappe/form/control.js | 13 ++++++++++++- frappe/public/less/desk.less | 20 ++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) 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; } From 175ed78f8af1e9dbe988e36e46e91f35a5398e6c Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 26 Nov 2015 18:08:19 +0530 Subject: [PATCH 5/6] [hotfix] catch exception during thumbnail generation --- frappe/core/doctype/file/file.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) 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)) From 2e01b030de9abc30868a57bea6bdde303a820c20 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 26 Nov 2015 18:41:19 +0600 Subject: [PATCH 6/6] bumped to version 6.10.1 --- frappe/__version__.py | 2 +- frappe/hooks.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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/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/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()