瀏覽代碼

Merge branch 'master' of https://github.com/frappe/frappe into text-editor-fix

version-14
Gaurav Naik 8 年之前
父節點
當前提交
e4acc66163
共有 23 個文件被更改,包括 494 次插入53 次删除
  1. +1
    -1
      frappe/__init__.py
  2. +10
    -8
      frappe/auth.py
  3. +0
    -0
      frappe/core/doctype/authentication_log/__init__.py
  4. +8
    -0
      frappe/core/doctype/authentication_log/authentication_log.js
  5. +341
    -0
      frappe/core/doctype/authentication_log/authentication_log.json
  6. +27
    -0
      frappe/core/doctype/authentication_log/authentication_log.py
  7. +8
    -0
      frappe/core/doctype/authentication_log/authentication_log_list.js
  8. +48
    -0
      frappe/core/doctype/authentication_log/test_authentication_log.py
  9. +7
    -11
      frappe/core/doctype/communication/feed.py
  10. +1
    -4
      frappe/core/page/permission_manager/permission_manager.py
  11. +8
    -0
      frappe/desk/page/activity/activity.js
  12. +2
    -1
      frappe/email/doctype/email_alert/email_alert.py
  13. +0
    -1
      frappe/email/email_body.py
  14. +4
    -4
      frappe/email/queue.py
  15. +2
    -1
      frappe/hooks.py
  16. +4
    -4
      frappe/print/doctype/print_format/print_format.json
  17. +7
    -6
      frappe/public/js/frappe/form/linked_with.js
  18. +1
    -1
      frappe/public/js/frappe/views/breadcrumbs.js
  19. +1
    -1
      frappe/public/js/frappe/views/reports/reportview.js
  20. +10
    -4
      frappe/tests/test_email.py
  21. +1
    -1
      frappe/utils/__init__.py
  22. +1
    -2
      frappe/website/js/website.js
  23. +2
    -3
      frappe/www/website_script.py

+ 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__ = '7.2.12'
__version__ = '7.2.15'
__title__ = "Frappe Framework"

local = Local()


+ 10
- 8
frappe/auth.py 查看文件

@@ -15,6 +15,7 @@ from frappe.sessions import Session, clear_sessions, delete_session
from frappe.modules.patch_handler import check_session_stopped
from frappe.translate import get_lang_code
from frappe.utils.password import check_password
from frappe.core.doctype.authentication_log.authentication_log import add_authentication_log

from urllib import quote

@@ -59,10 +60,6 @@ class HTTPRequest:
# check status
check_session_stopped()

# run login triggers
if frappe.form_dict.get('cmd')=='login':
frappe.local.login_manager.run_trigger('on_session_creation')

def validate_csrf_token(self):
if frappe.local.request and frappe.local.request.method=="POST":
if not frappe.local.session.data.csrf_token \
@@ -103,6 +100,9 @@ class LoginManager:
if frappe.local.form_dict.get('cmd')=='login' or frappe.local.request.path=="/api/method/login":
self.login()
self.resume = False

# run login triggers
self.run_trigger('on_session_creation')
else:
try:
self.resume = True
@@ -183,7 +183,7 @@ class LoginManager:
if not (user and pwd):
user, pwd = frappe.form_dict.get('usr'), frappe.form_dict.get('pwd')
if not (user and pwd):
self.fail('Incomplete login details')
self.fail('Incomplete login details', user=user)

self.check_if_enabled(user)
self.user = self.check_password(user, pwd)
@@ -192,7 +192,7 @@ class LoginManager:
"""raise exception if user not enabled"""
if user=='Administrator': return
if not cint(frappe.db.get_value('User', user, 'enabled')):
self.fail('User disabled or missing')
self.fail('User disabled or missing', user=user)

def check_password(self, user, pwd):
"""check password"""
@@ -200,10 +200,12 @@ class LoginManager:
# returns user in correct case
return check_password(user, pwd)
except frappe.AuthenticationError:
self.fail('Incorrect password')
self.fail('Incorrect password', user=user)

def fail(self, message):
def fail(self, message, user="NA"):
frappe.local.response['message'] = message
add_authentication_log(message, user, status="Failed")
frappe.db.commit()
raise frappe.AuthenticationError

def run_trigger(self, event='on_login'):


+ 0
- 0
frappe/core/doctype/authentication_log/__init__.py 查看文件


+ 8
- 0
frappe/core/doctype/authentication_log/authentication_log.js 查看文件

@@ -0,0 +1,8 @@
// Copyright (c) 2016, Frappe Technologies and contributors
// For license information, please see license.txt

frappe.ui.form.on('Authentication Log', {
refresh: function(frm) {

}
});

+ 341
- 0
frappe/core/doctype/authentication_log/authentication_log.json 查看文件

