Refactor messages view to use master3
This commit is contained in:
parent
84ebf5d929
commit
c3fb86e391
|
@ -331,13 +331,24 @@ class Form(object):
|
||||||
|
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
def remove_field(self, key):
|
def insert_before(self, field, newfield):
|
||||||
if key in self.fields:
|
self.fields.insert_before(field, newfield)
|
||||||
self.fields.remove(key)
|
|
||||||
|
|
||||||
def remove_fields(self, *args):
|
def insert_after(self, field, newfield):
|
||||||
|
self.fields.insert_after(field, newfield)
|
||||||
|
|
||||||
|
def remove(self, *args):
|
||||||
for arg in args:
|
for arg in args:
|
||||||
self.remove_field(arg)
|
if arg in self.fields:
|
||||||
|
self.fields.remove(arg)
|
||||||
|
|
||||||
|
# TODO: deprecare / remove this
|
||||||
|
def remove_field(self, key):
|
||||||
|
self.remove(key)
|
||||||
|
|
||||||
|
# TODO: deprecare / remove this
|
||||||
|
def remove_fields(self, *args):
|
||||||
|
self.remove(*args)
|
||||||
|
|
||||||
def make_schema(self):
|
def make_schema(self):
|
||||||
if not self.model_class:
|
if not self.model_class:
|
||||||
|
@ -538,7 +549,7 @@ class Form(object):
|
||||||
label = HTML.tag('label', self.get_label(field_name), for_=field_name)
|
label = HTML.tag('label', self.get_label(field_name), for_=field_name)
|
||||||
field = self.render_field_value(field_name) or ''
|
field = self.render_field_value(field_name) or ''
|
||||||
field_div = HTML.tag('div', class_='field', c=field)
|
field_div = HTML.tag('div', class_='field', c=field)
|
||||||
return HTML.tag('div', class_='field-wrapper {}'.format(field), c=label + field_div)
|
return HTML.tag('div', class_='field-wrapper {}'.format(field_name), c=label + field_div)
|
||||||
|
|
||||||
def render_field_value(self, field_name):
|
def render_field_value(self, field_name):
|
||||||
record = self.model_instance
|
record = self.model_instance
|
||||||
|
|
|
@ -15,17 +15,17 @@
|
||||||
|
|
||||||
// validate message before sending
|
// validate message before sending
|
||||||
function validate_message_form() {
|
function validate_message_form() {
|
||||||
var form = $('#new-message');
|
var form = $('#deform');
|
||||||
|
|
||||||
if (! form.find('input[name="Message--recipients"]').val()) {
|
if (! form.find('input[name="recipients_"]').val()) {
|
||||||
alert("You must specify some recipient(s) for the message.");
|
alert("You must specify some recipient(s) for the message.");
|
||||||
$('.recipients input').data('ui-tagit').tagInput.focus();
|
$('.recipients_ input').data('ui-tagit').tagInput.focus();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! form.find('input[name="Message--subject"]').val()) {
|
if (! form.find('input[name="subject"]').val()) {
|
||||||
alert("You must provide a subject for the message.");
|
alert("You must provide a subject for the message.");
|
||||||
form.find('input[name="Message--subject"]').focus();
|
form.find('input[name="subject"]').focus();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
|
|
||||||
var recipients = $('.recipients input');
|
var recipients = $('.recipients_ input');
|
||||||
|
|
||||||
recipients.tagit({
|
recipients.tagit({
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<%def name="extra_styles()">
|
<%def name="extra_styles()">
|
||||||
${parent.extra_styles()}
|
${parent.extra_styles()}
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
.field-wrapper.recipients .everyone {
|
.recipients .everyone {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,8 +158,9 @@ class MasterView3(MasterView2):
|
||||||
|
|
||||||
def save_create_form(self, form):
|
def save_create_form(self, form):
|
||||||
self.before_create(form)
|
self.before_create(form)
|
||||||
obj = self.objectify(form, self.form_deserialized)
|
with self.Session().no_autoflush:
|
||||||
self.before_create_flush(obj, form)
|
obj = self.objectify(form, self.form_deserialized)
|
||||||
|
self.before_create_flush(obj, form)
|
||||||
self.Session.add(obj)
|
self.Session.add(obj)
|
||||||
self.Session.flush()
|
self.Session.flush()
|
||||||
return obj
|
return obj
|
||||||
|
|
|
@ -26,79 +26,21 @@ Message Views
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
import json
|
|
||||||
import pytz
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from rattail import enum
|
|
||||||
from rattail.db import model
|
from rattail.db import model
|
||||||
from rattail.time import localtime
|
from rattail.time import localtime
|
||||||
|
|
||||||
import formalchemy
|
import colander
|
||||||
from formalchemy.helpers import text_field
|
from deform import widget as dfwidget
|
||||||
from pyramid import httpexceptions
|
from pyramid import httpexceptions
|
||||||
from webhelpers2.html import tags, HTML
|
from webhelpers2.html import tags, HTML
|
||||||
|
|
||||||
from tailbone import forms
|
|
||||||
from tailbone.db import Session
|
from tailbone.db import Session
|
||||||
from tailbone.views import MasterView2 as MasterView
|
from tailbone.views import MasterView3 as MasterView
|
||||||
from tailbone.util import raw_datetime
|
from tailbone.util import raw_datetime
|
||||||
|
|
||||||
|
|
||||||
class SenderFieldRenderer(forms.renderers.UserFieldRenderer):
|
|
||||||
|
|
||||||
def render_readonly(self, **kwargs):
|
|
||||||
sender = self.raw_value
|
|
||||||
if sender is self.request.user:
|
|
||||||
return 'you'
|
|
||||||
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 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):
|
|
||||||
uuids = self.value
|
|
||||||
value = ','.join(uuids) if uuids else ''
|
|
||||||
return text_field(self.name, value=value, **kwargs)
|
|
||||||
|
|
||||||
def deserialize(self):
|
|
||||||
value = self.params.getone(self.name).split(',')
|
|
||||||
value = [uuid.strip() for uuid in value]
|
|
||||||
value = set([uuid for uuid in value if uuid])
|
|
||||||
return value
|
|
||||||
|
|
||||||
def render_readonly(self, **kwargs):
|
|
||||||
recipients = self.raw_value
|
|
||||||
if not recipients:
|
|
||||||
return ''
|
|
||||||
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')
|
|
||||||
max_display = 5
|
|
||||||
if len(recips) > max_display:
|
|
||||||
basic = HTML.literal("{}, ".format(', '.join(recips[:max_display-1])))
|
|
||||||
more = tags.link_to("({} more)".format(len(recips[max_display-1:])), '#', class_='more')
|
|
||||||
everyone = HTML.tag('span', class_='everyone', c=', '.join(recips[max_display-1:]))
|
|
||||||
return basic + more + everyone
|
|
||||||
return ', '.join(recips)
|
|
||||||
|
|
||||||
|
|
||||||
class MessagesView(MasterView):
|
class MessagesView(MasterView):
|
||||||
"""
|
"""
|
||||||
Base class for message views.
|
Base class for message views.
|
||||||
|
@ -109,7 +51,21 @@ class MessagesView(MasterView):
|
||||||
checkboxes = True
|
checkboxes = True
|
||||||
replying = False
|
replying = False
|
||||||
reply_header_sent_format = '%a %d %b %Y at %I:%M %p'
|
reply_header_sent_format = '%a %d %b %Y at %I:%M %p'
|
||||||
grid_columns = ['subject', 'sender', 'recipients', 'sent']
|
|
||||||
|
grid_columns = [
|
||||||
|
'subject',
|
||||||
|
'sender',
|
||||||
|
'recipients',
|
||||||
|
'sent',
|
||||||
|
]
|
||||||
|
|
||||||
|
form_fields = [
|
||||||
|
'sender',
|
||||||
|
'recipients',
|
||||||
|
'sent',
|
||||||
|
'subject',
|
||||||
|
'body',
|
||||||
|
]
|
||||||
|
|
||||||
def get_index_title(self):
|
def get_index_title(self):
|
||||||
if self.listing:
|
if self.listing:
|
||||||
|
@ -117,7 +73,7 @@ class MessagesView(MasterView):
|
||||||
if self.viewing:
|
if self.viewing:
|
||||||
message = self.get_instance()
|
message = self.get_instance()
|
||||||
recipient = self.get_recipient(message)
|
recipient = self.get_recipient(message)
|
||||||
if recipient and recipient.status == enum.MESSAGE_STATUS_ARCHIVE:
|
if recipient and recipient.status == self.enum.MESSAGE_STATUS_ARCHIVE:
|
||||||
return "Message Archive"
|
return "Message Archive"
|
||||||
elif not recipient:
|
elif not recipient:
|
||||||
return "Sent Messages"
|
return "Sent Messages"
|
||||||
|
@ -178,7 +134,7 @@ class MessagesView(MasterView):
|
||||||
def render_sent(self, message, column_name):
|
def render_sent(self, message, column_name):
|
||||||
return raw_datetime(self.rattail_config, message.sent)
|
return raw_datetime(self.rattail_config, message.sent)
|
||||||
|
|
||||||
def render_sender(self, message, column_name):
|
def render_sender(self, message, field):
|
||||||
sender = message.sender
|
sender = message.sender
|
||||||
if sender is self.request.user:
|
if sender is self.request.user:
|
||||||
return 'you'
|
return 'you'
|
||||||
|
@ -197,50 +153,63 @@ class MessagesView(MasterView):
|
||||||
return "{}, ...".format(', '.join(recips[:4]))
|
return "{}, ...".format(', '.join(recips[:4]))
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def make_form(self, instance, **kwargs):
|
def render_recipients_full(self, message, field):
|
||||||
form = super(MessagesView, self).make_form(instance, **kwargs)
|
recipients = message.recipients
|
||||||
if self.creating:
|
if not recipients:
|
||||||
form.id = 'new-message'
|
return ""
|
||||||
form.cancel_url = self.request.get_referrer(default=self.request.route_url('messages.inbox'))
|
recips = [r for r in recipients
|
||||||
form.create_label = "Send Message"
|
if r.recipient is not self.request.user]
|
||||||
return form
|
recips = sorted([r.recipient.display_name for r in recips])
|
||||||
|
if len(recips) < len(recipients) and (
|
||||||
|
message.sender is not self.request.user or not recips):
|
||||||
|
recips.insert(0, 'you')
|
||||||
|
max_display = 5
|
||||||
|
if len(recips) > max_display:
|
||||||
|
basic = HTML.literal("{}, ".format(', '.join(recips[:max_display-1])))
|
||||||
|
more = tags.link_to("({} more)".format(len(recips[max_display-1:])), '#', class_='more')
|
||||||
|
everyone = HTML.tag('span', class_='everyone', c=', '.join(recips[max_display-1:]))
|
||||||
|
return basic + more + everyone
|
||||||
|
return ', '.join(recips)
|
||||||
|
|
||||||
|
# TODO!!
|
||||||
|
# def make_form(self, instance, **kwargs):
|
||||||
|
# form = super(MessagesView, self).make_form(instance, **kwargs)
|
||||||
|
# if self.creating:
|
||||||
|
# form.id = 'new-message'
|
||||||
|
# form.cancel_url = self.request.get_referrer(default=self.request.route_url('messages.inbox'))
|
||||||
|
# form.create_label = "Send Message"
|
||||||
|
# return form
|
||||||
|
|
||||||
|
def configure_form(self, f):
|
||||||
|
super(MessagesView, self).configure_form(f)
|
||||||
|
|
||||||
def configure_fieldset(self, fs):
|
|
||||||
# TODO: A fair amount of this still seems hacky...
|
# TODO: A fair amount of this still seems hacky...
|
||||||
|
|
||||||
|
f.set_renderer('sender', self.render_sender)
|
||||||
|
f.set_label('sender', "From")
|
||||||
|
|
||||||
|
f.set_type('sent', 'datetime')
|
||||||
|
|
||||||
|
f.set_renderer('recipients', self.render_recipients_full)
|
||||||
|
f.set_label('recipients', "To")
|
||||||
|
|
||||||
|
f.set_widget('body', dfwidget.TextAreaWidget(cols=50, rows=15))
|
||||||
|
|
||||||
if self.creating:
|
if self.creating:
|
||||||
|
f.remove('sender', 'sent')
|
||||||
|
|
||||||
# Must create a new 'sender' field so that we can feed it the
|
f.insert_after('recipients', 'recipients_')
|
||||||
# current user as default value, but prevent attaching user to the
|
f.remove('recipients')
|
||||||
# (new) underlying message instance...ugh
|
f.set_node('recipients_', colander.SchemaNode(colander.Set()))
|
||||||
fs.append(formalchemy.Field('sender', value=self.request.user,
|
f.set_widget('recipients_', RecipientsWidget())
|
||||||
renderer=forms.renderers.UserFieldRenderer,
|
f.set_label('recipients_', "To")
|
||||||
label="From", readonly=True))
|
|
||||||
|
|
||||||
# Sort of the same thing for recipients, although most of that logic is below.
|
|
||||||
fs.append(RecipientsField('recipients', label="To", renderer=RecipientsFieldRenderer))
|
|
||||||
|
|
||||||
fs.configure(include=[
|
|
||||||
fs.sender,
|
|
||||||
fs.recipients,
|
|
||||||
fs.subject,
|
|
||||||
fs.body.textarea(size='50x15'),
|
|
||||||
])
|
|
||||||
|
|
||||||
# We'll assign some properties directly on the new message;
|
|
||||||
# apparently that's safe and won't cause it to be committed.
|
|
||||||
# Notably, we can't assign the sender yet. Also the actual
|
|
||||||
# recipients assignment is handled by that field's sync().
|
|
||||||
message = fs.model
|
|
||||||
|
|
||||||
if self.replying:
|
if self.replying:
|
||||||
old_message = self.get_instance()
|
old_message = self.get_instance()
|
||||||
message.subject = "Re: {}".format(old_message.subject)
|
f.set_default('subject', "Re: {}".format(old_message.subject))
|
||||||
message.body = self.get_reply_body(old_message)
|
f.set_default('body', self.get_reply_body(old_message))
|
||||||
|
|
||||||
# Determine an initial set of recipients, based on reply
|
# Determine an initial set of recipients, based on reply method.
|
||||||
# method. This value will be set to a 'pseudo' field to avoid
|
|
||||||
# touching the new model instance and causing a crap commit.
|
|
||||||
|
|
||||||
# If replying to all, massage the list a little so that the
|
# If replying to all, massage the list a little so that the
|
||||||
# current user is not listed, and the sender is listed first.
|
# current user is not listed, and the sender is listed first.
|
||||||
|
@ -250,37 +219,36 @@ class MessagesView(MasterView):
|
||||||
if self.filter_reply_recipient(r.recipient)]
|
if self.filter_reply_recipient(r.recipient)]
|
||||||
value = dict(value)
|
value = dict(value)
|
||||||
value.pop(self.request.user.uuid, None)
|
value.pop(self.request.user.uuid, None)
|
||||||
value = sorted(value.iteritems(), key=lambda r: r[1])
|
value = sorted(value.items(), key=lambda r: r[1])
|
||||||
value = [r[0] for r in value]
|
value = [r[0] for r in value]
|
||||||
if old_message.sender is not self.request.user and old_message.sender.active:
|
if old_message.sender is not self.request.user and old_message.sender.active:
|
||||||
value.insert(0, old_message.sender_uuid)
|
value.insert(0, old_message.sender_uuid)
|
||||||
fs.recipients.set(value=value)
|
f.set_default('recipients_', ','.join(value))
|
||||||
|
|
||||||
# Just a normal reply, to sender only.
|
# Just a normal reply, to sender only.
|
||||||
elif self.filter_reply_recipient(old_message.sender):
|
elif self.filter_reply_recipient(old_message.sender):
|
||||||
fs.recipients.set(value=[old_message.sender.uuid])
|
f.set_default('recipients_', old_message.sender.uuid)
|
||||||
|
|
||||||
# Set focus to message body instead of recipients, when replying.
|
# TODO?
|
||||||
fs.focus = fs.body
|
# # Set focus to message body instead of recipients, when replying.
|
||||||
|
# fs.focus = fs.body
|
||||||
|
|
||||||
elif self.viewing:
|
elif self.viewing:
|
||||||
|
f.remove('body')
|
||||||
|
|
||||||
# Viewing an existing message is a heck of a lot easier...
|
def objectify(self, form, data):
|
||||||
fs.configure(include=[
|
message = super(MessagesView, self).objectify(form, data)
|
||||||
fs.sender.with_renderer(SenderFieldRenderer).label("From"),
|
|
||||||
fs.recipients.with_renderer(RecipientsFieldRenderer).label("To"),
|
|
||||||
fs.sent,
|
|
||||||
fs.subject,
|
|
||||||
])
|
|
||||||
|
|
||||||
def before_create(self, form):
|
if self.creating:
|
||||||
"""
|
if self.request.user:
|
||||||
This is where we must assign the current user as sender for new
|
message.sender = self.request.user
|
||||||
messages, for now. I'm still not quite happy with this...
|
|
||||||
"""
|
for uuid in data['recipients_']:
|
||||||
super(MessagesView, self).before_create(form)
|
user = self.Session.query(model.User).get(uuid)
|
||||||
message = form.fieldset.model
|
if user:
|
||||||
message.sender = self.request.user
|
message.add_recipient(user, status=self.enum.MESSAGE_STATUS_INBOX)
|
||||||
|
|
||||||
|
return message
|
||||||
|
|
||||||
def flash_after_create(self, obj):
|
def flash_after_create(self, obj):
|
||||||
self.request.session.flash("Message has been sent: {}".format(
|
self.request.session.flash("Message has been sent: {}".format(
|
||||||
|
@ -290,8 +258,7 @@ class MessagesView(MasterView):
|
||||||
return user.active
|
return user.active
|
||||||
|
|
||||||
def get_reply_header(self, message):
|
def get_reply_header(self, message):
|
||||||
sent = pytz.utc.localize(message.sent)
|
sent = localtime(self.rattail_config, message.sent, from_utc=True)
|
||||||
sent = localtime(self.rattail_config, sent)
|
|
||||||
sent = sent.strftime(self.reply_header_sent_format)
|
sent = sent.strftime(self.reply_header_sent_format)
|
||||||
return "On {}, {} wrote:".format(sent, message.sender.person.display_name)
|
return "On {}, {} wrote:".format(sent, message.sender.person.display_name)
|
||||||
|
|
||||||
|
@ -318,7 +285,6 @@ class MessagesView(MasterView):
|
||||||
|
|
||||||
def template_kwargs_create(self, **kwargs):
|
def template_kwargs_create(self, **kwargs):
|
||||||
kwargs['available_recipients'] = self.get_available_recipients()
|
kwargs['available_recipients'] = self.get_available_recipients()
|
||||||
kwargs['json'] = json
|
|
||||||
if self.replying:
|
if self.replying:
|
||||||
kwargs['original_message'] = self.get_instance()
|
kwargs['original_message'] = self.get_instance()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
@ -371,7 +337,7 @@ class MessagesView(MasterView):
|
||||||
return self.redirect(self.request.get_referrer(
|
return self.redirect(self.request.get_referrer(
|
||||||
default=self.request.route_url('messages_inbox')))
|
default=self.request.route_url('messages_inbox')))
|
||||||
|
|
||||||
new_status = enum.MESSAGE_STATUS_INBOX if dest == 'inbox' else enum.MESSAGE_STATUS_ARCHIVE
|
new_status = self.enum.MESSAGE_STATUS_INBOX if dest == 'inbox' else self.enum.MESSAGE_STATUS_ARCHIVE
|
||||||
if recipient.status != new_status:
|
if recipient.status != new_status:
|
||||||
recipient.status = new_status
|
recipient.status = new_status
|
||||||
return self.redirect(self.request.route_url('messages.{}'.format(
|
return self.redirect(self.request.route_url('messages.{}'.format(
|
||||||
|
@ -385,7 +351,7 @@ class MessagesView(MasterView):
|
||||||
if self.request.method == 'POST':
|
if self.request.method == 'POST':
|
||||||
uuids = self.request.POST.get('uuids', '').split(',')
|
uuids = self.request.POST.get('uuids', '').split(',')
|
||||||
if uuids:
|
if uuids:
|
||||||
new_status = enum.MESSAGE_STATUS_INBOX if dest == 'inbox' else enum.MESSAGE_STATUS_ARCHIVE
|
new_status = self.enum.MESSAGE_STATUS_INBOX if dest == 'inbox' else self.enum.MESSAGE_STATUS_ARCHIVE
|
||||||
for uuid in uuids:
|
for uuid in uuids:
|
||||||
recip = self.Session.query(model.MessageRecipient)\
|
recip = self.Session.query(model.MessageRecipient)\
|
||||||
.filter(model.MessageRecipient.message_uuid == uuid)\
|
.filter(model.MessageRecipient.message_uuid == uuid)\
|
||||||
|
@ -436,7 +402,7 @@ class InboxView(MessagesView):
|
||||||
|
|
||||||
def query(self, session):
|
def query(self, session):
|
||||||
q = super(InboxView, self).query(session)
|
q = super(InboxView, self).query(session)
|
||||||
return q.filter(model.MessageRecipient.status == enum.MESSAGE_STATUS_INBOX)
|
return q.filter(model.MessageRecipient.status == self.enum.MESSAGE_STATUS_INBOX)
|
||||||
|
|
||||||
|
|
||||||
class ArchiveView(MessagesView):
|
class ArchiveView(MessagesView):
|
||||||
|
@ -452,7 +418,7 @@ class ArchiveView(MessagesView):
|
||||||
|
|
||||||
def query(self, session):
|
def query(self, session):
|
||||||
q = super(ArchiveView, self).query(session)
|
q = super(ArchiveView, self).query(session)
|
||||||
return q.filter(model.MessageRecipient.status == enum.MESSAGE_STATUS_ARCHIVE)
|
return q.filter(model.MessageRecipient.status == self.enum.MESSAGE_STATUS_ARCHIVE)
|
||||||
|
|
||||||
|
|
||||||
class SentView(MessagesView):
|
class SentView(MessagesView):
|
||||||
|
@ -481,6 +447,20 @@ class SentView(MessagesView):
|
||||||
default_active=True, default_verb='contains')
|
default_active=True, default_verb='contains')
|
||||||
|
|
||||||
|
|
||||||
|
class RecipientsWidget(dfwidget.TextInputWidget):
|
||||||
|
|
||||||
|
def deserialize(self, field, pstruct):
|
||||||
|
if pstruct is colander.null:
|
||||||
|
return []
|
||||||
|
elif not isinstance(pstruct, six.string_types):
|
||||||
|
raise colander.Invalid(field.schema, "Pstruct is not a string")
|
||||||
|
if self.strip:
|
||||||
|
pstruct = pstruct.strip()
|
||||||
|
if not pstruct:
|
||||||
|
return []
|
||||||
|
return pstruct.split(',')
|
||||||
|
|
||||||
|
|
||||||
def includeme(config):
|
def includeme(config):
|
||||||
|
|
||||||
config.add_tailbone_permission('messages', 'messages.list', "List/Search Messages")
|
config.add_tailbone_permission('messages', 'messages.list', "List/Search Messages")
|
||||||
|
|
Loading…
Reference in a new issue