@@ -35,6 +35,7 @@ before_script: | |||
- echo "USE mysql;\nGRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost';\n" | mysql -u root -ptravis | |||
- cd ~/frappe-bench | |||
- npm install babel-core less chokidar babel-preset-es2015 babel-preset-es2016 babel-preset-es2017 babel-preset-babili | |||
- bench use test_site | |||
- bench reinstall --yes | |||
- bench start & | |||
@@ -13,7 +13,7 @@ import os, sys, importlib, inspect, json | |||
from .exceptions import * | |||
from .utils.jinja import get_jenv, get_template, render_template | |||
__version__ = '8.0.55' | |||
__version__ = '8.0.56' | |||
__title__ = "Frappe Framework" | |||
local = Local() | |||
@@ -65,7 +65,7 @@ def application(request): | |||
elif frappe.request.path.startswith('/private/files/'): | |||
response = frappe.utils.response.download_private_file(request.path) | |||
elif frappe.local.request.method in ('GET', 'HEAD'): | |||
elif frappe.local.request.method in ('GET', 'HEAD', 'POST'): | |||
response = frappe.website.render.render() | |||
else: | |||
@@ -122,6 +122,7 @@ def make_form_dict(request): | |||
frappe.local.form_dict.pop("_") | |||
def handle_exception(e): | |||
response = None | |||
http_status_code = getattr(e, "http_status_code", 500) | |||
return_as_message = False | |||
@@ -185,6 +185,9 @@ class LoginManager: | |||
if not (user and pwd): | |||
self.fail('Incomplete login details', user=user) | |||
if cint(frappe.db.get_value("System Settings", "System Settings", "allow_login_using_mobile_number")): | |||
user = frappe.db.get_value("User", filters={"mobile_no": user}, fieldname="name") or user | |||
self.check_if_enabled(user) | |||
self.user = self.check_password(user, pwd) | |||
@@ -24,7 +24,7 @@ const build_map = make_build_map(); | |||
// command line args | |||
const action = process.argv[2] || '--build'; | |||
if (!['--build', '--watch'].includes(action)) { | |||
if (['--build', '--watch'].indexOf(action) === -1) { | |||
console.log('Invalid argument: ', action); | |||
return; | |||
} | |||
@@ -31,7 +31,7 @@ def bundle(no_compress, make_copy=False, verbose=False): | |||
make_asset_dirs(make_copy=make_copy) | |||
# new nodejs build system | |||
command = 'node ../apps/frappe/frappe/build.js --build' | |||
command = 'node --use_strict ../apps/frappe/frappe/build.js --build' | |||
if not no_compress: | |||
command += ' --minify' | |||
subprocess.call(command.split(' ')) | |||
@@ -42,7 +42,7 @@ def watch(no_compress): | |||
"""watch and rebuild if necessary""" | |||
# new nodejs file watcher | |||
command = 'node ../apps/frappe/frappe/build.js --watch' | |||
command = 'node --use_strict ../apps/frappe/frappe/build.js --watch' | |||
subprocess.call(command.split(' ')) | |||
# setup() | |||
@@ -11,7 +11,8 @@ from frappe.core.doctype.communication.comment import (notify_mentions, | |||
from frappe.core.doctype.communication.email import (validate_email, | |||
notify, _notify, update_parent_status) | |||
from frappe.utils.bot import BotReply | |||
from email.utils import parseaddr | |||
from frappe.utils import parse_addr | |||
from collections import Counter | |||
exclude_from_linked_with = True | |||
@@ -140,14 +141,9 @@ class Communication(Document): | |||
else: | |||
if self.sent_or_received=='Sent': | |||
validate_email_add(self.sender, throw=True) | |||
sender_name, sender_email = parseaddr(self.sender) | |||
if not sender_name: | |||
sender_name = get_fullname(sender_email) | |||
if sender_name == sender_email: | |||
sender_name = None | |||
sender_name, sender_email = parse_addr(self.sender) | |||
if sender_name == sender_email: | |||
sender_name = None | |||
self.sender = sender_email | |||
self.sender_full_name = sender_name or get_fullname(frappe.session.user) if frappe.session.user!='Administrator' else None | |||
@@ -5,9 +5,9 @@ from __future__ import unicode_literals, absolute_import | |||
from six.moves import range | |||
import frappe | |||
import json | |||
from email.utils import formataddr, parseaddr | |||
from email.utils import formataddr | |||
from frappe.utils import (get_url, get_formatted_email, cint, | |||
validate_email_add, split_emails, time_diff_in_seconds) | |||
validate_email_add, split_emails, time_diff_in_seconds, parse_addr) | |||
from frappe.utils.file_manager import get_file | |||
from frappe.email.queue import check_email_limit | |||
from frappe.utils.scheduler import log | |||
@@ -321,11 +321,11 @@ def get_cc(doc, recipients=None, fetched_from_email_account=False): | |||
# exclude unfollows, recipients and unsubscribes | |||
exclude = [] #added to remove account check | |||
exclude += [d[0] for d in frappe.db.get_all("User", ["name"], {"thread_notify": 0}, as_list=True)] | |||
exclude += [(parseaddr(email)[1] or "").lower() for email in recipients] | |||
exclude += [(parse_addr(email)[1] or "").lower() for email in recipients] | |||
if fetched_from_email_account: | |||
# exclude sender when pulling email | |||
exclude += [parseaddr(doc.sender)[1]] | |||
exclude += [parse_addr(doc.sender)[1]] | |||
if doc.reference_doctype and doc.reference_name: | |||
exclude += [d[0] for d in frappe.db.get_all("Email Unsubscribe", ["email"], | |||
@@ -356,7 +356,7 @@ def filter_email_list(doc, email_list, exclude, is_cc=False): | |||
email_address_list = [] | |||
for email in list(set(email_list)): | |||
email_address = (parseaddr(email)[1] or "").lower() | |||
email_address = (parse_addr(email)[1] or "").lower() | |||
if not email_address: | |||
continue | |||
@@ -4,8 +4,27 @@ from __future__ import unicode_literals | |||
import frappe | |||
import unittest | |||
test_records = frappe.get_test_records('Communication') | |||
class TestCommunication(unittest.TestCase): | |||
pass | |||
def test_parse_addr(self): | |||
valid_email_list = ["me@example.com", "a.nonymous@example.com", "name@tag@example.com", | |||
"foo@example.com", 'Full Name <full@example.com>', | |||
'"Full Name with quotes and <weird@chars.com>" <weird@example.com>', | |||
'foo@bar@google.com', 'Surname, Name <name.surname@domain.com>', | |||
'Purchase@ABC <purchase@abc.com>', 'xyz@abc2.com <xyz@abc.com>', | |||
'Name [something else] <name@domain.com>', | |||
'.com@test@yahoo.com'] | |||
invalid_email_list = ['[invalid!email]', 'invalid-email', | |||
'tes2', 'e', 'rrrrrrrr', 'manas','[[[sample]]]', | |||
'[invalid!email].com'] | |||
for x in valid_email_list: | |||
self.assertTrue(frappe.utils.parse_addr(x)) | |||
for x in invalid_email_list: | |||
self.assertFalse(frappe.utils.parse_addr(x)[0]) | |||
@@ -770,6 +770,37 @@ | |||
"set_only_once": 0, | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"description": "User can login using Email id or Mobile number", | |||
"fieldname": "allow_login_using_mobile_number", | |||
"fieldtype": "Check", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_global_search": 0, | |||
"in_list_view": 0, | |||
"in_standard_filter": 0, | |||
"label": "Allow Login using Mobile Number", | |||
"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 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
@@ -902,7 +933,7 @@ | |||
"issingle": 1, | |||
"istable": 0, | |||
"max_attachments": 0, | |||
"modified": "2017-05-11 15:27:11.079447", | |||
"modified": "2017-05-19 09:12:50.353887", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "System Settings", | |||
@@ -14,6 +14,7 @@ | |||
"engine": "InnoDB", | |||
"fields": [ | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -42,6 +43,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -73,6 +75,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -102,6 +105,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -133,6 +137,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -163,6 +168,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -193,6 +199,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 1, | |||
"collapsible": 0, | |||
@@ -223,6 +230,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -252,6 +260,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 1, | |||
"collapsible": 0, | |||
@@ -283,6 +292,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -311,6 +321,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -341,6 +352,7 @@ | |||
"width": "50%" | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -370,6 +382,7 @@ | |||
"unique": 1 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -400,6 +413,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -429,6 +443,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -458,6 +473,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -488,6 +504,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -516,6 +533,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -546,6 +564,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
@@ -575,6 +594,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -606,6 +626,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -635,6 +656,37 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "mobile_no", | |||
"fieldtype": "Data", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_global_search": 0, | |||
"in_list_view": 0, | |||
"in_standard_filter": 0, | |||
"label": "Mobile No", | |||
"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": 1 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -665,6 +717,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -693,6 +746,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -720,6 +774,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -748,6 +803,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -777,6 +833,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
@@ -806,6 +863,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -834,6 +892,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -864,6 +923,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -892,6 +952,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -921,6 +982,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
@@ -951,6 +1013,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -981,6 +1044,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1009,6 +1073,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
@@ -1038,6 +1103,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1069,6 +1135,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
@@ -1099,6 +1166,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1128,6 +1196,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1158,6 +1227,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
@@ -1189,6 +1259,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1218,6 +1289,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1248,6 +1320,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1280,6 +1353,7 @@ | |||
"width": "50%" | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1310,6 +1384,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
@@ -1340,6 +1415,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1370,6 +1446,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1403,6 +1480,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1432,6 +1510,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1461,6 +1540,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1490,6 +1570,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1520,6 +1601,7 @@ | |||
"width": "50%" | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1550,6 +1632,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1580,6 +1663,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1609,6 +1693,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1639,6 +1724,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 1, | |||
@@ -1668,6 +1754,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1696,6 +1783,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1724,6 +1812,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1752,6 +1841,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1780,6 +1870,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1808,6 +1899,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1836,6 +1928,7 @@ | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_bulk_edit": 0, | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
@@ -1878,7 +1971,7 @@ | |||
"istable": 0, | |||
"max_attachments": 5, | |||
"menu_index": 0, | |||
"modified": "2017-04-01 14:38:25.869060", | |||
"modified": "2017-05-19 09:12:35.697915", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "User", | |||
@@ -149,6 +149,7 @@ frappe.customize_form.set_primary_action = function(frm) { | |||
callback: function(r) { | |||
if(!r.exc) { | |||
frappe.customize_form.clear_locals_and_refresh(frm); | |||
frm.script_manager.trigger("doc_type"); | |||
} | |||
} | |||
}); | |||
@@ -7,7 +7,7 @@ import frappe | |||
from frappe import _ | |||
from frappe.utils import validate_email_add | |||
from frappe.model.document import Document | |||
from email.utils import parseaddr | |||
from frappe.utils import parse_addr | |||
class EmailGroup(Document): | |||
def onload(self): | |||
@@ -26,7 +26,7 @@ class EmailGroup(Document): | |||
for user in frappe.db.get_all(doctype, [email_field, unsubscribed_field or "name"]): | |||
try: | |||
email = parseaddr(user.get(email_field))[1] | |||
email = parse_addr(user.get(email_field))[1] | |||
if email: | |||
frappe.get_doc({ | |||
"doctype": "Email Group Member", | |||
@@ -14,6 +14,7 @@ from frappe.utils.scheduler import log | |||
from frappe.email.queue import send | |||
from frappe.email.doctype.email_group.email_group import add_subscribers | |||
from frappe.utils.file_manager import get_file | |||
from frappe.utils import parse_addr | |||
class Newsletter(Document): | |||
@@ -135,17 +136,15 @@ def return_unsubscribed_page(email, name): | |||
def create_lead(email_id): | |||
"""create a lead if it does not exist""" | |||
from email.utils import parseaddr | |||
from frappe.model.naming import get_default_naming_series | |||
real_name, email_id = parseaddr(email_id) | |||
full_name, email_id = parse_addr(email_id) | |||
if frappe.db.get_value("Lead", {"email_id": email_id}): | |||
return | |||
lead = frappe.get_doc({ | |||
"doctype": "Lead", | |||
"email_id": email_id, | |||
"lead_name": real_name or email_id, | |||
"lead_name": full_name or email_id, | |||
"status": "Lead", | |||
"naming_series": get_default_naming_series("Lead"), | |||
"company": frappe.db.get_default("Company"), | |||
@@ -8,6 +8,7 @@ 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) | |||
import email.utils | |||
from frappe.utils import parse_addr | |||
def get_email(recipients, sender='', msg='', subject='[No Subject]', | |||
text_content = None, footer=None, print_html=None, formatted=None, attachments=None, | |||
@@ -179,8 +180,7 @@ class EMail: | |||
def replace_sender(self): | |||
if cint(self.email_account.always_use_account_email_id_as_sender): | |||
self.set_header('X-Original-From', self.sender) | |||
sender_name, sender_email = email.utils.parseaddr(self.sender) | |||
sender_name, sender_email = parse_addr(self.sender) | |||
self.sender = email.utils.formataddr((sender_name or self.email_account.name, self.email_account.email_id)) | |||
def set_message_id(self, message_id, is_notification=False): | |||
@@ -9,7 +9,7 @@ from email.header import decode_header | |||
import frappe | |||
from frappe import _ | |||
from frappe.utils import (extract_email_id, convert_utc_to_user_timezone, now, | |||
cint, cstr, strip, markdown) | |||
cint, cstr, strip, markdown, parse_addr) | |||
from frappe.utils.scheduler import log | |||
from frappe.utils.file_manager import get_random_filename, save_file, MaxFileSizeReachedError | |||
import re | |||
@@ -411,7 +411,7 @@ class Email: | |||
if self.from_email: | |||
self.from_email = self.from_email.lower() | |||
self.from_real_name = email.utils.parseaddr(_from_email)[0] if "@" in _from_email else _from_email | |||
self.from_real_name = parse_addr(_from_email)[0] if "@" in _from_email else _from_email | |||
def decode_email(self, email): | |||
if not email: return | |||
@@ -5,9 +5,6 @@ import frappe | |||
''' | |||
FrappeClient is a library that helps you connect with other frappe systems | |||
''' | |||
class AuthError(Exception): | |||
@@ -29,7 +29,11 @@ | |||
</div> | |||
{% endif %} | |||
<div class="print-format"> | |||
<div class="print-format {% if landscape %} landscape {% endif %}" | |||
{% if columns.length > 20 %} | |||
style="font-size: 4.0pt" | |||
{% endif %} | |||
> | |||
{% if print_settings.letter_head %} | |||
<div {% if print_settings.repeat_header_footer %} id="header-html" class="hidden-pdf" {% endif %}> | |||
<div class="letter-head">{{ print_settings.letter_head.header }}</div> | |||
@@ -29,8 +29,7 @@ frappe.search.AwesomeBar = Class.extend({ | |||
}, | |||
item: function(item, term) { | |||
var d = this.get_item(item.value); | |||
var name = d.prefix ? __(d.prefix + ' ' + (d.label || d.value)) : | |||
__(d.label || d.value); | |||
var name = __(d.label || d.value); | |||
var html = '<span>' + name + '</span>'; | |||
if(d.description && d.value!==d.description) { | |||
html += '<br><span class="text-muted ellipsis">' + __(d.description) + '</span>'; | |||
@@ -123,8 +122,7 @@ frappe.search.AwesomeBar = Class.extend({ | |||
add_help: function() { | |||
this.options.push({ | |||
label: __("Help on Search"), | |||
value: "Help on Search", | |||
value: __("Help on Search"), | |||
index: -10, | |||
default: "Help", | |||
onclick: function() { | |||
@@ -140,7 +138,7 @@ frappe.search.AwesomeBar = Class.extend({ | |||
<tr><td>'+__("Calculate")+'</td><td>'+ | |||
__("e.g. (55 + 434) / 4 or =Math.sin(Math.PI/2)...")+'</td></tr>\ | |||
</table>' | |||
msgprint(txt, "Search Help"); | |||
msgprint(txt, __("Search Help")); | |||
} | |||
}); | |||
}, | |||
@@ -208,8 +206,8 @@ frappe.search.AwesomeBar = Class.extend({ | |||
make_global_search: function(txt) { | |||
var me = this; | |||
this.options.push({ | |||
label: __("Search for '" + txt.bold() + "'"), | |||
value: __("Search for '" + txt + "'"), | |||
label: __("Search for '{0}'", [txt.bold()]), | |||
value: __("Search for '{0}'", [txt]), | |||
match: txt, | |||
index: 100, | |||
default: "Search", | |||
@@ -87,8 +87,7 @@ frappe.search.utils = { | |||
if(level) { | |||
out.push({ | |||
type: "In List", | |||
prefix: __("Find {0} in ", [__(parts[0]).bold()]), | |||
label: me.bolden_match_part(__(item), parts[1]), | |||
label: __('Find {0} in {1}', [__(parts[0]), me.bolden_match_part(__(item), parts[1])]), | |||
value: __('Find {0} in {1}', [__(parts[0]), __(item)]), | |||
route_options: {"name": ["like", "%" + parts[0] + "%"]}, | |||
index: 1 + level, | |||
@@ -110,8 +109,7 @@ frappe.search.utils = { | |||
if(level) { | |||
out.push({ | |||
type: "New", | |||
prefix: "New ", | |||
label: me.bolden_match_part(__(item), keywords.substr(4)), | |||
label: __("New {0}", [me.bolden_match_part(__(item), keywords.substr(4))]), | |||
value: __("New {0}", [__(item)]), | |||
index: 1 + level, | |||
match: item, | |||
@@ -131,8 +129,8 @@ frappe.search.utils = { | |||
var option = function(type, route, order) { | |||
return { | |||
type: type, | |||
label: __("{0}" + " " + type, [me.bolden_match_part(__(target), keywords)]), | |||
value: __(__(target) + " " + type), | |||
label: me.bolden_match_part(__(target), keywords) + " " + __(type), | |||
value: __(target) + " " + __(type), | |||
index: level + order, | |||
match: target, | |||
route: route, | |||
@@ -191,8 +189,7 @@ frappe.search.utils = { | |||
route = ["query-report", item]; | |||
out.push({ | |||
type: "Report", | |||
prefix: "Report ", | |||
label: me.bolden_match_part(__(item), keywords), | |||
label: __("Report {0}" , [me.bolden_match_part(__(item), keywords)]), | |||
value: __("Report {0}" , [__(item)]), | |||
index: level, | |||
route: route | |||
@@ -216,8 +213,7 @@ frappe.search.utils = { | |||
var page = me.pages[item]; | |||
out.push({ | |||
type: "Page", | |||
prefix: "Open ", | |||
label: me.bolden_match_part(__(item), keywords), | |||
label: __("Open {0}", [me.bolden_match_part(__(item), keywords)]), | |||
value: __("Open {0}", [__(item)]), | |||
match: item, | |||
index: level, | |||
@@ -229,8 +225,6 @@ frappe.search.utils = { | |||
if(__('calendar').indexOf(keywords.toLowerCase()) === 0) { | |||
out.push({ | |||
type: "Calendar", | |||
prefix: "Open ", | |||
label: __('Calendar'), | |||
value: __("Open {0}", [__(target)]), | |||
index: me.fuzzy_search(keywords, 'Calendar'), | |||
match: target, | |||
@@ -240,8 +234,6 @@ frappe.search.utils = { | |||
if(__('email inbox').indexOf(keywords.toLowerCase()) === 0) { | |||
out.push({ | |||
type: "Inbox", | |||
prefix: "Open ", | |||
label: __('Email Inbox'), | |||
value: __("Open {0}", [__('Email Inbox')]), | |||
index: me.fuzzy_search(keywords, 'email inbox'), | |||
match: target, | |||
@@ -261,8 +253,7 @@ frappe.search.utils = { | |||
if(module._doctype) return; | |||
ret = { | |||
type: "Module", | |||
prefix: "Open ", | |||
label: me.bolden_match_part(__(item), keywords), | |||
label: __("Open {0}", [me.bolden_match_part(__(item), keywords)]), | |||
value: __("Open {0}", [__(item)]), | |||
index: level, | |||
} | |||
@@ -575,6 +566,5 @@ frappe.search.utils = { | |||
return rendered; | |||
} | |||
} | |||
} | |||
}, | |||
} |
@@ -77,7 +77,8 @@ frappe.views.ReportView = frappe.ui.BaseList.extend({ | |||
this.add_totals_row = 0; | |||
this.page = this.parent.page; | |||
this._body = $('<div>').appendTo(this.page.main); | |||
this.page_title = __('Report')+ ': ' + __(this.docname ? (this.doctype + ' - ' + this.docname) : this.doctype); | |||
this.page_title = __('Report')+ ': ' + (this.docname ? | |||
__(this.doctype) + ' - ' + __(this.docname) : __(this.doctype)); | |||
this.page.set_title(this.page_title); | |||
this.init_user_settings(); | |||
this.make({ | |||
@@ -723,7 +724,7 @@ frappe.views.ReportView = frappe.ui.BaseList.extend({ | |||
} | |||
open_url_post(frappe.request.url, args); | |||
}, __("Export Report: " + me.doctype), __("Download")); | |||
}, __("Export Report: {0}",[__(me.doctype)]), __("Download")); | |||
}, true); | |||
}, | |||
@@ -95,6 +95,11 @@ frappe.render_grid = function(opts) { | |||
} | |||
} | |||
// show landscape view if columns more than 10 | |||
if(opts.columns.length > 10) { | |||
opts.landscape = true; | |||
} | |||
// render content | |||
if(!opts.content) { | |||
opts.content = frappe.render_template("print_grid", opts); | |||
@@ -106,6 +111,11 @@ frappe.render_grid = function(opts) { | |||
var html = frappe.render_template("print_template", opts); | |||
var w = window.open(); | |||
if(!w) { | |||
frappe.msgprint(__("Please enable pop-ups in your browser")) | |||
} | |||
w.document.write(html); | |||
w.document.close(); | |||
} |
@@ -12,6 +12,11 @@ | |||
margin: auto; | |||
} | |||
.print-format.landscape { | |||
max-width: 11.69in; | |||
padding: 0.2in; | |||
} | |||
.page-break { | |||
padding: 30px 0px; | |||
border-bottom: 1px dashed #888; | |||
@@ -8,10 +8,9 @@ from werkzeug.test import Client | |||
import os, re, urllib, sys, json, hashlib, requests, traceback | |||
from markdown2 import markdown as _markdown | |||
from .html_utils import sanitize_html | |||
import frappe | |||
from frappe.utils.identicon import Identicon | |||
from email.utils import parseaddr, formataddr | |||
# utility functions like cint, int, flt, etc. | |||
from frappe.utils.data import * | |||
@@ -62,9 +61,8 @@ def get_formatted_email(user): | |||
def extract_email_id(email): | |||
"""fetch only the email part of the Email Address""" | |||
from email.utils import parseaddr | |||
fullname, email_id = parseaddr(email) | |||
if isinstance(email_id, basestring) and not isinstance(email_id, unicode): | |||
full_name, email_id = parse_addr(email) | |||
if email_id and isinstance(email_id, basestring) and not isinstance(email_id, unicode): | |||
email_id = email_id.decode("utf-8", "ignore") | |||
return email_id | |||
@@ -88,13 +86,12 @@ def validate_email_add(email_str, throw=False): | |||
else: | |||
e = extract_email_id(e) | |||
match = re.match("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", e.lower()) | |||
match = re.match("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", e.lower()) if e else None | |||
if not match: | |||
_valid = False | |||
else: | |||
matched = match.group(0) | |||
if match: | |||
match = matched==e.lower() | |||
@@ -450,18 +447,47 @@ def markdown(text, sanitize=True, linkify=True): | |||
return html | |||
def sanitize_email(emails): | |||
from email.utils import parseaddr, formataddr | |||
sanitized = [] | |||
for e in split_emails(emails): | |||
if not validate_email_add(e): | |||
continue | |||
fullname, email_id = parseaddr(e) | |||
sanitized.append(formataddr((fullname, email_id))) | |||
full_name, email_id = parse_addr(e) | |||
sanitized.append(formataddr((full_name, email_id))) | |||
return ", ".join(sanitized) | |||
def parse_addr(email_string): | |||
""" | |||
Return email_id and user_name based on email string | |||
Raise error if email string is not valid | |||
""" | |||
name, email = parseaddr(email_string) | |||
if check_format(email): | |||
return (name, email) | |||
else: | |||
email_regex = re.compile(r"([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)") | |||
email_list = re.findall(email_regex, email_string) | |||
if len(email_list) > 0 and check_format(email_list[0]): | |||
#take only first email address | |||
return (name, email_list[0]) | |||
return (None, email) | |||
def check_format(email_id): | |||
""" | |||
Check if email_id is valid. valid email:text@example.com | |||
String check ensures that email_id contains both '.' and | |||
'@' and index of '@' is less than '.' | |||
""" | |||
is_valid = False | |||
try: | |||
pos = email_id.rindex("@") | |||
is_valid = pos > 0 and (email_id.rindex(".") > pos) and (len(email_id) - pos > 4) | |||
except Exception, e: | |||
#print(e) | |||
pass | |||
return is_valid | |||
def get_installed_apps_info(): | |||
out = [] | |||
for app in frappe.get_installed_apps(): | |||
@@ -102,10 +102,11 @@ def get_allowed_functions_for_jenv(): | |||
"user": user, | |||
"get_fullname": frappe.utils.get_fullname, | |||
"get_gravatar": frappe.utils.get_gravatar_url, | |||
"full_name": getattr(frappe.local, "session", None) and frappe.local.session.data.full_name or "Guest", | |||
"full_name": frappe.local.session.data.full_name if getattr(frappe.local, "session", None) else "Guest", | |||
"render_template": frappe.render_template, | |||
'session': { | |||
'user': user | |||
'user': user, | |||
'csrf_token': frappe.local.session.data.csrf_token if getattr(frappe.local, "session", None) else '' | |||
}, | |||
}, | |||
"autodoc": { | |||
@@ -16,7 +16,12 @@ | |||
</div> | |||
<input type="text" id="login_email" | |||
class="form-control" placeholder="{{ _('Email address') }}" required autofocus> | |||
class="form-control" placeholder="{{ | |||
_('Email address or Mobile number') | |||
if frappe.utils.cint(frappe.db.get_value('System Settings', 'System Settings', 'allow_login_using_mobile_number')) | |||
else _('Email address') }}" | |||
required autofocus> | |||
<input type="password" id="login_password" | |||
class="form-control" placeholder="{{ _('Password') }}" required> | |||
@@ -0,0 +1,30 @@ | |||
{ | |||
"name": "frappe", | |||
"scripts": { | |||
"test": "echo \"Error: no test specified\" && exit 1" | |||
}, | |||
"repository": { | |||
"type": "git", | |||
"url": "git+https://github.com/frappe/frappe.git" | |||
}, | |||
"author": "Frappé Technologies Pvt. Ltd.", | |||
"license": "MIT", | |||
"bugs": { | |||
"url": "https://github.com/frappe/frappe/issues" | |||
}, | |||
"homepage": "https://frappe.io", | |||
"dependencies": { | |||
"babel-core": "^6.24.1", | |||
"babel-preset-babili": "0.0.12", | |||
"babel-preset-es2015": "^6.24.1", | |||
"babel-preset-es2016": "^6.24.1", | |||
"babel-preset-es2017": "^6.24.1", | |||
"chokidar": "^1.7.0", | |||
"cookie": "^0.3.1", | |||
"express": "^4.15.3", | |||
"less": "^2.7.2", | |||
"redis": "^2.7.1", | |||
"socket.io": "^2.0.1", | |||
"superagent": "^3.5.2" | |||
} | |||
} |
@@ -6,7 +6,7 @@ httplib2 | |||
jinja2 | |||
markdown2 | |||
markupsafe | |||
mysql-python==1.2.5 | |||
mysqlclient==1.3.8 | |||
python-geoip | |||
python-geoip-geolite2 | |||
python-dateutil | |||