@@ -0,0 +1,341 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-01-23 16:56:25.875531",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "user_details",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "User Details",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "user",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "User",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "full_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Full Name",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_8",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "date",
"fieldtype": "Datetime",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Date",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "subject",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Subject",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "operation",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Operation",
"length": 0,
"no_copy": 0,
"options": "\nLogin\nLogout",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Status",
"length": 0,
"no_copy": 0,
"options": "\nSuccess\nFailed",
"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
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 1,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-01-24 14:51:13.726113",
"modified_by": "Administrator",
"module": "Core",
"name": "Authentication Log",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 0
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "subject",
"track_seen": 0
}

+ 27
- 0
frappe/core/doctype/authentication_log/authentication_log.py 查看文件

@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe
from frappe.utils import get_fullname, now
from frappe.model.document import Document

class AuthenticationLog(Document):
def before_insert(self):
self.full_name = get_fullname(self.user)
self.date = now()

def add_authentication_log(subject, user, operation="Login", status="Success"):
frappe.get_doc({
"doctype": "Authentication Log",
"user": user,
"status": status,
"subject": subject,
"operation": operation,
}).insert(ignore_permissions=True)

def clear_authentication_logs():
"""clear 100 day old authentication logs"""
frappe.db.sql("""delete from `tabAuthentication Log` where
creation<DATE_SUB(NOW(), INTERVAL 100 DAY)""")

+ 8
- 0
frappe/core/doctype/authentication_log/authentication_log_list.js 查看文件

@@ -0,0 +1,8 @@
frappe.listview_settings['Authentication Log'] = {
get_indicator: function(doc) {
if(doc.operation == "Login" && doc.status == "Success")
return [__(doc.status), "green"];
else if(doc.operation == "Login" && doc.status == "Failed")
return [__(doc.status), "red"];
}
};

+ 48
- 0
frappe/core/doctype/authentication_log/test_authentication_log.py 查看文件

@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals

import frappe
import unittest

# test_records = frappe.get_test_records('Authentication Log')

class TestAuthenticationLog(unittest.TestCase):
def test_authentication_log(self):
from frappe.auth import LoginManager, CookieManager

# test user login log
frappe.local.form_dict = { 'cmd': 'login' }

frappe.form_dict = {
'sid': 'Guest',
'pwd': 'admin',
'usr': 'Administrator'
}

frappe.local.cookie_manager = CookieManager()
frappe.local.login_manager = LoginManager()

auth_log = self.get_auth_log()
self.assertEquals(auth_log.status, 'Success')

# test user logout log
frappe.local.login_manager.logout()
auth_log = self.get_auth_log(operation='Logout')
self.assertEquals(auth_log.status, 'Success')

# test invalid login
frappe.form_dict.update({ 'pwd': 'password' })
self.assertRaises(frappe.AuthenticationError, LoginManager)
auth_log = self.get_auth_log()
self.assertEquals(auth_log.status, 'Failed')

def get_auth_log(self, operation='Login'):
names = frappe.db.sql_list("""select name from `tabAuthentication Log`
where user='Administrator' and operation='{operation}' order by
creation desc""".format(operation=operation))

name = names[0]
auth_log = frappe.get_doc('Authentication Log', name)
return auth_log

+ 7
- 11
frappe/core/doctype/communication/feed.py 查看文件

@@ -9,6 +9,7 @@ from frappe.model.document import Document
from frappe.utils import get_fullname
from frappe import _
from frappe.core.doctype.communication.comment import add_info_comment
from frappe.core.doctype.authentication_log.authentication_log import add_authentication_log

def update_feed(doc, method=None):
"adds a new communication with comment_type='Updated'"
@@ -53,19 +54,14 @@ def update_feed(doc, method=None):
}).insert(ignore_permissions=True)

def login_feed(login_manager):
add_info_comment(**{
"subject": _("{0} logged in").format(get_fullname(login_manager.user)),
"full_name": get_fullname(login_manager.user)
})
if login_manager.user != "Guest":
subject = _("{0} logged in").format(get_fullname(login_manager.user))
add_authentication_log(subject, login_manager.user)

def logout_feed(user, reason):
if not user:
return

add_info_comment(**{
"subject": _("{0} logged out: <b>{1}</b>").format(get_fullname(user), reason),
"full_name": get_fullname(user),
})
if user and user != "Guest":
subject = _("{0} logged out: {1}").format(get_fullname(user), frappe.bold(reason))
add_authentication_log(subject, user, operation="Logout")

def get_feed_match_conditions(user=None, force=True):
if not user: user = frappe.session.user


+ 1
- 4
frappe/core/page/permission_manager/permission_manager.py 查看文件

