Merge branch 'hotfix'

This commit is contained in:
Nabin Hait 2017-05-23 11:36:54 +05:30
commit fd06f8ea9c
28 changed files with 291 additions and 80 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,9 +5,6 @@ import frappe
'''
FrappeClient is a library that helps you connect with other frappe systems
'''
class AuthError(Exception):

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
package.json Normal file
View file

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

View file

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