Skip to content

Commit

Permalink
Merge branch 'master' into fix/replace-paster-make-config
Browse files Browse the repository at this point in the history
  • Loading branch information
mpolidori committed Jan 27, 2020
2 parents 5e99eb1 + 2f5ba7a commit 0b0dbf5
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 17 deletions.
3 changes: 1 addition & 2 deletions ckan/config/middleware/flask_app.py
Expand Up @@ -134,8 +134,7 @@ def make_flask_stack(conf, **app_conf):
DebugToolbarExtension(app)

from werkzeug.debug import DebuggedApplication
app = DebuggedApplication(app, True)
app = app.app
app.wsgi_app = DebuggedApplication(app.wsgi_app, True)

log = logging.getLogger('werkzeug')
log.setLevel(logging.DEBUG)
Expand Down
24 changes: 18 additions & 6 deletions ckan/lib/mailer.py
Expand Up @@ -6,6 +6,7 @@
import socket
import logging
from time import time
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header
from email import utils
Expand All @@ -30,14 +31,23 @@ class MailerException(Exception):

def _mail_recipient(recipient_name, recipient_email,
sender_name, sender_url, subject,
body, headers=None):
body, body_html=None, headers=None):

if not headers:
headers = {}

mail_from = config.get('smtp.mail_from')
reply_to = config.get('smtp.reply_to')
msg = MIMEText(body.encode('utf-8'), 'plain', 'utf-8')
if body_html:
# multipart
msg = MIMEMultipart('alternative')
part1 = MIMEText(body.encode('utf-8'), 'plain', 'utf-8')
part2 = MIMEText(body_html.encode('utf-8'), 'html', 'utf-8')
msg.attach(part1)
msg.attach(part2)
else:
# just plain text
msg = MIMEText(body.encode('utf-8'), 'plain', 'utf-8')
for k, v in headers.items():
if k in msg.keys():
msg.replace_header(k, v)
Expand Down Expand Up @@ -107,19 +117,21 @@ def _mail_recipient(recipient_name, recipient_email,


def mail_recipient(recipient_name, recipient_email, subject,
body, headers={}):
body, body_html=None, headers={}):
'''Sends an email'''
site_title = config.get('ckan.site_title')
site_url = config.get('ckan.site_url')
return _mail_recipient(recipient_name, recipient_email,
site_title, site_url, subject, body,
headers=headers)
body_html=body_html, headers=headers)


def mail_user(recipient, subject, body, headers={}):
def mail_user(recipient, subject, body, body_html=None, headers={}):
'''Sends an email to a CKAN user'''
if (recipient.email is None) or not len(recipient.email):
raise MailerException(_("No recipient email address available!"))
mail_recipient(recipient.display_name, recipient.email, subject,
body, headers=headers)
body, body_html=body_html, headers=headers)


def get_reset_link_body(user):
Expand Down
7 changes: 7 additions & 0 deletions ckan/plugins/toolkit.py
Expand Up @@ -108,6 +108,10 @@ class _Toolkit(object):
'HelperError',
# Enqueue background job
'enqueue_job',
# Email a recipient
'mail_recipient',
# Email a user
'mail_user',

# Fully defined in this file ##
'add_template_directory',
Expand Down Expand Up @@ -148,6 +152,7 @@ def _initialize(self):
HelperError
)
from ckan.lib.jobs import enqueue as enqueue_job
from ckan.lib import mailer

import ckan.common as converters
if six.PY2:
Expand Down Expand Up @@ -269,6 +274,8 @@ def _initialize(self):
t['auth_disallow_anonymous_access'] = (
logic.auth_disallow_anonymous_access
)
t['mail_recipient'] = mailer.mail_recipient
t['mail_user'] = mailer.mail_user

# class functions
t['render_snippet'] = self._render_snippet
Expand Down
43 changes: 41 additions & 2 deletions ckan/tests/lib/test_mailer.py
Expand Up @@ -17,8 +17,8 @@