@@ -15,10 +15,7 @@ def get_roles_and_doctypes():
send_translations(frappe.get_lang_dict("doctype", "DocPerm"))
return {
"doctypes": [d[0] for d in frappe.db.sql("""select name from `tabDocType` dt where
istable=0 and
name not in ('DocType') and
exists(select * from `tabDocField` where parent=dt.name) and
exists(select * from `tabDocPerm` dp,`tabRole` role where dp.role = role.name and dp.parent=dt.name and not role.disabled)""")],
istable=0 and name not in ('DocType')""")],
"roles": [d[0] for d in frappe.db.sql("""select name from tabRole where
name != 'Administrator' and disabled=0""")]
}


+ 8
- 0
frappe/desk/page/activity/activity.js 查看文件

@@ -72,6 +72,14 @@ frappe.pages['activity'].on_page_load = function(wrapper) {
}, 'fa fa-th')
}

this.page.add_menu_item(__('Authentication Log'), function() {
frappe.route_options = {
"user": user
}

frappe.set_route('Report', "Authentication Log");
}, 'fa fa-th')

this.page.add_menu_item(__('Show Likes'), function() {
frappe.route_options = {
show_likes: true


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

@@ -91,8 +91,8 @@ def get_context(context):
'''Build recipients and send email alert'''
context = get_context(doc)

recipients = []
for recipient in self.recipients:
recipients = []
if recipient.condition:
if not eval(recipient.condition, context):
continue
@@ -108,6 +108,7 @@ def get_context(context):
if not recipients:
return

recipients = list(set(recipients))
subject = self.subject

context = {"doc": doc, "alert": self, "comments": None}


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

@@ -188,7 +188,6 @@ class EMail:
self.msg_root["Message-Id"] = '<' + message_id + '>'
else:
self.msg_root["Message-Id"] = get_message_id()
self.msg_root["References"] = '<notification>'

def set_in_reply_to(self, in_reply_to):
"""Used to send the Message-Id of a received email back as In-Reply-To"""


+ 4
- 4
frappe/email/queue.py 查看文件

@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
import HTMLParser
import smtplib
import smtplib, quopri
from frappe import msgprint, throw, _
from frappe.email.smtp import SMTPServer, get_outgoing_email_account
from frappe.email.email_body import get_email, get_formatted_html
@@ -174,7 +174,7 @@ def get_unsubscribe_message(unsubscribe_message, expose_recipients):
text = "\n<!--cc message-->"
else:
text = ""
text += "\n\n{unsubscribe_message}: <!--unsubscribe url-->".format(unsubscribe_message=unsubscribe_message)
text += "\n\n{unsubscribe_message}: <!--unsubscribe url-->\n".format(unsubscribe_message=unsubscribe_message)

return frappe._dict({
"html": html,
@@ -359,7 +359,7 @@ def prepare_message(email, recipient, recipients_list):
if email.reference_doctype: # is missing the check for unsubscribe message but will not add as there will be no unsubscribe url
unsubscribe_url = get_unsubcribed_url(email.reference_doctype, email.reference_name, recipient,
email.unsubscribe_method, email.unsubscribe_params)
message = message.replace("<!--unsubscribe url-->", unsubscribe_url)
message = message.replace("<!--unsubscribe url-->", quopri.encodestring(unsubscribe_url))

if email.expose_recipients == "header":
pass
@@ -375,7 +375,7 @@ def prepare_message(email, recipient, recipients_list):
email_sent_message = _("This email was sent to {0} and copied to {1}").format(email_sent_to,email_sent_cc)
else:
email_sent_message = _("This email was sent to {0}").format(email_sent_to)
message = message.replace("<!--cc message-->", email_sent_message)
message = message.replace("<!--cc message-->", quopri.encodestring(email_sent_message))

message = message.replace("<!--recipient-->", recipient)
return message


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

@@ -137,7 +137,8 @@ scheduler_events = {
"frappe.utils.scheduler.restrict_scheduler_events_if_dormant",
"frappe.limits.update_space_usage",
"frappe.email.doctype.auto_email_report.auto_email_report.send_daily",
"frappe.desk.page.backups.backups.delete_downloadable_backups"
"frappe.desk.page.backups.backups.delete_downloadable_backups",
"frappe.core.doctype.authentication_log.authentication_log.clear_authentication_logs"
],
"monthly": [
"frappe.email.doctype.auto_email_report.auto_email_report.send_monthly"


+ 4
- 4
frappe/print/doctype/print_format/print_format.json 查看文件

@@ -304,7 +304,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"default": "0",
"fieldname": "align_labels_left",
"fieldtype": "Check",
"hidden": 0,
@@ -333,7 +333,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"default": "0",
"fieldname": "show_section_headings",
"fieldtype": "Check",
"hidden": 0,
@@ -362,7 +362,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"default": "0",
"fieldname": "line_breaks",
"fieldtype": "Check",
"hidden": 0,
@@ -653,7 +653,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-11-07 05:23:46.983533",
"modified": "2017-01-24 15:35:56.250107",
"modified_by": "Administrator",
"module": "Print",
"name": "Print Format",


+ 7
- 6
frappe/public/js/frappe/form/linked_with.js 查看文件

@@ -52,12 +52,13 @@ frappe.ui.form.LinkedWith = Class.extend({
var me = this;
var already_loaded = Object.keys(locals.DocType);
var doctypes_to_load = [];
$.each(Object.keys(me.frm.__linked_doctypes), function(i, v) {
if (already_loaded.indexOf(v)===-1) {
doctypes_to_load.push(v);
}
});

if (me.frm.__linked_doctypes) {
$.each(Object.keys(me.frm.__linked_doctypes), function(i, v) {
if (already_loaded.indexOf(v)===-1) {
doctypes_to_load.push(v);
}
});
}
// load all doctypes sequentially using with_doctype
return $.when.apply($, $.map(doctypes_to_load, function(dt) {
return frappe.model.with_doctype(dt, function() {


+ 1
- 1
frappe/public/js/frappe/views/breadcrumbs.js 查看文件

@@ -51,7 +51,7 @@ frappe.breadcrumbs = {
label = module_info ? module_info.label : breadcrumbs.module;


if(module_info) {
if(module_info && !module_info.blocked) {
$(repl('<li><a href="#modules/%(module)s">%(label)s</a></li>',
{ module: breadcrumbs.module, label: __(label) }))
.appendTo($breadcrumbs);


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

@@ -212,7 +212,7 @@ frappe.views.ReportView = frappe.ui.Listing.extend({

set_route_filters: function(first_load) {
var me = this;
if(frappe.route_options) {
if(frappe.route_options && !this.list_settings.filters) {
this.set_filters_from_route_options();
return true;
} else if(this.list_settings


+ 10
- 4
frappe/tests/test_email.py 查看文件

@@ -3,7 +3,7 @@

from __future__ import unicode_literals

import unittest, frappe, re
import unittest, frappe, re, email

from frappe.test_runner import make_test_records
make_test_records("User")
@@ -87,6 +87,7 @@ class TestEmail(unittest.TestCase):
self.assertTrue('This email was sent to test@example.com and copied to test1@example.com' in frappe.flags.sent_mail)

def test_expose(self):
from frappe.utils.verified_command import verify_request
frappe.sendmail(recipients=['test@example.com'],
cc=['test1@example.com'],
sender="admin@example.com",
@@ -103,9 +104,14 @@ class TestEmail(unittest.TestCase):
where status='Sent'""", as_dict=1)[0].message
self.assertTrue('<!--recipient-->' in message)

