@@ -0,0 +1,40 @@ | |||||
{ | |||||
"base_template": "lib/website/templates/base.html", | |||||
"framework_version": "3.9.0", | |||||
"modules": { | |||||
"Calendar": { | |||||
"color": "#2980b9", | |||||
"icon": "icon-calendar", | |||||
"label": "Calendar", | |||||
"link": "Calendar/Event", | |||||
"type": "view" | |||||
}, | |||||
"Finder": { | |||||
"color": "#14C7DE", | |||||
"icon": "icon-folder-open", | |||||
"label": "Finder", | |||||
"link": "finder", | |||||
"type": "page" | |||||
}, | |||||
"Messages": { | |||||
"color": "#9b59b6", | |||||
"icon": "icon-comments", | |||||
"label": "Messages", | |||||
"link": "messages", | |||||
"type": "page" | |||||
}, | |||||
"To Do": { | |||||
"color": "#f1c40f", | |||||
"icon": "icon-check", | |||||
"label": "To Do", | |||||
"link": "todo", | |||||
"type": "page" | |||||
}, | |||||
"Website": { | |||||
"color": "#16a085", | |||||
"icon": "icon-globe", | |||||
"link": "website-home", | |||||
"type": "module" | |||||
} | |||||
} | |||||
} |
@@ -18,4 +18,5 @@ slugify | |||||
termcolor | termcolor | ||||
werkzeug | werkzeug | ||||
semantic_version | semantic_version | ||||
lxml | |||||
inlinestyler | inlinestyler |
@@ -220,6 +220,13 @@ def set_user(username): | |||||
def get_request_header(key, default=None): | def get_request_header(key, default=None): | ||||
return request.headers.get(key, default) | return request.headers.get(key, default) | ||||
def sendmail(recipients=[], sender="", subject="No Subject", message="No Message", as_markdown=False): | |||||
import webnotes.utils.email_lib | |||||
if as_markdown: | |||||
webnotes.utils.email_lib.sendmail_md(recipients, sender=sender, subject=subject, msg=message) | |||||
else: | |||||
webnotes.utils.email_lib.sendmail(recipients, sender=sender, subject=subject, msg=message) | |||||
logger = None | logger = None | ||||
whitelisted = [] | whitelisted = [] | ||||
guest_methods = [] | guest_methods = [] | ||||
@@ -3,6 +3,13 @@ | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import webnotes | import webnotes | ||||
import json | |||||
import urllib | |||||
from email.utils import formataddr | |||||
from webnotes.webutils import is_signup_enabled | |||||
from webnotes.utils import get_url, cstr | |||||
from webnotes.utils.email_lib.email_body import get_email | |||||
from webnotes.utils.email_lib.smtp import send | |||||
class DocType(): | class DocType(): | ||||
def __init__(self, doc, doclist=[]): | def __init__(self, doc, doclist=[]): | ||||
@@ -36,13 +43,11 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = | |||||
# since we are using fullname and email, | # since we are using fullname and email, | ||||
# if the fullname has any incompatible characters,formataddr can deal with it | # if the fullname has any incompatible characters,formataddr can deal with it | ||||
try: | try: | ||||
import json | |||||
sender = json.loads(sender) | sender = json.loads(sender) | ||||
except ValueError: | except ValueError: | ||||
pass | pass | ||||
if isinstance(sender, (tuple, list)) and len(sender)==2: | if isinstance(sender, (tuple, list)) and len(sender)==2: | ||||
from email.utils import formataddr | |||||
sender = formataddr(sender) | sender = formataddr(sender) | ||||
comm = webnotes.new_bean('Communication') | comm = webnotes.new_bean('Communication') | ||||
@@ -76,7 +81,6 @@ def get_customer_supplier(args=None): | |||||
""" | """ | ||||
Get Customer/Supplier, given a contact, if a unique match exists | Get Customer/Supplier, given a contact, if a unique match exists | ||||
""" | """ | ||||
import webnotes | |||||
if not args: args = webnotes.local.form_dict | if not args: args = webnotes.local.form_dict | ||||
if not args.get('contact'): | if not args.get('contact'): | ||||
raise Exception, "Please specify a contact to fetch Customer/Supplier" | raise Exception, "Please specify a contact to fetch Customer/Supplier" | ||||
@@ -92,7 +96,6 @@ def get_customer_supplier(args=None): | |||||
return {} | return {} | ||||
def send_comm_email(d, name, sent_via=None, print_html=None, attachments='[]', send_me_a_copy=False): | def send_comm_email(d, name, sent_via=None, print_html=None, attachments='[]', send_me_a_copy=False): | ||||
from json import loads | |||||
footer = None | footer = None | ||||
if sent_via: | if sent_via: | ||||
@@ -105,7 +108,6 @@ def send_comm_email(d, name, sent_via=None, print_html=None, attachments='[]', s | |||||
footer = set_portal_link(sent_via, d) | footer = set_portal_link(sent_via, d) | ||||
from webnotes.utils.email_lib.smtp import get_email | |||||
mail = get_email(d.recipients, sender=d.sender, subject=d.subject, | mail = get_email(d.recipients, sender=d.sender, subject=d.subject, | ||||
msg=d.content, footer=footer) | msg=d.content, footer=footer) | ||||
@@ -115,20 +117,17 @@ def send_comm_email(d, name, sent_via=None, print_html=None, attachments='[]', s | |||||
if print_html: | if print_html: | ||||
mail.add_attachment(name.replace(' ','').replace('/','-') + '.html', print_html) | mail.add_attachment(name.replace(' ','').replace('/','-') + '.html', print_html) | ||||
for a in loads(attachments): | |||||
for a in json.loads(attachments): | |||||
try: | try: | ||||
mail.attach_file(a) | mail.attach_file(a) | ||||
except IOError, e: | except IOError, e: | ||||
webnotes.msgprint("""Unable to find attachment %s. Please resend without attaching this file.""" % a, | webnotes.msgprint("""Unable to find attachment %s. Please resend without attaching this file.""" % a, | ||||
raise_exception=True) | raise_exception=True) | ||||
mail.send() | |||||
send(mail) | |||||
def set_portal_link(sent_via, comm): | def set_portal_link(sent_via, comm): | ||||
"""set portal link in footer""" | """set portal link in footer""" | ||||
from webnotes.webutils import is_signup_enabled | |||||
from webnotes.utils import get_url, cstr | |||||
import urllib | |||||
footer = None | footer = None | ||||
@@ -143,29 +142,3 @@ def set_portal_link(sent_via, comm): | |||||
<a href="%s" target="_blank">View this on our website</a>""" % url | <a href="%s" target="_blank">View this on our website</a>""" % url | ||||
return footer | return footer | ||||
def get_user(doctype, txt, searchfield, start, page_len, filters): | |||||
from erpnext.controllers.queries import get_match_cond | |||||
return webnotes.conn.sql("""select name, concat_ws(' ', first_name, middle_name, last_name) | |||||
from `tabProfile` | |||||
where ifnull(enabled, 0)=1 | |||||
and docstatus < 2 | |||||
and (%(key)s like "%(txt)s" | |||||
or concat_ws(' ', first_name, middle_name, last_name) like "%(txt)s") | |||||
%(mcond)s | |||||
limit %(start)s, %(page_len)s """ % {'key': searchfield, | |||||
'txt': "%%%s%%" % txt, 'mcond':get_match_cond(doctype, searchfield), | |||||
'start': start, 'page_len': page_len}) | |||||
def get_lead(doctype, txt, searchfield, start, page_len, filters): | |||||
from erpnext.controllers.queries import get_match_cond | |||||
return webnotes.conn.sql(""" select name, lead_name from `tabLead` | |||||
where docstatus < 2 | |||||
and (%(key)s like "%(txt)s" | |||||
or lead_name like "%(txt)s" | |||||
or company_name like "%(txt)s") | |||||
%(mcond)s | |||||
order by lead_name asc | |||||
limit %(start)s, %(page_len)s """ % {'key': searchfield,'txt': "%%%s%%" % txt, | |||||
'mcond':get_match_cond(doctype, searchfield), 'start': start, | |||||
'page_len': page_len}) |
@@ -7,6 +7,8 @@ from webnotes import conf | |||||
import webnotes.utils | import webnotes.utils | ||||
from webnotes.modules import get_doc_path | from webnotes.modules import get_doc_path | ||||
standard_format = "templates/print_formats/standard.html" | |||||
class DocType: | class DocType: | ||||
def __init__(self, d, dl): | def __init__(self, d, dl): | ||||
self.doc, self.doclist = d,dl | self.doc, self.doclist = d,dl | ||||
@@ -39,8 +41,9 @@ class DocType: | |||||
webnotes.clear_cache(doctype=self.doc.doc_type) | webnotes.clear_cache(doctype=self.doc.doc_type) | ||||
def get_args(): | def get_args(): | ||||
if not webnotes.form_dict.doctype or not webnotes.form_dict.name \ | |||||
or not webnotes.form_dict.format: | |||||
if not webnotes.form_dict.format: | |||||
webnotes.form_dict.format = standard_format | |||||
if not webnotes.form_dict.doctype or not webnotes.form_dict.name: | |||||
return { | return { | ||||
"body": """<h1>Error</h1> | "body": """<h1>Error</h1> | ||||
<p>Parameters doctype, name and format required</p> | <p>Parameters doctype, name and format required</p> | ||||
@@ -61,12 +64,12 @@ def get_args(): | |||||
"comment": webnotes.session.user | "comment": webnotes.session.user | ||||
} | } | ||||
def get_html(doc, doclist): | |||||
def get_html(doc, doclist, print_format=None): | |||||
from jinja2 import Environment | from jinja2 import Environment | ||||
from webnotes.core.doctype.print_format.print_format import get_print_format | from webnotes.core.doctype.print_format.print_format import get_print_format | ||||
template = Environment().from_string(get_print_format(doc.doctype, | template = Environment().from_string(get_print_format(doc.doctype, | ||||
webnotes.form_dict.format)) | |||||
print_format or webnotes.form_dict.format)) | |||||
doctype = webnotes.get_doctype(doc.doctype) | doctype = webnotes.get_doctype(doc.doctype) | ||||
args = { | args = { | ||||
@@ -79,15 +82,18 @@ def get_html(doc, doclist): | |||||
html = template.render(args) | html = template.render(args) | ||||
return html | return html | ||||
def get_print_format(doctype, format): | |||||
def get_print_format(doctype, format_name): | |||||
if format_name==standard_format: | |||||
return format_name | |||||
# server, find template | # server, find template | ||||
path = os.path.join(get_doc_path(webnotes.conn.get_value("DocType", doctype, "module"), | path = os.path.join(get_doc_path(webnotes.conn.get_value("DocType", doctype, "module"), | ||||
"Print Format", format), format + ".html") | |||||
"Print Format", format_name), format_name + ".html") | |||||
if os.path.exists(path): | if os.path.exists(path): | ||||
with open(path, "r") as pffile: | with open(path, "r") as pffile: | ||||
return pffile.read() | return pffile.read() | ||||
else: | else: | ||||
html = webnotes.conn.get_value("Print Format", format, "html") | |||||
html = webnotes.conn.get_value("Print Format", format_name, "html") | |||||
if html: | if html: | ||||
return html | return html | ||||
else: | else: | ||||
@@ -34,7 +34,7 @@ cur_frm.cscript.user_image = function(doc) { | |||||
} | } | ||||
cur_frm.cscript.refresh = function(doc) { | cur_frm.cscript.refresh = function(doc) { | ||||
if(!doc.__unsaved && doc.language !== wn.boot.profile.language) { | |||||
if(!doc.__unsaved && wn.languages && doc.language !== wn.boot.profile.language) { | |||||
msgprint("Refreshing..."); | msgprint("Refreshing..."); | ||||
window.location.reload(); | window.location.reload(); | ||||
} | } | ||||
@@ -2,7 +2,7 @@ | |||||
# MIT License. See license.txt | # MIT License. See license.txt | ||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import webnotes, json | |||||
import webnotes, json, os | |||||
from webnotes.utils import cint, now, cstr | from webnotes.utils import cint, now, cstr | ||||
from webnotes import throw, msgprint, _ | from webnotes import throw, msgprint, _ | ||||
from webnotes.auth import _update_password | from webnotes.auth import _update_password | ||||
@@ -127,73 +127,21 @@ class DocType: | |||||
(self.doc.first_name and " " or '') + (self.doc.last_name or '') | (self.doc.first_name and " " or '') + (self.doc.last_name or '') | ||||
def password_reset_mail(self, link): | def password_reset_mail(self, link): | ||||
"""reset password""" | |||||
txt = """ | |||||
## %(title)s | |||||
#### Password Reset | |||||
Dear %(first_name)s, | |||||
Please click on the following link to update your new password: | |||||
<a href="%(link)s">%(link)s</a> | |||||
Thank you,<br> | |||||
%(user_fullname)s | |||||
""" | |||||
self.send_login_mail("Password Reset", | |||||
txt, {"link": link}) | |||||
self.send_login_mail("Password Reset", "templates/emails/password_reset.html", {"link": link}) | |||||
def password_update_mail(self, password): | def password_update_mail(self, password): | ||||
txt = """ | |||||
## %(title)s | |||||
#### Password Update Notification | |||||
Dear %(first_name)s, | |||||
Your password has been updated. Here is your new password: %(new_password)s | |||||
Thank you,<br> | |||||
%(user_fullname)s | |||||
""" | |||||
self.send_login_mail("Password Update", | |||||
txt, {"new_password": password}) | |||||
self.send_login_mail("Password Update", "templates/emails/password_update.html", {"new_password": password}) | |||||
def send_welcome_mail(self): | def send_welcome_mail(self): | ||||
"""send welcome mail to user with password and login url""" | |||||
from webnotes.utils import random_string, get_url | from webnotes.utils import random_string, get_url | ||||
self.doc.reset_password_key = random_string(32) | self.doc.reset_password_key = random_string(32) | ||||
link = get_url("/update-password?key=" + self.doc.reset_password_key) | link = get_url("/update-password?key=" + self.doc.reset_password_key) | ||||
txt = """ | |||||
## %(title)s | |||||
Dear %(first_name)s, | |||||
A new account has been created for you. | |||||
Your login id is: %(user)s | |||||
To complete your registration, please click on the link below: | |||||
<a href="%(link)s">%(link)s</a> | |||||
Thank you,<br> | |||||
%(user_fullname)s | |||||
""" | |||||
self.send_login_mail("New Account", txt, | |||||
{ "link": link }) | |||||
def send_login_mail(self, subject, txt, add_args): | |||||
self.send_login_mail("Verify Your Account", "templates/emails/new_user.html", {"link": link}) | |||||
def send_login_mail(self, subject, template, add_args): | |||||
"""send mail with login details""" | """send mail with login details""" | ||||
import os | |||||
from webnotes.utils.email_lib import sendmail_md | |||||
from webnotes.profile import get_user_fullname | from webnotes.profile import get_user_fullname | ||||
from webnotes.utils import get_url | from webnotes.utils import get_url | ||||
@@ -216,7 +164,8 @@ Thank you,<br> | |||||
sender = webnotes.session.user not in ("Administrator", "Guest") and webnotes.session.user or None | sender = webnotes.session.user not in ("Administrator", "Guest") and webnotes.session.user or None | ||||
sendmail_md(recipients=self.doc.email, sender=sender, subject=subject, msg=txt % args) | |||||
webnotes.sendmail(recipients=self.doc.email, sender=sender, subject=subject, | |||||
message=webnotes.get_template(template).render(args)) | |||||
def a_system_manager_should_exist(self): | def a_system_manager_should_exist(self): | ||||
if not self.get_other_system_managers(): | if not self.get_other_system_managers(): | ||||
@@ -89,23 +89,13 @@ def delete(arg=None): | |||||
def notify(arg=None): | def notify(arg=None): | ||||
from webnotes.utils import cstr, get_fullname, get_url | from webnotes.utils import cstr, get_fullname, get_url | ||||
fn = get_fullname(webnotes.user.name) or webnotes.user.name | |||||
url = get_url() | |||||
message = '''You have a message from <b>%s</b>: | |||||
%s | |||||
To answer, please login to your erpnext account at \ | |||||
<a href=\"%s\" target='_blank'>%s</a> | |||||
''' % (fn, arg['txt'], url, url) | |||||
sender = webnotes.conn.get_value("Profile", webnotes.user.name, "email") \ | |||||
or webnotes.user.name | |||||
recipient = [webnotes.conn.get_value("Profile", arg["contact"], "email") \ | |||||
or arg["contact"]] | |||||
from webnotes.utils.email_lib import sendmail | |||||
sendmail(recipient, sender, message, arg.get("subject") or "You have a message from %s" % (fn,)) | |||||
webnotes.sendmail(\ | |||||
recipients=[webnotes.conn.get_value("Profile", arg["contact"], "email") or arg["contact"]], | |||||
sender= webnotes.conn.get_value("Profile", webnotes.session.user, "email"), | |||||
subject="New Message from " + get_fullname(webnotes.user.name), | |||||
message=webnotes.get_template("templates/emails/new_message.html").render({ | |||||
"from": get_fullname(webnotes.user.name), | |||||
"message": arg['txt'], | |||||
"link": get_url() | |||||
}) | |||||
) |
@@ -1,3 +1,5 @@ | |||||
execute:import inlinestyler # new requirement | |||||
execute:webnotes.reload_doc('core', 'doctype', 'doctype', force=True) #2014-01-24 | execute:webnotes.reload_doc('core', 'doctype', 'doctype', force=True) #2014-01-24 | ||||
execute:webnotes.reload_doc('core', 'doctype', 'docfield', force=True) #2013-13-26 | execute:webnotes.reload_doc('core', 'doctype', 'docfield', force=True) #2013-13-26 | ||||
execute:webnotes.reload_doc('core', 'doctype', 'docperm') #2013-13-26 | execute:webnotes.reload_doc('core', 'doctype', 'docperm') #2013-13-26 | ||||
@@ -1,10 +1,18 @@ | |||||
/*! | /*! | ||||
* Bootstrap v3.0.3 (http://getbootstrap.com) | |||||
* Bootstrap v3.1.0 (http://getbootstrap.com) | |||||
* Copyright 2011-2014 Twitter, Inc. | * Copyright 2011-2014 Twitter, Inc. | ||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | ||||
*/ | */ | ||||
/*! normalize.css v2.1.3 | MIT License | git.io/normalize */ | |||||
/*! normalize.css v3.0.0 | MIT License | git.io/normalize */ | |||||
html { | |||||
font-family: sans-serif; | |||||
-webkit-text-size-adjust: 100%; | |||||
-ms-text-size-adjust: 100%; | |||||
} | |||||
body { | |||||
margin: 0; | |||||
} | |||||
article, | article, | ||||
aside, | aside, | ||||
details, | details, | ||||
@@ -21,8 +29,10 @@ summary { | |||||
} | } | ||||
audio, | audio, | ||||
canvas, | canvas, | ||||
progress, | |||||
video { | video { | ||||
display: inline-block; | display: inline-block; | ||||
vertical-align: baseline; | |||||
} | } | ||||
audio:not([controls]) { | audio:not([controls]) { | ||||
display: none; | display: none; | ||||
@@ -32,29 +42,13 @@ audio:not([controls]) { | |||||
template { | template { | ||||
display: none; | display: none; | ||||
} | } | ||||
html { | |||||
font-family: sans-serif; | |||||
-ms-text-size-adjust: 100%; | |||||
-webkit-text-size-adjust: 100%; | |||||
} | |||||
body { | |||||
margin: 0; | |||||
} | |||||
a { | a { | ||||
background: transparent; | background: transparent; | ||||
} | } | ||||
a:focus { | |||||
outline: thin dotted; | |||||
} | |||||
a:active, | a:active, | ||||
a:hover { | a:hover { | ||||
outline: 0; | outline: 0; | ||||
} | } | ||||
h1 { | |||||
margin: .67em 0; | |||||
font-size: 2em; | |||||
} | |||||
abbr[title] { | abbr[title] { | ||||
border-bottom: 1px dotted; | border-bottom: 1px dotted; | ||||
} | } | ||||
@@ -65,28 +59,14 @@ strong { | |||||
dfn { | dfn { | ||||
font-style: italic; | font-style: italic; | ||||
} | } | ||||
hr { | |||||
height: 0; | |||||
-moz-box-sizing: content-box; | |||||
box-sizing: content-box; | |||||
h1 { | |||||
margin: .67em 0; | |||||
font-size: 2em; | |||||
} | } | ||||
mark { | mark { | ||||
color: #000; | color: #000; | ||||
background: #ff0; | background: #ff0; | ||||
} | } | ||||
code, | |||||
kbd, | |||||
pre, | |||||
samp { | |||||
font-family: monospace, serif; | |||||
font-size: 1em; | |||||
} | |||||
pre { | |||||
white-space: pre-wrap; | |||||
} | |||||
q { | |||||
quotes: "\201C" "\201D" "\2018" "\2019"; | |||||
} | |||||
small { | small { | ||||
font-size: 80%; | font-size: 80%; | ||||
} | } | ||||
@@ -110,28 +90,34 @@ svg:not(:root) { | |||||
overflow: hidden; | overflow: hidden; | ||||
} | } | ||||
figure { | figure { | ||||
margin: 0; | |||||
margin: 1em 40px; | |||||
} | } | ||||
fieldset { | |||||
padding: .35em .625em .75em; | |||||
margin: 0 2px; | |||||
border: 1px solid #c0c0c0; | |||||
hr { | |||||
height: 0; | |||||
-moz-box-sizing: content-box; | |||||
box-sizing: content-box; | |||||
} | } | ||||
legend { | |||||
padding: 0; | |||||
border: 0; | |||||
pre { | |||||
overflow: auto; | |||||
} | |||||
code, | |||||
kbd, | |||||
pre, | |||||
samp { | |||||
font-family: monospace, monospace; | |||||
font-size: 1em; | |||||
} | } | ||||
button, | button, | ||||
input, | input, | ||||
optgroup, | |||||
select, | select, | ||||
textarea { | textarea { | ||||
margin: 0; | margin: 0; | ||||
font-family: inherit; | |||||
font-size: 100%; | |||||
font: inherit; | |||||
color: inherit; | |||||
} | } | ||||
button, | |||||
input { | |||||
line-height: normal; | |||||
button { | |||||
overflow: visible; | |||||
} | } | ||||
button, | button, | ||||
select { | select { | ||||
@@ -148,11 +134,23 @@ button[disabled], | |||||
html input[disabled] { | html input[disabled] { | ||||
cursor: default; | cursor: default; | ||||
} | } | ||||
button::-moz-focus-inner, | |||||
input::-moz-focus-inner { | |||||
padding: 0; | |||||
border: 0; | |||||
} | |||||
input { | |||||
line-height: normal; | |||||
} | |||||
input[type="checkbox"], | input[type="checkbox"], | ||||
input[type="radio"] { | input[type="radio"] { | ||||
box-sizing: border-box; | box-sizing: border-box; | ||||
padding: 0; | padding: 0; | ||||
} | } | ||||
input[type="number"]::-webkit-inner-spin-button, | |||||
input[type="number"]::-webkit-outer-spin-button { | |||||
height: auto; | |||||
} | |||||
input[type="search"] { | input[type="search"] { | ||||
-webkit-box-sizing: content-box; | -webkit-box-sizing: content-box; | ||||
-moz-box-sizing: content-box; | -moz-box-sizing: content-box; | ||||
@@ -163,19 +161,29 @@ input[type="search"]::-webkit-search-cancel-button, | |||||
input[type="search"]::-webkit-search-decoration { | input[type="search"]::-webkit-search-decoration { | ||||
-webkit-appearance: none; | -webkit-appearance: none; | ||||
} | } | ||||
button::-moz-focus-inner, | |||||
input::-moz-focus-inner { | |||||
fieldset { | |||||
padding: .35em .625em .75em; | |||||
margin: 0 2px; | |||||
border: 1px solid #c0c0c0; | |||||
} | |||||
legend { | |||||
padding: 0; | padding: 0; | ||||
border: 0; | border: 0; | ||||
} | } | ||||
textarea { | textarea { | ||||
overflow: auto; | overflow: auto; | ||||
vertical-align: top; | |||||
} | |||||
optgroup { | |||||
font-weight: bold; | |||||
} | } | ||||
table { | table { | ||||
border-spacing: 0; | border-spacing: 0; | ||||
border-collapse: collapse; | border-collapse: collapse; | ||||
} | } | ||||
td, | |||||
th { | |||||
padding: 0; | |||||
} | |||||
@media print { | @media print { | ||||
* { | * { | ||||
color: #000 !important; | color: #000 !important; | ||||
@@ -213,9 +221,6 @@ table { | |||||
img { | img { | ||||
max-width: 100% !important; | max-width: 100% !important; | ||||
} | } | ||||
@page { | |||||
margin: 2cm .5cm; | |||||
} | |||||
p, | p, | ||||
h2, | h2, | ||||
h3 { | h3 { | ||||
@@ -296,6 +301,9 @@ a:focus { | |||||
outline: 5px auto -webkit-focus-ring-color; | outline: 5px auto -webkit-focus-ring-color; | ||||
outline-offset: -2px; | outline-offset: -2px; | ||||
} | } | ||||
figure { | |||||
margin: 0; | |||||
} | |||||
img { | img { | ||||
vertical-align: middle; | vertical-align: middle; | ||||
} | } | ||||
@@ -1620,6 +1628,7 @@ table th[class*="col-"] { | |||||
} | } | ||||
} | } | ||||
fieldset { | fieldset { | ||||
min-width: 0; | |||||
padding: 0; | padding: 0; | ||||
margin: 0; | margin: 0; | ||||
border: 0; | border: 0; | ||||
@@ -1662,11 +1671,6 @@ select[multiple], | |||||
select[size] { | select[size] { | ||||
height: auto; | height: auto; | ||||
} | } | ||||
select optgroup { | |||||
font-family: inherit; | |||||
font-size: inherit; | |||||
font-style: inherit; | |||||
} | |||||
input[type="file"]:focus, | input[type="file"]:focus, | ||||
input[type="radio"]:focus, | input[type="radio"]:focus, | ||||
input[type="checkbox"]:focus { | input[type="checkbox"]:focus { | ||||
@@ -1674,10 +1678,6 @@ input[type="checkbox"]:focus { | |||||
outline: 5px auto -webkit-focus-ring-color; | outline: 5px auto -webkit-focus-ring-color; | ||||
outline-offset: -2px; | outline-offset: -2px; | ||||
} | } | ||||
input[type="number"]::-webkit-outer-spin-button, | |||||
input[type="number"]::-webkit-inner-spin-button { | |||||
height: auto; | |||||
} | |||||
output { | output { | ||||
display: block; | display: block; | ||||
padding-top: 7px; | padding-top: 7px; | ||||
@@ -1699,7 +1699,7 @@ output { | |||||
border-radius: 4px; | border-radius: 4px; | ||||
} | } | ||||
.form-control:focus { | .form-control:focus { | ||||
border-color: #66afe9; | |||||
border-color: #000; | |||||
outline: 0; | outline: 0; | ||||
} | } | ||||
.form-control:-moz-placeholder { | .form-control:-moz-placeholder { | ||||
@@ -1720,6 +1720,7 @@ output { | |||||
fieldset[disabled] .form-control { | fieldset[disabled] .form-control { | ||||
cursor: not-allowed; | cursor: not-allowed; | ||||
background-color: #eee; | background-color: #eee; | ||||
opacity: 1; | |||||
} | } | ||||
textarea.form-control { | textarea.form-control { | ||||
height: auto; | height: auto; | ||||
@@ -1794,7 +1795,8 @@ select.input-sm { | |||||
height: 30px; | height: 30px; | ||||
line-height: 30px; | line-height: 30px; | ||||
} | } | ||||
textarea.input-sm { | |||||
textarea.input-sm, | |||||
select[multiple].input-sm { | |||||
height: auto; | height: auto; | ||||
} | } | ||||
.input-lg { | .input-lg { | ||||
@@ -1808,7 +1810,8 @@ select.input-lg { | |||||
height: 46px; | height: 46px; | ||||
line-height: 46px; | line-height: 46px; | ||||
} | } | ||||
textarea.input-lg { | |||||
textarea.input-lg, | |||||
select[multiple].input-lg { | |||||
height: auto; | height: auto; | ||||
} | } | ||||
.has-feedback { | .has-feedback { | ||||
@@ -3312,7 +3315,10 @@ select.input-group-lg > .input-group-btn > .btn { | |||||
} | } | ||||
textarea.input-group-lg > .form-control, | textarea.input-group-lg > .form-control, | ||||
textarea.input-group-lg > .input-group-addon, | textarea.input-group-lg > .input-group-addon, | ||||
textarea.input-group-lg > .input-group-btn > .btn { | |||||
textarea.input-group-lg > .input-group-btn > .btn, | |||||
select[multiple].input-group-lg > .form-control, | |||||
select[multiple].input-group-lg > .input-group-addon, | |||||
select[multiple].input-group-lg > .input-group-btn > .btn { | |||||
height: auto; | height: auto; | ||||
} | } | ||||
.input-group-sm > .form-control, | .input-group-sm > .form-control, | ||||
@@ -3332,7 +3338,10 @@ select.input-group-sm > .input-group-btn > .btn { | |||||
} | } | ||||
textarea.input-group-sm > .form-control, | textarea.input-group-sm > .form-control, | ||||
textarea.input-group-sm > .input-group-addon, | textarea.input-group-sm > .input-group-addon, | ||||
textarea.input-group-sm > .input-group-btn > .btn { | |||||
textarea.input-group-sm > .input-group-btn > .btn, | |||||
select[multiple].input-group-sm > .form-control, | |||||
select[multiple].input-group-sm > .input-group-addon, | |||||
select[multiple].input-group-sm > .input-group-btn > .btn { | |||||
height: auto; | height: auto; | ||||
} | } | ||||
.input-group-addon, | .input-group-addon, | ||||
@@ -3717,6 +3726,7 @@ textarea.input-group-sm > .input-group-btn > .btn { | |||||
} | } | ||||
.navbar-brand { | .navbar-brand { | ||||
float: left; | float: left; | ||||
height: 20px; | |||||
padding: 8px 15px; | padding: 8px 15px; | ||||
font-size: 18px; | font-size: 18px; | ||||
line-height: 20px; | line-height: 20px; | ||||
@@ -3725,11 +3735,6 @@ textarea.input-group-sm > .input-group-btn > .btn { | |||||
.navbar-brand:focus { | .navbar-brand:focus { | ||||
text-decoration: none; | text-decoration: none; | ||||
} | } | ||||
.navbar-brand > .glyphicon { | |||||
float: left; | |||||
margin-top: -2px; | |||||
margin-right: 5px; | |||||
} | |||||
@media (min-width: 768px) { | @media (min-width: 768px) { | ||||
.navbar > .container .navbar-brand, | .navbar > .container .navbar-brand, | ||||
.navbar > .container-fluid .navbar-brand { | .navbar > .container-fluid .navbar-brand { | ||||
@@ -4834,8 +4839,8 @@ a.list-group-item-danger.active:focus { | |||||
.panel > .panel-body + .table-responsive { | .panel > .panel-body + .table-responsive { | ||||
border-top: 1px solid #ddd; | border-top: 1px solid #ddd; | ||||
} | } | ||||
.panel > .table > tbody:first-child th, | |||||
.panel > .table > tbody:first-child td { | |||||
.panel > .table > tbody:first-child > tr:first-child th, | |||||
.panel > .table > tbody:first-child > tr:first-child td { | |||||
border-top: 0; | border-top: 0; | ||||
} | } | ||||
.panel > .table-bordered, | .panel > .table-bordered, | ||||
@@ -32,6 +32,14 @@ | |||||
font-weight: normal; | font-weight: normal; | ||||
font-style: normal; | font-style: normal; | ||||
} | } | ||||
@media screen and (-webkit-min-device-pixel-ratio:0) { | |||||
@font-face { | |||||
font-family: 'FontAwesome'; | |||||
src: url('../lib/css/font/fontawesome-webfont.svg#fontawesomeregular?v=3.2.1') format('svg'); | |||||
} | |||||
} | |||||
/* FONT AWESOME CORE | /* FONT AWESOME CORE | ||||
* -------------------------- */ | * -------------------------- */ | ||||
[class^="icon-"], | [class^="icon-"], | ||||
@@ -0,0 +1,9 @@ | |||||
// include in bootstrap.less | |||||
@font-family-sans-serif: Arial, sans-serif; | |||||
@input-border-focus: #000; | |||||
@alert-padding: 10px; | |||||
label { | |||||
font-weight: normal; | |||||
} |
@@ -389,7 +389,7 @@ $.extend(_p, { | |||||
lh = cstr(wn.boot.letter_heads[cur_frm.doc.letter_head]); | lh = cstr(wn.boot.letter_heads[cur_frm.doc.letter_head]); | ||||
} else if (cp.letter_head) { | } else if (cp.letter_head) { | ||||
lh = cp.letter_head; | lh = cp.letter_head; | ||||
} | |||||
} | |||||
return lh; | return lh; | ||||
}, | }, | ||||
@@ -1,5 +1,5 @@ | |||||
/*! | /*! | ||||
* Bootstrap v3.0.3 (http://getbootstrap.com) | |||||
* Bootstrap v3.1.0 (http://getbootstrap.com) | |||||
* Copyright 2011-2014 Twitter, Inc. | * Copyright 2011-2014 Twitter, Inc. | ||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | ||||
*/ | */ |
@@ -100,13 +100,8 @@ $.extend(wn.meta, { | |||||
}, | }, | ||||
get_print_formats: function(doctype) { | get_print_formats: function(doctype) { | ||||
// if default print format is given, use it | |||||
var print_format_list = []; | |||||
if(locals.DocType[doctype].default_print_format) | |||||
print_format_list.push(locals.DocType[doctype].default_print_format) | |||||
if(!in_list(print_format_list, "Standard")) | |||||
print_format_list.push("Standard"); | |||||
var print_format_list = ["Standard"]; | |||||
var default_print_format = locals.DocType[doctype].default_print_format; | |||||
var print_formats = wn.model.get("Print Format", {doc_type: doctype}) | var print_formats = wn.model.get("Print Format", {doc_type: doctype}) | ||||
.sort(function(a, b) { return (a > b) ? 1 : -1; }); | .sort(function(a, b) { return (a > b) ? 1 : -1; }); | ||||
@@ -114,6 +109,12 @@ $.extend(wn.meta, { | |||||
if(!in_list(print_format_list, d.name)) | if(!in_list(print_format_list, d.name)) | ||||
print_format_list.push(d.name); | print_format_list.push(d.name); | ||||
}); | }); | ||||
if(default_print_format && default_print_format != "Standard") { | |||||
var index = print_format_list.indexOf(default_print_format) - 1; | |||||
print_format_list.sort().splice(index, 1); | |||||
print_format_list.unshift(default_print_format); | |||||
} | |||||
return print_format_list; | return print_format_list; | ||||
}, | }, | ||||
@@ -125,7 +125,7 @@ bsEditor = Class.extend({ | |||||
clean_html: function() { | clean_html: function() { | ||||
var html = this.editor.html() || ""; | var html = this.editor.html() || ""; | ||||
if(!strip(this.editor.text())) html = ""; | |||||
if(!strip(this.editor.text()) && !(this.editor.find("img"))) html = ""; | |||||
// html = html.replace(/(<br>|\s|<div><br><\/div>| )*$/, ''); | // html = html.replace(/(<br>|\s|<div><br><\/div>| )*$/, ''); | ||||
// remove custom typography (use CSS!) | // remove custom typography (use CSS!) | ||||
@@ -0,0 +1,5 @@ | |||||
<h3>New Message</h3> | |||||
<p>You have a new message from: <b>{{ from }}</b></p> | |||||
<p>{{ message }}</p> | |||||
<hr> | |||||
<p><a href="{{ link }}">Login and view in Browser</a></p> |
@@ -0,0 +1,10 @@ | |||||
<h3>{{ title }}</h3> | |||||
<p>Dear {{ first_name }}{% if last_name %} {{ last_name}}{% endif %},</p> | |||||
<p>A new account has been created for you.</p> | |||||
<p>Your login id is: <b>{{ user }}</b> | |||||
<p>Click on the button below to complete your registration and set a new password.</p> | |||||
<p><a class="btn-primary" href="{{ link }}">Complete Registration</a></p> | |||||
<br> | |||||
<p>You can also copy-paste this link in your browser <a href="{{ link }}">{{ link }}</a></p> | |||||
<p>Thank you,<br> | |||||
{{ user_fullname }}</p> |
@@ -0,0 +1,6 @@ | |||||
<h3>Password Reset</h3> | |||||
<p>Dear {{ first_name }}{% if last_name %} {{ last_name}}{% endif %},</p> | |||||
<p>Please click on the following link to update your new password:</p> | |||||
<p><a href="{{ link }}">{{ link }}</a></p> | |||||
<p>Thank you,<br> | |||||
{{ user_fullname }}</p> |
@@ -0,0 +1,5 @@ | |||||
<h3>Password Update Notification</h3> | |||||
<p>Dear {{ first_name }}{% if last_name %} {{ last_name}}{% endif %},</p> | |||||
<p>Your password has been updated. Here is your new password: {{ new_password }}</p> | |||||
<p>Thank you,<br> | |||||
{{ user_fullname }}</p> |
@@ -25,9 +25,9 @@ body { | |||||
-webkit-text-size-adjust:none; | -webkit-text-size-adjust:none; | ||||
width: 100%!important; | width: 100%!important; | ||||
height: 100%; | height: 100%; | ||||
background-color: #f6f6f6; | |||||
} | } | ||||
/* ------------------------------------- | /* ------------------------------------- | ||||
ELEMENTS | ELEMENTS | ||||
------------------------------------- */ | ------------------------------------- */ | ||||
@@ -115,7 +115,7 @@ table.footer-wrap a{ | |||||
------------------------------------- */ | ------------------------------------- */ | ||||
h1,h2,h3{ | h1,h2,h3{ | ||||
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; line-height: 1.1; margin-bottom:15px; color:#000; | font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; line-height: 1.1; margin-bottom:15px; color:#000; | ||||
margin: 40px 0 10px; | |||||
margin: 20px 0 10px; | |||||
line-height: 1.2; | line-height: 1.2; | ||||
font-weight:200; | font-weight:200; | ||||
} | } | ||||
@@ -129,7 +129,10 @@ h2 { | |||||
h3 { | h3 { | ||||
font-size: 22px; | font-size: 22px; | ||||
} | } | ||||
hr { | |||||
margin: 20px 0; | |||||
border-top: 1px solid #eee; | |||||
} | |||||
p, ul, ol { | p, ul, ol { | ||||
margin-bottom: 10px; | margin-bottom: 10px; | ||||
font-weight: normal; | font-weight: normal; |
@@ -30,7 +30,7 @@ def main(app=None, module=None, doctype=None, verbose=False): | |||||
for doctype in module.test_dependencies: | for doctype in module.test_dependencies: | ||||
make_test_records(doctype, verbose=verbose) | make_test_records(doctype, verbose=verbose) | ||||
test_suite.addTest(unittest.TestLoader().loadTestsFromModule(sys.modules[module])) | |||||
test_suite.addTest(unittest.TestLoader().loadTestsFromModule(module)) | |||||
ret = unittest.TextTestRunner(verbosity=1+(verbose and 1 or 0)).run(test_suite) | ret = unittest.TextTestRunner(verbosity=1+(verbose and 1 or 0)).run(test_suite) | ||||
else: | else: | ||||
ret = run_all_tests(app, verbose) | ret = run_all_tests(app, verbose) | ||||
@@ -4,10 +4,6 @@ | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import os, sys | import os, sys | ||||
sys.path.append('.') | |||||
sys.path.append('lib/py') | |||||
sys.path.append('erpnext') | |||||
import unittest, webnotes | import unittest, webnotes | ||||
from webnotes.test_runner import make_test_records | from webnotes.test_runner import make_test_records | ||||
@@ -288,4 +288,4 @@ def google_translate(lang, untranslated): | |||||
return dict(zip(untranslated, translated)) | return dict(zip(untranslated, translated)) | ||||
else: | else: | ||||
print "unable to translate" | print "unable to translate" | ||||
return {} | |||||
return {} |
@@ -5,9 +5,11 @@ | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
from werkzeug.test import Client | from werkzeug.test import Client | ||||
import os | |||||
import re | |||||
import urllib | |||||
import webnotes | import webnotes | ||||
import os | |||||
no_value_fields = ['Section Break', 'Column Break', 'HTML', 'Table', 'FlexTable', | no_value_fields = ['Section Break', 'Column Break', 'HTML', 'Table', 'FlexTable', | ||||
'Button', 'Image', 'Graph'] | 'Button', 'Image', 'Graph'] | ||||
@@ -64,7 +66,6 @@ def extract_email_id(email): | |||||
def validate_email_add(email_str): | def validate_email_add(email_str): | ||||
"""Validates the email string""" | """Validates the email string""" | ||||
email = extract_email_id(email_str) | email = extract_email_id(email_str) | ||||
import re | |||||
return 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])?", email.lower()) | return 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])?", email.lower()) | ||||
def get_request_site_address(full_address=False): | def get_request_site_address(full_address=False): | ||||
@@ -277,7 +278,6 @@ def dict_to_str(args, sep='&'): | |||||
""" | """ | ||||
Converts a dictionary to URL | Converts a dictionary to URL | ||||
""" | """ | ||||
import urllib | |||||
t = [] | t = [] | ||||
for k in args.keys(): | for k in args.keys(): | ||||
t.append(str(k)+'='+urllib.quote(str(args[k] or ''))) | t.append(str(k)+'='+urllib.quote(str(args[k] or ''))) | ||||
@@ -670,7 +670,6 @@ def strip_html(text): | |||||
""" | """ | ||||
removes anything enclosed in and including <> | removes anything enclosed in and including <> | ||||
""" | """ | ||||
import re | |||||
return re.compile(r'<.*?>').sub('', text) | return re.compile(r'<.*?>').sub('', text) | ||||
def escape_html(text): | def escape_html(text): | ||||
@@ -831,7 +830,6 @@ def get_url(uri=None): | |||||
url = "http://" + subdomain | url = "http://" + subdomain | ||||
if uri: | if uri: | ||||
import urllib | |||||
url = urllib.basejoin(url, uri) | url = urllib.basejoin(url, uri) | ||||
return url | return url | ||||
@@ -896,14 +894,26 @@ def get_disk_usage(): | |||||
return 0 | return 0 | ||||
err, out = execute_in_shell("du -hsm {files_path}".format(files_path=files_path)) | err, out = execute_in_shell("du -hsm {files_path}".format(files_path=files_path)) | ||||
return cint(out.split("\n")[-2].split("\t")[0]) | return cint(out.split("\n")[-2].split("\t")[0]) | ||||
def expand_partial_links(html): | |||||
import re | |||||
def scrub_urls(html): | |||||
html = expand_relative_urls(html) | |||||
html = quote_urls(html) | |||||
return html | |||||
def expand_relative_urls(html): | |||||
# expand relative urls | |||||
url = get_url() | url = get_url() | ||||
if not url.endswith("/"): url += "/" | if not url.endswith("/"): url += "/" | ||||
return re.sub('(href|src){1}([\s]*=[\s]*[\'"]?)/*((?!http)[^\'" >]+)([\'"]?)', | |||||
'\g<1>\g<2>{}\g<3>\g<4>'.format(url), | |||||
html) | |||||
return re.sub('(href|src){1}([\s]*=[\s]*[\'"]?)((?!http)[^\'" >]+)([\'"]?)', | |||||
'\g<1>\g<2>{}\g<3>\g<4>'.format(url), html) | |||||
def quote_urls(html): | |||||
def _quote_url(match): | |||||
groups = list(match.groups()) | |||||
groups[2] = urllib.quote(groups[2], safe="/:") | |||||
return "".join(groups) | |||||
return re.sub('(href|src){1}([\s]*=[\s]*[\'"]?)((?:http)[^\'">]+)([\'"]?)', | |||||
_quote_url, html) | |||||
def touch_file(path): | def touch_file(path): | ||||
with open(path, 'a'): | with open(path, 'a'): | ||||
@@ -912,4 +922,4 @@ def touch_file(path): | |||||
def get_test_client(): | def get_test_client(): | ||||
from webnotes.app import application | from webnotes.app import application | ||||
return Client(application) | |||||
return Client(application) |
@@ -5,6 +5,9 @@ from __future__ import unicode_literals | |||||
import webnotes | import webnotes | ||||
from webnotes import conf | from webnotes import conf | ||||
from webnotes.utils.email_lib.email_body import get_email | |||||
from webnotes.utils.email_lib.smtp import send | |||||
def sendmail_md(recipients, sender=None, msg=None, subject=None): | def sendmail_md(recipients, sender=None, msg=None, subject=None): | ||||
"""send markdown email""" | """send markdown email""" | ||||
import markdown2 | import markdown2 | ||||
@@ -12,12 +15,11 @@ def sendmail_md(recipients, sender=None, msg=None, subject=None): | |||||
def sendmail(recipients, sender='', msg='', subject='[No Subject]'): | def sendmail(recipients, sender='', msg='', subject='[No Subject]'): | ||||
"""send an html email as multipart with attachments and all""" | """send an html email as multipart with attachments and all""" | ||||
from webnotes.utils.email_lib.smtp import get_email | |||||
get_email(recipients, sender, msg, subject).send() | |||||
send(get_email(recipients, sender, msg, subject)) | |||||
def sendmail_to_system_managers(subject, content): | def sendmail_to_system_managers(subject, content): | ||||
from webnotes.utils.email_lib.smtp import get_email | |||||
get_email(get_system_managers(), None, content, subject).send() | |||||
send(get_email(get_system_managers(), None, content, subject)) | |||||
@webnotes.whitelist() | @webnotes.whitelist() | ||||
def get_contact_list(): | def get_contact_list(): | ||||
@@ -3,31 +3,29 @@ | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import webnotes | import webnotes | ||||
from webnotes import msgprint, throw, _ | |||||
from webnotes.model.doc import Document | |||||
from webnotes.utils import cint, get_url | |||||
import HTMLParser | |||||
import urllib | import urllib | ||||
from webnotes import msgprint, throw, _ | |||||
from webnotes.utils.email_lib.smtp import SMTPServer, send | |||||
from webnotes.utils.email_lib.email_body import get_email, get_formatted_html | |||||
from webnotes.utils.email_lib.html2text import html2text | |||||
from webnotes.utils import cint, get_url, nowdate | |||||
class BulkLimitCrossedError(webnotes.ValidationError): pass | class BulkLimitCrossedError(webnotes.ValidationError): pass | ||||
def send(recipients=None, sender=None, doctype='Profile', email_field='email', | def send(recipients=None, sender=None, doctype='Profile', email_field='email', | ||||
subject='[No Subject]', message='[No Content]', ref_doctype=None, ref_docname=None, | subject='[No Subject]', message='[No Content]', ref_doctype=None, ref_docname=None, | ||||
add_unsubscribe_link=True): | add_unsubscribe_link=True): | ||||
"""send bulk mail if not unsubscribed and within conf.bulk_mail_limit""" | |||||
import webnotes | |||||
def is_unsubscribed(rdata): | def is_unsubscribed(rdata): | ||||
if not rdata: return 1 | |||||
if not rdata: | |||||
return 1 | |||||
return cint(rdata.unsubscribed) | return cint(rdata.unsubscribed) | ||||
def check_bulk_limit(new_mails): | def check_bulk_limit(new_mails): | ||||
from webnotes import conf | |||||
from webnotes.utils import nowdate | |||||
this_month = webnotes.conn.sql("""select count(*) from `tabBulk Email` where | this_month = webnotes.conn.sql("""select count(*) from `tabBulk Email` where | ||||
month(creation)=month(%s)""" % nowdate())[0][0] | month(creation)=month(%s)""" % nowdate())[0][0] | ||||
monthly_bulk_mail_limit = conf.get('monthly_bulk_mail_limit') or 500 | |||||
monthly_bulk_mail_limit = webnotes.conf.get('monthly_bulk_mail_limit') or 500 | |||||
if this_month + len(recipients) > monthly_bulk_mail_limit: | if this_month + len(recipients) > monthly_bulk_mail_limit: | ||||
throw("{bulk} ({limit}) {cross}".format(**{ | throw("{bulk} ({limit}) {cross}".format(**{ | ||||
@@ -36,10 +34,10 @@ def send(recipients=None, sender=None, doctype='Profile', email_field='email', | |||||
"cross": _("crossed") | "cross": _("crossed") | ||||
}), exc=BulkLimitCrossedError) | }), exc=BulkLimitCrossedError) | ||||
def update_message(doc): | |||||
updated = message | |||||
def update_message(formatted, doc, add_unsubscribe_link): | |||||
updated = formatted | |||||
if add_unsubscribe_link: | if add_unsubscribe_link: | ||||
updated += """<div style="padding: 7px; border-top: 1px solid #aaa; | |||||
unsubscribe_link = """<div style="padding: 7px; border-top: 1px solid #aaa; | |||||
margin-top: 17px;"> | margin-top: 17px;"> | ||||
<small><a href="%s/?%s"> | <small><a href="%s/?%s"> | ||||
Unsubscribe</a> from this list.</small></div>""" % (get_url(), | Unsubscribe</a> from this list.</small></div>""" % (get_url(), | ||||
@@ -49,6 +47,8 @@ def send(recipients=None, sender=None, doctype='Profile', email_field='email', | |||||
"type": doctype, | "type": doctype, | ||||
"email_field": email_field | "email_field": email_field | ||||
})) | })) | ||||
updated = updated.replace("<!--unsubscribe link here-->", unsubscribe_link) | |||||
return updated | return updated | ||||
@@ -56,36 +56,33 @@ def send(recipients=None, sender=None, doctype='Profile', email_field='email', | |||||
if not sender or sender == "Administrator": | if not sender or sender == "Administrator": | ||||
sender = webnotes.conn.get_value('Email Settings', None, 'auto_email_id') | sender = webnotes.conn.get_value('Email Settings', None, 'auto_email_id') | ||||
check_bulk_limit(len(recipients)) | check_bulk_limit(len(recipients)) | ||||
import HTMLParser | |||||
from webnotes.utils.email_lib.html2text import html2text | |||||
from webnotes.utils import expand_partial_links | |||||
try: | try: | ||||
message = expand_partial_links(message) | |||||
text_content = html2text(message) | text_content = html2text(message) | ||||
except HTMLParser.HTMLParseError: | except HTMLParser.HTMLParseError: | ||||
text_content = "[See html attachment]" | text_content = "[See html attachment]" | ||||
formatted = get_formatted_html(subject, message) | |||||
for r in filter(None, list(set(recipients))): | for r in filter(None, list(set(recipients))): | ||||
rdata = webnotes.conn.sql("""select * from `tab%s` where %s=%s""" % (doctype, | rdata = webnotes.conn.sql("""select * from `tab%s` where %s=%s""" % (doctype, | ||||
email_field, '%s'), (r,), as_dict=1) | email_field, '%s'), (r,), as_dict=1) | ||||
doc = rdata and rdata[0] or {} | doc = rdata and rdata[0] or {} | ||||
if not is_unsubscribed(doc): | if not is_unsubscribed(doc): | ||||
# add to queue | # add to queue | ||||
add(r, sender, subject, update_message(doc), text_content, ref_doctype, ref_docname) | |||||
add(r, sender, subject, update_message(formatted, doc, add_unsubscribe_link), | |||||
text_content, ref_doctype, ref_docname) | |||||
def add(email, sender, subject, message, text_content=None, ref_doctype=None, ref_docname=None): | |||||
"""add to bulk mail queue""" | |||||
from webnotes.utils.email_lib.smtp import get_email | |||||
e = Document('Bulk Email') | |||||
def add(email, sender, subject, formatted, text_content=None, | |||||
ref_doctype=None, ref_docname=None): | |||||
"""add to bulk mail queue""" | |||||
e = webnotes.doc('Bulk Email') | |||||
e.sender = sender | e.sender = sender | ||||
e.recipient = email | e.recipient = email | ||||
try: | try: | ||||
e.message = get_email(email, sender=e.sender, msg=message, subject=subject, | |||||
e.message = get_email(email, sender=e.sender, formatted=formatted, subject=subject, | |||||
text_content = text_content).as_string() | text_content = text_content).as_string() | ||||
except webnotes.ValidationError: | except webnotes.ValidationError: | ||||
# bad email id - don't add to queue | # bad email id - don't add to queue | ||||
@@ -116,15 +113,11 @@ def unsubscribe(): | |||||
def flush(from_test=False): | def flush(from_test=False): | ||||
"""flush email queue, every time: called from scheduler""" | """flush email queue, every time: called from scheduler""" | ||||
import webnotes | |||||
from webnotes import conf | |||||
from webnotes.utils.email_lib.smtp import SMTPServer, get_email | |||||
smptserver = SMTPServer() | smptserver = SMTPServer() | ||||
auto_commit = not from_test | auto_commit = not from_test | ||||
if webnotes.flags.mute_emails or conf.get("mute_emails") or False: | |||||
if webnotes.flags.mute_emails or webnotes.conf.get("mute_emails") or False: | |||||
msgprint(_("Emails are muted")) | msgprint(_("Emails are muted")) | ||||
from_test = True | from_test = True | ||||
@@ -0,0 +1,217 @@ | |||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||||
# MIT License. See license.txt | |||||
from __future__ import unicode_literals | |||||
import webnotes | |||||
from webnotes.utils import scrub_urls | |||||
import email.utils | |||||
from inlinestyler.utils import inline_css | |||||
def get_email(recipients, sender='', msg='', subject='[No Subject]', | |||||
text_content = None, footer=None, formatted=None): | |||||
"""send an html email as multipart with attachments and all""" | |||||
email = EMail(sender, recipients, subject) | |||||
if (not '<br>' in msg) and (not '<p>' in msg) and (not '<div' in msg): | |||||
msg = msg.replace('\n', '<br>') | |||||
email.set_html(msg, text_content, footer=footer, formatted=formatted) | |||||
return email | |||||
class EMail: | |||||
""" | |||||
Wrapper on the email module. Email object represents emails to be sent to the client. | |||||
Also provides a clean way to add binary `FileData` attachments | |||||
Also sets all messages as multipart/alternative for cleaner reading in text-only clients | |||||
""" | |||||
def __init__(self, sender='', recipients=[], subject='', alternative=0, reply_to=None): | |||||
from email.mime.multipart import MIMEMultipart | |||||
from email import Charset | |||||
Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8') | |||||
if isinstance(recipients, basestring): | |||||
recipients = recipients.replace(';', ',').replace('\n', '') | |||||
recipients = recipients.split(',') | |||||
# remove null | |||||
recipients = filter(None, (r.strip() for r in recipients)) | |||||
self.sender = sender | |||||
self.reply_to = reply_to or sender | |||||
self.recipients = recipients | |||||
self.subject = subject | |||||
self.msg_root = MIMEMultipart('mixed') | |||||
self.msg_multipart = MIMEMultipart('alternative') | |||||
self.msg_root.attach(self.msg_multipart) | |||||
self.cc = [] | |||||
self.html_set = False | |||||
def set_html(self, message, text_content = None, footer=None, formatted=None): | |||||
"""Attach message in the html portion of multipart/alternative""" | |||||
if not formatted: | |||||
formatted = get_formatted_html(self.subject, message, footer) | |||||
# this is the first html part of a multi-part message, | |||||
# convert to text well | |||||
if not self.html_set: | |||||
if text_content: | |||||
self.set_text(text_content) | |||||
else: | |||||
self.set_html_as_text(message) | |||||
self.set_part_html(formatted) | |||||
self.html_set = True | |||||
def set_text(self, message): | |||||
""" | |||||
Attach message in the text portion of multipart/alternative | |||||
""" | |||||
from email.mime.text import MIMEText | |||||
part = MIMEText(message.encode('utf-8'), 'plain', 'utf-8') | |||||
self.msg_multipart.attach(part) | |||||
def set_part_html(self, message): | |||||
from email.mime.text import MIMEText | |||||
part = MIMEText(message.encode('utf-8'), 'html', 'utf-8') | |||||
self.msg_multipart.attach(part) | |||||
def set_html_as_text(self, html): | |||||
"""return html2text""" | |||||
import HTMLParser | |||||
from webnotes.utils.email_lib.html2text import html2text | |||||
try: | |||||
self.set_text(html2text(html)) | |||||
except HTMLParser.HTMLParseError: | |||||
pass | |||||
def set_message(self, message, mime_type='text/html', as_attachment=0, filename='attachment.html'): | |||||
"""Append the message with MIME content to the root node (as attachment)""" | |||||
from email.mime.text import MIMEText | |||||
maintype, subtype = mime_type.split('/') | |||||
part = MIMEText(message, _subtype = subtype) | |||||
if as_attachment: | |||||
part.add_header('Content-Disposition', 'attachment', filename=filename) | |||||
self.msg_root.attach(part) | |||||
def attach_file(self, n): | |||||
"""attach a file from the `FileData` table""" | |||||
from webnotes.utils.file_manager import get_file | |||||
res = get_file(n) | |||||
if not res: | |||||
return | |||||
self.add_attachment(res[0], res[1]) | |||||
def add_attachment(self, fname, fcontent, content_type=None): | |||||
"""add attachment""" | |||||
from email.mime.audio import MIMEAudio | |||||
from email.mime.base import MIMEBase | |||||
from email.mime.image import MIMEImage | |||||
from email.mime.text import MIMEText | |||||
import mimetypes | |||||
if not content_type: | |||||
content_type, encoding = mimetypes.guess_type(fname) | |||||
if content_type is None: | |||||
# No guess could be made, or the file is encoded (compressed), so | |||||
# use a generic bag-of-bits type. | |||||
content_type = 'application/octet-stream' | |||||
maintype, subtype = content_type.split('/', 1) | |||||
if maintype == 'text': | |||||
# Note: we should handle calculating the charset | |||||
if isinstance(fcontent, unicode): | |||||
fcontent = fcontent.encode("utf-8") | |||||
part = MIMEText(fcontent, _subtype=subtype, _charset="utf-8") | |||||
elif maintype == 'image': | |||||
part = MIMEImage(fcontent, _subtype=subtype) | |||||
elif maintype == 'audio': | |||||
part = MIMEAudio(fcontent, _subtype=subtype) | |||||
else: | |||||
part = MIMEBase(maintype, subtype) | |||||
part.set_payload(fcontent) | |||||
# Encode the payload using Base64 | |||||
from email import encoders | |||||
encoders.encode_base64(part) | |||||
# Set the filename parameter | |||||
if fname: | |||||
part.add_header(b'Content-Disposition', | |||||
("attachment; filename=%s" % fname).encode('utf-8')) | |||||
self.msg_root.attach(part) | |||||
def validate(self): | |||||
"""validate the email ids""" | |||||
from webnotes.utils import validate_email_add | |||||
def _validate(email): | |||||
"""validate an email field""" | |||||
if email and not validate_email_add(email): | |||||
webnotes.msgprint("%s is not a valid email id" % email, | |||||
raise_exception = 1) | |||||
return email | |||||
if not self.sender: | |||||
self.sender = webnotes.conn.get_value('Email Settings', None, | |||||
'auto_email_id') or webnotes.conf.get('auto_email_id') or None | |||||
if not self.sender: | |||||
webnotes.msgprint("""Please specify 'Auto Email Id' \ | |||||
in Setup > Email Settings""") | |||||
if not "expires_on" in webnotes.conf: | |||||
webnotes.msgprint("""Alternatively, \ | |||||
you can also specify 'auto_email_id' in site_config.json""") | |||||
raise webnotes.ValidationError | |||||
self.sender = _validate(self.sender) | |||||
self.reply_to = _validate(self.reply_to) | |||||
for e in self.recipients + (self.cc or []): | |||||
_validate(e.strip()) | |||||
def make(self): | |||||
"""build into msg_root""" | |||||
self.msg_root['Subject'] = self.subject.encode("utf-8") | |||||
self.msg_root['From'] = self.sender.encode("utf-8") | |||||
self.msg_root['To'] = ', '.join([r.strip() for r in self.recipients]).encode("utf-8") | |||||
self.msg_root['Date'] = email.utils.formatdate() | |||||
if not self.reply_to: | |||||
self.reply_to = self.sender | |||||
self.msg_root['Reply-To'] = self.reply_to.encode("utf-8") | |||||
if self.cc: | |||||
self.msg_root['CC'] = ', '.join([r.strip() for r in self.cc]).encode("utf-8") | |||||
def as_string(self): | |||||
"""validate, build message and convert to string""" | |||||
self.validate() | |||||
self.make() | |||||
return self.msg_root.as_string() | |||||
def get_formatted_html(subject, message, footer=None): | |||||
message = scrub_urls(message) | |||||
return inline_css(webnotes.get_template("templates/emails/standard.html").render({ | |||||
"content": message, | |||||
"footer": get_footer(footer), | |||||
"title": subject | |||||
})) | |||||
def get_footer(footer=None): | |||||
"""append a footer (signature)""" | |||||
footer = footer or "" | |||||
# control panel | |||||
footer += webnotes.conn.get_value('Control Panel',None,'mail_footer') or '' | |||||
# hooks | |||||
for f in webnotes.get_hooks("mail_footer"): | |||||
footer += webnotes.get_attr(f) | |||||
footer += "<!--unsubscribe link here-->" | |||||
return footer |
@@ -2,247 +2,43 @@ | |||||
# MIT License. See license.txt | # MIT License. See license.txt | ||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
""" | |||||
Sends email via outgoing server specified in "Control Panel" | |||||
Allows easy adding of Attachments of "File" objects | |||||
""" | |||||
import webnotes | import webnotes | ||||
from webnotes import conf | |||||
from webnotes import msgprint | |||||
from webnotes.utils import cint, expand_partial_links | |||||
import email.utils | |||||
def get_email(recipients, sender='', msg='', subject='[No Subject]', text_content = None, footer=None): | |||||
"""send an html email as multipart with attachments and all""" | |||||
email = EMail(sender, recipients, subject) | |||||
if (not '<br>' in msg) and (not '<p>' in msg) and (not '<div' in msg): | |||||
msg = msg.replace('\n', '<br>') | |||||
email.set_html(msg, text_content, footer=footer) | |||||
return email | |||||
class EMail: | |||||
""" | |||||
Wrapper on the email module. Email object represents emails to be sent to the client. | |||||
Also provides a clean way to add binary `FileData` attachments | |||||
Also sets all messages as multipart/alternative for cleaner reading in text-only clients | |||||
""" | |||||
def __init__(self, sender='', recipients=[], subject='', alternative=0, reply_to=None): | |||||
from email.mime.multipart import MIMEMultipart | |||||
from email import Charset | |||||
Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8') | |||||
if isinstance(recipients, basestring): | |||||
recipients = recipients.replace(';', ',').replace('\n', '') | |||||
recipients = recipients.split(',') | |||||
# remove null | |||||
recipients = filter(None, (r.strip() for r in recipients)) | |||||
self.sender = sender | |||||
self.reply_to = reply_to or sender | |||||
self.recipients = recipients | |||||
self.subject = subject | |||||
self.msg_root = MIMEMultipart('mixed') | |||||
self.msg_multipart = MIMEMultipart('alternative') | |||||
self.msg_root.attach(self.msg_multipart) | |||||
self.cc = [] | |||||
self.html_set = False | |||||
def set_html(self, message, text_content = None, footer=None): | |||||
"""Attach message in the html portion of multipart/alternative""" | |||||
message = message + self.get_footer(footer) | |||||
message = expand_partial_links(message) | |||||
# this is the first html part of a multi-part message, | |||||
# convert to text well | |||||
if not self.html_set: | |||||
if text_content: | |||||
self.set_text(text_content) | |||||
else: | |||||
self.set_html_as_text(message) | |||||
self.set_part_html(message) | |||||
self.html_set = True | |||||
def set_text(self, message): | |||||
""" | |||||
Attach message in the text portion of multipart/alternative | |||||
""" | |||||
from email.mime.text import MIMEText | |||||
part = MIMEText(message.encode('utf-8'), 'plain', 'utf-8') | |||||
self.msg_multipart.attach(part) | |||||
def set_part_html(self, message): | |||||
from email.mime.text import MIMEText | |||||
part = MIMEText(message.encode('utf-8'), 'html', 'utf-8') | |||||
self.msg_multipart.attach(part) | |||||
def set_html_as_text(self, html): | |||||
"""return html2text""" | |||||
import HTMLParser | |||||
from webnotes.utils.email_lib.html2text import html2text | |||||
try: | |||||
self.set_text(html2text(html)) | |||||
except HTMLParser.HTMLParseError: | |||||
pass | |||||
def set_message(self, message, mime_type='text/html', as_attachment=0, filename='attachment.html'): | |||||
"""Append the message with MIME content to the root node (as attachment)""" | |||||
from email.mime.text import MIMEText | |||||
maintype, subtype = mime_type.split('/') | |||||
part = MIMEText(message, _subtype = subtype) | |||||
if as_attachment: | |||||
part.add_header('Content-Disposition', 'attachment', filename=filename) | |||||
self.msg_root.attach(part) | |||||
def get_footer(self, footer=None): | |||||
"""append a footer (signature)""" | |||||
footer = footer or "" | |||||
footer += webnotes.conn.get_value('Control Panel',None,'mail_footer') or '' | |||||
other_footers = webnotes.get_hooks().mail_footer or [] | |||||
for f in other_footers: | |||||
footer += f | |||||
return footer | |||||
def attach_file(self, n): | |||||
"""attach a file from the `FileData` table""" | |||||
from webnotes.utils.file_manager import get_file | |||||
res = get_file(n) | |||||
if not res: | |||||
return | |||||
self.add_attachment(res[0], res[1]) | |||||
import smtplib | |||||
import _socket | |||||
from webnotes.utils import cint | |||||
def send(email, as_bulk=False): | |||||
"""send the message or add it to Outbox Email""" | |||||
if webnotes.flags.mute_emails or webnotes.conf.get("mute_emails") or False: | |||||
webnotes.msgprint("Emails are muted") | |||||
return | |||||
def add_attachment(self, fname, fcontent, content_type=None): | |||||
"""add attachment""" | |||||
from email.mime.audio import MIMEAudio | |||||
from email.mime.base import MIMEBase | |||||
from email.mime.image import MIMEImage | |||||
from email.mime.text import MIMEText | |||||
import mimetypes | |||||
if not content_type: | |||||
content_type, encoding = mimetypes.guess_type(fname) | |||||
if content_type is None: | |||||
# No guess could be made, or the file is encoded (compressed), so | |||||
# use a generic bag-of-bits type. | |||||
content_type = 'application/octet-stream' | |||||
maintype, subtype = content_type.split('/', 1) | |||||
if maintype == 'text': | |||||
# Note: we should handle calculating the charset | |||||
if isinstance(fcontent, unicode): | |||||
fcontent = fcontent.encode("utf-8") | |||||
part = MIMEText(fcontent, _subtype=subtype, _charset="utf-8") | |||||
elif maintype == 'image': | |||||
part = MIMEImage(fcontent, _subtype=subtype) | |||||
elif maintype == 'audio': | |||||
part = MIMEAudio(fcontent, _subtype=subtype) | |||||
else: | |||||
part = MIMEBase(maintype, subtype) | |||||
part.set_payload(fcontent) | |||||
# Encode the payload using Base64 | |||||
from email import encoders | |||||
encoders.encode_base64(part) | |||||
try: | |||||
smtpserver = SMTPServer() | |||||
if hasattr(smtpserver, "always_use_login_id_as_sender") and \ | |||||
cint(smtpserver.always_use_login_id_as_sender) and smtpserver.login: | |||||
if not email.reply_to: | |||||
email.reply_to = email.sender | |||||
email.sender = smtpserver.login | |||||
smtpserver.sess.sendmail(email.sender, email.recipients + (email.cc or []), | |||||
email.as_string()) | |||||
# Set the filename parameter | |||||
if fname: | |||||
part.add_header(b'Content-Disposition', | |||||
("attachment; filename=%s" % fname).encode('utf-8')) | |||||
self.msg_root.attach(part) | |||||
def validate(self): | |||||
"""validate the email ids""" | |||||
from webnotes.utils import validate_email_add | |||||
def _validate(email): | |||||
"""validate an email field""" | |||||
if email and not validate_email_add(email): | |||||
webnotes.msgprint("%s is not a valid email id" % email, | |||||
raise_exception = 1) | |||||
return email | |||||
if not self.sender: | |||||
self.sender = webnotes.conn.get_value('Email Settings', None, | |||||
'auto_email_id') or conf.get('auto_email_id') or None | |||||
if not self.sender: | |||||
webnotes.msgprint("""Please specify 'Auto Email Id' \ | |||||
in Setup > Email Settings""") | |||||
if not "expires_on" in conf: | |||||
webnotes.msgprint("""Alternatively, \ | |||||
you can also specify 'auto_email_id' in conf.py""") | |||||
raise webnotes.ValidationError | |||||
self.sender = _validate(self.sender) | |||||
self.reply_to = _validate(self.reply_to) | |||||
for e in self.recipients + (self.cc or []): | |||||
_validate(e.strip()) | |||||
def make(self): | |||||
"""build into msg_root""" | |||||
self.msg_root['Subject'] = self.subject.encode("utf-8") | |||||
self.msg_root['From'] = self.sender.encode("utf-8") | |||||
self.msg_root['To'] = ', '.join([r.strip() for r in self.recipients]).encode("utf-8") | |||||
self.msg_root['Date'] = email.utils.formatdate() | |||||
if not self.reply_to: | |||||
self.reply_to = self.sender | |||||
self.msg_root['Reply-To'] = self.reply_to.encode("utf-8") | |||||
if self.cc: | |||||
self.msg_root['CC'] = ', '.join([r.strip() for r in self.cc]).encode("utf-8") | |||||
def as_string(self): | |||||
"""validate, build message and convert to string""" | |||||
self.validate() | |||||
self.make() | |||||
return self.msg_root.as_string() | |||||
def send(self, as_bulk=False): | |||||
"""send the message or add it to Outbox Email""" | |||||
if webnotes.flags.mute_emails or conf.get("mute_emails") or False: | |||||
webnotes.msgprint("Emails are muted") | |||||
return | |||||
import smtplib | |||||
try: | |||||
smtpserver = SMTPServer() | |||||
if hasattr(smtpserver, "always_use_login_id_as_sender") and \ | |||||
cint(smtpserver.always_use_login_id_as_sender) and smtpserver.login: | |||||
if not self.reply_to: | |||||
self.reply_to = self.sender | |||||
self.sender = smtpserver.login | |||||
smtpserver.sess.sendmail(self.sender, self.recipients + (self.cc or []), | |||||
self.as_string()) | |||||
except smtplib.SMTPSenderRefused: | |||||
webnotes.msgprint("""Invalid Outgoing Mail Server's Login Id or Password. \ | |||||
Please rectify and try again.""") | |||||
raise | |||||
except smtplib.SMTPRecipientsRefused: | |||||
webnotes.msgprint("""Invalid Recipient (To) Email Address. \ | |||||
Please rectify and try again.""") | |||||
raise | |||||
except smtplib.SMTPSenderRefused: | |||||
webnotes.msgprint("""Invalid Outgoing Mail Server's Login Id or Password. \ | |||||
Please rectify and try again.""") | |||||
raise | |||||
except smtplib.SMTPRecipientsRefused: | |||||
webnotes.msgprint("""Invalid Recipient (To) Email Address. \ | |||||
Please rectify and try again.""") | |||||
raise | |||||
class SMTPServer: | class SMTPServer: | ||||
def __init__(self, login=None, password=None, server=None, port=None, use_ssl=None): | def __init__(self, login=None, password=None, server=None, port=None, use_ssl=None): | ||||
import webnotes.model.doc | |||||
from webnotes.utils import cint | |||||
# get defaults from control panel | # get defaults from control panel | ||||
try: | try: | ||||
es = webnotes.model.doc.Document('Email Settings','Email Settings') | |||||
es = webnotes.doc('Email Settings','Email Settings') | |||||
except webnotes.DoesNotExistError: | except webnotes.DoesNotExistError: | ||||
es = None | es = None | ||||
@@ -261,11 +57,11 @@ class SMTPServer: | |||||
self.password = es.mail_password | self.password = es.mail_password | ||||
self.always_use_login_id_as_sender = es.always_use_login_id_as_sender | self.always_use_login_id_as_sender = es.always_use_login_id_as_sender | ||||
else: | else: | ||||
self.server = conf.get("mail_server") or "" | |||||
self.port = conf.get("mail_port") or None | |||||
self.use_ssl = cint(conf.get("use_ssl") or 0) | |||||
self.login = conf.get("mail_login") or "" | |||||
self.password = conf.get("mail_password") or "" | |||||
self.server = webnotes.conf.get("mail_server") or "" | |||||
self.port = webnotes.conf.get("mail_port") or None | |||||
self.use_ssl = cint(webnotes.conf.get("use_ssl") or 0) | |||||
self.login = webnotes.conf.get("mail_login") or "" | |||||
self.password = webnotes.conf.get("mail_password") or "" | |||||
@property | @property | ||||
def sess(self): | def sess(self): | ||||
@@ -273,10 +69,6 @@ class SMTPServer: | |||||
if self._sess: | if self._sess: | ||||
return self._sess | return self._sess | ||||
from webnotes.utils import cint | |||||
import smtplib | |||||
import _socket | |||||
# check if email server specified | # check if email server specified | ||||
if not self.server: | if not self.server: | ||||
err_msg = 'Outgoing Mail Server not specified' | err_msg = 'Outgoing Mail Server not specified' | ||||
@@ -306,7 +98,7 @@ class SMTPServer: | |||||
# check if logged correctly | # check if logged correctly | ||||
if ret[0]!=235: | if ret[0]!=235: | ||||
msgprint(ret[1]) | |||||
webnotes.msgprint(ret[1]) | |||||
raise webnotes.OutgoingEmailError, ret[1] | raise webnotes.OutgoingEmailError, ret[1] | ||||
return self._sess | return self._sess | ||||