Skip to content

Commit 8e7295c

Browse files
committed
Management of pending liaisons.
If an user can approve pending liaisons he/she have access to: - List of pending liaisons he/she can approve - Detailed view of a pending liaison with an approval button Fixes ietf-tools#357 - Legacy-Id: 2466
1 parent 57f1cba commit 8e7295c

10 files changed

Lines changed: 336 additions & 14 deletions

ietf/liaisons/forms.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import datetime
22

33
from django import forms
4-
from django.conf import settings
54
from django.forms.util import ErrorList
65
from django.template.loader import render_to_string
76

@@ -151,6 +150,7 @@ def save_extra_fields(self, liaison):
151150
liaison.last_modified_date = now
152151
from_entity = self.get_from_entity()
153152
liaison.from_raw_body = from_entity.name
153+
liaison.from_raw_code = self.cleaned_data.get('from_field')
154154
organization = self.get_to_entity()
155155
liaison.to_body = organization.name
156156
liaison.to_poc = self.get_poc(organization)
@@ -165,7 +165,6 @@ def save_attachments(self, instance):
165165
continue
166166
attached_file = self.files.get(key)
167167
extension=attached_file.name.rsplit('.', 1)
168-
basename = extension[0]
169168
if len(extension) > 1:
170169
extension = '.' + extension[1]
171170
else:
@@ -248,7 +247,8 @@ def save_extra_fields(self, liaison):
248247

249248
def liaison_form_factory(request, **kwargs):
250249
user = request.user
251-
if can_add_outgoing_liaison(user):
250+
force_incoming = 'incoming' in request.GET.keys()
251+
if not force_incoming and can_add_outgoing_liaison(user):
252252
return OutgoingLiaisonForm(user, **kwargs)
253253
elif can_add_incoming_liaison(user):
254254
return IncomingLiaisonForm(user, **kwargs)
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
2+
from south.db import db
3+
from django.db import models
4+
from ietf.liaisons.models import *
5+
6+
class Migration:
7+
8+
def forwards(self, orm):
9+
10+
# Adding field 'LiaisonDetail.from_raw_code'
11+
db.add_column('liaison_detail', 'from_raw_code', orm['liaisons.liaisondetail:from_raw_code'])
12+
13+
# Deleting field 'OutgoingLiaisonApproval.normalized_entity_code'
14+
db.delete_column('liaisons_outgoingliaisonapproval', 'normalized_entity_code')
15+
16+
17+
18+
def backwards(self, orm):
19+
20+
# Deleting field 'LiaisonDetail.from_raw_code'
21+
db.delete_column('liaison_detail', 'from_raw_code')
22+
23+
# Adding field 'OutgoingLiaisonApproval.normalized_entity_code'
24+
db.add_column('liaisons_outgoingliaisonapproval', 'normalized_entity_code', orm['liaisons.outgoingliaisonapproval:normalized_entity_code'])
25+
26+
27+
28+
models = {
29+
'idtracker.personororginfo': {
30+
'Meta': {'db_table': "'person_or_org_info'"},
31+
'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}),
32+
'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}),
33+
'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
34+
'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
35+
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}),
36+
'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}),
37+
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
38+
'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
39+
'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}),
40+
'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}),
41+
'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}),
42+
'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
43+
'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
44+
'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
45+
'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'})
46+
},
47+
'liaisons.frombodies': {
48+
'Meta': {'db_table': "'from_bodies'"},
49+
'body_name': ('django.db.models.fields.CharField', [], {'max_length': '35', 'blank': 'True'}),
50+
'email_priority': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
51+
'from_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
52+
'is_liaison_manager': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
53+
'other_sdo': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
54+
'poc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'db_column': "'poc'"})
55+
},
56+
'liaisons.liaisondetail': {
57+
'Meta': {'db_table': "'liaison_detail'"},
58+
'approval': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['liaisons.OutgoingLiaisonApproval']", 'null': 'True', 'blank': 'True'}),
59+
'body': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
60+
'by_secretariat': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
61+
'cc1': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
62+
'cc2': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
63+
'deadline_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
64+
'detail_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
65+
'from_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
66+
'from_raw_body': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
67+
'from_raw_code': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
68+
'last_modified_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
69+
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'db_column': "'person_or_org_tag'"}),
70+
'purpose': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['liaisons.LiaisonPurpose']", 'null': 'True'}),
71+
'purpose_text': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_column': "'purpose'", 'blank': 'True'}),
72+
'replyto': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
73+
'response_contact': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
74+
'submitted_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
75+
'submitter_email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
76+
'submitter_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
77+
'technical_contact': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
78+
'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
79+
'to_body': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
80+
'to_email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
81+
'to_poc': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
82+
},
83+
'liaisons.liaisonmanagers': {
84+
'Meta': {'db_table': "'liaison_managers'"},
85+
'email_priority': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
86+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
87+
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'db_column': "'person_or_org_tag'"}),
88+
'sdo': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['liaisons.SDOs']"})
89+
},
90+
'liaisons.liaisonpurpose': {
91+
'Meta': {'db_table': "'liaison_purpose'"},
92+
'purpose_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
93+
'purpose_text': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'})
94+
},
95+
'liaisons.outgoingliaisonapproval': {
96+
'approval_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
97+
'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
98+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
99+
},
100+
'liaisons.sdoauthorizedindividual': {
101+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
102+
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'db_column': "'person_or_org_tag'"}),
103+
'sdo': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['liaisons.SDOs']"})
104+
},
105+
'liaisons.sdos': {
106+
'Meta': {'db_table': "'sdos'"},
107+
'sdo_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
108+
'sdo_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
109+
},
110+
'liaisons.uploads': {
111+
'Meta': {'db_table': "'uploads'"},
112+
'detail': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['liaisons.LiaisonDetail']"}),
113+
'file_extension': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'}),
114+
'file_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
115+
'file_title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
116+
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'db_column': "'person_or_org_tag'"})
117+
}
118+
}
119+
120+
complete_apps = ['liaisons']

