3
0
Fork 0
wuttaweb/src/wuttaweb/views/email.py

320 lines
9.1 KiB
Python

# -*- coding: utf-8; -*-
################################################################################
#
# wuttaweb -- Web App for Wutta Framework
# Copyright © 2024-2025 Lance Edgar
#
# This file is part of Wutta Framework.
#
# Wutta Framework is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# Wutta Framework is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Views for email settings
"""
import colander
from wuttaweb.views import MasterView
from wuttaweb.forms.schema import EmailRecipients
class EmailSettingView(MasterView): # pylint: disable=abstract-method
"""
Master view for :term:`email settings <email setting>`.
"""
model_name = "email_setting"
model_key = "key"
model_title = "Email Setting"
url_prefix = "/email/settings"
filterable = False
sortable = True
sort_on_backend = False
paginated = False
creatable = False
deletable = False
labels = {
"key": "Email Key",
"replyto": "Reply-To",
}
grid_columns = [
"key",
"subject",
"to",
"enabled",
]
# TODO: why does this not work?
sort_defaults = "key"
form_fields = [
"key",
"description",
"subject",
"sender",
"replyto",
"to",
"cc",
"bcc",
"notes",
"enabled",
]
def __init__(self, request, context=None):
super().__init__(request, context=context)
self.email_handler = self.app.get_email_handler()
def get_grid_data(self, columns=None, session=None):
"""
This view calls
:meth:`~wuttjamaican:wuttjamaican.email.EmailHandler.get_email_settings()`
on the :attr:`email_handler` to obtain its grid data.
"""
data = []
for setting in self.email_handler.get_email_settings().values():
data.append(self.normalize_setting(setting))
return data
def normalize_setting(self, setting): # pylint: disable=empty-docstring
""" """
key = setting.__name__
return {
"key": key,
"description": setting.__doc__,
"subject": self.email_handler.get_auto_subject(
key, rendered=False, setting=setting
),
"sender": self.email_handler.get_auto_sender(key),
"replyto": self.email_handler.get_auto_replyto(key) or colander.null,
"to": self.email_handler.get_auto_to(key),
"cc": self.email_handler.get_auto_cc(key),
"bcc": self.email_handler.get_auto_bcc(key),
"notes": self.email_handler.get_notes(key) or colander.null,
"enabled": self.email_handler.is_enabled(key),
}
def configure_grid(self, grid): # pylint: disable=empty-docstring
""" """
g = grid
super().configure_grid(g)
# key
g.set_searchable("key")
g.set_link("key")
# subject
g.set_searchable("subject")
g.set_link("subject")
# to
g.set_renderer("to", self.render_to_short)
def render_to_short( # pylint: disable=empty-docstring,unused-argument
self, setting, field, value
):
""" """
recips = value
if not recips:
return None
if len(recips) < 3:
return ", ".join(recips)
recips = ", ".join(recips[:2])
return f"{recips}, ..."
def get_instance( # pylint: disable=empty-docstring,arguments-differ,unused-argument
self, **kwargs
):
""" """
key = self.request.matchdict["key"]
setting = self.email_handler.get_email_setting(key, instance=False)
if setting:
return self.normalize_setting(setting)
raise self.notfound()
def get_instance_title(self, instance): # pylint: disable=empty-docstring
""" """
setting = instance
return setting["subject"]
def configure_form(self, form): # pylint: disable=empty-docstring
""" """
f = form
super().configure_form(f)
# description
f.set_readonly("description")
# replyto
f.set_required("replyto", False)
# to
f.set_node("to", EmailRecipients())
# cc
f.set_node("cc", EmailRecipients())
# bcc
f.set_node("bcc", EmailRecipients())
# notes
f.set_widget("notes", "notes")
f.set_required("notes", False)
# enabled
f.set_node("enabled", colander.Boolean())
def persist( # pylint: disable=too-many-branches,empty-docstring,arguments-differ,unused-argument
self, setting, **kwargs
):
""" """
session = self.Session()
key = self.request.matchdict["key"]
def save(name, value):
self.app.save_setting(
session, f"{self.config.appname}.email.{key}.{name}", value
)
def delete(name):
self.app.delete_setting(
session, f"{self.config.appname}.email.{key}.{name}"
)
# subject
if setting["subject"]:
save("subject", setting["subject"])
else:
delete("subject")
# sender
if setting["sender"]:
save("sender", setting["sender"])
else:
delete("sender")
# replyto
if setting["replyto"]:
save("replyto", setting["replyto"])
else:
delete("replyto")
# to
if setting["to"]:
save("to", setting["to"])
else:
delete("to")
# cc
if setting["cc"]:
save("cc", setting["cc"])
else:
delete("cc")
# bcc
if setting["bcc"]:
save("bcc", setting["bcc"])
else:
delete("bcc")
# notes
if setting["notes"]:
save("notes", setting["notes"])
else:
delete("notes")
# enabled
save("enabled", "true" if setting["enabled"] else "false")
def render_to_response(self, template, context): # pylint: disable=empty-docstring
""" """
if self.viewing:
setting = context["instance"]
context["setting"] = setting
context["has_html_template"] = self.email_handler.get_auto_body_template(
setting["key"], "html"
)
context["has_txt_template"] = self.email_handler.get_auto_body_template(
setting["key"], "txt"
)
return super().render_to_response(template, context)
def preview(self):
"""
View for showing a rendered preview of a given email template.
This will render the email template according to the "mode"
requested - i.e. HTML or TXT.
"""
key = self.request.matchdict["key"]
setting = self.email_handler.get_email_setting(key)
context = setting.sample_data()
mode = self.request.params.get("mode", "html")
if mode == "txt":
body = self.email_handler.get_auto_txt_body(key, context)
self.request.response.content_type = "text/plain"
else: # html
body = self.email_handler.get_auto_html_body(key, context)
self.request.response.text = body
return self.request.response
@classmethod
def defaults(cls, config): # pylint: disable=empty-docstring
""" """
cls._email_defaults(config)
cls._defaults(config)
@classmethod
def _email_defaults(cls, config):
""" """
route_prefix = cls.get_route_prefix()
permission_prefix = cls.get_permission_prefix()
model_title_plural = cls.get_model_title_plural()
instance_url_prefix = cls.get_instance_url_prefix()
# fix permission group
config.add_wutta_permission_group(
permission_prefix, model_title_plural, overwrite=False
)
# preview
config.add_route(f"{route_prefix}.preview", f"{instance_url_prefix}/preview")
config.add_view(
cls,
attr="preview",
route_name=f"{route_prefix}.preview",
permission=f"{permission_prefix}.view",
)
def defaults(config, **kwargs): # pylint: disable=missing-function-docstring
base = globals()
EmailSettingView = kwargs.get( # pylint: disable=invalid-name,redefined-outer-name
"EmailSettingView", base["EmailSettingView"]
)
EmailSettingView.defaults(config)
def includeme(config): # pylint: disable=missing-function-docstring
defaults(config)