class MailerBase(object):
def mime_encode(self, msg, recipient_name):
text = MIMEText(msg.encode("utf-8"), "plain", "utf-8")
def mime_encode(self, msg, recipient_name, subtype='plain'):
text = MIMEText(msg.encode("utf-8"), subtype, "utf-8")
encoded_body = text.get_payload().strip()
return encoded_body

Expand Down Expand Up @@ -57,11 +57,50 @@ def test_mail_recipient(self, mail_server):
assert list(test_email["headers"].keys())[0] in msg[3], msg[3]
assert list(test_email["headers"].values())[0] in msg[3], msg[3]
assert test_email["subject"] in msg[3], msg[3]
assert msg[3].startswith('Content-Type: text/plain'), msg[3]
expected_body = self.mime_encode(
test_email["body"], test_email["recipient_name"]
)
assert expected_body in msg[3]

def test_mail_recipient_with_html(self, mail_server):
user = factories.User()

msgs = mail_server.get_smtp_messages()
assert msgs == []

# send email
test_email = {
"recipient_name": "Bob",
"recipient_email": user["email"],
"subject": "Meeting",
"body": "The meeting is cancelled.",
"body_html": "The <a href=\"meeting\">meeting</a> is cancelled.",
"headers": {"header1": "value1"},
}
mailer.mail_recipient(**test_email)

# check it went to the mock smtp server
msgs = mail_server.get_smtp_messages()
assert len(msgs) == 1
msg = msgs[0]
assert msg[1] == config["smtp.mail_from"]
assert msg[2] == [test_email["recipient_email"]]
assert test_email["headers"].keys()[0] in msg[3], msg[3]
assert test_email["headers"].values()[0] in msg[3], msg[3]
assert test_email["subject"] in msg[3], msg[3]
assert msg[3].startswith('Content-Type: multipart'), msg[3]
expected_plain_body = self.mime_encode(
test_email["body"], test_email["recipient_name"],
subtype='plain'
)
assert expected_plain_body in msg[3]
expected_html_body = self.mime_encode(
test_email["body_html"], test_email["recipient_name"],
subtype='html'
)
assert expected_html_body in msg[3]

def test_mail_user(self, mail_server):

user = factories.User()
Expand Down
19 changes: 13 additions & 6 deletions ckan/views/resource.py
Expand Up @@ -696,26 +696,33 @@ def get(
extra_vars.update(post_extra)

package_type = _get_package_type(id)
data = extra_vars[u'data']
view_type = None
data = extra_vars[u'data'] if u'data' in extra_vars else None
if data and u'view_type' in data:
view_type = data.get(u'view_type')
else:
view_type = request.args.get(u'view_type')

# view_id exists only when updating
if view_id:
if not data:
if not data or not view_type:
try:
data = get_action(u'resource_view_show')(
view_data = get_action(u'resource_view_show')(
context, {
u'id': view_id
}
)
view_type = view_data[u'view_type']
if data:
data.update(view_data)
else:
data = view_data
except (NotFound, NotAuthorized):
return base.abort(404, _(u'View not found'))

view_type = data.get(u'view_type')
# might as well preview when loading good existing view
if not extra_vars[u'errors']:
to_preview = True

view_type = view_type or request.args.get(u'view_type')
data[u'view_type'] = view_type
view_plugin = lib_datapreview.get_view_plugin(view_type)
if not view_plugin:
Expand Down
2 changes: 1 addition & 1 deletion doc/maintaining/configuration.rst
Expand Up @@ -607,7 +607,7 @@ Example::
Default value: ``admin``


Makes role permissions apply to all the groups down the hierarchy from the groups that the role is applied to.
Makes role permissions apply to all the groups or organizations down the hierarchy from the groups or organizations that the role is applied to.

e.g. a particular user has the 'admin' role for group 'Department of Health'. If you set the value of this option to 'admin' then the user will automatically have the same admin permissions for the child groups of 'Department of Health' such as 'Cancer Research' (and its children too and so on).

Expand Down

0 comments on commit 0b0dbf5

Please sign in to comment.