Allow multiple related liaisons. Close #739

- Legacy-Id: 8103
This commit is contained in:
Emilio A. Sánchez López 2014-07-18 16:15:16 +00:00
parent 439a3b16c7
commit 0ccbe9144f
7 changed files with 129 additions and 61 deletions

View file

@ -12,13 +12,14 @@ from django.utils.html import format_html
from django.utils.encoding import force_text
from django.utils.safestring import mark_safe
from ietf.name.models import DocRelationshipName
from ietf.liaisons.accounts import (can_add_outgoing_liaison, can_add_incoming_liaison,
get_person_for_user, is_secretariat, is_sdo_liaison_manager)
from ietf.liaisons.utils import IETFHM
from ietf.liaisons.widgets import (FromWidget, ReadOnlyWidget, ButtonWidget,
ShowAttachmentsWidget, RelatedLiaisonWidget)
from ietf.liaisons.models import LiaisonStatement, LiaisonStatementPurposeName, LiaisonStatementEvent
from ietf.liaisons.models import (LiaisonStatement, LiaisonStatementPurposeName, LiaisonStatementEvent,
RelatedLiaisonStatement)
from ietf.group.models import Group, Role
from ietf.person.models import Person, Email
from ietf.doc.models import Document
@ -45,7 +46,7 @@ class LiaisonForm(forms.Form):
require=['id_attach_title', 'id_attach_file'],
required_label='title and file'),
required=False)
related_to = forms.ModelChoiceField(LiaisonStatement.objects.all(), label=u'Related Liaison', widget=RelatedLiaisonWidget, required=False)
related_to = forms.ModelMultipleChoiceField(LiaisonStatement.objects.all(), label=u'Related Liaison', widget=RelatedLiaisonWidget, required=False)
fieldsets = [('From', ('from_field', 'response_contacts')),
('To', ('organization', 'to_poc')),
@ -162,6 +163,10 @@ class LiaisonForm(forms.Form):
except UnicodeEncodeError as e:
raise forms.ValidationError('Invalid email address: %s (check character %d)' % (addr,e.start))
def clean_related_to(self):
related_list = self.data.getlist('related_to')
return [i for i in related_list if i]
def clean_technical_contact(self):
value = self.cleaned_data.get('technical_contact', None)
self.check_email(value)
@ -218,21 +223,16 @@ class LiaisonForm(forms.Form):
l.purpose = LiaisonStatementPurposeName.objects.get(order=self.cleaned_data["purpose"])
l.body = self.cleaned_data["body"].strip()
l.deadline = self.cleaned_data["deadline_date"]
l.related_to = self.cleaned_data["related_to"]
l.response_contacts = self.cleaned_data["response_contacts"]
l.technical_contact = self.cleaned_data["technical_contact"]
now = datetime.datetime.now()
l.modified = now
l.submitted = datetime.datetime.combine(self.cleaned_data["submitted_date"], now.time())
if not l.approved:
l.approved = now
self.save_extra_fields(l)
l.save() # we have to save here to make sure we get an id for the attachments
self.save_attachments(l)
self.save_related_liaisons(l)
return l
@ -278,6 +278,16 @@ class LiaisonForm(forms.Form):
attach_file.write(attached_file.read())
attach_file.close()
def save_related_liaisons(self, instance):
rel = DocRelationshipName.objects.get(slug='refold')
for i in self.cleaned_data.get('related_to', []):
try:
instance.source_of_set.get_or_create(
target=LiaisonStatement.objects.get(id=i),
relationship=rel)
except LiaisonStatement.DoesNotExist:
continue
def clean_title(self):
title = self.cleaned_data.get('title', None)
if self.instance and self.instance.pk:
@ -389,10 +399,10 @@ class OutgoingLiaisonForm(LiaisonForm):
super(OutgoingLiaisonForm, self).save_extra_fields(liaison)
from_entity = self.get_from_entity()
needs_approval = from_entity.needs_approval(self.person)
if not needs_approval or self.cleaned_data.get('approved', False):
liaison.approved = datetime.datetime.now()
else:
liaison.approved = None
#if not needs_approval or self.cleaned_data.get('approved', False):
#liaison.approved = datetime.datetime.now()
#else:
#liaison.approved = None
def clean_to_poc(self):
value = self.cleaned_data.get('to_poc', None)

View file

@ -39,12 +39,12 @@ class LiaisonStatement(models.Model):
state = models.ForeignKey(LiaisonStatementState, default='pending')
def name(self):
if self.from_group:
frm = self.from_group.acronym or self.from_group.name
if self.from_groups.count():
frm = ', '.join([i.acronym or i.name for i in self.from_groups.all()])
else:
frm = self.from_name
if self.to_group:
to = self.to_group.acronym or self.to_group.name
if self.to_groups.count():
to = ', '.join([i.acronym or i.name for i in self.to_groups.all()])
else:
to = self.to_name
return slugify("liaison" + " " + self.submitted.strftime("%Y-%m-%d") + " " + frm[:50] + " " + to[:50] + " " + self.title[:115])

View file

@ -119,12 +119,10 @@ def liaison_list(request):
}, context_instance=RequestContext(request))
def ajax_liaison_list(request):
sort, order_by = normalize_sort(request)
liaisons = LiaisonStatement.objects.exclude(approved=None).order_by(order_by)
liaisons = SearchLiaisonForm().get_results()
return render_to_response('liaisons/liaison_table.html', {
"liaisons": liaisons,
"sort": sort,
}, context_instance=RequestContext(request))
@can_submit_liaison_required
@ -147,8 +145,13 @@ def liaison_approval_detail(request, object_id):
send_liaison_by_email(request, liaison)
return redirect('liaison_list')
relations_by = [i.target for i in liaison.source_of_set.filter(target__state__slug='approved')]
relations_to = [i.source for i in liaison.target_of_set.filter(source__state__slug='approved')]
return render_to_response('liaisons/approval_detail.html', {
"liaison": liaison,
"relations_to": relations_to,
"relations_by": relations_by,
"is_approving": True,
}, context_instance=RequestContext(request))
@ -201,13 +204,15 @@ def liaison_detail(request, object_id):
liaison.save()
can_take_care = False
relations = liaison.source_of_set.filter(target__state__slug='approved')
relations_by = [i.target for i in liaison.source_of_set.filter(target__state__slug='approved')]
relations_to = [i.source for i in liaison.target_of_set.filter(source__state__slug='approved')]
return render_to_response("liaisons/detail.html", {
"liaison": liaison,
"can_edit": can_edit,
"can_take_care": can_take_care,
"relations": relations,
"relations_to": relations_to,
"relations_by": relations_by,
}, context_instance=RequestContext(request))
def liaison_edit(request, object_id):

