diff --git a/tailbone/templates/email-bounces/view.mako b/tailbone/templates/email-bounces/view.mako index 610118ed..f8372c88 100644 --- a/tailbone/templates/email-bounces/view.mako +++ b/tailbone/templates/email-bounces/view.mako @@ -48,9 +48,7 @@ <%def name="render_this_page()"> ${parent.render_this_page()} -
+ %def> diff --git a/tailbone/views/bouncer.py b/tailbone/views/bouncer.py index 628ed07c..3416bbed 100644 --- a/tailbone/views/bouncer.py +++ b/tailbone/views/bouncer.py @@ -2,7 +2,7 @@ ################################################################################ # # Rattail -- Retail Software Framework -# Copyright © 2010-2022 Lance Edgar +# Copyright © 2010-2023 Lance Edgar # # This file is part of Rattail. # @@ -24,18 +24,12 @@ Views for Email Bounces """ -from __future__ import unicode_literals, absolute_import - import os import datetime -import six - from rattail.db import model -from rattail.bouncer import get_handler from rattail.bouncer.config import get_profile_keys -from pyramid.response import FileResponse from webhelpers2.html import HTML, tags from tailbone.views import MasterView @@ -50,6 +44,7 @@ class EmailBounceView(MasterView): url_prefix = '/email-bounces' creatable = False editable = False + downloadable = True labels = { 'config_key': "Source", @@ -70,7 +65,8 @@ class EmailBounceView(MasterView): self.handler_options = sorted(get_profile_keys(self.rattail_config)) def get_handler(self, bounce): - return get_handler(self.rattail_config, bounce.config_key) + app = self.get_rattail_app() + return app.get_bounce_handler(bounce.config_key) def configure_grid(self, g): super(EmailBounceView, self).configure_grid(g) @@ -142,11 +138,16 @@ class EmailBounceView(MasterView): path = handler.msgpath(bounce) if os.path.exists(path): with open(path, 'rb') as f: - kwargs['message'] = f.read() + # TODO: how to determine encoding? (is utf_8 guaranteed?) + kwargs['message'] = f.read().decode('utf_8') else: kwargs['message'] = "(file not found)" return kwargs + def download_path(self, bounce, filename): + handler = self.get_handler(bounce) + return handler.msgpath(bounce) + # TODO: should require POST here def process(self): """ @@ -169,20 +170,13 @@ class EmailBounceView(MasterView): self.request.session.flash("Email bounce has been marked UN-processed.") return self.redirect(self.get_action_url('view', bounce)) - def download(self): - """ - View for downloading the message file associated with a bounce. - """ - bounce = self.get_instance() - handler = self.get_handler(bounce) - path = handler.msgpath(bounce) - response = FileResponse(path, request=self.request) - response.headers[b'Content-Length'] = six.binary_type(os.path.getsize(path)) - response.headers[b'Content-Disposition'] = b'attachment; filename="bounce.eml"' - return response - @classmethod def defaults(cls, config): + cls._bounce_defaults(config) + cls._defaults(config) + + @classmethod + def _bounce_defaults(cls, config): config.add_tailbone_permission_group('emailbounces', "Email Bounces", overwrite=False) @@ -200,15 +194,6 @@ class EmailBounceView(MasterView): config.add_tailbone_permission('emailbounces', 'emailbounces.unprocess', "Mark Email Bounce as UN-processed") - # download raw email - config.add_route('emailbounces.download', '/email-bounces/{uuid}/download') - config.add_view(cls, attr='download', route_name='emailbounces.download', - permission='emailbounces.download') - config.add_tailbone_permission('emailbounces', 'emailbounces.download', - "Download raw message of Email Bounce") - - cls._defaults(config) - def defaults(config, **kwargs): base = globals() diff --git a/tailbone/views/master.py b/tailbone/views/master.py index 5027f230..4aacc9f1 100644 --- a/tailbone/views/master.py +++ b/tailbone/views/master.py @@ -1593,9 +1593,9 @@ class MasterView(View): """ obj = self.get_instance() filename = self.request.GET.get('filename', None) - if not filename: - raise self.notfound() path = self.download_path(obj, filename) + if not path or not os.path.exists(path): + raise self.notfound() response = FileResponse(path, request=self.request) response.content_length = os.path.getsize(path) content_type = self.download_content_type(path, filename)