ietf/liaisons/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ class Meta:
3434
class OutgoingLiaisonApproval(models.Model):
3535
approved = models.BooleanField(default=True)
3636
approval_date = models.DateField(null=True, blank=True)
37-
normalized_entity_code = models.CharField(max_length=255)
3837

3938

4039
class LiaisonDetail(models.Model):
@@ -62,6 +61,7 @@ class LiaisonDetail(models.Model):
6261
purpose = models.ForeignKey(LiaisonPurpose,null=True)
6362
replyto = models.CharField(blank=True, null=True, max_length=255)
6463
from_raw_body = models.CharField(blank=True, null=True, max_length=255)
64+
from_raw_code = models.CharField(blank=True, null=True, max_length=255)
6565
approval = models.ForeignKey(OutgoingLiaisonApproval, blank=True, null=True)
6666
def __str__(self):
6767
return self.title or "<no title>"

ietf/liaisons/urls.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
# there's an opportunity for date-based filtering.
1212
urlpatterns = patterns('django.views.generic.list_detail',
13-
url(r'^$', 'object_list', info_dict, name='liaison_list'),
1413
url(r'^(?P<object_id>\d+)/$', 'object_detail', info_dict, name='liaison_detail'),
1514
)
1615

@@ -23,6 +22,9 @@
2322
)
2423

2524
urlpatterns += patterns('ietf.liaisons.views',
25+
url(r'^$', 'liaison_list', name='liaison_list'),
26+
url(r'^for_approval/$', 'liaison_approval_list', name='liaison_approval_list'),
27+
url(r'^for_approval/(?P<object_id>\d+)/$', 'liaison_approval_detail', name='liaison_approval_detail'),
2628
url(r'^add/$', 'add_liaison', name='add_liaison'),
2729
url(r'^ajax/get_info/$', 'get_info', name='get_info'),
2830
)

