Skip to content

Commit

Permalink
Make recipients for sendEMailToAdmins configurable (#798)
Browse files Browse the repository at this point in the history
It is now possible to set recipients for admin emails via config `conf["viur.email.admin_recipients"]`.
Otherwise all root users are still addressed. By using `normalize_to_list` and its enhancement,
a method as value is also possible. This allows e.g. dynamic recipient selections like own `UserSkel` queries.
Also `conf["viur.email.recipientOverride"]` is now callable.
  • Loading branch information
sveneberth authored Jul 25, 2023
1 parent cfefb35 commit f93c456
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 28 deletions.
5 changes: 4 additions & 1 deletion core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,15 @@ def __setitem__(self, key, value):
# If set, we'll enable sending emails from the local development server. Otherwise, they'll just be logged.
"viur.email.sendFromLocalDevelopmentServer": False,

# If set, all outgoing emails will be sent to this address (overriding the 'dests'-parameter in utils.sendEmail)
# If set, all outgoing emails will be sent to this address (overriding the 'dests'-parameter in email.sendEmail)
"viur.email.recipientOverride": None,

# If set, this sender will be used, regardless of what the templates advertise as sender
"viur.email.senderOverride": None,

# Sets recipients for mails send with email.sendEMailToAdmins. If not set, all root users will be used.
"viur.email.admin_recipients": None,

# If set, ViUR calls this function instead of rendering the viur.errorTemplate if an exception occurs
"viur.errorHandler": None,

Expand Down
60 changes: 33 additions & 27 deletions core/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import json, base64
from urllib import request
from abc import ABC, abstractmethod
from typing import Any, Union, List, Dict
from typing import Any, Callable, Union, List, Dict
from viur.core.config import conf
from viur.core import utils, db
from viur.core.tasks import CallDeferred, QueryIter, PeriodicTask, DeleteEntitiesIter
Expand Down Expand Up @@ -119,10 +119,14 @@ def sendEmailDeferred(emailKey: db.Key):
logging.exception(e)


def normalizeToList(value: Union[None, Any, List[Any]]) -> List[Any]:
def normalize_to_list(value: Union[None, Any, List[Any], Callable[[], List]]) -> List[Any]:
"""
Convert the given value to a list.
Convert the given value to a list.
If the value parameter is callable, it will be called first to get the actual value.
"""
if callable(value):
value = value()
if value is None:
return []
if isinstance(value, list):
Expand All @@ -141,7 +145,7 @@ def sendEMail(*,
headers: Dict[str, str] = None,
attachments: List[Dict[str, Any]] = None,
context: Union[db.DATASTORE_BASE_TYPES, List[db.DATASTORE_BASE_TYPES], db.Entity] = None,
**kwargs) -> Any:
**kwargs) -> bool:
"""
General purpose function for sending e-mail.
This function allows for sending e-mails, also with generated content using the Jinja2 template engine.
Expand Down Expand Up @@ -176,14 +180,14 @@ def sendEMail(*,
transportClass = conf["viur.email.transportClass"] # First, ensure we're able to send email at all
assert issubclass(transportClass, EmailTransport), "No or invalid email transportclass specified!"
# Ensure that all recipient parameters (dest, cc, bcc) are a list
dests = normalizeToList(dests)
cc = normalizeToList(cc)
bcc = normalizeToList(bcc)
dests = normalize_to_list(dests)
cc = normalize_to_list(cc)
bcc = normalize_to_list(bcc)
assert dests or cc or bcc, "No destination address given"
assert all([isinstance(x, str) and x for x in dests]), "Found non-string or empty destination address"
assert all([isinstance(x, str) and x for x in cc]), "Found non-string or empty cc address"
assert all([isinstance(x, str) and x for x in bcc]), "Found non-string or empty bcc address"
attachments = normalizeToList(attachments)
attachments = normalize_to_list(attachments)
if not (bool(stringTemplate) ^ bool(tpl)):
raise ValueError("You have to set the params 'tpl' xor a 'stringTemplate'.")
if attachments:
Expand All @@ -202,7 +206,7 @@ def sendEMail(*,
if conf["viur.email.recipientOverride"]:
logging.warning("Overriding destination %s with %s", dests, conf["viur.email.recipientOverride"])
oldDests = dests
newDests = normalizeToList(conf["viur.email.recipientOverride"])
newDests = normalize_to_list(conf["viur.email.recipientOverride"])
dests = []
for newDest in newDests:
if newDest.startswith("@"):
Expand Down Expand Up @@ -246,29 +250,31 @@ def sendEMail(*,
return True


def sendEMailToAdmins(subject: str, body: str, *args, **kwargs):
def sendEMailToAdmins(subject: str, body: str, *args, **kwargs) -> bool:
"""
Sends an e-mail to the root users of the current app.
Sends an e-mail to the root users of the current app.
If conf["viur.email.admin_recipients"] is set, these recipients
will be used instead of the root users.
:param subject: Defines the subject of the message.
:param body: Defines the message body.
:param subject: Defines the subject of the message.
:param body: Defines the message body.
"""
success = False
try:
if "user" in dir(conf["viur.mainApp"]):
users = []
for userSkel in conf["viur.mainApp"].user.viewSkel().all().filter("access =", "root").fetch():
users.append(userSkel["name"])

if users:
ret = sendEMail(dests=users, stringTemplate=os.linesep.join((subject, body)), *args, **kwargs)
success = True
return ret
else:
logging.warning("There are no root-users.")

except Exception:
raise
users = []
if conf["viur.email.admin_recipients"] is not None:
users = normalize_to_list(conf["viur.email.admin_recipients"])
elif "user" in dir(conf["viur.mainApp"]):
for user_skel in conf["viur.mainApp"].user.viewSkel().all().filter("access =", "root").fetch():
users.append(user_skel["name"])

if users:
ret = sendEMail(dests=users, stringTemplate=os.linesep.join((subject, body)), *args, **kwargs)
success = True
return ret
else:
logging.warning("There are no recipients for admin e-mails available.")

finally:
if not success:
Expand Down

0 comments on commit f93c456

Please sign in to comment.