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.readonly_fields = set(readonly_fields or [])
self.model_instance = model_instance self.model_instance = model_instance
self.model_class = model_class 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) self.model_class = type(self.model_instance)
if self.model_class and self.fields is None: if self.model_class and self.fields is None:
self.set_fields(self.make_fields()) self.set_fields(self.make_fields())
@ -647,7 +647,10 @@ class Form(object):
# get initial form values from model instance # get initial form values from model instance
kwargs = {} kwargs = {}
if self.model_instance: 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 # create form
form = deform.Form(schema, **kwargs) form = deform.Form(schema, **kwargs)

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2017 Lance Edgar # Copyright © 2010-2018 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -26,27 +26,18 @@ Email Views
from __future__ import unicode_literals, absolute_import from __future__ import unicode_literals, absolute_import
import six
from rattail import mail from rattail import mail
from rattail.db import api from rattail.db import api
from rattail.config import parse_list from rattail.config import parse_list
import formalchemy import colander
from formalchemy.helpers import text_area from deform import widget as dfwidget
from pyramid.httpexceptions import HTTPFound
from webhelpers2.html import HTML from webhelpers2.html import HTML
from tailbone import forms
from tailbone.db import Session from tailbone.db import Session
from tailbone.views import View, MasterView2 as MasterView from tailbone.views import View, MasterView3 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)
class ProfilesView(MasterView): class ProfilesView(MasterView):
@ -61,6 +52,7 @@ class ProfilesView(MasterView):
pageable = False pageable = False
creatable = False creatable = False
deletable = False deletable = False
grid_columns = [ grid_columns = [
'key', 'key',
'prefix', 'prefix',
@ -69,6 +61,20 @@ class ProfilesView(MasterView):
'enabled', 'enabled',
] ]
form_fields = [
'key',
'fallback_key',
'description',
'prefix',
'subject',
'sender',
'replyto',
'to',
'cc',
'bcc',
'enabled',
]
def get_data(self, session=None): def get_data(self, session=None):
data = [] data = []
for email in mail.iter_emails(self.rattail_config): for email in mail.iter_emails(self.rattail_config):
@ -113,13 +119,13 @@ class ProfilesView(MasterView):
'key': email.key, 'key': email.key,
'fallback_key': email.fallback_key, 'fallback_key': email.fallback_key,
'description': email.__doc__, 'description': email.__doc__,
'prefix': email.get_prefix(data, magic=False), 'prefix': email.get_prefix(data, magic=False) or '',
'subject': email.get_subject(data, render=False), 'subject': email.get_subject(data, render=False) or '',
'sender': email.get_sender(), 'sender': email.get_sender() or '',
'replyto': email.get_replyto(), 'replyto': email.get_replyto() or '',
'to': get_recips('to'), 'to': get_recips('to') or '',
'cc': get_recips('cc'), 'cc': get_recips('cc') or '',
'bcc': get_recips('bcc'), 'bcc': get_recips('bcc') or '',
'enabled': email.get_enabled(), 'enabled': email.get_enabled(),
} }
@ -140,45 +146,58 @@ class ProfilesView(MasterView):
return profile['key'] != 'user_feedback' return profile['key'] != 'user_feedback'
return True return True
def make_form(self, email, **kwargs): def configure_form(self, f):
""" super(ProfilesView, self).configure_form(f)
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']))
form = forms.AlchemyForm(self.request, fs, # key
creating=self.creating, f.set_readonly('key')
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
def save_form(self, form): # fallback_key
fs = form.fieldset f.set_readonly('fallback_key')
fs.sync()
key = fs.key._value
session = Session() # description
api.save_setting(session, 'rattail.mail.{}.prefix'.format(key), fs.prefix._value) f.set_readonly('description')
api.save_setting(session, 'rattail.mail.{}.subject'.format(key), fs.subject._value)
api.save_setting(session, 'rattail.mail.{}.from'.format(key), fs.sender._value) # prefix
api.save_setting(session, 'rattail.mail.{}.replyto'.format(key), fs.replyto._value) f.set_label('prefix', "Subject Prefix")
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', ', ')) # subject
api.save_setting(session, 'rattail.mail.{}.bcc'.format(key), (fs.bcc._value or '').replace('\n', ', ')) f.set_label('subject', "Subject Text")
api.save_setting(session, 'rattail.mail.{}.enabled'.format(key), unicode(fs.enabled._value).lower())
# 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): def template_kwargs_view(self, **kwargs):
key = self.request.matchdict['key'] key = self.request.matchdict['key']
@ -186,6 +205,25 @@ class ProfilesView(MasterView):
return kwargs 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): class EmailPreview(View):
""" """
Lists available email templates, and can show previews of each. 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. # Forms submitted via POST are only used for sending emails.
if self.request.method == 'POST': if self.request.method == 'POST':
self.email_template() self.email_template()
return HTTPFound(location=self.request.get_referrer( url = self.request.get_referrer(default=self.request.route_url('emailprofiles'))
default=self.request.route_url('emailprofiles'))) return self.redirect(url)
# Maybe render a preview? # Maybe render a preview?
key = self.request.GET.get('key') key = self.request.GET.get('key')

View file

@ -61,6 +61,9 @@ class MasterView3(MasterView2):
if schema is None: if schema is None:
schema = self.make_form_schema() 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: if not self.creating:
kwargs['model_instance'] = instance kwargs['model_instance'] = instance
kwargs = self.make_form_kwargs(**kwargs) 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 Configure the primary form. By default this just sets any primary key
fields to be readonly (if we have a :attr:`model_class`). fields to be readonly (if we have a :attr:`model_class`).
""" """
if self.editing:
if self.editing and self.model_class: model_class = self.get_model_class(error=False)
mapper = orm.class_mapper(self.model_class) if model_class:
for key in mapper.primary_key: mapper = orm.class_mapper(model_class)
for field in form.fields: for key in mapper.primary_key:
if field == key.name: for field in form.fields:
form.set_readonly(field) if field == key.name:
break form.set_readonly(field)
break
form.remove_field('uuid') form.remove_field('uuid')