Refactor email profiles view to use master3

This commit is contained in:
Lance Edgar 2018-01-27 19:16:07 -06:00
parent 8d62960548
commit e0650d26cf
3 changed files with 115 additions and 70 deletions

View file

@ -335,7 +335,7 @@ class Form(object):
self.readonly_fields = set(readonly_fields or [])
self.model_instance = model_instance
self.model_class = model_class
if self.model_instance and not self.model_class:
if self.model_instance and not self.model_class and not isinstance(self.model_instance, dict):
self.model_class = type(self.model_instance)
if self.model_class and self.fields is None:
self.set_fields(self.make_fields())
@ -647,7 +647,10 @@ class Form(object):
# get initial form values from model instance
kwargs = {}
if self.model_instance:
kwargs['appstruct'] = schema.dictify(self.model_instance)
if self.model_class:
kwargs['appstruct'] = schema.dictify(self.model_instance)
else:
kwargs['appstruct'] = self.model_instance
# create form
form = deform.Form(schema, **kwargs)

View file

@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2017 Lance Edgar
# Copyright © 2010-2018 Lance Edgar
#
# This file is part of Rattail.
#
@ -26,27 +26,18 @@ Email Views
from __future__ import unicode_literals, absolute_import
import six
from rattail import mail
from rattail.db import api
from rattail.config import parse_list
import formalchemy
from formalchemy.helpers import text_area
from pyramid.httpexceptions import HTTPFound
import colander
from deform import widget as dfwidget
from webhelpers2.html import HTML
from tailbone import forms
from tailbone.db import Session
from tailbone.views import View, MasterView2 as MasterView
class EmailListFieldRenderer(formalchemy.TextAreaFieldRenderer):
def render(self, **kwargs):
if isinstance(kwargs.get('size'), tuple):
kwargs['size'] = 'x'.join([str(i) for i in kwargs['size']])
value = '\n'.join(parse_list(self.value))
return text_area(self.name, content=value, **kwargs)
from tailbone.views import View, MasterView3 as MasterView
class ProfilesView(MasterView):
@ -61,6 +52,7 @@ class ProfilesView(MasterView):
pageable = False
creatable = False
deletable = False
grid_columns = [
'key',
'prefix',
@ -69,6 +61,20 @@ class ProfilesView(MasterView):
'enabled',
]
form_fields = [
'key',
'fallback_key',
'description',
'prefix',
'subject',
'sender',
'replyto',
'to',
'cc',
'bcc',
'enabled',
]
def get_data(self, session=None):
data = []
for email in mail.iter_emails(self.rattail_config):
@ -113,13 +119,13 @@ class ProfilesView(MasterView):
'key': email.key,
'fallback_key': email.fallback_key,
'description': email.__doc__,
'prefix': email.get_prefix(data, magic=False),
'subject': email.get_subject(data, render=False),
'sender': email.get_sender(),
'replyto': email.get_replyto(),
'to': get_recips('to'),
'cc': get_recips('cc'),
'bcc': get_recips('bcc'),
'prefix': email.get_prefix(data, magic=False) or '',
'subject': email.get_subject(data, render=False) or '',
'sender': email.get_sender() or '',
'replyto': email.get_replyto() or '',
'to': get_recips('to') or '',
'cc': get_recips('cc') or '',
'bcc': get_recips('bcc') or '',
'enabled': email.get_enabled(),
}
@ -140,45 +146,58 @@ class ProfilesView(MasterView):
return profile['key'] != 'user_feedback'
return True
def make_form(self, email, **kwargs):
"""
Make a simple form for use with CRUD views.
"""
fs = forms.GenericFieldSet(email)
fs.append(formalchemy.Field('key', value=email['key'], readonly=True))
fs.append(formalchemy.Field('fallback_key', value=email['fallback_key'], readonly=True))
fs.append(formalchemy.Field('description', value=email['description'], readonly=True))
fs.append(formalchemy.Field('prefix', value=email['prefix'], label="Subject Prefix"))
fs.append(formalchemy.Field('subject', value=email['subject'], label="Subject Text"))
fs.append(formalchemy.Field('sender', value=email['sender'], label="From"))
fs.append(formalchemy.Field('replyto', value=email['replyto'], label="Reply-To"))
fs.append(formalchemy.Field('to', value=email['to'], renderer=EmailListFieldRenderer, size='60x6'))
fs.append(formalchemy.Field('cc', value=email['cc'], renderer=EmailListFieldRenderer, size='60x2'))
fs.append(formalchemy.Field('bcc', value=email['bcc'], renderer=EmailListFieldRenderer, size='60x2'))
fs.append(formalchemy.Field('enabled', type=formalchemy.types.Boolean, value=email['enabled']))
def configure_form(self, f):
super(ProfilesView, self).configure_form(f)
form = forms.AlchemyForm(self.request, fs,
creating=self.creating,
editing=self.editing,
action_url=self.request.current_route_url(_query=None),
cancel_url=self.get_action_url('view', email))
form.readonly = self.viewing
return form
# key
f.set_readonly('key')
def save_form(self, form):
fs = form.fieldset
fs.sync()
key = fs.key._value
# fallback_key
f.set_readonly('fallback_key')
session = Session()
api.save_setting(session, 'rattail.mail.{}.prefix'.format(key), fs.prefix._value)
api.save_setting(session, 'rattail.mail.{}.subject'.format(key), fs.subject._value)
api.save_setting(session, 'rattail.mail.{}.from'.format(key), fs.sender._value)
api.save_setting(session, 'rattail.mail.{}.replyto'.format(key), fs.replyto._value)
api.save_setting(session, 'rattail.mail.{}.to'.format(key), (fs.to._value or '').replace('\n', ', '))
api.save_setting(session, 'rattail.mail.{}.cc'.format(key), (fs.cc._value or '').replace('\n', ', '))
api.save_setting(session, 'rattail.mail.{}.bcc'.format(key), (fs.bcc._value or '').replace('\n', ', '))
api.save_setting(session, 'rattail.mail.{}.enabled'.format(key), unicode(fs.enabled._value).lower())
# description
f.set_readonly('description')
# prefix
f.set_label('prefix', "Subject Prefix")
# subject
f.set_label('subject', "Subject Text")
# sender
f.set_label('sender', "From")
# replyto
f.set_label('replyto', "Reply-To")
# to
f.set_widget('to', dfwidget.TextAreaWidget(cols=60, rows=6))
# cc
f.set_widget('cc', dfwidget.TextAreaWidget(cols=60, rows=2))
# bcc
f.set_widget('bcc', dfwidget.TextAreaWidget(cols=60, rows=2))
# enabled
f.set_type('enabled', 'boolean')
def make_form_schema(self):
return EmailProfileSchema()
def save_edit_form(self, form):
key = self.request.matchdict['key']
data = self.form_deserialized
session = self.Session()
api.save_setting(session, 'rattail.mail.{}.prefix'.format(key), data['prefix'])
api.save_setting(session, 'rattail.mail.{}.subject'.format(key), data['subject'])
api.save_setting(session, 'rattail.mail.{}.from'.format(key), data['sender'])
api.save_setting(session, 'rattail.mail.{}.replyto'.format(key), data['replyto'])
api.save_setting(session, 'rattail.mail.{}.to'.format(key), (data['to'] or '').replace('\n', ', '))
api.save_setting(session, 'rattail.mail.{}.cc'.format(key), (data['cc'] or '').replace('\n', ', '))
api.save_setting(session, 'rattail.mail.{}.bcc'.format(key), (data['bcc'] or '').replace('\n', ', '))
api.save_setting(session, 'rattail.mail.{}.enabled'.format(key), six.text_type(data['enabled']).lower())
return data
def template_kwargs_view(self, **kwargs):
key = self.request.matchdict['key']
@ -186,6 +205,25 @@ class ProfilesView(MasterView):
return kwargs
class EmailProfileSchema(colander.MappingSchema):
prefix = colander.SchemaNode(colander.String())
subject = colander.SchemaNode(colander.String())
sender = colander.SchemaNode(colander.String())
replyto = colander.SchemaNode(colander.String(), missing='')
to = colander.SchemaNode(colander.String())
cc = colander.SchemaNode(colander.String(), missing='')
bcc = colander.SchemaNode(colander.String(), missing='')
enabled = colander.SchemaNode(colander.Boolean())
class EmailPreview(View):
"""
Lists available email templates, and can show previews of each.
@ -196,8 +234,8 @@ class EmailPreview(View):
# Forms submitted via POST are only used for sending emails.
if self.request.method == 'POST':
self.email_template()
return HTTPFound(location=self.request.get_referrer(
default=self.request.route_url('emailprofiles')))
url = self.request.get_referrer(default=self.request.route_url('emailprofiles'))
return self.redirect(url)
# Maybe render a preview?
key = self.request.GET.get('key')

View file

@ -61,6 +61,9 @@ class MasterView3(MasterView2):
if schema is None:
schema = self.make_form_schema()
# TODO: SQLAlchemy class instance is assumed *unless* we get a dict
# (seems like we should be smarter about this somehow)
# if not self.creating and not isinstance(instance, dict):
if not self.creating:
kwargs['model_instance'] = instance
kwargs = self.make_form_kwargs(**kwargs)
@ -103,14 +106,15 @@ class MasterView3(MasterView2):
Configure the primary form. By default this just sets any primary key
fields to be readonly (if we have a :attr:`model_class`).
"""
if self.editing and self.model_class:
mapper = orm.class_mapper(self.model_class)
for key in mapper.primary_key:
for field in form.fields:
if field == key.name:
form.set_readonly(field)
break
if self.editing:
model_class = self.get_model_class(error=False)
if model_class:
mapper = orm.class_mapper(model_class)
for key in mapper.primary_key:
for field in form.fields:
if field == key.name:
form.set_readonly(field)
break
form.remove_field('uuid')