View file

@ -84,14 +84,13 @@ class ShowAttachmentsWidget(Widget):
class RelatedLiaisonWidget(TextInput):
def value_from_datadict(self, data, files, name):
return [i for i in data.getlist(name) if i]
def render(self, name, value, attrs=None):
if not value:
value = ''
title = ''
noliaison = 'inline'
deselect = 'none'
else:
liaison = LiaisonStatement.objects.get(pk=value)
html = u''
for liaison_id in value:
liaison = LiaisonStatement.objects.get(pk=liaison_id)
title = liaison.title
if not title:
attachments = liaison.attachments.all()
@ -101,11 +100,21 @@ class RelatedLiaisonWidget(TextInput):
title = 'Liaison #%s' % liaison.pk
noliaison = 'none'
deselect = 'inline'
html = u'<span class="noRelated" style="display: %s;">No liaison selected</span>' % conditional_escape(noliaison)
html += u'<span class="relatedLiaisonWidgetTitle">%s</span>' % conditional_escape(title)
html += u'<input type="hidden" name="%s" class="relatedLiaisonWidgetValue" value="%s" /> ' % (conditional_escape(name), conditional_escape(value))
html += u'<div>'
html += u'<span class="noRelated" style="display: %s;"></span>' % conditional_escape(noliaison)
html += u'<span class="relatedLiaisonWidgetTitle">%s</span>' % conditional_escape(title)
html += u'<input type="hidden" name="%s" class="relatedLiaisonWidgetValue" value="%s" /> ' % (conditional_escape(name), conditional_escape(liaison_id))
html += u'<span style="display: none;" class="listURL">%s</span> ' % urlreverse('ajax_liaison_list')
html += u'<div style="display: none;" class="relatedLiaisonWidgetDialog" id="related-dialog" title="Select a liaison statement"></div> '
html += '<input type="button" style="display: %s;" class="id_no_%s" id="id_no_%s" value="Deselect liaison statement" />' % (conditional_escape(deselect), conditional_escape(name), conditional_escape(name))
html += u'</div>'
html += u'<div>'
html += u'<span class="noRelated" style="display: inline;"></span>'
html += u'<span class="relatedLiaisonWidgetTitle"></span>'
html += u'<input type="hidden" name="%s" class="relatedLiaisonWidgetValue" value="" /> ' % conditional_escape(name)
html += u'<span style="display: none;" class="listURL">%s</span> ' % urlreverse('ajax_liaison_list')
html += u'<div style="display: none;" class="relatedLiaisonWidgetDialog" id="related-dialog" title="Select a liaison"></div> '
html += '<input type="button" id="id_%s" value="Select liaison" /> ' % conditional_escape(name)
html += '<input type="button" style="display: %s;" id="id_no_%s" value="Deselect liaison" />' % (conditional_escape(deselect), conditional_escape(name))
html += u'<div style="display: none;" class="relatedLiaisonWidgetDialog" id="related-dialog" title="Select a liaison statement"></div> '
html += '<input type="button" class="id_%s" id="id_%s" value="Select liaison statement" /> ' % (conditional_escape(name), conditional_escape(name))
html += '<input type="button" style="display: none;" class="id_no_%s" id="id_no_%s" value="Deselect liaison statement" />' % (conditional_escape(name), conditional_escape(name))
html += u'</div>'
return mark_safe(html)