ietf/liaisons/utils.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ def get_managed_list(self):
165165
def can_send_on_behalf(self, person):
166166
return []
167167

168+
def can_approve_list(self, person):
169+
return []
170+
168171

169172
class IETFEntityManager(EntityManager):
170173

@@ -180,6 +183,11 @@ def can_send_on_behalf(self, person):
180183
return self.get_managed_list()
181184
return []
182185

186+
def can_approve_list(self, person):
187+
if is_ietfchair(person):
188+
return self.get_managed_list()
189+
return []
190+
183191

184192
class IABEntityManager(EntityManager):
185193

@@ -196,6 +204,12 @@ def can_send_on_behalf(self, person):
196204
return self.get_managed_list()
197205
return []
198206

207+
def can_approve_list(self, person):
208+
if (is_iabchair(person) or
209+
is_iab_executive_director(person)):
210+
return self.get_managed_list()
211+
return []
212+
199213

200214
class AreaEntityManager(EntityManager):
201215

@@ -222,6 +236,10 @@ def can_send_on_behalf(self, person):
222236
query_filter = {'areadirector__in': person.areadirector_set.all()}
223237
return self.get_managed_list(query_filter)
224238

239+
def can_approve_list(self, person):
240+
query_filter = {'areadirector__in': person.areadirector_set.all()}
241+
return self.get_managed_list(query_filter)
242+
225243

226244
class WGEntityManager(EntityManager):
227245

@@ -250,6 +268,10 @@ def can_send_on_behalf(self, person):
250268
query_filter = {'pk__in': wgs}
251269
return self.get_managed_list(query_filter)
252270

271+
def can_approve_list(self, person):
272+
query_filter = {'areagroup__area__areadirector__in': person.areadirector_set.all()}
273+
return self.get_managed_list(query_filter)
274+
253275

254276
class SDOEntityManager(EntityManager):
255277

@@ -329,4 +351,13 @@ def get_entities_for_person(self, person):
329351
entities.append(('IETF Working Groups', wgs))
330352
return entities
331353

354+
def get_all_can_approve_codes(self, person):
355+
entities = []
356+
for key in ['ietf', 'iesg', 'iab']:
357+
entities += self.managers[key].can_approve_list(person)
358+
entities += self.managers['area'].can_approve_list(person)
359+
entities += self.managers['wg'].can_approve_list(person)
360+
return [i[0] for i in entities]
361+
362+
332363
IETFHM = IETFHierarchyManager()

ietf/liaisons/views.py

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
# Copyright The IETF Trust 2007, All Rights Reserved
2+
import datetime
3+
24
from django.conf import settings
35
from django.core.urlresolvers import reverse
6+
from django.db.models import Q
47
from django.http import HttpResponse, HttpResponseRedirect
58
from django.shortcuts import render_to_response
69
from django.template import RequestContext
710
from django.utils import simplejson
11+
from django.views.generic.list_detail import object_list, object_detail
812

9-
from ietf.liaisons.accounts import get_person_for_user
13+
from ietf.liaisons.accounts import (get_person_for_user, can_add_outgoing_liaison,
14+
can_add_incoming_liaison)
1015
from ietf.liaisons.decorators import can_submit_liaison
1116
from ietf.liaisons.forms import liaison_form_factory
12-
from ietf.liaisons.models import SDOs
17+
from ietf.liaisons.models import LiaisonDetail, OutgoingLiaisonApproval
1318
from ietf.liaisons.utils import IETFHM
1419

1520

@@ -24,12 +29,7 @@ def add_liaison(request):
2429
if not settings.DEBUG:
2530
liaison.send_by_mail()
2631
else:
27-
mail = liaison.send_by_email(fake=True)
28-
return render_to_response('liaisons/liaison_mail_detail.html',
29-
{'mail': mail,
30-
'message': mail.message(),
31-
'liaison': liaison},
32-
context_instance=RequestContext(request))
32+
return _fake_email_view(request, liaison)
3333
return HttpResponseRedirect(reverse('liaison_list'))
3434
else:
3535
form = liaison_form_factory(request)
@@ -41,6 +41,7 @@ def add_liaison(request):
4141
)
4242

