Add support for toggling visibility of email profile settings

This commit is contained in:
Lance Edgar 2022-08-06 18:38:17 -05:00
parent 7d3f2e6bdf
commit d52a186e12
5 changed files with 188 additions and 18 deletions

View file

@ -1492,6 +1492,12 @@ class GridAction(object):
return self.url(row, i) return self.url(row, i)
return self.url 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): def render_label(self):
""" """
Render the label "text" within the actions column of a grid Render the label "text" within the actions column of a grid

View file

@ -135,7 +135,7 @@
</div> </div>
<b-table <b-table
:data="data" :data="visibleData"
## :columns="columns" ## :columns="columns"
:loading="loading" :loading="loading"
:row-class="getRowClass" :row-class="getRowClass"
@ -211,7 +211,7 @@
@click.prevent="${action.click_handler}" @click.prevent="${action.click_handler}"
% endif % endif
> >
<i class="fas fa-${action.icon}"></i> ${action.render_icon()|n}
${action.render_label()|n} ${action.render_label()|n}
</a> </a>
&nbsp; &nbsp;
@ -296,15 +296,24 @@
let ${grid.component_studly} = { let ${grid.component_studly} = {
template: '#${grid.component}-template', template: '#${grid.component}-template',
mixins: [FormPosterMixin],
props: { props: {
csrftoken: String, csrftoken: String,
}, },
computed: { computed: {
// note, can use this with v-model for hidden 'uuids' fields // note, can use this with v-model for hidden 'uuids' fields
selected_uuids: function() { selected_uuids: function() {
return this.checkedRowUUIDs().join(',') 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: { methods: {

View file

@ -481,7 +481,7 @@
</%def> </%def>
<%def name="render_grid_component()"> <%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': % if master.deletable and request.has_perm('{}.delete'.format(permission_prefix)) and master.delete_confirm == 'simple':
@deleteActionClicked="deleteObject" @deleteActionClicked="deleteObject"
% endif % endif

View file

@ -0,0 +1,63 @@
## -*- coding: utf-8; -*-
<%inherit file="/master/index.mako" />
<%def name="render_grid_component()">
% if master.has_perm('configure'):
<b-field horizontal label="Showing:">
<b-select v-model="showEmails" @input="updateVisibleEmails()">
<option value="available">Available Emails</option>
<option value="all">All Emails</option>
<option value="hidden">Hidden Emails</option>
</b-select>
</b-field>
% endif
${parent.render_grid_component()}
</%def>
<%def name="modify_this_page_vars()">
${parent.modify_this_page_vars()}
% if master.has_perm('configure'):
<script type="text/javascript">
ThisPageData.showEmails = 'available'
ThisPage.methods.updateVisibleEmails = function() {
this.$refs.grid.showEmails = this.showEmails
}
${grid.component_studly}Data.showEmails = 'available'
${grid.component_studly}.computed.visibleData = function() {
if (this.showEmails == 'available') {
return this.data.filter(email => email.hidden == 'No')
} else if (this.showEmails == 'hidden') {
return this.data.filter(email => email.hidden == 'Yes')
}
// showing all
return this.data
}
${grid.component_studly}.methods.renderLabelToggleHidden = function(row) {
return row.hidden == 'Yes' ? "Un-hide" : "Hide"
}
${grid.component_studly}.methods.toggleHidden = function(row) {
let url = '${url('{}.toggle_hidden'.format(route_prefix))}'
let params = {
key: row.key,
hidden: row.hidden == 'No'? true : false,
}
this.submitForm(url, params, response => {
row.hidden = params.hidden ? 'Yes' : 'No'
})
}
</script>
% endif
</%def>
${parent.body()}

View file

@ -27,6 +27,7 @@ Email Views
from __future__ import unicode_literals, absolute_import from __future__ import unicode_literals, absolute_import
import re import re
import warnings
import six import six
@ -38,6 +39,7 @@ import colander
from deform import widget as dfwidget from deform import widget as dfwidget
from webhelpers2.html import HTML from webhelpers2.html import HTML
from tailbone import grids
from tailbone.db import Session from tailbone.db import Session
from tailbone.views import View, MasterView from tailbone.views import View, MasterView
@ -63,6 +65,7 @@ class EmailSettingView(MasterView):
'subject', 'subject',
'to', 'to',
'enabled', 'enabled',
'hidden',
] ]
form_fields = [ form_fields = [
@ -77,11 +80,19 @@ class EmailSettingView(MasterView):
'cc', 'cc',
'bcc', 'bcc',
'enabled', 'enabled',
'hidden',
] ]
def __init__(self, request): def __init__(self, request):
super(EmailSettingView, self).__init__(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): def get_handler(self):
app = self.get_rattail_app() app = self.get_rattail_app()
@ -89,9 +100,12 @@ class EmailSettingView(MasterView):
def get_data(self, session=None): def get_data(self, session=None):
data = [] data = []
for email in self.handler.iter_emails(): if self.has_perm('configure'):
key = email.key or email.__name__ emails = self.email_handler.get_all_emails()
email = email(self.rattail_config, key) 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)) data.append(self.normalize(email))
return data return data
@ -112,6 +126,20 @@ class EmailSettingView(MasterView):
g.set_renderer('to', self.render_to_short) g.set_renderer('to', self.render_to_short)
g.sorters['to'] = g.make_simple_sorter('to', foldcase=True) 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): def render_to_short(self, email, column):
profile = email['_email'] profile = email['_email']
if self.rattail_config.production(): if self.rattail_config.production():
@ -133,7 +161,7 @@ class EmailSettingView(MasterView):
if recips: if recips:
return ', '.join(recips) return ', '.join(recips)
data = email.obtain_sample_data(self.request) data = email.obtain_sample_data(self.request)
return { normal = {
'_email': email, '_email': email,
'key': email.key, 'key': email.key,
'fallback_key': email.fallback_key, 'fallback_key': email.fallback_key,
@ -147,10 +175,13 @@ class EmailSettingView(MasterView):
'bcc': get_recips('bcc') or '', 'bcc': get_recips('bcc') or '',
'enabled': email.get_enabled(), '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): def get_instance(self):
key = self.request.matchdict['key'] 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): def get_instance_title(self, email):
return email['_email'].get_complete_subject(render=False) return email['_email'].get_complete_subject(render=False)
@ -207,8 +238,20 @@ class EmailSettingView(MasterView):
# enabled # enabled
f.set_type('enabled', 'boolean') 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): 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): def save_edit_form(self, form):
key = self.request.matchdict['key'] 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.{}.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.{}.bcc'.format(key), (data['bcc'] or '').replace('\n', ', '))
app.save_setting(session, 'rattail.mail.{}.enabled'.format(key), six.text_type(data['enabled']).lower()) 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 return data
def template_kwargs_view(self, **kwargs): def template_kwargs_view(self, **kwargs):
key = self.request.matchdict['key'] key = self.request.matchdict['key']
kwargs['email'] = self.handler.get_email(key) kwargs['email'] = self.email_handler.get_email(key)
return kwargs return kwargs
def configure_get_simple_settings(self): def configure_get_simple_settings(self):
@ -240,10 +285,48 @@ class EmailSettingView(MasterView):
'type': bool}, '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 # TODO: deprecate / remove this
ProfilesView = EmailSettingView 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): class RecipientsType(colander.String):
""" """
Custom schema type for email recipients. This is used to present the 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()) enabled = colander.SchemaNode(colander.Boolean())
hidden = colander.SchemaNode(colander.Boolean())
class EmailPreview(View): class EmailPreview(View):
""" """
@ -292,7 +377,14 @@ class EmailPreview(View):
def __init__(self, request): def __init__(self, request):
super(EmailPreview, self).__init__(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): def get_handler(self):
app = self.get_rattail_app() app = self.get_rattail_app()
@ -319,20 +411,20 @@ class EmailPreview(View):
if recipient: if recipient:
key = self.request.POST.get('email_key') key = self.request.POST.get('email_key')
if key: if key:
email = self.handler.get_email(key) email = self.email_handler.get_email(key)
data = email.obtain_sample_data(self.request) data = email.obtain_sample_data(self.request)
self.handler.send_message(email, data, self.email_handler.send_message(email, data,
subject_prefix="[PREVIEW] ", subject_prefix="[PREVIEW] ",
to=[recipient], to=[recipient],
cc=None, bcc=None) cc=None, bcc=None)
self.request.session.flash( self.request.session.flash(
"Preview for '{}' was emailed to {}".format( "Preview for '{}' was emailed to {}".format(
key, recipient)) key, recipient))
def preview_template(self, key, type_): def preview_template(self, key, type_):
email = self.handler.get_email(key) email = self.email_handler.get_email(key)
template = email.get_template(type_) template = email.get_template(type_)
data = email.obtain_sample_data(self.request) data = email.obtain_sample_data(self.request)
self.request.response.text = template.render(**data) self.request.response.text = template.render(**data)