View file

@ -21,37 +21,47 @@
<tr>
<td style="width:18ex;">Submission Date:</td>
<td>{{ liaison.submitted|date:"Y-m-d" }}</td></tr>
{% if liaison.from_contact %}<tr>
<td>Sender:</td>
<td>
<a href="mailto:{{ liaison.from_contact.address }}">{{ liaison.from_contact.person }}</a>
</td>
</tr>{% endif %}
<tr>
<td>From:</td>
<td>{{ liaison.from_name }}
{% if liaison.from_contact %}(<a href="mailto:{{ liaison.from_contact.address }}">{{ liaison.from_contact.person }}</a>){% endif %}
<td>
{% for group in liaison.from_groups.all %}
{{ group.name }}
{% empty %}
{{ liaison.from_name }}
{% endfor %}
</td>
</tr>
<tr>
<td>To:</td>
<td>
{% if liaison.from_contact %}
{{ liaison.to_name }} ({{ liaison.to_contact|parse_email_list }})
{% else %}
{{ liaison.to_name|urlize }}
{% endif %}
{% for group in liaison.to_groups.all %}
{{ group.name }}
{% empty %}
{{ liaison.to_name }}
{% endfor %}
</td>
</tr>
{% if liaison.from_contact %}
<tr>
<td>Cc:</td><td>{{ liaison.cc|parse_email_list|make_one_per_line|safe|linebreaksbr }}</td>
<td>Cc:</td><td>{{ liaison.cc_contacts|parse_email_list|make_one_per_line|safe|linebreaksbr }}</td>
</tr>
<tr>
<td>Response Contact:</td>
<td>{{ liaison.response_contact|parse_email_list|make_one_per_line|safe|linebreaksbr }}</td>
<td>{{ liaison.response_contacts|parse_email_list|make_one_per_line|safe|linebreaksbr }}</td>
</tr>
<tr>
<td>Technical Contact:</td>
<td>{{ liaison.technical_contact|parse_email_list|make_one_per_line|safe|linebreaksbr }}</td>
<td>{{ liaison.technical_contacts|parse_email_list|make_one_per_line|safe|linebreaksbr }}</td>
</tr>
<tr>
@ -74,11 +84,22 @@
{% endif %}
{% endif %}
{% if relations %}
{% if relations_by %}
<tr>
<td>Liaisons referred by this one:</td>
<td>
{% for rel in relations_by %}
<a href="{% url "liaison_detail" rel.pk %}">{% if rel.title %}{{ rel.title }}{% else %}Liaison #{{ rel.pk }}{% endif %}</a><br />
{% endfor %}
</td>
</tr>
{% endif %}
{% if relations_to %}
<tr>
<td>Liaisons referring to this one:</td>
<td>
{% for rel in relations %}
{% for rel in relations_to %}
<a href="{% url "liaison_detail" rel.pk %}">{% if rel.title %}{{ rel.title }}{% else %}Liaison #{{ rel.pk }}{% endif %}</a><br />
{% endfor %}
</td>

