瀏覽代碼

Merge branch 'hotfix'

version-14
Nabin Hait 8 年之前
父節點
當前提交
fd06f8ea9c
共有 28 個檔案被更改,包括 291 行新增80 行删除
  1. +1
    -0
      .travis.yml
  2. +1
    -1
      frappe/__init__.py
  3. +2
    -1
      frappe/app.py
  4. +3
    -0
      frappe/auth.py
  5. +1
    -1
      frappe/build.js
  6. +2
    -2
      frappe/build.py
  7. +5
    -9
      frappe/core/doctype/communication/communication.py
  8. +5
    -5
      frappe/core/doctype/communication/email.py
  9. +21
    -2
      frappe/core/doctype/communication/test_communication.py
  10. +32
    -1
      frappe/core/doctype/system_settings/system_settings.json
  11. +94
    -1
      frappe/core/doctype/user/user.json
  12. +1
    -0
      frappe/custom/doctype/customize_form/customize_form.js
  13. +2
    -2
      frappe/email/doctype/email_group/email_group.py
  14. +3
    -4
      frappe/email/doctype/newsletter/newsletter.py
  15. +2
    -2
      frappe/email/email_body.py
  16. +2
    -2
      frappe/email/receive.py
  17. +0
    -3
      frappe/frappeclient.py
  18. +5
    -1
      frappe/public/html/print_template.html
  19. +5
    -7
      frappe/public/js/frappe/ui/toolbar/awesome_bar.js
  20. +9
    -19
      frappe/public/js/frappe/ui/toolbar/search_utils.js
  21. +3
    -2
      frappe/public/js/frappe/views/reports/reportview.js
  22. +10
    -0
      frappe/public/js/lib/microtemplate.js
  23. +5
    -0
      frappe/templates/styles/standard.css
  24. +37
    -11
      frappe/utils/__init__.py
  25. +3
    -2
      frappe/utils/jinja.py
  26. +6
    -1
      frappe/www/login.html
  27. +30
    -0
      package.json
  28. +1
    -1
      requirements.txt

+ 1
- 0
.travis.yml 查看文件

@@ -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 &


+ 1
- 1
frappe/__init__.py 查看文件

@@ -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()


+ 2
- 1
frappe/app.py 查看文件

@@ -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



+ 3
- 0
frappe/auth.py 查看文件

@@ -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)



+ 1
- 1
frappe/build.js 查看文件

@@ -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;
}


+ 2
- 2
frappe/build.py 查看文件

@@ -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()


+ 5
- 9
frappe/core/doctype/communication/communication.py 查看文件

@@ -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
- 5
frappe/core/doctype/communication/email.py 查看文件

@@ -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



+ 21
- 2
frappe/core/doctype/communication/test_communication.py 查看文件

@@ -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])


+ 32
- 1
frappe/core/doctype/system_settings/system_settings.json 查看文件

@@ -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",


+ 94
- 1
frappe/core/doctype/user/user.json 查看文件

@@ -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",


+ 1
- 0
frappe/custom/doctype/customize_form/customize_form.js 查看文件

@@ -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");
}
}
});


+ 2
- 2
frappe/email/doctype/email_group/email_group.py 查看文件

@@ -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",


+ 3
- 4
frappe/email/doctype/newsletter/newsletter.py 查看文件

@@ -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"),


+ 2
- 2
frappe/email/email_body.py 查看文件

@@ -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):


+ 2
- 2
frappe/email/receive.py 查看文件

@@ -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


+ 0
- 3
frappe/frappeclient.py 查看文件

@@ -5,9 +5,6 @@ import frappe

'''
FrappeClient is a library that helps you connect with other frappe systems



'''

class AuthError(Exception):


+ 5
- 1
frappe/public/html/print_template.html 查看文件

@@ -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>


+ 5
- 7
frappe/public/js/frappe/ui/toolbar/awesome_bar.js 查看文件

@@ -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",


+ 9
- 19
frappe/public/js/frappe/ui/toolbar/search_utils.js 查看文件

@@ -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;
}

}

}
},
}

+ 3
- 2
frappe/public/js/frappe/views/reports/reportview.js 查看文件

@@ -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);
},


+ 10
- 0
frappe/public/js/lib/microtemplate.js 查看文件

@@ -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();
}

+ 5
- 0
frappe/templates/styles/standard.css 查看文件

@@ -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;


+ 37
- 11
frappe/utils/__init__.py 查看文件

@@ -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():


+ 3
- 2
frappe/utils/jinja.py 查看文件

@@ -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": {


+ 6
- 1
frappe/www/login.html 查看文件

@@ -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>


+ 30
- 0
package.json 查看文件

@@ -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"
}
}

+ 1
- 1
requirements.txt 查看文件

@@ -6,7 +6,7 @@ httplib2
jinja2
markdown2
markupsafe
mysql-python==1.2.5
mysqlclient==1.3.8
python-geoip
python-geoip-geolite2
python-dateutil


Loading…
取消
儲存