4343

44+
@can_submit_liaison
4445
def get_info(request):
4546
person = get_person_for_user(request.user)
4647

@@ -71,3 +72,76 @@ def get_info(request):
7172
'needs_approval': from_entity.needs_approval(person=person)})
7273
json_result = simplejson.dumps(result)
7374
return HttpResponse(json_result, mimetype='text/javascript')
75+
76+
77+
def _fake_email_view(request, liaison):
78+
mail = liaison.send_by_email(fake=True)
79+
return render_to_response('liaisons/liaison_mail_detail.html',
80+
{'mail': mail,
81+
'message': mail.message(),
82+
'liaison': liaison},
83+
context_instance=RequestContext(request))
84+
85+
86+
def liaison_list(request):
87+
user = request.user
88+
can_send_outgoing = can_add_outgoing_liaison(user)
89+
can_send_incoming = can_add_incoming_liaison(user)
90+
91+
person = get_person_for_user(request.user)
92+
approval_codes = IETFHM.get_all_can_approve_codes(person)
93+
can_approve = LiaisonDetail.objects.filter(approval__isnull=False, approval__approved=False, from_raw_code__in=approval_codes).count()
94+
95+
public_liaisons = LiaisonDetail.objects.filter(Q(approval__isnull=True)|Q(approval__approved=True)).order_by("-submitted_date")
96+
97+
return object_list(request, public_liaisons,
98+
allow_empty=True,
99+
template_name='liaisons/liaisondetail_list.html',
100+
extra_context={'can_manage': can_approve or can_send_incoming or can_send_outgoing,
101+
'can_approve': can_approve,
102+
'can_send_incoming': can_send_incoming,
103+
'can_send_outgoing': can_send_outgoing},
104+
)
105+
106+
107+
@can_submit_liaison
108+
def liaison_approval_list(request):
109+
person = get_person_for_user(request.user)
110+
approval_codes = IETFHM.get_all_can_approve_codes(person)
111+
to_approve = LiaisonDetail.objects.filter(approval__isnull=False, approval__approved=False, from_raw_code__in=approval_codes).order_by("-submitted_date")
112+
113+
return object_list(request, to_approve,
114+
allow_empty=True,
115+
template_name='liaisons/liaisondetail_approval_list.html',
116+
)
117+
118+
119+
@can_submit_liaison
120+
def liaison_approval_detail(request, object_id):
121+
person = get_person_for_user(request.user)
122+
approval_codes = IETFHM.get_all_can_approve_codes(person)
123+
to_approve = LiaisonDetail.objects.filter(approval__isnull=False, approval__approved=False, from_raw_code__in=approval_codes).order_by("-submitted_date")
124+
125+
if request.method=='POST' and request.POST.get('do_approval', False):
126+
try:
127+
liaison = to_approve.get(pk=object_id)
128+
approval = liaison.approval
129+
if not approval:
130+
approval = OutgoingLiaisonApproval.objects.create(approved=True, approval_date=datetime.datetime.now())
131+
liaison.approval = approval
132+
liaison.save()
133+
else:
134+
approval.approved=True
135+
approval.save()
136+
if not settings.DEBUG:
137+
liaison.send_by_mail()
138+
else:
139+
return _fake_email_view(request, liaison)
140+
except LiaisonDetail.DoesNotExist:
141+
pass
142+
return HttpResponseRedirect(reverse('liaison_list'))
143+
return object_detail(request,
144+
to_approve,
145+
object_id=object_id,
146+
template_name='liaisons/liaisondetail_approval_detail.html',
147+
)

0 commit comments

Comments
 (0)