View file

@ -30,5 +30,27 @@ This page depends on Javascript being enabled to work properly. Please enable Ja
{% block js %}
<script type="text/javascript" src="/js/jquery-ui-1.9.0.custom/minified/jquery-ui.custom.min.js"></script>
<script type="text/javascript" src="/js/jquery.tablesorter.min.js"></script>
<script type="text/javascript" src="/js/liaisons.js"></script>
<script type="text/javascript">
$(document).ready(function()
{
var widgetIETF = {css: ["evenrow", "oddrow"]};
$.tablesorter.addWidget({
id: "ietf",
format: function(table) {
var $tr, row = -1, odd;
$("tr:visible", table.tBodies[0]).each(function (i) {
$tr = $(this);
if (!$tr.hasClass(table.config.cssChildRow)) row++;
odd = (row % 2 == 0);
$tr.removeClass(
widgetIETF.css[odd ? 0 : 1]).addClass(
widgetIETF.css[odd ? 1 : 0])
});
}
});
}
);
</script>
{% endblock %}

View file

@ -138,10 +138,10 @@ jQuery(function () {
var cancel = form.find('#id_cancel');
var cancel_dialog = form.find('#cancel-dialog');
var config = {};
var related_trigger = form.find('#id_related_to');
var related_trigger = form.find('.id_related_to');
var related_url = form.find('#id_related_to').parent().find('.listURL').text();
var related_dialog = form.find('#related-dialog');
var unrelate_trigger = form.find('#id_no_related_to');
var unrelate_trigger = form.find('.id_no_related_to');
var readConfig = function() {
var confcontainer = form.find('.formconfig');
@ -283,20 +283,22 @@ jQuery(function () {
var link = jQuery(this).text();;
var pk = jQuery(this).nextAll('.liaisonPK').text();
var widget = related_trigger.parent();
widget.find('.relatedLiaisonWidgetTitle').text(link);
widget.find('.relatedLiaisonWidgetValue').val(pk);
widget.find('.noRelated').hide();
unrelate_trigger.show();
var newwidget = widget.clone()
related_dialog.dialog('close');
newwidget.find('.id_related_to').click(selectRelated);
newwidget.find('.id_no_related_to').click(selectNoRelated);
newwidget.find('.relatedLiaisonWidgetTitle').text(link);
newwidget.find('.relatedLiaisonWidgetValue').val(pk);
newwidget.find('.noRelated').hide();
newwidget.find('.id_related_to').hide();
newwidget.find('.id_no_related_to').show();
widget.before(newwidget);
return false;
};
var selectNoRelated = function() {
var widget = jQuery(this).parent();
widget.find('.relatedLiaisonWidgetTitle').text('');
widget.find('.noRelated').show();
widget.find('.relatedLiaisonWidgetValue').val('');
jQuery(this).hide();
widget.remove();
return false;
};
@ -315,10 +317,9 @@ jQuery(function () {
dataType: 'html',
success: function(response){
related_dialog.html(response);
related_dialog.find('th a').click(function() {
widget.find('.listURL').text(related_url + jQuery(this).attr('href'));
trigger.click();
return false;
related_dialog.find("#LiaisonListTable").tablesorter({
sortList: [[0, 1]],
widgets: ["ietf"]
});
related_dialog.find('td a').click(getRelatedLink);
}