Add support for sending new messages.

This commit is contained in:
Lance Edgar 2016-02-05 14:42:56 -06:00
parent 9d802d8f25
commit 687b83e2e1
9 changed files with 228 additions and 18 deletions

View file

@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2014 Lance Edgar
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
@ -20,11 +20,12 @@
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Autocomplete View
"""
from __future__ import unicode_literals, absolute_import
from tailbone.views.core import View
from tailbone.db import Session
@ -73,4 +74,4 @@ class AutocompleteView(View):
if not term:
return []
results = self.query(term).all()
return [{u'label': self.display(x), u'value': self.value(x)} for x in results]
return [{'label': self.display(x), 'value': self.value(x)} for x in results]

View file

@ -103,6 +103,7 @@ class MasterView(View):
form = self.make_form(self.model_class)
if self.request.method == 'POST':
if form.validate():
self.before_create(form)
form.save()
instance = form.fieldset.model
self.after_create(instance)
@ -549,6 +550,12 @@ class MasterView(View):
"""
fieldset.configure()
def before_create(self, form):
"""
Event hook, called just after the form to create a new instance has
been validated, but prior to the form itself being saved.
"""
def after_create(self, instance):
"""
Event hook, called just after a new instance is saved.

View file

@ -30,12 +30,13 @@ from rattail import enum
from rattail.db import model
import formalchemy
from formalchemy.helpers import hidden_field
from pyramid import httpexceptions
from webhelpers.html import tags
from webhelpers.html import tags, HTML
from tailbone import forms
from tailbone.db import Session
from tailbone.views import MasterView
from tailbone.views import MasterView, AutocompleteView
class SubjectFieldRenderer(formalchemy.FieldRenderer):
@ -56,13 +57,31 @@ class SenderFieldRenderer(forms.renderers.UserFieldRenderer):
return super(SenderFieldRenderer, self).render_readonly(**kwargs)
class RecipientsField(formalchemy.Field):
"""
Custom field for recipients, used when sending new messages.
"""
is_collection = True
def sync(self):
if not self.is_readonly():
message = self.parent.model
for uuid in set(self._deserialize()):
user = Session.query(model.User).get(uuid)
if user:
message.add_recipient(user, status=enum.MESSAGE_STATUS_INBOX)
class RecipientsFieldRenderer(formalchemy.FieldRenderer):
def render(self, **kwargs):
return HTML.tag('ul')
def render_readonly(self, **kwargs):
recipients = self.raw_value
if not recipients:
return ''
recips = filter(lambda r: r.recipient is not self.request.user, recipients)
recips = [r for r in recipients if r.recipient is not self.request.user]
recips = sorted([r.recipient.display_name for r in recips])
if len(recips) < len(recipients):
recips.insert(0, 'you')
@ -87,7 +106,6 @@ class MessagesView(MasterView):
Base class for message views.
"""
model_class = model.Message
creatable = False
editable = False
deletable = False
checkboxes = True
@ -151,16 +169,27 @@ class MessagesView(MasterView):
return {}
def configure_fieldset(self, fs):
fs.configure(
include=[
if self.creating:
fs.append(RecipientsField('recipients', label="To", renderer=RecipientsFieldRenderer))
fs.configure(include=[
fs.recipients,
fs.subject,
fs.body.textarea(size='50x10'),
])
else:
fs.configure(include=[
fs.sender.with_renderer(SenderFieldRenderer).label("From"),
fs.recipients.with_renderer(RecipientsFieldRenderer).label("To"),
fs.sent.with_renderer(forms.renderers.DateTimeFieldRenderer(self.rattail_config)),
fs.body,
fs.subject,
fs.body.textarea(size='50x10'),
])
if self.viewing:
del fs.body
del fs.body # ..really?
def before_create(self, form):
message = form.fieldset.model
message.sender = self.request.user
def get_recipient(self, message):
for recip in message.recipients:
@ -265,6 +294,22 @@ class SentView(MessagesView):
g.filters['sender'].default_active = False
class RecipientsAutocomplete(AutocompleteView):
"""
Autocomplete AJAX view for the recipients field when sending a new message.
"""
def query(self, term):
return Session.query(model.User)\
.join(model.Person)\
.filter(model.User.active == True)\
.filter(model.Person.display_name.ilike('%{}%'.format(term)))\
.order_by(model.Person.display_name)
def display(self, user):
return user.person.display_name
def includeme(config):
config.add_tailbone_permission('messages', 'messages.list', "List/Search Messages")
@ -279,6 +324,10 @@ def includeme(config):
config.add_view(ArchiveView, attr='index', route_name='messages.archive',
permission='messages.list')
# move (single)
config.add_route('messages.move', '/messages/{uuid}/move')
config.add_view(MessagesView, attr='move', route_name='messages.move')
# move bulk
config.add_route('messages.move_bulk', '/messages/move-bulk')
config.add_view(MessagesView, attr='move_bulk', route_name='messages.move_bulk')
@ -288,10 +337,9 @@ def includeme(config):
config.add_view(SentView, attr='index', route_name='messages.sent',
permission='messages.list')
# view
config.add_route('messages.view', '/messages/{uuid}')
config.add_view(MessagesView, attr='view', route_name='messages.view')
# recipients autocomplete
config.add_route('messages.recipients', '/messages/recipients')
config.add_view(RecipientsAutocomplete, route_name='messages.recipients',
renderer='json', permission='messages.create')
# move (single)
config.add_route('messages.move', '/messages/{uuid}/move')
config.add_view(MessagesView, attr='move', route_name='messages.move')
MessagesView.defaults(config)