From d52a186e1254fb31813189e8fba7337543d00030 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 6 Aug 2022 18:38:17 -0500 Subject: [PATCH] Add support for toggling visibility of email profile settings --- tailbone/grids/core.py | 6 + tailbone/templates/grids/buefy.mako | 13 +- tailbone/templates/master/index.mako | 2 +- tailbone/templates/settings/email/index.mako | 63 ++++++++++ tailbone/views/email.py | 122 ++++++++++++++++--- 5 files changed, 188 insertions(+), 18 deletions(-) create mode 100644 tailbone/templates/settings/email/index.mako diff --git a/tailbone/grids/core.py b/tailbone/grids/core.py index 5360c894..b15dcafd 100644 --- a/tailbone/grids/core.py +++ b/tailbone/grids/core.py @@ -1492,6 +1492,12 @@ class GridAction(object): return self.url(row, i) return self.url + def render_icon(self): + """ + Render the HTML snippet for the action link icon. + """ + return HTML.tag('i', class_='fas fa-{}'.format(self.icon)) + def render_label(self): """ Render the label "text" within the actions column of a grid diff --git a/tailbone/templates/grids/buefy.mako b/tailbone/templates/grids/buefy.mako index 0801bbe8..11b9a86b 100644 --- a/tailbone/templates/grids/buefy.mako +++ b/tailbone/templates/grids/buefy.mako @@ -135,7 +135,7 @@ - + ${action.render_icon()|n} ${action.render_label()|n}   @@ -296,15 +296,24 @@ let ${grid.component_studly} = { template: '#${grid.component}-template', + mixins: [FormPosterMixin], + props: { csrftoken: String, }, computed: { + // note, can use this with v-model for hidden 'uuids' fields selected_uuids: function() { return this.checkedRowUUIDs().join(',') }, + + // nb. this can be overridden if needed, e.g. to dynamically + // show/hide certain records in a static data set + visibleData() { + return this.data + }, }, methods: { diff --git a/tailbone/templates/master/index.mako b/tailbone/templates/master/index.mako index 5830519b..053e09fb 100644 --- a/tailbone/templates/master/index.mako +++ b/tailbone/templates/master/index.mako @@ -481,7 +481,7 @@ <%def name="render_grid_component()"> - <${grid.component} :csrftoken="csrftoken" + <${grid.component} ref="grid" :csrftoken="csrftoken" % if master.deletable and request.has_perm('{}.delete'.format(permission_prefix)) and master.delete_confirm == 'simple': @deleteActionClicked="deleteObject" % endif diff --git a/tailbone/templates/settings/email/index.mako b/tailbone/templates/settings/email/index.mako new file mode 100644 index 00000000..11881285 --- /dev/null +++ b/tailbone/templates/settings/email/index.mako @@ -0,0 +1,63 @@ +## -*- coding: utf-8; -*- +<%inherit file="/master/index.mako" /> + +<%def name="render_grid_component()"> + % if master.has_perm('configure'): + + + + + + + + % endif + + ${parent.render_grid_component()} + + +<%def name="modify_this_page_vars()"> + ${parent.modify_this_page_vars()} + % if master.has_perm('configure'): + + % endif + + +${parent.body()} diff --git a/tailbone/views/email.py b/tailbone/views/email.py index 8a03f8a2..a6620932 100644 --- a/tailbone/views/email.py +++ b/tailbone/views/email.py @@ -27,6 +27,7 @@ Email Views from __future__ import unicode_literals, absolute_import import re +import warnings import six @@ -38,6 +39,7 @@ import colander from deform import widget as dfwidget from webhelpers2.html import HTML +from tailbone import grids from tailbone.db import Session from tailbone.views import View, MasterView @@ -63,6 +65,7 @@ class EmailSettingView(MasterView): 'subject', 'to', 'enabled', + 'hidden', ] form_fields = [ @@ -77,11 +80,19 @@ class EmailSettingView(MasterView): 'cc', 'bcc', 'enabled', + 'hidden', ] def __init__(self, request): super(EmailSettingView, self).__init__(request) - self.handler = self.get_handler() + self.email_handler = self.get_handler() + + @property + def handler(self): + warnings.warn("the `handler` property is deprecated! " + "please use `email_handler` instead", + DeprecationWarning, stacklevel=2) + return self.email_handler def get_handler(self): app = self.get_rattail_app() @@ -89,9 +100,12 @@ class EmailSettingView(MasterView): def get_data(self, session=None): data = [] - for email in self.handler.iter_emails(): - key = email.key or email.__name__ - email = email(self.rattail_config, key) + if self.has_perm('configure'): + emails = self.email_handler.get_all_emails() + else: + emails = self.email_handler.get_available_emails() + for key, Email in six.iteritems(emails): + email = Email(self.rattail_config, key) data.append(self.normalize(email)) return data @@ -112,6 +126,20 @@ class EmailSettingView(MasterView): g.set_renderer('to', self.render_to_short) g.sorters['to'] = g.make_simple_sorter('to', foldcase=True) + # hidden + if self.has_perm('configure'): + g.sorters['hidden'] = g.make_simple_sorter('hidden') + g.set_type('hidden', 'boolean') + else: + g.remove('hidden') + + # toggle hidden + if self.has_perm('configure'): + g.main_actions.append( + self.make_action('toggle_hidden', url='#', icon='ban', + click_handler='toggleHidden(props.row)', + factory=ToggleHidden)) + def render_to_short(self, email, column): profile = email['_email'] if self.rattail_config.production(): @@ -133,7 +161,7 @@ class EmailSettingView(MasterView): if recips: return ', '.join(recips) data = email.obtain_sample_data(self.request) - return { + normal = { '_email': email, 'key': email.key, 'fallback_key': email.fallback_key, @@ -147,10 +175,13 @@ class EmailSettingView(MasterView): 'bcc': get_recips('bcc') or '', 'enabled': email.get_enabled(), } + if self.has_perm('configure'): + normal['hidden'] = self.email_handler.email_is_hidden(email.key) + return normal def get_instance(self): key = self.request.matchdict['key'] - return self.normalize(self.handler.get_email(key)) + return self.normalize(self.email_handler.get_email(key)) def get_instance_title(self, email): return email['_email'].get_complete_subject(render=False) @@ -207,8 +238,20 @@ class EmailSettingView(MasterView): # enabled f.set_type('enabled', 'boolean') + # hidden + if self.has_perm('configure'): + f.set_type('hidden', 'boolean') + else: + f.remove('hidden') + def make_form_schema(self): - return EmailProfileSchema() + schema = EmailProfileSchema() + + if not self.has_perm('configure'): + hidden = schema.get('hidden') + schema.children.remove(hidden) + + return schema def save_edit_form(self, form): key = self.request.matchdict['key'] @@ -223,11 +266,13 @@ class EmailSettingView(MasterView): app.save_setting(session, 'rattail.mail.{}.cc'.format(key), (data['cc'] or '').replace('\n', ', ')) app.save_setting(session, 'rattail.mail.{}.bcc'.format(key), (data['bcc'] or '').replace('\n', ', ')) app.save_setting(session, 'rattail.mail.{}.enabled'.format(key), six.text_type(data['enabled']).lower()) + if self.has_perm('configure'): + app.save_setting(session, 'rattail.mail.{}.hidden'.format(key), six.text_type(data['hidden']).lower()) return data def template_kwargs_view(self, **kwargs): key = self.request.matchdict['key'] - kwargs['email'] = self.handler.get_email(key) + kwargs['email'] = self.email_handler.get_email(key) return kwargs def configure_get_simple_settings(self): @@ -240,10 +285,48 @@ class EmailSettingView(MasterView): 'type': bool}, ] + def toggle_hidden(self): + app = self.get_rattail_app() + data = self.request.json_body + name = 'rattail.mail.{}.hidden'.format(data['key']) + app.save_setting(self.Session(), name, + 'true' if data['hidden'] else 'false') + return {'ok': True} + + @classmethod + def defaults(cls, config): + cls._email_defaults(config) + cls._defaults(config) + + @classmethod + def _email_defaults(cls, config): + route_prefix = cls.get_route_prefix() + url_prefix = cls.get_url_prefix() + permission_prefix = cls.get_permission_prefix() + model_title_plural = cls.get_model_title_plural() + + # toggle hidden + config.add_route('{}.toggle_hidden'.format(route_prefix), + '{}/toggle-hidden'.format(url_prefix), + request_method='POST') + config.add_view(cls, attr='toggle_hidden', + route_name='{}.toggle_hidden'.format(route_prefix), + permission='{}.configure'.format(permission_prefix), + renderer='json') + # TODO: deprecate / remove this ProfilesView = EmailSettingView +class ToggleHidden(grids.GridAction): + """ + Grid action for toggling the 'hidden' flag for an email profile. + """ + + def render_label(self): + return '{{ renderLabelToggleHidden(props.row) }}' + + class RecipientsType(colander.String): """ Custom schema type for email recipients. This is used to present the @@ -284,6 +367,8 @@ class EmailProfileSchema(colander.MappingSchema): enabled = colander.SchemaNode(colander.Boolean()) + hidden = colander.SchemaNode(colander.Boolean()) + class EmailPreview(View): """ @@ -292,7 +377,14 @@ class EmailPreview(View): def __init__(self, request): super(EmailPreview, self).__init__(request) - self.handler = self.get_handler() + self.email_handler = self.get_handler() + + @property + def handler(self): + warnings.warn("the `handler` property is deprecated! " + "please use `email_handler` instead", + DeprecationWarning, stacklevel=2) + return self.email_handler def get_handler(self): app = self.get_rattail_app() @@ -319,20 +411,20 @@ class EmailPreview(View): if recipient: key = self.request.POST.get('email_key') if key: - email = self.handler.get_email(key) + email = self.email_handler.get_email(key) data = email.obtain_sample_data(self.request) - self.handler.send_message(email, data, - subject_prefix="[PREVIEW] ", - to=[recipient], - cc=None, bcc=None) + self.email_handler.send_message(email, data, + subject_prefix="[PREVIEW] ", + to=[recipient], + cc=None, bcc=None) self.request.session.flash( "Preview for '{}' was emailed to {}".format( key, recipient)) def preview_template(self, key, type_): - email = self.handler.get_email(key) + email = self.email_handler.get_email(key) template = email.get_template(type_) data = email.obtain_sample_data(self.request) self.request.response.text = template.render(**data)