Refactor magic recipients field when sending new message.
Uses local cache of user mappings instead of AJAX calls; has just enough customization hooks to allow for a department/user mapping for MJ...
This commit is contained in:
parent
ad9cd8be8e
commit
c65bc6f229
|
@ -7,43 +7,53 @@
|
|||
${h.stylesheet_link(request.static_url('tailbone:static/css/jquery.tagit.css'))}
|
||||
<script type="text/javascript">
|
||||
|
||||
% if initial_recipient_names is not Undefined:
|
||||
var initial_recipients = {
|
||||
% for i, (uuid, name) in enumerate(initial_recipient_names.iteritems(), 1):
|
||||
'${uuid}': "${name.replace('"', '\\""')|n}"${',' if i < len(initial_recipient_names) else ''}
|
||||
% endfor
|
||||
};
|
||||
% endif
|
||||
var recipient_mappings = new Map([
|
||||
<% last = len(available_recipients) %>
|
||||
% for i, (uuid, entry) in enumerate(sorted(available_recipients.iteritems(), key=lambda r: r[1]), 1):
|
||||
['${uuid}', ${json.dumps(entry)|n}]${',' if i < last else ''}
|
||||
% endfor
|
||||
]);
|
||||
|
||||
$(function() {
|
||||
|
||||
var recipients = $('.recipients input');
|
||||
|
||||
recipients.tagit({
|
||||
|
||||
autocomplete: {
|
||||
delay: 0,
|
||||
minLength: 2,
|
||||
autoFocus: true,
|
||||
removeConfirmation: true,
|
||||
|
||||
source: function(request, response) {
|
||||
$.get('${url('messages.recipients')}', {term: request.term}, response);
|
||||
},
|
||||
select: function(event, ui) {
|
||||
recipients.tagit('createTag', ui.item.value + ',' + ui.item.label);
|
||||
// Preventing the tag input to be updated with the chosen value.
|
||||
return false;
|
||||
var term = request.term.toLowerCase();
|
||||
var data = [];
|
||||
recipient_mappings.forEach(function(name, uuid) {
|
||||
if (!name.toLowerCase && name.name) {
|
||||
name = name.name;
|
||||
}
|
||||
if (name.toLowerCase().indexOf(term) >= 0) {
|
||||
data.push({value: uuid, label: name});
|
||||
}
|
||||
});
|
||||
response(data);
|
||||
}
|
||||
},
|
||||
beforeTagAdded: function(event, ui) {
|
||||
var label = ui.tagLabel.split(',');
|
||||
var value = label.shift();
|
||||
$(ui.tag).find('.tagit-hidden-field').val(value);
|
||||
if (label.length) {
|
||||
$(ui.tag).find('.tagit-label').text(label.join());
|
||||
} else {
|
||||
$(ui.tag).find('.tagit-label').text(initial_recipients[value]);
|
||||
}
|
||||
},
|
||||
removeConfirmation: true
|
||||
|
||||
beforeTagAdded: ${self.before_tag_added()},
|
||||
|
||||
beforeTagRemoved: function(event, ui) {
|
||||
|
||||
// Unfortunately we're responsible for cleaning up the hidden
|
||||
// field, since the values there do not match the tag labels.
|
||||
var tags = recipients.tagit('assignedTags');
|
||||
var uuid = ui.tag.data('uuid');
|
||||
tags = tags.filter(function(element) {
|
||||
return element != uuid;
|
||||
});
|
||||
recipients.data('ui-tagit')._updateSingleTagsField(tags);
|
||||
}
|
||||
});
|
||||
|
||||
// set focus to recipients field
|
||||
|
@ -61,6 +71,18 @@
|
|||
</style>
|
||||
</%def>
|
||||
|
||||
<%def name="before_tag_added()">
|
||||
function(event, ui) {
|
||||
|
||||
// Lookup the name in cached mapping, and show that on the tag, instead
|
||||
// of the UUID. The tagit widget should take care of keeping the
|
||||
// hidden field in sync for us, still using the UUID.
|
||||
var uuid = ui.tagLabel;
|
||||
var name = recipient_mappings.get(uuid);
|
||||
ui.tag.find('.tagit-label').html(name);
|
||||
}
|
||||
</%def>
|
||||
|
||||
<%def name="context_menu_items()">
|
||||
% if request.has_perm('messages.list'):
|
||||
<li>${h.link_to("Go to my Message Inbox", url('messages.inbox'))}</li>
|
||||
|
|
|
@ -63,6 +63,9 @@ class AutocompleteView(View):
|
|||
"""
|
||||
return instance.uuid
|
||||
|
||||
def get_data(self, term):
|
||||
return self.query(term).all()
|
||||
|
||||
def __call__(self):
|
||||
"""
|
||||
View implementation.
|
||||
|
@ -73,5 +76,5 @@ class AutocompleteView(View):
|
|||
term = self.prepare_term(term)
|
||||
if not term:
|
||||
return []
|
||||
results = self.query(term).all()
|
||||
results = self.get_data(term)
|
||||
return [{'label': self.display(x), 'value': self.value(x)} for x in results]
|
||||
|
|
|
@ -26,6 +26,7 @@ Message Views
|
|||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
import json
|
||||
import pytz
|
||||
|
||||
from rattail import enum
|
||||
|
@ -39,7 +40,7 @@ from webhelpers.html import tags, HTML
|
|||
|
||||
from tailbone import forms
|
||||
from tailbone.db import Session
|
||||
from tailbone.views import MasterView, AutocompleteView
|
||||
from tailbone.views import MasterView
|
||||
|
||||
|
||||
class SubjectFieldRenderer(formalchemy.FieldRenderer):
|
||||
|
@ -80,7 +81,7 @@ class RecipientsFieldRenderer(formalchemy.FieldRenderer):
|
|||
def render(self, **kwargs):
|
||||
uuids = self.value
|
||||
value = ','.join(uuids) if uuids else ''
|
||||
return text_field(self.name, value=value, maxlength=self.length, **kwargs)
|
||||
return text_field(self.name, value=value, **kwargs)
|
||||
|
||||
def deserialize(self):
|
||||
value = self.params.getone(self.name).split(',')
|
||||
|
@ -150,8 +151,8 @@ class MessagesView(MasterView):
|
|||
|
||||
def query(self, session):
|
||||
return session.query(model.Message)\
|
||||
.outerjoin(model.MessageRecipient)\
|
||||
.filter(model.MessageRecipient.recipient == self.request.user)
|
||||
.outerjoin(model.MessageRecipient)\
|
||||
.filter(model.MessageRecipient.recipient == self.request.user)
|
||||
|
||||
def configure_grid(self, g):
|
||||
|
||||
|
@ -206,9 +207,9 @@ class MessagesView(MasterView):
|
|||
message.subject = "Re: {}".format(old_message.subject)
|
||||
message.body = self.get_reply_body(old_message)
|
||||
if self.replying == 'all':
|
||||
value = [(r.recipient_uuid, r.recipient.person.display_name)
|
||||
value = [(r.recipient.uuid, r.recipient.person.display_name)
|
||||
for r in old_message.recipients
|
||||
if r.recipient.active]
|
||||
if self.filter_reply_recipient(r.recipient)]
|
||||
value = dict(value)
|
||||
value.pop(self.request.user.uuid, None)
|
||||
value = sorted(value.iteritems(), key=lambda r: r[1])
|
||||
|
@ -216,8 +217,8 @@ class MessagesView(MasterView):
|
|||
if old_message.sender is not self.request.user and old_message.sender.active:
|
||||
value.insert(0, old_message.sender_uuid)
|
||||
fs.recipients.set(value=value)
|
||||
else:
|
||||
fs.recipients.set(value=[old_message.sender_uuid])
|
||||
elif self.filter_reply_recipient(old_message.sender):
|
||||
fs.recipients.set(value=[old_message.sender.uuid])
|
||||
fs.focus = fs.body
|
||||
|
||||
elif self.viewing:
|
||||
|
@ -228,6 +229,9 @@ class MessagesView(MasterView):
|
|||
fs.subject,
|
||||
])
|
||||
|
||||
def filter_reply_recipient(self, user):
|
||||
return user.active
|
||||
|
||||
def get_reply_header(self, message):
|
||||
sent = pytz.utc.localize(message.sent)
|
||||
sent = localtime(self.rattail_config, sent)
|
||||
|
@ -247,22 +251,34 @@ class MessagesView(MasterView):
|
|||
return '\n'.join(lines)
|
||||
|
||||
def get_recipient(self, message):
|
||||
for recip in message.recipients:
|
||||
if recip.recipient is self.request.user:
|
||||
return recip
|
||||
"""
|
||||
Fetch the recipient from the given message, which corresponds to the
|
||||
current (request) user.
|
||||
"""
|
||||
for recipient in message.recipients:
|
||||
if recipient.recipient is self.request.user:
|
||||
return recipient
|
||||
|
||||
def template_kwargs_create(self, **kwargs):
|
||||
kwargs['available_recipients'] = self.get_available_recipients()
|
||||
kwargs['json'] = json
|
||||
if self.replying:
|
||||
message = self.get_instance()
|
||||
kwargs['original_message'] = message
|
||||
names = {message.sender_uuid: message.sender.person.display_name}
|
||||
if self.replying == 'all':
|
||||
for recip in message.recipients:
|
||||
if recip.recipient is not message.sender:
|
||||
names[recip.recipient_uuid] = recip.recipient.person.display_name
|
||||
kwargs['initial_recipient_names'] = names
|
||||
kwargs['original_message'] = self.get_instance()
|
||||
return kwargs
|
||||
|
||||
def get_available_recipients(self):
|
||||
"""
|
||||
Return the full mapping of recipients which may be included in a
|
||||
message sent by the current user.
|
||||
"""
|
||||
recips = {}
|
||||
users = Session.query(model.User)\
|
||||
.join(model.Person)\
|
||||
.filter(model.User.active == True)
|
||||
for user in users:
|
||||
recips[user.uuid] = user.person.display_name
|
||||
return recips
|
||||
|
||||
def template_kwargs_view(self, **kwargs):
|
||||
message = kwargs['instance']
|
||||
return {'message': message,
|
||||
|
@ -395,29 +411,13 @@ class SentView(MessagesView):
|
|||
|
||||
def query(self, session):
|
||||
return session.query(model.Message)\
|
||||
.filter(model.Message.sender == self.request.user)
|
||||
.filter(model.Message.sender == self.request.user)
|
||||
|
||||
def configure_grid(self, g):
|
||||
super(SentView, self).configure_grid(g)
|
||||
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")
|
||||
|
@ -437,9 +437,4 @@ def includeme(config):
|
|||
config.add_view(SentView, attr='index', route_name='messages.sent',
|
||||
permission='messages.list')
|
||||
|
||||
# recipients autocomplete
|
||||
config.add_route('messages.recipients', '/messages/recipients')
|
||||
config.add_view(RecipientsAutocomplete, route_name='messages.recipients',
|
||||
renderer='json', permission='messages.create')
|
||||
|
||||
MessagesView.defaults(config)
|
||||
|
|
Loading…
Reference in a new issue