frappe.local.flags.signed_query_string = re.search('(?<=/api/method/frappe.email.queue.unsubscribe\?).*(?=\n)', frappe.flags.sent_mail).group(0)
from frappe.utils.verified_command import verify_request
self.assertTrue(verify_request())
email_obj = email.message_from_string(frappe.flags.sent_mail)
for part in email_obj.walk():
content = part.get_payload(decode=True)

if content:
frappe.local.flags.signed_query_string = re.search('(?<=/api/method/frappe.email.queue.unsubscribe\?).*(?=\n)', content).group(0)
self.assertTrue(verify_request())
break

def test_expired(self):
self.test_email_queue()


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

@@ -134,7 +134,7 @@ def has_gravatar(email):
# since querying gravatar for every item will be slow
return ''

hexdigest = md5.md5(frappe.as_unicode(email)).hexdigest()
hexdigest = md5.md5(frappe.as_unicode(email).encode('utf-8')).hexdigest()

gravatar_url = "https://secure.gravatar.com/avatar/{hash}?d=404&s=200".format(hash=hexdigest)
try:


+ 1
- 2
frappe/website/js/website.js 查看文件

@@ -334,8 +334,7 @@ $.extend(frappe, {
// Utility functions

function valid_email(id) {
return (id.toLowerCase().search("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")==-1) ? 0 : 1;

return /"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"/.test(id.toLowerCase());
}

var validate_email = valid_email;


+ 2
- 3
frappe/www/website_script.py 查看文件

@@ -18,6 +18,5 @@ def get_context(context):
context.javascript += "\n" + js

if not frappe.conf.developer_mode:
context["google_analytics_id"] = frappe.conf.get("google_analytics_id") \
or frappe.db.get_single_value("Website Settings", "google_analytics_id")

context["google_analytics_id"] = (frappe.db.get_single_value("Website Settings", "google_analytics_id")
or frappe.conf.get("google_analytics_id"))

Loading…
取消
儲存