Remove proxy layer from liaisons tool, do some minor cleanups of

duplicated code, make sure the custom form widgets escape their input.

There's still a bit of custom proxying going on in the IETFHM class
hierarchy.
 - Legacy-Id: 6794
This commit is contained in:
Ole Laursen 2013-12-05 13:54:48 +00:00
parent 97bf521ebb
commit 4250a95556
36 changed files with 940 additions and 2808 deletions

View file

@ -2,7 +2,7 @@
# coding: latin-1
from types import ModuleType
import urls, models, views, forms, accounts, admin, utils, widgets, decorators, sitemaps, feeds
import urls, models, views, forms, admin, utils, widgets, sitemaps, feeds
# These people will be sent a stack trace if there's an uncaught exception in
# code any of the modules imported above:

View file

@ -1,14 +1,15 @@
from django.conf import settings
from ietf.person.models import Person
from ietf.group.models import Role
from ietf.utils.proxy import proxy_personify_role
from ietf.idtracker.models import Role, PersonOrOrgInfo
LIAISON_EDIT_GROUPS = ['Secretariat']
LIAISON_EDIT_GROUPS = ['Secretariat'] # this is not working anymore, refers to old auth model
def get_ietf_chair():
person = PersonOrOrgInfo.objects.filter(role=Role.IETF_CHAIR)
return person and person[0] or None
try:
return proxy_personify_role(Role.objects.get(name="chair", group__acronym="ietf"))
except Role.DoesNotExist:
return None
def get_iesg_chair():
@ -16,48 +17,62 @@ def get_iesg_chair():
def get_iab_chair():
person = PersonOrOrgInfo.objects.filter(role=Role.IAB_CHAIR)
return person and person[0] or None
def get_iab_executive_director():
person = PersonOrOrgInfo.objects.filter(role=Role.IAB_EXCUTIVE_DIRECTOR)
return person and person[0] or None
def get_person_for_user(user):
try:
return user.get_profile().person()
except:
return proxy_personify_role(Role.objects.get(name="chair", group__acronym="iab"))
except Role.DoesNotExist:
return None
def get_irtf_chair():
try:
return proxy_personify_role(Role.objects.get(name="chair", group__acronym="irtf"))
except Role.DoesNotExist:
return None
def get_iab_executive_director():
try:
return proxy_personify_role(Role.objects.get(name="execdir", group__acronym="iab"))
except Person.DoesNotExist:
return None
def get_person_for_user(user):
if not user.is_authenticated():
return None
try:
p = user.get_profile()
p.email = lambda: (p.plain_name(), p.email_address())
return p
except Person.DoesNotExist:
return None
def is_areadirector(person):
return bool(person.areadirector_set.all())
return bool(Role.objects.filter(person=person, name="ad", group__state="active", group__type="area"))
def is_wgchair(person):
return bool(person.wgchair_set.all())
return bool(Role.objects.filter(person=person, name="chair", group__state="active", group__type="wg"))
def is_wgsecretary(person):
return bool(person.wgsecretary_set.all())
def has_role(person, role):
return bool(person.role_set.filter(pk=role))
return bool(Role.objects.filter(person=person, name="sec", group__state="active", group__type="wg"))
def is_ietfchair(person):
return has_role(person, Role.IETF_CHAIR)
return bool(Role.objects.filter(person=person, name="chair", group__acronym="ietf"))
def is_iabchair(person):
return has_role(person, Role.IAB_CHAIR)
return bool(Role.objects.filter(person=person, name="chair", group__acronym="iab"))
def is_iab_executive_director(person):
return has_role(person, Role.IAB_EXCUTIVE_DIRECTOR)
return bool(Role.objects.filter(person=person, name="execdir", group__acronym="iab"))
def is_irtfchair(person):
return bool(Role.objects.filter(person=person, name="chair", group__acronym="irtf"))
def can_add_outgoing_liaison(user):
@ -74,15 +89,17 @@ def can_add_outgoing_liaison(user):
def is_sdo_liaison_manager(person):
return bool(person.liaisonmanagers_set.all())
return bool(Role.objects.filter(person=person, name="liaiman", group__type="sdo"))
def is_sdo_authorized_individual(person):
return bool(person.sdoauthorizedindividual_set.all())
return bool(Role.objects.filter(person=person, name="auth", group__type="sdo"))
def is_secretariat(user):
return bool(user.groups.filter(name='Secretariat'))
if isinstance(user, basestring):
return False
return user.is_authenticated() and bool(Role.objects.filter(person__user=user, name="secr", group__acronym="secretariat"))
def can_add_incoming_liaison(user):
@ -102,36 +119,14 @@ def can_add_liaison(user):
def is_sdo_manager_for_outgoing_liaison(person, liaison):
from ietf.liaisons.utils import IETFHM, SDOEntity
from ietf.liaisons.models import SDOs
from_entity = IETFHM.get_entity_by_key(liaison.from_raw_code)
sdo = None
if not from_entity:
try:
sdo = SDOs.objects.get(sdo_name=liaison.from_body())
except SDOs.DoesNotExist:
pass
elif isinstance(from_entity, SDOEntity):
sdo = from_entity.obj
if sdo:
return bool(sdo.liaisonmanagers_set.filter(person=person))
if liaison.from_group and liaison.from_group.type_id == "sdo":
return bool(liaison.from_group.role_set.filter(name="liaiman", person=person))
return False
def is_sdo_manager_for_incoming_liaison(person, liaison):
from ietf.liaisons.utils import IETFHM, SDOEntity
from ietf.liaisons.models import SDOs
to_entity = IETFHM.get_entity_by_key(liaison.to_raw_code)
sdo = None
if not to_entity:
try:
sdo = SDOs.objects.get(sdo_name=liaison.to_body)
except SDOs.DoesNotExist:
pass
elif isinstance(to_entity, SDOEntity):
sdo = to_entity.obj
if sdo:
return bool(sdo.liaisonmanagers_set.filter(person=person))
if liaison.to_group and liaison.to_group.type_id == "sdo":
return bool(liaison.to_group.role_set.filter(name="liaiman", person=person))
return False
@ -143,6 +138,3 @@ def can_edit_liaison(user, liaison):
return (is_sdo_manager_for_outgoing_liaison(person, liaison) or
is_sdo_manager_for_incoming_liaison(person, liaison))
return False
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from accountsREDESIGN import *

View file

@ -1,140 +0,0 @@
from ietf.person.models import Person
from ietf.group.models import Role
from ietf.utils.proxy import proxy_personify_role
LIAISON_EDIT_GROUPS = ['Secretariat'] # this is not working anymore, refers to old auth model
def get_ietf_chair():
try:
return proxy_personify_role(Role.objects.get(name="chair", group__acronym="ietf"))
except Role.DoesNotExist:
return None
def get_iesg_chair():
return get_ietf_chair()
def get_iab_chair():
try:
return proxy_personify_role(Role.objects.get(name="chair", group__acronym="iab"))
except Role.DoesNotExist:
return None
def get_irtf_chair():
try:
return proxy_personify_role(Role.objects.get(name="chair", group__acronym="irtf"))
except Role.DoesNotExist:
return None
def get_iab_executive_director():
try:
return proxy_personify_role(Role.objects.get(name="execdir", group__acronym="iab"))
except Person.DoesNotExist:
return None
def get_person_for_user(user):
if not user.is_authenticated():
return None
try:
p = user.get_profile()
p.email = lambda: (p.plain_name(), p.email_address())
return p
except Person.DoesNotExist:
return None
def is_areadirector(person):
return bool(Role.objects.filter(person=person, name="ad", group__state="active", group__type="area"))
def is_wgchair(person):
return bool(Role.objects.filter(person=person, name="chair", group__state="active", group__type="wg"))
def is_wgsecretary(person):
return bool(Role.objects.filter(person=person, name="sec", group__state="active", group__type="wg"))
def is_ietfchair(person):
return bool(Role.objects.filter(person=person, name="chair", group__acronym="ietf"))
def is_iabchair(person):
return bool(Role.objects.filter(person=person, name="chair", group__acronym="iab"))
def is_iab_executive_director(person):
return bool(Role.objects.filter(person=person, name="execdir", group__acronym="iab"))
def is_irtfchair(person):
return bool(Role.objects.filter(person=person, name="chair", group__acronym="irtf"))
def can_add_outgoing_liaison(user):
person = get_person_for_user(user)
if not person:
return False
if (is_areadirector(person) or is_wgchair(person) or
is_wgsecretary(person) or is_ietfchair(person) or
is_iabchair(person) or is_iab_executive_director(person) or
is_sdo_liaison_manager(person) or is_secretariat(user)):
return True
return False
def is_sdo_liaison_manager(person):
return bool(Role.objects.filter(person=person, name="liaiman", group__type="sdo"))
def is_sdo_authorized_individual(person):
return bool(Role.objects.filter(person=person, name="auth", group__type="sdo"))
def is_secretariat(user):
if isinstance(user, basestring):
return False
return user.is_authenticated() and bool(Role.objects.filter(person__user=user, name="secr", group__acronym="secretariat"))
def can_add_incoming_liaison(user):
person = get_person_for_user(user)
if not person:
return False
if (is_sdo_liaison_manager(person) or
is_sdo_authorized_individual(person) or
is_secretariat(user)):
return True
return False
def can_add_liaison(user):
return can_add_incoming_liaison(user) or can_add_outgoing_liaison(user)
def is_sdo_manager_for_outgoing_liaison(person, liaison):
if liaison.from_group and liaison.from_group.type_id == "sdo":
return bool(liaison.from_group.role_set.filter(name="liaiman", person=person))
return False
def is_sdo_manager_for_incoming_liaison(person, liaison):
if liaison.to_group and liaison.to_group.type_id == "sdo":
return bool(liaison.to_group.role_set.filter(name="liaiman", person=person))
return False
def can_edit_liaison(user, liaison):
if is_secretariat(user):
return True
person = get_person_for_user(user)
if is_sdo_liaison_manager(person):
return (is_sdo_manager_for_outgoing_liaison(person, liaison) or
is_sdo_manager_for_incoming_liaison(person, liaison))
return False

View file

@ -8,10 +8,3 @@ class LiaisonStatementAdmin(admin.ModelAdmin):
ordering = ('title', )
raw_id_fields = ('from_contact', 'related_to', 'from_group', 'to_group', 'attachments')
admin.site.register(LiaisonStatement, LiaisonStatementAdmin)
class LiaisonDetailAdmin(admin.ModelAdmin):
list_display = ['pk', 'title', 'from_id', 'to_body', 'submitted_date', 'purpose', 'related_to' ]
list_display_links = ['pk', 'title']
ordering = ('title', )
admin.site.register(LiaisonDetail, LiaisonDetailAdmin)

View file

@ -1,5 +0,0 @@
from ietf.ietfauth.decorators import passes_test_decorator
from ietf.liaisons.accounts import can_add_liaison
can_submit_liaison = passes_test_decorator(lambda u, *args, **kwargs: can_add_liaison(u),
"Restricted to participants who are authorized to submit liaison statements on behalf of the various IETF entities")

View file

@ -1,24 +1,22 @@
# Copyright The IETF Trust 2007, All Rights Reserved
import re, datetime
from django.conf import settings
from django.contrib.syndication.feeds import Feed, FeedDoesNotExist
from django.utils.feedgenerator import Atom1Feed
from django.db.models import Q
from ietf.liaisons.models import LiaisonDetail, FromBodies
from ietf.idtracker.models import Acronym
from datetime import datetime, time
import re
from django.core.urlresolvers import reverse as urlreverse
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.group.models import Group
from ietf.liaisons.proxy import LiaisonDetailProxy as LiaisonDetail
from ietf.liaisons.models import LiaisonStatement
from ietf.group.models import Group
from ietf.liaisons.models import LiaisonStatement
# A slightly funny feed class, the 'object' is really
# just a dict with some parameters that items() uses
# to construct a queryset.
class Liaisons(Feed):
feed_type = Atom1Feed
def get_object(self, bits):
obj = {}
if bits[0] == 'recent':
@ -27,77 +25,50 @@ class Liaisons(Feed):
obj['title'] = 'Recent Liaison Statements'
obj['limit'] = 15
return obj
if bits[0] == 'from':
if bits[0] == 'from':
if len(bits) != 2:
raise FeedDoesNotExist
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
try:
group = Group.objects.get(acronym=bits[1])
obj['filter'] = { 'from_group': group }
obj['title'] = u'Liaison Statements from %s' % group.name
try:
group = Group.objects.get(acronym=bits[1])
obj['filter'] = { 'from_group': group }
obj['title'] = u'Liaison Statements from %s' % group.name
return obj
except Group.DoesNotExist:
# turn all-nonword characters into one-character
# wildcards to make it easier to construct a URL that
# matches
search_string = re.sub(r"[^a-zA-Z1-9]", ".", bits[1])
statements = LiaisonStatement.objects.filter(from_name__iregex=search_string)
if statements:
name = statements[0].from_name
obj['filter'] = { 'from_name': name }
obj['title'] = u'Liaison Statements from %s' % name
return obj
except Group.DoesNotExist:
# turn all-nonword characters into one-character
# wildcards to make it easier to construct the URL
search_string = re.sub(r"[^a-zA-Z1-9]", ".", bits[1])
statements = LiaisonStatement.objects.filter(from_name__iregex=search_string)
if statements:
name = statements[0].from_name
obj['filter'] = { 'from_name': name }
obj['title'] = u'Liaison Statements from %s' % name
return obj
else:
raise FeedDoesNotExist
try:
acronym = Acronym.objects.get(acronym=bits[1])
obj['filter'] = {'from_id': acronym.acronym_id}
body = bits[1]
except Acronym.DoesNotExist:
# Find body matches. Turn all non-word characters
# into wildcards for the like search.
# Note that supplying sql here means that this is
# mysql-specific (e.g., postgresql wants 'ilike' for
# the same operation)
body_list = FromBodies.objects.values('from_id','body_name').extra(where=['body_name like "%s"' % re.sub('\W', '_', bits[1])])
if not body_list:
raise FeedDoesNotExist
frmlist = [b['from_id'] for b in body_list]
# Assume that all of the matches have the same name.
# This is not guaranteed (e.g., a url like '-----------'
# will match several bodies) but is true of well-formed
# inputs.
body = body_list[0]['body_name']
obj['filter'] = {'from_id__in': frmlist}
obj['title'] = 'Liaison Statements from %s' % body
return obj
if bits[0] == 'to':
else:
raise FeedDoesNotExist
if bits[0] == 'to':
if len(bits) != 2:
raise FeedDoesNotExist
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
obj['filter'] = dict(to_name__icontains=bits[1])
else:
# The schema uses two different fields for the same
# basic purpose, depending on whether it's a Secretariat-submitted
# or Liaison-tool-submitted document.
obj['q'] = [ (Q(by_secretariat=0) & Q(to_body__icontains=bits[1])) | (Q(by_secretariat=1) & Q(submitter_name__icontains=bits[1])) ]
obj['title'] = 'Liaison Statements where to matches %s' % bits[1]
return obj
obj['filter'] = dict(to_name__icontains=bits[1])
obj['title'] = 'Liaison Statements where to matches %s' % bits[1]
return obj
if bits[0] == 'subject':
if len(bits) != 2:
raise FeedDoesNotExist
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
obj['q'] = [ Q(title__icontains=bits[1]) | Q(attachments__title__icontains=bits[1]) ]
else:
obj['q'] = [ Q(title__icontains=bits[1]) | Q(uploads__file_title__icontains=bits[1]) ]
obj['title'] = 'Liaison Statements where subject matches %s' % bits[1]
obj['q'] = [ Q(title__icontains=bits[1]) | Q(attachments__title__icontains=bits[1]) ]
obj['title'] = 'Liaison Statements where subject matches %s' % bits[1]
return obj
raise FeedDoesNotExist
def get_feed(self, url=None):
if not url:
raise FeedDoesNotExist
else:
if url:
return Feed.get_feed(self, url=url)
else:
raise FeedDoesNotExist
def title(self, obj):
return obj['title']
@ -106,12 +77,16 @@ class Liaisons(Feed):
# no real equivalent for any objects
return '/liaison/'
def item_link(self, obj):
# no real equivalent for any objects
return urlreverse("liaison_detail", kwargs={ "object_id": obj.pk })
def description(self, obj):
return self.title(obj)
def items(self, obj):
# Start with the common queryset
qs = LiaisonDetail.objects.all().order_by("-submitted_date")
qs = LiaisonStatement.objects.all().order_by("-submitted")
if obj.has_key('q'):
qs = qs.filter(*obj['q'])
if obj.has_key('filter'):
@ -123,7 +98,7 @@ class Liaisons(Feed):
def item_pubdate(self, item):
# this method needs to return a datetime instance, even
# though the database has only date, not time
return datetime.combine(item.submitted_date, time(0,0,0))
return item.submitted
def item_author_name(self, item):
return item.from_body()
return item.from_name

View file

@ -1,4 +1,4 @@
import datetime
import datetime, os
from email.utils import parseaddr
from django import forms
@ -8,26 +8,31 @@ from django.forms.util import ErrorList
from django.core.validators import email_re
from django.template.loader import render_to_string
from ietf.idtracker.models import PersonOrOrgInfo
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.models import LiaisonDetail, Uploads, OutgoingLiaisonApproval, SDOs
from ietf.liaisons.utils import IETFHM
from ietf.liaisons.widgets import (FromWidget, ReadOnlyWidget, ButtonWidget,
ShowAttachmentsWidget, RelatedLiaisonWidget)
from ietf.liaisons.models import LiaisonStatement, LiaisonStatementPurposeName
from ietf.group.models import Group, Role
from ietf.person.models import Person, Email
from ietf.doc.models import Document
class LiaisonForm(forms.ModelForm):
class LiaisonForm(forms.Form):
person = forms.ModelChoiceField(Person.objects.all())
from_field = forms.ChoiceField(widget=FromWidget, label=u'From')
replyto = forms.CharField(label=u'Reply to')
organization = forms.ChoiceField()
to_poc = forms.CharField(widget=ReadOnlyWidget, label="POC", required=False)
response_contact = forms.CharField(required=False, max_length=255)
technical_contact = forms.CharField(required=False, max_length=255)
cc1 = forms.CharField(widget=forms.Textarea, label="CC", required=False, help_text='Please insert one email address per line')
purpose_text = forms.CharField(widget=forms.Textarea, label='Other purpose')
purpose = forms.ChoiceField()
deadline_date = forms.DateField(label='Deadline')
submitted_date = forms.DateField(label='Submission date', initial=datetime.date.today())
title = forms.CharField(label=u'Title')
body = forms.CharField(widget=forms.Textarea, required=False)
attachments = forms.CharField(label='Attachments', widget=ShowAttachmentsWidget, required=False)
attach_title = forms.CharField(label='Title', required=False)
attach_file = forms.FileField(label='File', required=False)
@ -36,38 +41,51 @@ class LiaisonForm(forms.ModelForm):
require=['id_attach_title', 'id_attach_file'],
required_label='title and file'),
required=False)
related_to = forms.ModelChoiceField(LiaisonDetail.objects.all(), label=u'Related Liaison', widget=RelatedLiaisonWidget, required=False)
related_to = forms.ModelChoiceField(LiaisonStatement.objects.all(), label=u'Related Liaison', widget=RelatedLiaisonWidget, required=False)
fieldsets = [('From', ('from_field', 'replyto')),
('To', ('organization', 'to_poc')),
('Other email addresses', ('response_contact', 'technical_contact', 'cc1')),
('Purpose', ('purpose', 'purpose_text', 'deadline_date')),
('Purpose', ('purpose', 'deadline_date')),
('References', ('related_to', )),
('Liaison Statement', ('title', 'submitted_date', 'body', 'attachments')),
('Add attachment', ('attach_title', 'attach_file', 'attach_button')),
]
class Meta:
model = LiaisonDetail
class Media:
js = ("/js/jquery-1.5.1.min.js",
"/js/jquery-ui-1.8.11.custom.min.js",
"/js/liaisons.js", )
css = {'all': ("/css/liaisons.css",
"/css/jquery-ui-themes/jquery-ui-1.8.11.custom.css")}
def __init__(self, user, *args, **kwargs):
self.user = user
self.fake_person = None
self.person = get_person_for_user(user)
if kwargs.get('data', None):
kwargs['data'].update({'person': self.person.pk})
if is_secretariat(self.user) and 'from_fake_user' in kwargs['data'].keys():
self.fake_person = PersonOrOrgInfo.objects.get(pk=kwargs['data']['from_fake_user'])
self.fake_person = Person.objects.get(pk=kwargs['data']['from_fake_user'])
kwargs['data'].update({'person': self.fake_person.pk})
else:
kwargs['data'].update({'person': self.person.pk})
self.instance = kwargs.pop("instance", None)
super(LiaisonForm, self).__init__(*args, **kwargs)
# now copy in values from instance, like a ModelForm
if self.instance:
self.initial["person"] = self.instance.from_contact.person_id if self.instance.from_contact else None
self.initial["replyto"] = self.instance.reply_to
self.initial["to_poc"] = self.instance.to_contact
self.initial["response_contact"] = self.instance.response_contact
self.initial["technical_contact"] = self.instance.technical_contact
self.initial["cc1"] = self.instance.cc
self.initial["purpose"] = self.instance.purpose.order
self.initial["deadline_date"] = self.instance.deadline
self.initial["submitted_date"] = self.instance.submitted.date() if self.instance.submitted else None
self.initial["title"] = self.instance.title
self.initial["body"] = self.instance.body
self.initial["attachments"] = self.instance.attachments.all()
self.initial["related_to"] = self.instance.related_to_id
if "approved" in self.fields:
self.initial["approved"] = bool(self.instance.approved)
self.fields["purpose"].choices = [("", "---------")] + [(str(l.order), l.name) for l in LiaisonStatementPurposeName.objects.all()]
self.hm = IETFHM
self.set_from_field()
self.set_replyto_field()
@ -81,25 +99,19 @@ class LiaisonForm(forms.ModelForm):
def set_required_fields(self):
purpose = self.data.get('purpose', None)
if purpose == '5':
self.fields['purpose_text'].required=True
else:
self.fields['purpose_text'].required=False
if purpose in ['1', '2']:
self.fields['deadline_date'].required=True
self.fields['deadline_date'].required = True
else:
self.fields['deadline_date'].required=False
self.fields['deadline_date'].required = False
def reset_required_fields(self):
self.fields['purpose_text'].required=True
self.fields['deadline_date'].required=True
self.fields['deadline_date'].required = True
def set_from_field(self):
assert NotImplemented
def set_replyto_field(self):
email = self.person.email()
self.fields['replyto'].initial = email and email[1]
self.fields['replyto'].initial = self.person.email()[1]
def set_organization_field(self):
assert NotImplemented
@ -171,7 +183,7 @@ class LiaisonForm(forms.ModelForm):
return self.hm.get_entity_by_key(organization_key)
def get_poc(self, organization):
return ', '.join([i.email()[1] for i in organization.get_poc()])
return ', '.join(u"%s <%s>" % i.email() for i in organization.get_poc())
def clean_cc1(self):
value = self.cleaned_data.get('cc1', '')
@ -191,34 +203,53 @@ class LiaisonForm(forms.ModelForm):
return ','.join(result)
def get_cc(self, from_entity, to_entity):
#Old automatic Cc code, now we retrive it from cleaned_data
#persons = to_entity.get_cc(self.person)
#persons += from_entity.get_from_cc(self.person)
#return ', '.join(['%s <%s>' % i.email() for i in persons])
cc = self.cleaned_data.get('cc1', '')
return cc
return self.cleaned_data.get('cc1', '')
def save(self, *args, **kwargs):
liaison = super(LiaisonForm, self).save(*args, **kwargs)
self.save_extra_fields(liaison)
self.save_attachments(liaison)
return liaison
l = self.instance
if not l:
l = LiaisonStatement()
l.title = self.cleaned_data["title"]
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.reply_to = self.cleaned_data["replyto"]
l.response_contact = self.cleaned_data["response_contact"]
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)
return l
def save_extra_fields(self, liaison):
now = datetime.datetime.now()
liaison.last_modified_date = now
from_entity = self.get_from_entity()
liaison.from_raw_body = from_entity.name
liaison.from_raw_code = self.cleaned_data.get('from_field')
liaison.from_name = from_entity.name
liaison.from_group = from_entity.obj
e = self.cleaned_data["person"].email_set.order_by('-active', '-time')
if e:
liaison.from_contact = e[0]
organization = self.get_to_entity()
liaison.to_raw_code = self.cleaned_data.get('organization')
liaison.to_body = organization.name
liaison.to_poc = self.get_poc(organization)
liaison.submitter_name, liaison.submitter_email = self.person.email()
liaison.cc1 = self.get_cc(from_entity, organization)
liaison.save()
liaison.to_name = organization.name
liaison.to_group = organization.obj
liaison.to_contact = self.get_poc(organization)
liaison.cc = self.get_cc(from_entity, organization)
def save_attachments(self, instance):
written = instance.attachments.all().count()
for key in self.files.keys():
title_key = key.replace('file', 'title')
if not key.startswith('attach_file_') or not title_key in self.data.keys():
@ -229,13 +260,18 @@ class LiaisonForm(forms.ModelForm):
extension = '.' + extension[1]
else:
extension = ''
attach = Uploads.objects.create(
file_title = self.data.get(title_key),
person = self.person,
detail = instance,
file_extension = extension,
written += 1
name = instance.name() + ("-attachment-%s" % written)
attach, _ = Document.objects.get_or_create(
name = name,
defaults=dict(
title = self.data.get(title_key),
type_id = "liai-att",
external_url = name + extension, # strictly speaking not necessary, but just for the time being ...
)
)
attach_file = open('%sfile%s%s' % (settings.LIAISON_ATTACH_PATH, attach.pk, attach.file_extension), 'w')
instance.attachments.add(attach)
attach_file = open(os.path.join(settings.LIAISON_ATTACH_PATH, attach.name + extension), 'w')
attach_file.write(attached_file.read())
attach_file.close()
@ -245,8 +281,7 @@ class LiaisonForm(forms.ModelForm):
exclude_filter = {'pk': self.instance.pk}
else:
exclude_filter = {}
exists = bool(LiaisonDetail.objects.exclude(**exclude_filter).filter(title__iexact=title).count())
if exists:
if LiaisonStatement.objects.exclude(**exclude_filter).filter(title__iexact=title).exists():
raise forms.ValidationError('A liaison statement with the same title has previously been submitted.')
return title
@ -255,20 +290,26 @@ class IncomingLiaisonForm(LiaisonForm):
def set_from_field(self):
if is_secretariat(self.user):
sdos = SDOs.objects.all()
sdos = Group.objects.filter(type="sdo", state="active")
else:
sdo_managed = [i.sdo for i in self.person.liaisonmanagers_set.all()]
sdo_authorized = [i.sdo for i in self.person.sdoauthorizedindividual_set.all()]
sdos = set(sdo_managed).union(sdo_authorized)
self.fields['from_field'].choices = [('sdo_%s' % i.pk, i.sdo_name) for i in sdos]
sdos = Group.objects.filter(type="sdo", state="active", role__person=self.person, role__name__in=("liaiman", "auth")).distinct()
self.fields['from_field'].choices = [('sdo_%s' % i.pk, i.name) for i in sdos.order_by("name")]
self.fields['from_field'].widget.submitter = unicode(self.person)
def set_replyto_field(self):
e = Email.objects.filter(person=self.person, role__group__state="active", role__name__in=["liaiman", "auth"])
if e:
addr = e[0].address
else:
addr = self.person.email_address()
self.fields['replyto'].initial = addr
def set_organization_field(self):
self.fields['organization'].choices = self.hm.get_all_incoming_entities()
def get_post_only(self):
from_entity = self.get_from_entity()
if is_secretariat(self.user) or self.person.sdoauthorizedindividual_set.filter(sdo=from_entity.obj):
if is_secretariat(self.user) or Role.objects.filter(person=self.person, group=from_entity.obj, name="auth"):
return False
return True
@ -278,6 +319,9 @@ class IncomingLiaisonForm(LiaisonForm):
return super(IncomingLiaisonForm, self).clean()
def liaison_manager_sdos(person):
return Group.objects.filter(type="sdo", state="active", role__person=person, role__name="liaiman").distinct()
class OutgoingLiaisonForm(LiaisonForm):
to_poc = forms.CharField(label="POC", required=True)
@ -301,17 +345,24 @@ class OutgoingLiaisonForm(LiaisonForm):
all_entities += i[1]
if all_entities:
self.fields['from_field'].widget.full_power_on = [i[0] for i in all_entities]
self.fields['from_field'].widget.reduced_to_set = ['sdo_%s' % i.sdo.pk for i in self.person.liaisonmanagers_set.all().distinct()]
self.fields['from_field'].widget.reduced_to_set = ['sdo_%s' % i.pk for i in liaison_manager_sdos(self.person)]
else:
self.fields['from_field'].choices = self.hm.get_entities_for_person(self.person)
self.fields['from_field'].widget.submitter = unicode(self.person)
self.fieldsets[0] = ('From', ('from_field', 'replyto', 'approved'))
def set_replyto_field(self):
e = Email.objects.filter(person=self.person, role__group__state="active", role__name__in=["ad", "chair"])
if e:
addr = e[0].address
else:
addr = self.person.email_address()
self.fields['replyto'].initial = addr
def set_organization_field(self):
# If the user is a liaison manager and is nothing more, reduce the To field to his SDOs
if not self.hm.get_entities_for_person(self.person) and is_sdo_liaison_manager(self.person):
sdos = [i.sdo for i in self.person.liaisonmanagers_set.all().distinct()]
self.fields['organization'].choices = [('sdo_%s' % i.pk, i.sdo_name) for i in sdos]
self.fields['organization'].choices = [('sdo_%s' % i.pk, i.name) for i in liaison_manager_sdos(self.person)]
else:
self.fields['organization'].choices = self.hm.get_all_outgoing_entities()
self.fieldsets[1] = ('To', ('organization', 'other_organization', 'to_poc'))
@ -336,16 +387,9 @@ class OutgoingLiaisonForm(LiaisonForm):
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):
approved = True
approval_date = datetime.datetime.now()
liaison.approved = datetime.datetime.now()
else:
approved = False
approval_date = None
approval = OutgoingLiaisonApproval.objects.create(
approved = approved,
approval_date = approval_date)
liaison.approval = approval
liaison.save()
liaison.approved = None
def clean_to_poc(self):
value = self.cleaned_data.get('to_poc', None)
@ -361,10 +405,10 @@ class OutgoingLiaisonForm(LiaisonForm):
person = self.fake_person or self.person
for i in self.hm.get_entities_for_person(person):
all_entities += i[1]
# If the from entity is one in which the user has full privileges the to entity could be anyone
# If the from entity is one in wich the user has full privileges the to entity could be anyone
if from_code in [i[0] for i in all_entities]:
return to_code
sdo_codes = ['sdo_%s' % i.sdo.pk for i in person.liaisonmanagers_set.all().distinct()]
sdo_codes = ['sdo_%s' % i.pk for i in liaison_manager_sdos(person)]
if to_code in sdo_codes:
return to_code
entity = self.get_to_entity()
@ -384,35 +428,28 @@ class EditLiaisonForm(LiaisonForm):
cc1 = forms.CharField(widget=forms.TextInput, label="CC", required=False)
class Meta:
model = LiaisonDetail
fields = ('from_raw_body', 'to_body', 'to_poc', 'cc1', 'last_modified_date', 'title',
'response_contact', 'technical_contact', 'purpose_text', 'body',
'response_contact', 'technical_contact', 'body',
'deadline_date', 'purpose', 'replyto', 'related_to')
def __init__(self, *args, **kwargs):
super(EditLiaisonForm, self).__init__(*args, **kwargs)
self.edit = True
self.initial.update({'attachments': self.instance.uploads_set.all()})
self.fields['submitted_date'].initial = self.instance.submitted_date
def set_from_field(self):
self.fields['from_field'].initial = self.instance.from_body
self.fields['from_field'].initial = self.instance.from_name
def set_replyto_field(self):
self.fields['replyto'].initial = self.instance.replyto
self.fields['replyto'].initial = self.instance.reply_to
def set_organization_field(self):
self.fields['organization'].initial = self.instance.to_body
self.fields['organization'].initial = self.instance.to_name
def save_extra_fields(self, liaison):
now = datetime.datetime.now()
liaison.last_modified_date = now
liaison.from_raw_body = self.cleaned_data.get('from_field')
liaison.to_body = self.cleaned_data.get('organization')
liaison.to_poc = self.cleaned_data['to_poc']
liaison.cc1 = self.cleaned_data['cc1']
liaison.save()
liaison.from_name = self.cleaned_data.get('from_field')
liaison.to_name = self.cleaned_data.get('organization')
liaison.to_contact = self.cleaned_data['to_poc']
liaison.cc = self.cleaned_data['cc1']
def liaison_form_factory(request, **kwargs):
user = request.user
@ -426,5 +463,3 @@ def liaison_form_factory(request, **kwargs):
return IncomingLiaisonForm(user, **kwargs)
return None
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.liaisons.formsREDESIGN import *

View file

@ -1,474 +0,0 @@
import datetime, os
from email.utils import parseaddr
from django import forms
from django.conf import settings
from django.db.models import Q
from django.forms.util import ErrorList
from django.core.validators import email_re
from django.template.loader import render_to_string
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
from ietf.liaisons.proxy import LiaisonDetailProxy
from ietf.group.models import Group, Role
from ietf.person.models import Person, Email
from ietf.doc.models import Document
class LiaisonForm(forms.Form):
person = forms.ModelChoiceField(Person.objects.all())
from_field = forms.ChoiceField(widget=FromWidget, label=u'From')
replyto = forms.CharField(label=u'Reply to')
organization = forms.ChoiceField()
to_poc = forms.CharField(widget=ReadOnlyWidget, label="POC", required=False)
response_contact = forms.CharField(required=False, max_length=255)
technical_contact = forms.CharField(required=False, max_length=255)
cc1 = forms.CharField(widget=forms.Textarea, label="CC", required=False, help_text='Please insert one email address per line')
purpose = forms.ChoiceField()
purpose_text = forms.CharField(widget=forms.Textarea, label='Other purpose')
deadline_date = forms.DateField(label='Deadline')
submitted_date = forms.DateField(label='Submission date', initial=datetime.date.today())
title = forms.CharField(label=u'Title')
body = forms.CharField(widget=forms.Textarea, required=False)
attachments = forms.CharField(label='Attachments', widget=ShowAttachmentsWidget, required=False)
attach_title = forms.CharField(label='Title', required=False)
attach_file = forms.FileField(label='File', required=False)
attach_button = forms.CharField(label='',
widget=ButtonWidget(label='Attach', show_on='id_attachments',
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)
fieldsets = [('From', ('from_field', 'replyto')),
('To', ('organization', 'to_poc')),
('Other email addresses', ('response_contact', 'technical_contact', 'cc1')),
('Purpose', ('purpose', 'purpose_text', 'deadline_date')),
('References', ('related_to', )),
('Liaison Statement', ('title', 'submitted_date', 'body', 'attachments')),
('Add attachment', ('attach_title', 'attach_file', 'attach_button')),
]
class Media:
js = ("/js/jquery-1.5.1.min.js",
"/js/jquery-ui-1.8.11.custom.min.js",
"/js/liaisons.js", )
css = {'all': ("/css/liaisons.css",
"/css/jquery-ui-themes/jquery-ui-1.8.11.custom.css")}
def __init__(self, user, *args, **kwargs):
self.user = user
self.fake_person = None
self.person = get_person_for_user(user)
if kwargs.get('data', None):
if is_secretariat(self.user) and 'from_fake_user' in kwargs['data'].keys():
self.fake_person = Person.objects.get(pk=kwargs['data']['from_fake_user'])
kwargs['data'].update({'person': self.fake_person.pk})
else:
kwargs['data'].update({'person': self.person.pk})
self.instance = kwargs.pop("instance", None)
super(LiaisonForm, self).__init__(*args, **kwargs)
# now copy in values from instance, like a ModelForm
if self.instance:
for name, field in self.fields.iteritems():
try:
x = getattr(self.instance, name)
if name == "purpose": # proxy has a name-clash on purpose so help it
x = x.order
try:
x = x.pk # foreign keys need the .pk, not the actual object
except AttributeError:
pass
self.initial[name] = x
except AttributeError:
# we have some fields on the form that aren't in the model
pass
self.fields["purpose"].choices = [("", "---------")] + [(str(l.order), l.name) for l in LiaisonStatementPurposeName.objects.all()]
self.hm = IETFHM
self.set_from_field()
self.set_replyto_field()
self.set_organization_field()
def __unicode__(self):
return self.as_div()
def get_post_only(self):
return False
def set_required_fields(self):
purpose = self.data.get('purpose', None)
if purpose == '5':
self.fields['purpose_text'].required=True
else:
self.fields['purpose_text'].required=False
if purpose in ['1', '2']:
self.fields['deadline_date'].required=True
else:
self.fields['deadline_date'].required=False
def reset_required_fields(self):
self.fields['purpose_text'].required=True
self.fields['deadline_date'].required=True
def set_from_field(self):
assert NotImplemented
def set_replyto_field(self):
self.fields['replyto'].initial = self.person.email()[1]
def set_organization_field(self):
assert NotImplemented
def as_div(self):
return render_to_string('liaisons/liaisonform.html', {'form': self})
def get_fieldsets(self):
if not self.fieldsets:
yield dict(name=None, fields=self)
else:
for fieldset, fields in self.fieldsets:
fieldset_dict = dict(name=fieldset, fields=[])
for field_name in fields:
if field_name in self.fields.keyOrder:
fieldset_dict['fields'].append(self[field_name])
if not fieldset_dict['fields']:
# if there is no fields in this fieldset, we continue to next fieldset
continue
yield fieldset_dict
def full_clean(self):
self.set_required_fields()
super(LiaisonForm, self).full_clean()
self.reset_required_fields()
def has_attachments(self):
for key in self.files.keys():
if key.startswith('attach_file_') and key.replace('file', 'title') in self.data.keys():
return True
return False
def check_email(self, value):
if not value:
return
emails = value.split(',')
for email in emails:
name, addr = parseaddr(email)
if not email_re.search(addr):
raise forms.ValidationError('Invalid email address: %s' % addr)
def clean_response_contact(self):
value = self.cleaned_data.get('response_contact', None)
self.check_email(value)
return value
def clean_technical_contact(self):
value = self.cleaned_data.get('technical_contact', None)
self.check_email(value)
return value
def clean_reply_to(self):
value = self.cleaned_data.get('reply_to', None)
self.check_email(value)
return value
def clean(self):
if not self.cleaned_data.get('body', None) and not self.has_attachments():
self._errors['body'] = ErrorList([u'You must provide a body or attachment files'])
self._errors['attachments'] = ErrorList([u'You must provide a body or attachment files'])
return self.cleaned_data
def get_from_entity(self):
organization_key = self.cleaned_data.get('from_field')
return self.hm.get_entity_by_key(organization_key)
def get_to_entity(self):
organization_key = self.cleaned_data.get('organization')
return self.hm.get_entity_by_key(organization_key)
def get_poc(self, organization):
return ', '.join(u"%s <%s>" % i.email() for i in organization.get_poc())
def clean_cc1(self):
value = self.cleaned_data.get('cc1', '')
result = []
errors = []
for address in value.split('\n'):
address = address.strip();
if not address:
continue
try:
self.check_email(address)
except forms.ValidationError:
errors.append(address)
result.append(address)
if errors:
raise forms.ValidationError('Invalid email addresses: %s' % ', '.join(errors))
return ','.join(result)
def get_cc(self, from_entity, to_entity):
#Old automatic Cc code, now we retrive it from cleaned_data
#persons = to_entity.get_cc(self.person)
#persons += from_entity.get_from_cc(self.person)
#return ', '.join(['%s <%s>' % i.email() for i in persons])
cc = self.cleaned_data.get('cc1', '')
return cc
def save(self, *args, **kwargs):
l = self.instance
if not l:
l = LiaisonDetailProxy()
l.title = self.cleaned_data["title"]
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.reply_to = self.cleaned_data["replyto"]
l.response_contact = self.cleaned_data["response_contact"]
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)
return l
def save_extra_fields(self, liaison):
from_entity = self.get_from_entity()
liaison.from_name = from_entity.name
liaison.from_group = from_entity.obj
e = self.cleaned_data["person"].email_set.order_by('-active')
if e:
liaison.from_contact = e[0]
organization = self.get_to_entity()
liaison.to_name = organization.name
liaison.to_group = organization.obj
liaison.to_contact = self.get_poc(organization)
liaison.cc = self.get_cc(from_entity, organization)
def save_attachments(self, instance):
written = instance.attachments.all().count()
for key in self.files.keys():
title_key = key.replace('file', 'title')
if not key.startswith('attach_file_') or not title_key in self.data.keys():
continue
attached_file = self.files.get(key)
extension=attached_file.name.rsplit('.', 1)
if len(extension) > 1:
extension = '.' + extension[1]
else:
extension = ''
written += 1
name = instance.name() + ("-attachment-%s" % written)
attach, _ = Document.objects.get_or_create(
name = name,
defaults=dict(
title = self.data.get(title_key),
type_id = "liai-att",
external_url = name + extension, # strictly speaking not necessary, but just for the time being ...
)
)
instance.attachments.add(attach)
attach_file = open(os.path.join(settings.LIAISON_ATTACH_PATH, attach.name + extension), 'w')
attach_file.write(attached_file.read())
attach_file.close()
def clean_title(self):
title = self.cleaned_data.get('title', None)
if self.instance and self.instance.pk:
exclude_filter = {'pk': self.instance.pk}
else:
exclude_filter = {}
exists = bool(LiaisonStatement.objects.exclude(**exclude_filter).filter(title__iexact=title).count())
if exists:
raise forms.ValidationError('A liaison statement with the same title has previously been submitted.')
return title
class IncomingLiaisonForm(LiaisonForm):
def set_from_field(self):
if is_secretariat(self.user):
sdos = Group.objects.filter(type="sdo", state="active")
else:
sdos = Group.objects.filter(type="sdo", state="active", role__person=self.person, role__name__in=("liaiman", "auth")).distinct()
self.fields['from_field'].choices = [('sdo_%s' % i.pk, i.name) for i in sdos.order_by("name")]
self.fields['from_field'].widget.submitter = unicode(self.person)
def set_replyto_field(self):
e = Email.objects.filter(person=self.person, role__group__state="active", role__name__in=["liaiman", "auth"])
if e:
addr = e[0].address
else:
addr = self.person.email_address()
self.fields['replyto'].initial = addr
def set_organization_field(self):
self.fields['organization'].choices = self.hm.get_all_incoming_entities()
def get_post_only(self):
from_entity = self.get_from_entity()
if is_secretariat(self.user) or Role.objects.filter(person=self.person, group=from_entity.obj, name="auth"):
return False
return True
def clean(self):
if 'send' in self.data.keys() and self.get_post_only():
self._errors['from_field'] = ErrorList([u'As an IETF Liaison Manager you can not send an incoming liaison statements, you only can post them'])
return super(IncomingLiaisonForm, self).clean()
def liaison_manager_sdos(person):
return Group.objects.filter(type="sdo", state="active", role__person=person, role__name="liaiman").distinct()
class OutgoingLiaisonForm(LiaisonForm):
to_poc = forms.CharField(label="POC", required=True)
approved = forms.BooleanField(label="Obtained prior approval", required=False)
other_organization = forms.CharField(label="Other SDO", required=True)
def get_to_entity(self):
organization_key = self.cleaned_data.get('organization')
organization = self.hm.get_entity_by_key(organization_key)
if organization_key == 'othersdo' and self.cleaned_data.get('other_organization', None):
organization.name=self.cleaned_data['other_organization']
return organization
def set_from_field(self):
if is_secretariat(self.user):
self.fields['from_field'].choices = self.hm.get_all_incoming_entities()
elif is_sdo_liaison_manager(self.person):
self.fields['from_field'].choices = self.hm.get_all_incoming_entities()
all_entities = []
for i in self.hm.get_entities_for_person(self.person):
all_entities += i[1]
if all_entities:
self.fields['from_field'].widget.full_power_on = [i[0] for i in all_entities]
self.fields['from_field'].widget.reduced_to_set = ['sdo_%s' % i.pk for i in liaison_manager_sdos(self.person)]
else:
self.fields['from_field'].choices = self.hm.get_entities_for_person(self.person)
self.fields['from_field'].widget.submitter = unicode(self.person)
self.fieldsets[0] = ('From', ('from_field', 'replyto', 'approved'))
def set_replyto_field(self):
e = Email.objects.filter(person=self.person, role__group__state="active", role__name__in=["ad", "chair"])
if e:
addr = e[0].address
else:
addr = self.person.email_address()
self.fields['replyto'].initial = addr
def set_organization_field(self):
# If the user is a liaison manager and is nothing more, reduce the To field to his SDOs
if not self.hm.get_entities_for_person(self.person) and is_sdo_liaison_manager(self.person):
self.fields['organization'].choices = [('sdo_%s' % i.pk, i.name) for i in liaison_manager_sdos(self.person)]
else:
self.fields['organization'].choices = self.hm.get_all_outgoing_entities()
self.fieldsets[1] = ('To', ('organization', 'other_organization', 'to_poc'))
def set_required_fields(self):
super(OutgoingLiaisonForm, self).set_required_fields()
organization = self.data.get('organization', None)
if organization == 'othersdo':
self.fields['other_organization'].required=True
else:
self.fields['other_organization'].required=False
def reset_required_fields(self):
super(OutgoingLiaisonForm, self).reset_required_fields()
self.fields['other_organization'].required=True
def get_poc(self, organization):
return self.cleaned_data['to_poc']
def save_extra_fields(self, liaison):
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
def clean_to_poc(self):
value = self.cleaned_data.get('to_poc', None)
self.check_email(value)
return value
def clean_organization(self):
to_code = self.cleaned_data.get('organization', None)
from_code = self.cleaned_data.get('from_field', None)
if not to_code or not from_code:
return to_code
all_entities = []
person = self.fake_person or self.person
for i in self.hm.get_entities_for_person(person):
all_entities += i[1]
# If the from entity is one in wich the user has full privileges the to entity could be anyone
if from_code in [i[0] for i in all_entities]:
return to_code
sdo_codes = ['sdo_%s' % i.pk for i in liaison_manager_sdos(person)]
if to_code in sdo_codes:
return to_code
entity = self.get_to_entity()
entity_name = entity and entity.name or to_code
if self.fake_person:
raise forms.ValidationError('%s is not allowed to send a liaison to: %s' % (self.fake_person, entity_name))
else:
raise forms.ValidationError('You are not allowed to send a liaison to: %s' % entity_name)
class EditLiaisonForm(LiaisonForm):
from_field = forms.CharField(widget=forms.TextInput, label=u'From')
replyto = forms.CharField(label=u'Reply to', widget=forms.TextInput)
organization = forms.CharField(widget=forms.TextInput)
to_poc = forms.CharField(widget=forms.TextInput, label="POC", required=False)
cc1 = forms.CharField(widget=forms.TextInput, label="CC", required=False)
class Meta:
fields = ('from_raw_body', 'to_body', 'to_poc', 'cc1', 'last_modified_date', 'title',
'response_contact', 'technical_contact', 'purpose_text', 'body',
'deadline_date', 'purpose', 'replyto', 'related_to')
def __init__(self, *args, **kwargs):
super(EditLiaisonForm, self).__init__(*args, **kwargs)
self.edit = True
self.initial.update({'attachments': self.instance.uploads_set.all()})
self.fields['submitted_date'].initial = self.instance.submitted_date
def set_from_field(self):
self.fields['from_field'].initial = self.instance.from_body
def set_replyto_field(self):
self.fields['replyto'].initial = self.instance.replyto
def set_organization_field(self):
self.fields['organization'].initial = self.instance.to_body
def save_extra_fields(self, liaison):
liaison.from_name = self.cleaned_data.get('from_field')
liaison.to_name = self.cleaned_data.get('organization')
liaison.to_contact = self.cleaned_data['to_poc']
liaison.cc = self.cleaned_data['cc1']

View file

@ -8,14 +8,11 @@ from ietf.utils.mail import send_mail_text
from ietf.liaisons.utils import role_persons_with_fixed_email
from ietf.group.models import Role
def send_liaison_by_email(request, liaison, fake=False):
if liaison.is_pending(): # this conditional should definitely be at the caller, not here
return notify_pending_by_email(request, liaison, fake)
def send_liaison_by_email(request, liaison):
subject = u'New Liaison Statement, "%s"' % (liaison.title)
from_email = settings.LIAISON_UNIVERSAL_FROM
to_email = liaison.to_poc.split(',')
cc = liaison.cc1.split(',')
to_email = liaison.to_contact.split(',')
cc = liaison.cc.split(',')
if liaison.technical_contact:
cc += liaison.technical_contact.split(',')
if liaison.response_contact:
@ -29,7 +26,7 @@ def send_liaison_by_email(request, liaison, fake=False):
send_mail_text(request, to_email, from_email, subject, body, cc=", ".join(cc), bcc=", ".join(bcc))
def notify_pending_by_email(request, liaison, fake):
def notify_pending_by_email(request, liaison):
# Broken: this does not find the list of approvers for the sending body
# For now, we are sending to statements@ietf.org so the Secretariat can nudge

View file

@ -5,78 +5,19 @@ from django.core.management.base import BaseCommand
from django.template.loader import render_to_string
from django.core.urlresolvers import reverse as urlreverse
from ietf.liaisons.models import LiaisonDetail
#from ietf.liaisons.mail import IETFEmailMessage
from ietf.utils.mail import send_mail_text
PREVIOUS_DAYS = {
14: 'in two weeks',
7: 'in one week',
4: 'in four days',
3: 'in three days',
2: 'in two days',
1: 'tomorrow',
0: 'today'}
from ietf.liaisons.models import LiaisonStatement
from ietf.liaisons.mails import possibly_send_deadline_reminder
class Command(BaseCommand):
help = (u"Check liaison deadlines and send a reminder if we are close to a deadline")
def send_reminder(self, liaison, days_to_go):
if days_to_go < 0:
subject = '[Liaison OUT OF DATE] %s' % liaison.title
days_msg = 'is out of date for %s days' % (-days_to_go)
else:
subject = '[Liaison deadline %s] %s' % (PREVIOUS_DAYS[days_to_go], liaison.title)
days_msg = 'expires %s' % PREVIOUS_DAYS[days_to_go]
from_email = settings.LIAISON_UNIVERSAL_FROM
to_email = liaison.to_poc.split(',')
cc = liaison.cc1.split(',')
if liaison.technical_contact:
cc += liaison.technical_contact.split(',')
if liaison.response_contact:
cc += liaison.response_contact.split(',')
bcc = ['statements@ietf.org']
body = render_to_string('liaisons/liaison_deadline_mail.txt',
{'liaison': liaison,
'days_msg': days_msg,
'url': settings.IDTRACKER_BASE_URL + urlreverse("liaison_approval_detail", kwargs=dict(object_id=liaison.pk)),
'referenced_url': settings.IDTRACKER_BASE_URL + urlreverse("liaison_detail", kwargs=dict(object_id=liaison.related_to.pk)) if liaison.related_to else None,
})
send_mail_text(context=None,to=to_email,frm=from_email,cc=cc,subject=subject,bcc=bcc,txt=body)
print 'Liaison %05s#: Deadline reminder Sent!' % liaison.pk
#mail = IETFEmailMessage(subject=subject,
# to=to_email,
# from_email=from_email,
# cc=cc,
# bcc=bcc,
# body=body)
#if not settings.DEBUG:
# mail.send()
# print 'Liaison %05s#: Deadline reminder Sent!' % liaison.pk
#else:
# print 'Liaison %05s#: Deadline reminder Not Sent because in DEBUG mode!' % liaison.pk
def handle(self, *args, **options):
today = datetime.date.today()
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.liaisons.mails import possibly_send_deadline_reminder
from ietf.liaisons.proxy import LiaisonDetailProxy as LiaisonDetail
cutoff = today - datetime.timedelta(14)
for l in LiaisonDetail.objects.filter(action_taken=False, deadline__gte=cutoff).exclude(deadline=None):
r = possibly_send_deadline_reminder(l)
if r:
print 'Liaison %05s#: Deadline reminder sent!' % liaison.pk
return
query = LiaisonDetail.objects.filter(deadline_date__isnull=False, action_taken=False, deadline_date__gte=today - datetime.timedelta(14))
for liaison in query:
delta = liaison.deadline_date - today
if delta.days < 0 or delta.days in PREVIOUS_DAYS.keys():
self.send_reminder(liaison, delta.days)
cutoff = today - datetime.timedelta(14)
for l in LiaisonStatement.objects.filter(action_taken=False, deadline__gte=cutoff).exclude(deadline=None):
r = possibly_send_deadline_reminder(l)
if r:
print 'Liaison %05s#: Deadline reminder sent!' % liaison.pk

View file

@ -5,7 +5,8 @@ from django.core.mail import EmailMessage
from django.core.management.base import BaseCommand
from django.template.loader import render_to_string
from ietf.liaisons.models import SDOs
from ietf.group.models import Group
from ietf.liaisons.mails import send_sdo_reminder
class Command(BaseCommand):
@ -16,56 +17,15 @@ class Command(BaseCommand):
)
def send_mail_to(self, person, sdo):
subject = 'Request for update list of authorized individuals'
email = person.email()[1]
name = ' '.join([i for i in (person.name_prefix, person.first_name, person.middle_initial, person.last_name, person.name_suffix) if i])
authorized_list = [i.person for i in sdo.sdoauthorizedindividual_set.all()]
body = render_to_string('liaisons/sdo_reminder.txt',
{'manager_name': name,
'sdo_name': sdo.sdo_name,
'individuals': authorized_list,
})
mail = EmailMessage(subject=subject,
to=[email],
from_email=settings.LIAISON_UNIVERSAL_FROM,
body = body)
if not settings.DEBUG:
mail.send()
msg = '%05s#: %s Mail Sent!' % (sdo.pk, sdo.sdo_name)
else:
msg = '%05s#: %s Mail Not Sent because in DEBUG mode!' % (sdo.pk, sdo.sdo_name)
return msg
def handle(self, *args, **options):
sdo_pk = options.get('sdo_pk', None)
return_output = options.get('return_output', False)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
msg_list = send_reminders_to_sdos(sdo_pk=sdo_pk)
return msg_list if return_output else None
query = SDOs.objects.all().order_by('pk')
if sdo_pk:
query = query.filter(pk=sdo_pk)
msg_list = []
for sdo in query:
manager = sdo.liaisonmanager()
if manager:
msg = self.send_mail_to(manager.person, sdo)
else:
msg = '%05s#: %s has no liaison manager' % (sdo.pk, sdo.sdo_name)
print msg
msg_list.append(msg)
if return_output:
return msg_list
msg_list = send_reminders_to_sdos(sdo_pk=sdo_pk)
return msg_list if return_output else None
def send_reminders_to_sdos(sdo_pk=None):
from ietf.group.models import Group
from ietf.liaisons.mails import send_sdo_reminder
sdos = Group.objects.filter(type="sdo").order_by('pk')
if sdo_pk:
sdos = sdos.filter(pk=sdo_pk)

View file

@ -1,369 +1,52 @@
# Copyright The IETF Trust 2007, All Rights Reserved
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.template.loader import render_to_string
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse as urlreverse
from ietf.idtracker.models import Acronym, PersonOrOrgInfo, Area, IESGLogin
#from ietf.liaisons.mail import IETFEmailMessage
from ietf.utils.mail import send_mail_text
from ietf.ietfauth.models import LegacyLiaisonUser
from ietf.utils.admin import admin_link
class LiaisonPurpose(models.Model):
purpose_id = models.AutoField(primary_key=True)
purpose_text = models.CharField(blank=True, max_length=50)
def __str__(self):
return self.purpose_text
class Meta:
db_table = 'liaison_purpose'
class FromBodies(models.Model):
from_id = models.AutoField(primary_key=True)
body_name = models.CharField(blank=True, max_length=35)
poc = models.ForeignKey(PersonOrOrgInfo, db_column='poc', null=True)
is_liaison_manager = models.BooleanField()
other_sdo = models.BooleanField()
email_priority = models.IntegerField(null=True, blank=True)
def __str__(self):
return self.body_name
class Meta:
db_table = 'from_bodies'
verbose_name = "From body"
verbose_name_plural = "From bodies"
contact_link = admin_link('poc', label='Contact')
from ietf.name.models import LiaisonStatementPurposeName
from ietf.doc.models import Document
from ietf.person.models import Email
from ietf.group.models import Group
class LiaisonStatement(models.Model):
title = models.CharField(blank=True, max_length=255)
purpose = models.ForeignKey(LiaisonStatementPurposeName)
body = models.TextField(blank=True)
deadline = models.DateField(null=True, blank=True)
related_to = models.ForeignKey('LiaisonStatement', blank=True, null=True)
class OutgoingLiaisonApproval(models.Model):
approved = models.BooleanField(default=True)
approval_date = models.DateField(null=True, blank=True)
from_group = models.ForeignKey(Group, related_name="liaisonstatement_from_set", null=True, blank=True, help_text="Sender group, if it exists")
from_name = models.CharField(max_length=255, help_text="Name of the sender body")
from_contact = models.ForeignKey(Email, blank=True, null=True)
to_group = models.ForeignKey(Group, related_name="liaisonstatement_to_set", null=True, blank=True, help_text="Recipient group, if it exists")
to_name = models.CharField(max_length=255, help_text="Name of the recipient body")
to_contact = models.CharField(blank=True, max_length=255, help_text="Contacts at recipient body")
reply_to = models.CharField(blank=True, max_length=255)
class LiaisonDetail(models.Model):
detail_id = models.AutoField(primary_key=True)
person = models.ForeignKey(PersonOrOrgInfo, null=True, db_column='person_or_org_tag')
submitted_date = models.DateField(null=True, blank=True)
last_modified_date = models.DateField(null=True, blank=True)
from_id = models.IntegerField(null=True, blank=True)
to_body = models.CharField(blank=True, null=True, max_length=255)
title = models.CharField(blank=True, null=True, max_length=255)
response_contact = models.CharField(blank=True, null=True, max_length=255)
technical_contact = models.CharField(blank=True, null=True, max_length=255)
purpose_text = models.TextField(blank=True, null=True, db_column='purpose')
body = models.TextField(blank=True,null=True)
deadline_date = models.DateField(null=True, blank=True)
cc1 = models.TextField(blank=True, null=True)
# unclear why cc2 is a CharField, but it's always
# either NULL or blank.
cc2 = models.CharField(blank=True, null=True, max_length=50)
submitter_name = models.CharField(blank=True, null=True, max_length=255)
submitter_email = models.CharField(blank=True, null=True, max_length=255)
by_secretariat = models.IntegerField(null=True, blank=True)
to_poc = models.CharField(blank=True, null=True, max_length=255)
to_email = models.CharField(blank=True, null=True, max_length=255)
purpose = models.ForeignKey(LiaisonPurpose,null=True)
replyto = models.CharField(blank=True, null=True, max_length=255)
from_raw_body = models.CharField(blank=True, null=True, max_length=255)
from_raw_code = models.CharField(blank=True, null=True, max_length=255)
to_raw_code = models.CharField(blank=True, null=True, max_length=255)
approval = models.ForeignKey(OutgoingLiaisonApproval, blank=True, null=True)
action_taken = models.BooleanField(default=False, db_column='taken_care')
related_to = models.ForeignKey('LiaisonDetail', blank=True, null=True)
def __str__(self):
return self.title or "<no title>"
def __unicode__(self):
return self.title or "<no title>"
def from_body(self):
"""The from_raw_body stores the name of the entity
sending the liaison.
For legacy liaisons (the ones with empty from_raw_body)
the legacy_from_body() is returned."""
if not self.from_raw_body:
return self.legacy_from_body()
return self.from_raw_body
def from_sdo(self):
try:
name = FromBodies.objects.get(pk=self.from_id).body_name
sdo = SDOs.objects.get(sdo_name=name)
return sdo
except ObjectDoesNotExist:
return None
def legacy_from_body(self):
"""The from_id field is a foreign key for either
FromBodies or Acronyms, depending on whether it's
the IETF or not. There is no flag field saying
which, so we just try it. If the index values
overlap, then this function will be ambiguous
and will return the value from FromBodies. Current
acronym IDs start at 925 so the day of reckoning
is not nigh."""
try:
from_body = FromBodies.objects.get(pk=self.from_id)
return from_body.body_name
except ObjectDoesNotExist:
pass
try:
acronym = Acronym.objects.get(pk=self.from_id)
try:
x = acronym.area
kind = "AREA"
except Area.DoesNotExist:
kind = "WG"
return "IETF %s %s" % (acronym.acronym.upper(), kind)
except ObjectDoesNotExist:
pass
return "<unknown body %s>" % self.from_id
def from_email(self):
"""If there is an entry in from_bodies, it has
the desired email priority. However, if it's from
an IETF WG, there is no entry in from_bodies, so
default to 1."""
try:
from_body = FromBodies.objects.get(pk=self.from_id)
email_priority = from_body.email_priority
except FromBodies.DoesNotExist:
email_priority = 1
return self.person.emailaddress_set.all().get(priority=email_priority)
def get_absolute_url(self):
return '/liaison/%d/' % self.detail_id
class Meta:
db_table = 'liaison_detail'
response_contact = models.CharField(blank=True, max_length=255)
technical_contact = models.CharField(blank=True, max_length=255)
cc = models.TextField(blank=True)
def notify_pending_by_email(self, fake):
from ietf.liaisons.utils import IETFHM
submitted = models.DateTimeField(null=True, blank=True)
modified = models.DateTimeField(null=True, blank=True)
approved = models.DateTimeField(null=True, blank=True)
from_entity = IETFHM.get_entity_by_key(self.from_raw_code)
if not from_entity:
return None
to_email = []
for person in from_entity.can_approve():
to_email.append('%s <%s>' % person.email())
subject = 'New Liaison Statement, "%s" needs your approval' % (self.title)
from_email = settings.LIAISON_UNIVERSAL_FROM
body = render_to_string('liaisons/pending_liaison_mail.txt', {
'liaison': self,
'url': settings.IDTRACKER_BASE_URL + urlreverse("liaison_approval_detail", kwargs=dict(object_id=self.pk)),
'referenced_url': settings.IDTRACKER_BASE_URL + urlreverse("liaison_detail", kwargs=dict(object_id=self.related_to.pk)) if self.related_to else None,
})
send_mail_text(context=None, to=to_email, frm=from_email, subject=subject, txt = body)
#mail = IETFEmailMessage(subject=subject,
# to=to_email,
# from_email=from_email,
# body = body)
#if not fake:
# mail.send()
#return mail
action_taken = models.BooleanField(default=False)
def send_by_email(self, fake=False):
if self.is_pending():
return self.notify_pending_by_email(fake)
subject = 'New Liaison Statement, "%s"' % (self.title)
from_email = settings.LIAISON_UNIVERSAL_FROM
to_email = self.to_poc.split(',')
cc = self.cc1.split(',')
if self.technical_contact:
cc += self.technical_contact.split(',')
if self.response_contact:
cc += self.response_contact.split(',')
bcc = ['statements@ietf.org']
body = render_to_string('liaisons/liaison_mail.txt', {
'liaison': self,
'url': settings.IDTRACKER_BASE_URL + urlreverse("liaison_detail", kwargs=dict(object_id=self.pk)),
'referenced_url': settings.IDTRACKER_BASE_URL + urlreverse("liaison_detail", kwargs=dict(object_id=self.related_to.pk)) if self.related_to else None,
})
send_mail_text(context=None,to=to_email,frm=from_email,subject=subject,txt=body,cc=cc,bcc=bcc)
#mail = IETFEmailMessage(subject=subject,
# to=to_email,
# from_email=from_email,
# cc = cc,
# bcc = bcc,
# body = body)
#if not fake:
# mail.send()
#return mail
attachments = models.ManyToManyField(Document, blank=True)
def is_pending(self):
return bool(self.approval and not self.approval.approved)
class SDOs(models.Model):
sdo_id = models.AutoField(primary_key=True, verbose_name='ID')
sdo_name = models.CharField(blank=True, max_length=255, verbose_name='SDO Name')
def __str__(self):
return self.sdo_name
def liaisonmanager(self):
try:
return self.liaisonmanagers_set.all()[0]
except:
return None
def sdo_contact(self):
try:
return self.sdoauthorizedindividual_set.all()[0]
except:
return None
class Meta:
verbose_name = 'SDO'
verbose_name_plural = 'SDOs'
db_table = 'sdos'
ordering = ('sdo_name', )
liaisonmanager_link = admin_link('liaisonmanager', label='Liaison')
sdo_contact_link = admin_link('sdo_contact')
class LiaisonStatementManager(models.Model):
person = models.ForeignKey(PersonOrOrgInfo, db_column='person_or_org_tag')
sdo = models.ForeignKey(SDOs, verbose_name='SDO')
def __unicode__(self):
return '%s (%s)' % (self.person, self.sdo)
class Meta:
abstract = True
# Helper functions, for use in the admin interface
def login_name(self):
login_name = None
try:
login_name = IESGLogin.objects.get(person=self.person).login_name
if User.objects.filter(username=login_name).count():
return login_name
except IESGLogin.DoesNotExist:
pass
try:
login_name = LegacyLiaisonUser.objects.get(person=self.person).login_name
except LegacyLiaisonUser.DoesNotExist:
pass
return login_name
def user(self):
login_name = self.login_name()
user = None
if login_name:
try:
return User.objects.get(username=login_name), login_name
except User.DoesNotExist:
pass
return None, login_name
def user_name(self):
user, login_name = self.user()
if user:
return u'<a href="/admin/auth/user/%s/">%s</a>' % (user.id, login_name)
def name(self):
from django.template.defaultfilters import slugify
if self.from_group:
frm = self.from_group.acronym or self.from_group.name
else:
if login_name:
return u'Add login: <a href="/admin/auth/user/add/?username=%s"><span style="color: red">%s</span></a>' % (login_name, login_name)
else:
return u'Add liaison user: <a href="/admin/ietfauth/legacyliaisonuser/add/?person=%s&login_name=%s&user_level=3"><span style="color: red">%s</span></a>' % (self.person.pk, self.person.email()[1], self.person, )
user_name.allow_tags = True
def groups(self):
user, login_name = self.user()
return ", ".join([ group.name for group in user.groups.all()])
person_link = admin_link('person')
sdo_link = admin_link('sdo', label='SDO')
class LiaisonManagers(LiaisonStatementManager):
email_priority = models.IntegerField(null=True, blank=True)
def email(self):
try:
return self.person.emailaddress_set.get(priority=self.email_priority)
except ObjectDoesNotExist:
return None
class Meta:
verbose_name = 'SDO Liaison Manager'
verbose_name_plural = 'SDO Liaison Managers'
db_table = 'liaison_managers'
ordering = ('sdo__sdo_name', )
frm = self.from_name
if self.to_group:
to = self.to_group.acronym or self.to_group.name
else:
to = self.to_name
return slugify("liaison" + " " + self.submitted.strftime("%Y-%m-%d") + " " + frm[:50] + " " + to[:50] + " " + self.title[:115])
class SDOAuthorizedIndividual(LiaisonStatementManager):
class Meta:
verbose_name = 'SDO Authorized Individual'
verbose_name_plural = 'SDO Authorized Individuals'
# This table is not used by any code right now.
#class LiaisonsInterim(models.Model):
# title = models.CharField(blank=True, max_length=255)
# submitter_name = models.CharField(blank=True, max_length=255)
# submitter_email = models.CharField(blank=True, max_length=255)
# submitted_date = models.DateField(null=True, blank=True)
# from_id = models.IntegerField(null=True, blank=True)
# def __str__(self):
# return self.title
# class Meta:
# db_table = 'liaisons_interim'
class Uploads(models.Model):
file_id = models.AutoField(primary_key=True)
file_title = models.CharField(blank=True, max_length=255)
person = models.ForeignKey(PersonOrOrgInfo, db_column='person_or_org_tag')
file_extension = models.CharField(blank=True, max_length=10)
detail = models.ForeignKey(LiaisonDetail)
def __str__(self):
return self.file_title
def filename(self):
return "file%s%s" % (self.file_id, self.file_extension)
class Meta:
db_table = 'uploads'
# empty table
#class SdoChairs(models.Model):
# sdo = models.ForeignKey(SDOs)
# person = models.ForeignKey(PersonOrOrgInfo, db_column='person_or_org_tag')
# email_priority = models.IntegerField(null=True, blank=True)
# class Meta:
# db_table = 'sdo_chairs'
# changes done by convert-096.py:changed maxlength to max_length
# removed core
# removed edit_inline
# removed num_in_admin
# removed raw_id_admin
if settings.USE_DB_REDESIGN_PROXY_CLASSES or hasattr(settings, "IMPORTING_FROM_OLD_SCHEMA"):
from ietf.name.models import LiaisonStatementPurposeName
from ietf.doc.models import Document
from ietf.person.models import Email
from ietf.group.models import Group
class LiaisonStatement(models.Model):
title = models.CharField(blank=True, max_length=255)
purpose = models.ForeignKey(LiaisonStatementPurposeName)
body = models.TextField(blank=True)
deadline = models.DateField(null=True, blank=True)
related_to = models.ForeignKey('LiaisonStatement', blank=True, null=True)
from_group = models.ForeignKey(Group, related_name="liaisonstatement_from_set", null=True, blank=True, help_text="Sender group, if it exists")
from_name = models.CharField(max_length=255, help_text="Name of the sender body")
from_contact = models.ForeignKey(Email, blank=True, null=True)
to_group = models.ForeignKey(Group, related_name="liaisonstatement_to_set", null=True, blank=True, help_text="Recipient group, if it exists")
to_name = models.CharField(max_length=255, help_text="Name of the recipient body")
to_contact = models.CharField(blank=True, max_length=255, help_text="Contacts at recipient body")
reply_to = models.CharField(blank=True, max_length=255)
response_contact = models.CharField(blank=True, max_length=255)
technical_contact = models.CharField(blank=True, max_length=255)
cc = models.TextField(blank=True)
submitted = models.DateTimeField(null=True, blank=True)
modified = models.DateTimeField(null=True, blank=True)
approved = models.DateTimeField(null=True, blank=True)
action_taken = models.BooleanField(default=False)
attachments = models.ManyToManyField(Document, blank=True)
def name(self):
from django.template.defaultfilters import slugify
if self.from_group:
frm = self.from_group.acronym or self.from_group.name
else:
frm = self.from_name
if self.to_group:
to = self.to_group.acronym or self.to_group.name
else:
to = self.to_name
return slugify("liaison" + " " + self.submitted.strftime("%Y-%m-%d") + " " + frm[:50] + " " + to[:50] + " " + self.title[:115])
def __unicode__(self):
return self.title or "<no title>"
LiaisonDetailOld = LiaisonDetail
def __unicode__(self):
return self.title or u"<no title>"

View file

@ -1,188 +0,0 @@
from ietf.utils.proxy import TranslatingManager
from ietf.liaisons.models import LiaisonStatement
from ietf.doc.models import Document
class LiaisonDetailProxy(LiaisonStatement):
objects = TranslatingManager(dict(submitted_date="submitted",
deadline_date="deadline",
to_body="to_name",
from_raw_body="from_name"))
def from_object(self, base):
for f in base._meta.fields:
setattr(self, f.name, getattr(base, f.name))
return self
#detail_id = models.AutoField(primary_key=True)
@property
def detail_id(self):
return self.id
#person = models.ForeignKey(PersonOrOrgInfo, null=True, db_column='person_or_org_tag')
@property
def person(self):
return self.from_contact.person if self.from_contact else ""
#submitted_date = models.DateField(null=True, blank=True)
@property
def submitted_date(self):
return self.submitted.date() if self.submitted else None
#last_modified_date = models.DateField(null=True, blank=True)
@property
def last_modified_date(self):
return self.modified.date() if self.modified else None
#from_id = models.IntegerField(null=True, blank=True)
@property
def from_id(self):
return self.from_group_id
#to_body = models.CharField(blank=True, null=True, max_length=255)
@property
def to_body(self):
return self.to_name
#title = models.CharField(blank=True, null=True, max_length=255) # same name
#response_contact = models.CharField(blank=True, null=True, max_length=255) # same name
#technical_contact = models.CharField(blank=True, null=True, max_length=255) # same name
#purpose_text = models.TextField(blank=True, null=True, db_column='purpose')
@property
def purpose_text(self):
return ""
#body = models.TextField(blank=True,null=True) # same name
#deadline_date = models.DateField(null=True, blank=True)
@property
def deadline_date(self):
return self.deadline
#cc1 = models.TextField(blank=True, null=True)
@property
def cc1(self):
return self.cc
#cc2 = models.CharField(blank=True, null=True, max_length=50) # unused
@property
def cc2(self):
return ""
#submitter_name = models.CharField(blank=True, null=True, max_length=255)
@property
def submitter_name(self):
i = self.to_name.find('<')
if i > 0:
return self.to_name[:i - 1]
else:
return self.to_name
#submitter_email = models.CharField(blank=True, null=True, max_length=255)
@property
def submitter_email(self):
import re
re_email = re.compile("<(.*)>")
match = re_email.search(self.to_name)
if match:
return match.group(1)
else:
return ""
#by_secretariat = models.IntegerField(null=True, blank=True)
@property
def by_secretariat(self):
return not self.from_contact
#to_poc = models.CharField(blank=True, null=True, max_length=255)
@property
def to_poc(self):
return self.to_contact
#to_email = models.CharField(blank=True, null=True, max_length=255)
@property
def to_email(self):
return ""
#purpose = models.ForeignKey(LiaisonPurpose,null=True)
#replyto = models.CharField(blank=True, null=True, max_length=255)
@property
def replyto(self):
return self.reply_to
#from_raw_body = models.CharField(blank=True, null=True, max_length=255)
@property
def from_raw_body(self):
return self.from_name
def raw_codify(self, group):
if not group:
return ""
if group.type_id in ("sdo", "wg", "area"):
return "%s_%s" % (group.type_id, group.id)
return group.acronym
#from_raw_code = models.CharField(blank=True, null=True, max_length=255)
@property
def from_raw_code(self):
return self.raw_codify(self.from_group)
#to_raw_code = models.CharField(blank=True, null=True, max_length=255)
@property
def to_raw_code(self):
return self.raw_codify(self.to_group)
#approval = models.ForeignKey(OutgoingLiaisonApproval, blank=True, null=True)
@property
def approval(self):
return bool(self.approved)
#action_taken = models.BooleanField(default=False, db_column='taken_care') # same name
#related_to = models.ForeignKey('LiaisonDetail', blank=True, null=True) # same name
@property
def uploads_set(self):
return UploadsProxy.objects.filter(liaisonstatement=self).order_by("name", "external_url")
@property
def liaisondetail_set(self):
return self.liaisonstatement_set
def __str__(self):
return unicode(self)
def __unicode__(self):
return self.title or "<no title>"
def from_body(self):
return self.from_name
def from_sdo(self):
return self.from_group if self.from_group and self.from_group.type_id == "sdo" else None
def from_email(self):
return self.from_contact.address
def get_absolute_url(self):
return '/liaison/%d/' % self.detail_id
class Meta:
proxy = True
def send_by_email(self, fake=False):
# grab this from module instead of stuffing in on the model
from ietf.liaisons.mails import send_liaison_by_email
# we don't have a request so just pass None for the time being
return send_liaison_by_email(None, self, fake)
def notify_pending_by_email(self, fake=False):
# grab this from module instead of stuffing in on the model
from ietf.liaisons.mails import notify_pending_by_email
# we don't have a request so just pass None for the time being
return notify_pending_by_email(None, self, fake)
def is_pending(self):
return not self.approved
class UploadsProxy(Document):
#file_id = models.AutoField(primary_key=True)
@property
def file_id(self):
if not self.external_url or self.external_url.startswith(self.name):
return self.name # new data
else:
return int(self.external_url.split(".")[0][len("file"):]) # old data
#file_title = models.CharField(blank=True, max_length=255)
@property
def file_title(self):
return self.title
#person = models.ForeignKey(PersonOrOrgInfo, db_column='person_or_org_tag')
#file_extension = models.CharField(blank=True, max_length=10)
@property
def file_extension(self):
t = self.external_url.split(".")
if len(t) > 1:
return "." + t[1]
else:
return ""
#detail = models.ForeignKey(LiaisonDetail)
@property
def detail(self):
return self.liaisonstatement_set.all()[0]
def filename(self):
return self.external_url
class Meta:
proxy = True

View file

@ -2,16 +2,17 @@
#
from django.contrib.sitemaps import Sitemap
from django.conf import settings
from ietf.liaisons.models import LiaisonDetail
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.liaisons.proxy import LiaisonDetailProxy as LiaisonDetail
from ietf.liaisons.models import LiaisonStatement
class LiaisonMap(Sitemap):
changefreq = "never"
def items(self):
return LiaisonDetail.objects.all()
return LiaisonStatement.objects.all()
def location(self, obj):
return "/liaison/%d/" % obj.detail_id
return "/liaison/%s/" % obj.pk
def lastmod(self, obj):
return obj.last_modified_date
return obj.modified

View file

@ -11,6 +11,11 @@ from ietf.utils.test_data import make_test_data
from ietf.utils.mail import outbox
from ietf.utils import TestCase
from ietf.liaisons.models import LiaisonStatement, LiaisonStatementPurposeName
from ietf.person.models import Person, Email
from ietf.group.models import Group, Role
from ietf.liaisons.mails import send_sdo_reminder, possibly_send_deadline_reminder
class LiaisonsUrlTestCase(SimpleUrlTestCase):
def testUrls(self):
self.doTestUrls(__file__)
@ -22,11 +27,6 @@ class LiaisonsUrlTestCase(SimpleUrlTestCase):
else:
return content
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.liaisons.models import LiaisonStatement, LiaisonStatementPurposeName
from ietf.person.models import Person, Email
from ietf.group.models import Group, Role
def make_liaison_models():
sdo = Group.objects.create(
name="United League of Marsmen",
@ -89,7 +89,58 @@ def make_liaison_models():
action_taken=False,
)
return l
class LiaisonTests(TestCase):
def test_overview(self):
make_test_data()
liaison = make_liaison_models()
r = self.client.get(urlreverse('liaison_list'))
self.assertEqual(r.status_code, 200)
self.assertTrue(liaison.title in r.content)
def test_details(self):
make_test_data()
liaison = make_liaison_models()
r = self.client.get(urlreverse("liaison_detail", kwargs={ 'object_id': liaison.pk }))
self.assertEqual(r.status_code, 200)
self.assertTrue(liaison.title in r.content)
def test_feeds(self):
make_test_data()
liaison = make_liaison_models()
r = self.client.get('/feed/liaison/recent/')
self.assertEqual(r.status_code, 200)
self.assertTrue(liaison.title in r.content)
r = self.client.get('/feed/liaison/from/%s/' % liaison.from_group.acronym)
self.assertEqual(r.status_code, 200)
self.assertTrue(liaison.title in r.content)
r = self.client.get('/feed/liaison/to/%s/' % liaison.to_name)
self.assertEqual(r.status_code, 200)
self.assertTrue(liaison.title in r.content)
r = self.client.get('/feed/liaison/subject/marsmen/')
self.assertEqual(r.status_code, 200)
self.assertTrue(liaison.title in r.content)
def test_sitemap(self):
make_test_data()
liaison = make_liaison_models()
r = self.client.get('/sitemap-liaison.xml')
self.assertEqual(r.status_code, 200)
self.assertTrue(urlreverse("liaison_detail", kwargs={ 'object_id': liaison.pk }) in r.content)
def test_help_pages(self):
self.assertEqual(self.client.get('/liaison/help/').status_code, 200)
self.assertEqual(self.client.get('/liaison/help/fields/').status_code, 200)
self.assertEqual(self.client.get('/liaison/help/from_ietf/').status_code, 200)
self.assertEqual(self.client.get('/liaison/help/to_ietf/').status_code, 200)
class LiaisonManagementTests(TestCase):
def setUp(self):
@ -114,52 +165,63 @@ class LiaisonManagementTests(TestCase):
url = urlreverse('liaison_detail', kwargs=dict(object_id=liaison.pk))
# normal get
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('form input[name=do_action_taken]')), 0)
self.assertEqual(len(q('form input[name=do_action_taken]')), 0)
# log in and get
self.client.login(remote_user="secretary")
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('form input[name=do_action_taken]')), 1)
self.assertEqual(len(q('form input[name=do_action_taken]')), 1)
# mark action taken
r = self.client.post(url, dict(do_action_taken="1"))
self.assertEquals(r.status_code, 200)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('form input[name=do_action_taken]')), 0)
self.assertEqual(len(q('form input[name=do_action_taken]')), 0)
liaison = LiaisonStatement.objects.get(id=liaison.id)
self.assertTrue(liaison.action_taken)
def test_approval(self):
def test_approval_process(self):
make_test_data()
liaison = make_liaison_models()
# has to come from WG to need approval
liaison.from_group = Group.objects.get(acronym="mars")
liaison.approved = None
liaison.save()
url = urlreverse('liaison_approval_detail', kwargs=dict(object_id=liaison.pk))
# check the overview page
url = urlreverse('liaison_approval_list')
# this liaison is for a WG so we need the AD for the area
login_testing_unauthorized(self, "ad", url)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue(liaison.title in r.content)
# check detail page
url = urlreverse('liaison_approval_detail', kwargs=dict(object_id=liaison.pk))
self.client.logout()
login_testing_unauthorized(self, "ad", url)
# normal get
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
self.assertEqual(r.status_code, 200)
self.assertTrue(liaison.title in r.content)
q = PyQuery(r.content)
self.assertEquals(len(q('form input[name=do_approval]')), 1)
self.assertEqual(len(q('form input[name=do_approval]')), 1)
# approve
mailbox_before = len(outbox)
r = self.client.post(url, dict(do_approval="1"))
self.assertEquals(r.status_code, 302)
self.assertEqual(r.status_code, 302)
liaison = LiaisonStatement.objects.get(id=liaison.id)
self.assertTrue(liaison.approved)
self.assertEquals(len(outbox), mailbox_before + 1)
self.assertEqual(len(outbox), mailbox_before + 1)
self.assertTrue("Liaison Statement" in outbox[-1]["Subject"])
def test_edit_liaison(self):
@ -171,9 +233,9 @@ class LiaisonManagementTests(TestCase):
# get
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('form input[name=from_field]')), 1)
self.assertEqual(len(q('form input[name=from_field]')), 1)
# edit
attachments_before = liaison.attachments.count()
@ -195,30 +257,30 @@ class LiaisonManagementTests(TestCase):
attach_file_1=test_file,
attach_title_1="attachment",
))
self.assertEquals(r.status_code, 302)
self.assertEqual(r.status_code, 302)
new_liaison = LiaisonStatement.objects.get(id=liaison.id)
self.assertEquals(new_liaison.from_name, "from")
self.assertEquals(new_liaison.reply_to, "replyto@example.com")
self.assertEquals(new_liaison.to_name, "org")
self.assertEquals(new_liaison.to_contact, "to_poc@example.com")
self.assertEquals(new_liaison.response_contact, "responce_contact@example.com")
self.assertEquals(new_liaison.technical_contact, "technical_contact@example.com")
self.assertEquals(new_liaison.cc, "cc@example.com")
self.assertEquals(new_liaison.purpose, LiaisonStatementPurposeName.objects.get(order=4))
self.assertEquals(new_liaison.deadline, liaison.deadline + datetime.timedelta(days=1)),
self.assertEquals(new_liaison.title, "title")
self.assertEquals(new_liaison.submitted.date(), (liaison.submitted + datetime.timedelta(days=1)).date())
self.assertEquals(new_liaison.body, "body")
self.assertEqual(new_liaison.from_name, "from")
self.assertEqual(new_liaison.reply_to, "replyto@example.com")
self.assertEqual(new_liaison.to_name, "org")
self.assertEqual(new_liaison.to_contact, "to_poc@example.com")
self.assertEqual(new_liaison.response_contact, "responce_contact@example.com")
self.assertEqual(new_liaison.technical_contact, "technical_contact@example.com")
self.assertEqual(new_liaison.cc, "cc@example.com")
self.assertEqual(new_liaison.purpose, LiaisonStatementPurposeName.objects.get(order=4))
self.assertEqual(new_liaison.deadline, liaison.deadline + datetime.timedelta(days=1)),
self.assertEqual(new_liaison.title, "title")
self.assertEqual(new_liaison.submitted.date(), (liaison.submitted + datetime.timedelta(days=1)).date())
self.assertEqual(new_liaison.body, "body")
self.assertEquals(new_liaison.attachments.count(), attachments_before + 1)
self.assertEqual(new_liaison.attachments.count(), attachments_before + 1)
attachment = new_liaison.attachments.order_by("-name")[0]
self.assertEquals(attachment.title, "attachment")
self.assertEqual(attachment.title, "attachment")
with open(os.path.join(self.liaison_dir, attachment.external_url)) as f:
written_content = f.read()
test_file.seek(0)
self.assertEquals(written_content, test_file.read())
self.assertEqual(written_content, test_file.read())
def test_add_incoming_liaison(self):
make_test_data()
@ -229,9 +291,9 @@ class LiaisonManagementTests(TestCase):
# get
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('form textarea[name=body]')), 1)
self.assertEqual(len(q('form textarea[name=body]')), 1)
# add new
mailbox_before = len(outbox)
@ -260,34 +322,34 @@ class LiaisonManagementTests(TestCase):
attach_title_1="attachment",
send="1",
))
self.assertEquals(r.status_code, 302)
self.assertEqual(r.status_code, 302)
l = LiaisonStatement.objects.all().order_by("-id")[0]
self.assertEquals(l.from_group, from_group)
self.assertEquals(l.from_contact.address, submitter.email_address())
self.assertEquals(l.reply_to, "replyto@example.com")
self.assertEquals(l.to_group, to_group)
self.assertEquals(l.response_contact, "responce_contact@example.com")
self.assertEquals(l.technical_contact, "technical_contact@example.com")
self.assertEquals(l.cc, "cc@example.com")
self.assertEquals(l.purpose, LiaisonStatementPurposeName.objects.get(order=4))
self.assertEquals(l.deadline, today + datetime.timedelta(days=1)),
self.assertEquals(l.related_to, liaison),
self.assertEquals(l.title, "title")
self.assertEquals(l.submitted.date(), today)
self.assertEquals(l.body, "body")
self.assertEqual(l.from_group, from_group)
self.assertEqual(l.from_contact.address, submitter.email_address())
self.assertEqual(l.reply_to, "replyto@example.com")
self.assertEqual(l.to_group, to_group)
self.assertEqual(l.response_contact, "responce_contact@example.com")
self.assertEqual(l.technical_contact, "technical_contact@example.com")
self.assertEqual(l.cc, "cc@example.com")
self.assertEqual(l.purpose, LiaisonStatementPurposeName.objects.get(order=4))
self.assertEqual(l.deadline, today + datetime.timedelta(days=1)),
self.assertEqual(l.related_to, liaison),
self.assertEqual(l.title, "title")
self.assertEqual(l.submitted.date(), today)
self.assertEqual(l.body, "body")
self.assertTrue(l.approved)
self.assertEquals(l.attachments.count(), 1)
self.assertEqual(l.attachments.count(), 1)
attachment = l.attachments.all()[0]
self.assertEquals(attachment.title, "attachment")
self.assertEqual(attachment.title, "attachment")
with open(os.path.join(self.liaison_dir, attachment.external_url)) as f:
written_content = f.read()
test_file.seek(0)
self.assertEquals(written_content, test_file.read())
self.assertEqual(written_content, test_file.read())
self.assertEquals(len(outbox), mailbox_before + 1)
self.assertEqual(len(outbox), mailbox_before + 1)
self.assertTrue("Liaison Statement" in outbox[-1]["Subject"])
def test_add_outgoing_liaison(self):
@ -299,9 +361,9 @@ class LiaisonManagementTests(TestCase):
# get
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('form textarea[name=body]')), 1)
self.assertEqual(len(q('form textarea[name=body]')), 1)
# add new
mailbox_before = len(outbox)
@ -333,35 +395,35 @@ class LiaisonManagementTests(TestCase):
attach_title_1="attachment",
send="1",
))
self.assertEquals(r.status_code, 302)
self.assertEqual(r.status_code, 302)
l = LiaisonStatement.objects.all().order_by("-id")[0]
self.assertEquals(l.from_group, from_group)
self.assertEquals(l.from_contact.address, submitter.email_address())
self.assertEquals(l.reply_to, "replyto@example.com")
self.assertEquals(l.to_group, to_group)
self.assertEquals(l.to_contact, "to_poc@example.com")
self.assertEquals(l.response_contact, "responce_contact@example.com")
self.assertEquals(l.technical_contact, "technical_contact@example.com")
self.assertEquals(l.cc, "cc@example.com")
self.assertEquals(l.purpose, LiaisonStatementPurposeName.objects.get(order=4))
self.assertEquals(l.deadline, today + datetime.timedelta(days=1)),
self.assertEquals(l.related_to, liaison),
self.assertEquals(l.title, "title")
self.assertEquals(l.submitted.date(), today)
self.assertEquals(l.body, "body")
self.assertEqual(l.from_group, from_group)
self.assertEqual(l.from_contact.address, submitter.email_address())
self.assertEqual(l.reply_to, "replyto@example.com")
self.assertEqual(l.to_group, to_group)
self.assertEqual(l.to_contact, "to_poc@example.com")
self.assertEqual(l.response_contact, "responce_contact@example.com")
self.assertEqual(l.technical_contact, "technical_contact@example.com")
self.assertEqual(l.cc, "cc@example.com")
self.assertEqual(l.purpose, LiaisonStatementPurposeName.objects.get(order=4))
self.assertEqual(l.deadline, today + datetime.timedelta(days=1)),
self.assertEqual(l.related_to, liaison),
self.assertEqual(l.title, "title")
self.assertEqual(l.submitted.date(), today)
self.assertEqual(l.body, "body")
self.assertTrue(not l.approved)
self.assertEquals(l.attachments.count(), 1)
self.assertEqual(l.attachments.count(), 1)
attachment = l.attachments.all()[0]
self.assertEquals(attachment.title, "attachment")
self.assertEqual(attachment.title, "attachment")
with open(os.path.join(self.liaison_dir, attachment.external_url)) as f:
written_content = f.read()
test_file.seek(0)
self.assertEquals(written_content, test_file.read())
self.assertEqual(written_content, test_file.read())
self.assertEquals(len(outbox), mailbox_before + 1)
self.assertEqual(len(outbox), mailbox_before + 1)
self.assertTrue("Liaison Statement" in outbox[-1]["Subject"])
# try adding statement to non-predefined organization
@ -383,46 +445,34 @@ class LiaisonManagementTests(TestCase):
submitted_date=today.strftime("%Y-%m-%d"),
body="body",
))
self.assertEquals(r.status_code, 302)
self.assertEqual(r.status_code, 302)
l = LiaisonStatement.objects.all().order_by("-id")[0]
self.assertEquals(l.to_group, None)
self.assertEquals(l.to_name, "Mars Institute")
self.assertEqual(l.to_group, None)
self.assertEqual(l.to_name, "Mars Institute")
def test_send_sdo_reminder(self):
make_test_data()
liaison = make_liaison_models()
from ietf.liaisons.mails import send_sdo_reminder
mailbox_before = len(outbox)
send_sdo_reminder(Group.objects.filter(type="sdo")[0])
self.assertEquals(len(outbox), mailbox_before + 1)
self.assertEqual(len(outbox), mailbox_before + 1)
self.assertTrue("authorized individuals" in outbox[-1]["Subject"])
def test_send_liaison_deadline_reminder(self):
make_test_data()
liaison = make_liaison_models()
from ietf.liaisons.mails import possibly_send_deadline_reminder
from ietf.liaisons.proxy import LiaisonDetailProxy as LiaisonDetail
l = LiaisonDetail.objects.all()[0]
mailbox_before = len(outbox)
possibly_send_deadline_reminder(l)
self.assertEquals(len(outbox), mailbox_before + 1)
possibly_send_deadline_reminder(liaison)
self.assertEqual(len(outbox), mailbox_before + 1)
self.assertTrue("deadline" in outbox[-1]["Subject"])
# try pushing the deadline
l.deadline = l.deadline + datetime.timedelta(days=30)
l.save()
liaison.deadline = liaison.deadline + datetime.timedelta(days=30)
liaison.save()
mailbox_before = len(outbox)
possibly_send_deadline_reminder(l)
self.assertEquals(len(outbox), mailbox_before)
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
# the above tests only work with the new schema
del LiaisonManagementTestCase
possibly_send_deadline_reminder(liaison)
self.assertEqual(len(outbox), mailbox_before)

View file

@ -1,33 +0,0 @@
200 /liaison/
200 /liaison/321/
200 /liaison/553/ # submitted by email
200 /liaison/337/ # test case for ticket #182
200 /liaison/458/ # non-ASCII title
200 /liaison/471/ # non-ASCII body and submitter name
301 /liaison/managers/
200 /liaison/help/to_ietf/
200 /liaison/help/from_ietf/
200 /liaison/help/fields/
200 /liaison/help/
404 /feed/liaison/
200 /feed/liaison/recent/
200 /feed/liaison/from/ccamp/
#200 /feed/liaison/from/MFA%20Forum/
200 /feed/liaison/from/MFA_Forum/
200 /feed/liaison/to/ccamp/
200 /feed/liaison/subject/H.248/
200 /feed/liaison/subject/2173/ # non-ASCII title
404 /feed/liaison/recent/foobar/
404 /feed/liaison/from/
404 /feed/liaison/from/nosuchorganization/
404 /feed/liaison/from/foo/bar/
404 /feed/liaison/to/
404 /feed/liaison/to/foo/bar/
404 /feed/liaison/subject/
404 /feed/liaison/subject/foo/bar/
200 /sitemap-liaison.xml

View file

@ -2,17 +2,8 @@
from django.conf.urls.defaults import patterns, url
from django.db.models import Q
from ietf.liaisons.models import LiaisonDetail
info_dict = {
'queryset': LiaisonDetail.objects.filter(Q(approval__isnull=True)|Q(approval__approved=True)).order_by("-submitted_date"),
}
# there's an opportunity for date-based filtering.
urlpatterns = patterns('django.views.generic.list_detail',
)
urlpatterns += patterns('django.views.generic.simple',
urlpatterns = patterns('django.views.generic.simple',
(r'^help/$', 'direct_to_template', {'template': 'liaisons/help.html'}),
(r'^help/fields/$', 'direct_to_template', {'template': 'liaisons/field_help.html'}),
(r'^help/from_ietf/$', 'direct_to_template', {'template': 'liaisons/guide_from_ietf.html'}),

View file

@ -1,10 +1,43 @@
from django.conf import settings
from django.db.models import Q
from ietf.idtracker.models import Area, IETFWG
from ietf.liaisons.models import SDOs, LiaisonManagers
from ietf.liaisons.accounts import (is_ietfchair, is_iabchair, is_iab_executive_director, is_irtfchair,
from ietf.group.models import Group, Role
from ietf.person.models import Person
from ietf.liaisons.models import LiaisonStatement
from ietf.ietfauth.utils import has_role, passes_test_decorator
from ietf.utils.proxy import proxy_personify_role
from ietf.liaisons.accounts import (is_ietfchair, is_iabchair, is_iab_executive_director,
get_ietf_chair, get_iab_chair, get_iab_executive_director,
is_secretariat)
is_secretariat, can_add_liaison, get_person_for_user)
can_submit_liaison_required = passes_test_decorator(
lambda u, *args, **kwargs: can_add_liaison(u),
"Restricted to participants who are authorized to submit liaison statements on behalf of the various IETF entities")
def approvable_liaison_statements(user):
liaisons = LiaisonStatement.objects.filter(approved=None)
if has_role(user, "Secretariat"):
return liaisons
# this is a bit complicated because IETFHM encodes the
# groups, it should just give us a list of ids or acronyms
group_codes = IETFHM.get_all_can_approve_codes(get_person_for_user(user))
group_acronyms = []
group_ids = []
for x in group_codes:
if "_" in x:
group_ids.append(x.split("_")[1])
else:
group_acronyms.append(x)
return liaisons.filter(Q(from_group__acronym__in=group_acronyms) | Q(from_group__pk__in=group_ids))
# the following is a biggish object hierarchy abstracting the entity
# names and auth rules for posting liaison statements in a sort of
# semi-declarational (and perhaps overengineered given the revamped
# schema) way - unfortunately, it's never been strong enough to do so
# fine-grained enough so the form code also has some rules
IETFCHAIR = {'name': u'The IETF Chair', 'address': u'chair@ietf.org'}
IESG = {'name': u'The IESG', 'address': u'iesg@ietf.org'}
@ -12,10 +45,7 @@ IAB = {'name': u'The IAB', 'address': u'iab@iab.org'}
IABCHAIR = {'name': u'The IAB Chair', 'address': u'iab-chair@iab.org'}
IABEXECUTIVEDIRECTOR = {'name': u'The IAB Executive Director', 'address': u'execd@iab.org'}
IRTFCHAIR = {'name': u'The IRTF Chair', 'address': u'irtf-chair@irtf.org'}
def get_all_sdo_managers():
return [i.person for i in LiaisonManagers.objects.all().distinct()]
IESGANDIAB = {'name': u'The IESG and IAB', 'address': u'iesg-iab@ietf.org'}
class FakePerson(object):
@ -27,7 +57,12 @@ class FakePerson(object):
def email(self):
return (self.name, self.address)
def all_sdo_managers():
return [proxy_personify_role(r) for r in Role.objects.filter(group__type="sdo", name="liaiman").select_related("person").distinct()]
def role_persons_with_fixed_email(group, role_name):
return [proxy_personify_role(r) for r in Role.objects.filter(group=group, name=role_name).select_related("person").distinct()]
class Entity(object):
poc = []
@ -84,34 +119,11 @@ class IETFEntity(Entity):
return [self.poc]
def full_user_list(self):
result = get_all_sdo_managers()
result = all_sdo_managers()
result.append(get_ietf_chair())
return result
class IRTFEntity(Entity):
poc = FakePerson(**IRTFCHAIR)
def get_from_cc(self, person):
result = []
if not is_irtfchair(person):
result.append(self.poc)
return result
def needs_approval(self, person=None):
if is_irtfchair(person):
return False
return True
def can_approve(self):
return [self.poc]
def full_user_list(self):
result.append(get_irtf_chair())
return result
class IABEntity(Entity):
chair = FakePerson(**IABCHAIR)
director = FakePerson(**IABEXECUTIVEDIRECTOR)
@ -136,27 +148,75 @@ class IABEntity(Entity):
return [self.chair]
def full_user_list(self):
result = get_all_sdo_managers()
result = all_sdo_managers()
result += [get_iab_chair(), get_iab_executive_director()]
return result
class IRTFEntity(Entity):
chair = FakePerson(**IRTFCHAIR)
poc = [chair,]
def get_from_cc(self, person):
result = []
return result
def needs_approval(self, person=None):
if is_irtfchair(person):
return False
return True
def can_approve(self):
return [self.chair]
def full_user_list(self):
result = [get_irtf_chair()]
return result
class IAB_IESG_Entity(Entity):
poc = [IABEntity.chair, IABEntity.director, FakePerson(**IETFCHAIR), FakePerson(**IESGANDIAB), ]
cc = [FakePerson(**IAB), FakePerson(**IESG), FakePerson(**IESGANDIAB)]
def __init__(self, name, obj=None):
self.name = name
self.obj = obj
self.iab = IABEntity(name, obj)
self.iesg = IETFEntity(name, obj)
def get_from_cc(self, person):
return list(set(self.iab.get_from_cc(person) + self.iesg.get_from_cc(person)))
def needs_approval(self, person=None):
if not self.iab.needs_approval(person):
return False
if not self.iesg.needs_approval(person):
return False
return True
def can_approve(self):
return list(set(self.iab.can_approve() + self.iesg.can_approve()))
def full_user_list(self):
return [get_ietf_chair(), get_iab_chair(), get_iab_executive_director()]
class AreaEntity(Entity):
def get_poc(self):
return [i.person for i in self.obj.areadirector_set.all()]
return role_persons_with_fixed_email(self.obj, "ad")
def get_cc(self, person=None):
return [FakePerson(**IETFCHAIR)]
def get_from_cc(self, person):
result = [i.person for i in self.obj.areadirector_set.all() if i.person!=person]
result = [p for p in role_persons_with_fixed_email(self.obj, "ad") if p != person]
result.append(FakePerson(**IETFCHAIR))
return result
def needs_approval(self, person=None):
# Check if person is an area director
if self.obj.areadirector_set.filter(person=person):
if self.obj.role_set.filter(person=person, name="ad"):
return False
return True
@ -164,7 +224,7 @@ class AreaEntity(Entity):
return self.get_poc()
def full_user_list(self):
result = get_all_sdo_managers()
result = all_sdo_managers()
result += self.get_poc()
return result
@ -172,34 +232,37 @@ class AreaEntity(Entity):
class WGEntity(Entity):
def get_poc(self):
return [i.person for i in self.obj.wgchair_set.all()]
return role_persons_with_fixed_email(self.obj, "chair")
def get_cc(self, person=None):
result = [i.person for i in self.obj.area_directors()]
if self.obj.email_address:
result.append(FakePerson(name ='%s Discussion List' % self.obj.group_acronym.name,
address = self.obj.email_address))
if self.obj.parent:
result = [p for p in role_persons_with_fixed_email(self.obj.parent, "ad") if p != person]
else:
result = []
if self.obj.list_subscribe:
result.append(FakePerson(name ='%s Discussion List' % self.obj.name,
address = self.obj.list_subscribe))
return result
def get_from_cc(self, person):
result = [i.person for i in self.obj.wgchair_set.all() if i.person!=person]
result += [i.person for i in self.obj.area_directors()]
if self.obj.email_address:
result.append(FakePerson(name ='%s Discussion List' % self.obj.group_acronym.name,
address = self.obj.email_address))
result = [p for p in role_persons_with_fixed_email(self.obj, "chair") if p != person]
result += role_persons_with_fixed_email(self.obj.parent, "ad") if self.obj.parent else []
if self.obj.list_subscribe:
result.append(FakePerson(name ='%s Discussion List' % self.obj.name,
address = self.obj.list_subscribe))
return result
def needs_approval(self, person=None):
# Check if person is director of this wg area
if self.obj.area.area.areadirector_set.filter(person=person):
if self.obj.parent and self.obj.parent.role_set.filter(person=person, name="ad"):
return False
return True
def can_approve(self):
return [i.person for i in self.obj.area.area.areadirector_set.all()]
return role_persons_with_fixed_email(self.obj.parent, "ad") if self.obj.parent else []
def full_user_list(self):
result = get_all_sdo_managers()
result = all_sdo_managers()
result += self.get_poc()
return result
@ -210,25 +273,19 @@ class SDOEntity(Entity):
return []
def get_cc(self, person=None):
manager = self.obj.liaisonmanager()
if manager:
return [manager.person]
return []
return role_persons_with_fixed_email(self.obj, "liaiman")
def get_from_cc(self, person=None):
manager = self.obj.liaisonmanager()
if manager and manager.person!=person:
return [manager.person]
return []
return [p for p in role_persons_with_fixed_email(self.obj, "liaiman") if p != person]
def post_only(self, person, user):
if is_secretariat(user) or person.sdoauthorizedindividual_set.filter(sdo=self.obj):
if is_secretariat(user) or self.obj.role_set.filter(person=person, name="auth"):
return False
return True
def full_user_list(self):
result = [i.person for i in self.obj.liaisonmanagers_set.all().distinct()]
result += [i.person for i in self.obj.sdoauthorizedindividual_set.all().distinct()]
result = role_persons_with_fixed_email(self.obj, "liaiman")
result += role_persons_with_fixed_email(self.obj, "auth")
return result
@ -314,10 +371,35 @@ class IRTFEntityManager(EntityManager):
return []
class IAB_IESG_EntityManager(EntityManager):
def __init__(self, *args, **kwargs):
super(IAB_IESG_EntityManager, self).__init__(*args, **kwargs)
self.entity = IAB_IESG_Entity(name=self.name)
def get_entity(self, pk=None):
return self.entity
def can_send_on_behalf(self, person):
if (is_iabchair(person) or
is_iab_executive_director(person) or
is_ietfchair(person)):
return self.get_managed_list()
return []
def can_approve_list(self, person):
if (is_iabchair(person) or
is_iab_executive_director(person) or
is_ietfchair(person)):
return self.get_managed_list()
return []
class AreaEntityManager(EntityManager):
def __init__(self, pk=None, name=None, queryset=None):
super(AreaEntityManager, self).__init__(pk, name, queryset)
from ietf.group.proxy import Area
if self.queryset == None:
self.queryset = Area.active_areas()
@ -336,11 +418,11 @@ class AreaEntityManager(EntityManager):
return AreaEntity(name=obj.area_acronym.name, obj=obj)
def can_send_on_behalf(self, person):
query_filter = {'areadirector__in': person.areadirector_set.all()}
query_filter = dict(role__person=person, role__name="ad")
return self.get_managed_list(query_filter)
def can_approve_list(self, person):
query_filter = {'areadirector__in': person.areadirector_set.all()}
query_filter = dict(role__person=person, role__name="ad")
return self.get_managed_list(query_filter)
@ -349,6 +431,7 @@ class WGEntityManager(EntityManager):
def __init__(self, pk=None, name=None, queryset=None):
super(WGEntityManager, self).__init__(pk, name, queryset)
if self.queryset == None:
from ietf.group.proxy import IETFWG, Area
self.queryset = IETFWG.objects.filter(group_type=1, status=IETFWG.ACTIVE, areagroup__area__status=Area.ACTIVE)
def get_managed_list(self, query_filter=None):
@ -366,13 +449,12 @@ class WGEntityManager(EntityManager):
return WGEntity(name=obj.group_acronym.name, obj=obj)
def can_send_on_behalf(self, person):
wgs = set([i.group_acronym.pk for i in person.wgchair_set.all()])
wgs = wgs.union([i.group_acronym.pk for i in person.wgsecretary_set.all()])
wgs = Group.objects.filter(role__person=person, role__name__in=("chair", "secretary")).values_list('pk', flat=True)
query_filter = {'pk__in': wgs}
return self.get_managed_list(query_filter)
def can_approve_list(self, person):
query_filter = {'areagroup__area__areadirector__in': person.areadirector_set.all()}
query_filter = dict(parent__role__person=person, parent__role__name="ad")
return self.get_managed_list(query_filter)
@ -381,10 +463,10 @@ class SDOEntityManager(EntityManager):
def __init__(self, pk=None, name=None, queryset=None):
super(SDOEntityManager, self).__init__(pk, name, queryset)
if self.queryset == None:
self.queryset = SDOs.objects.all()
self.queryset = Group.objects.filter(type="sdo")
def get_managed_list(self):
return [(u'%s_%s' % (self.pk, i.pk), i.sdo_name) for i in self.queryset.order_by('sdo_name')]
return [(u'%s_%s' % (self.pk, i.pk), i.name) for i in self.queryset.order_by('name')]
def get_entity(self, pk=None):
if not pk:
@ -393,7 +475,7 @@ class SDOEntityManager(EntityManager):
obj = self.queryset.get(pk=pk)
except self.queryset.model.DoesNotExist:
return None
return SDOEntity(name=obj.sdo_name, obj=obj)
return SDOEntity(name=obj.name, obj=obj)
class IETFHierarchyManager(object):
@ -402,7 +484,7 @@ class IETFHierarchyManager(object):
self.managers = {'ietf': IETFEntityManager(pk='ietf', name=u'The IETF'),
'iesg': IETFEntityManager(pk='iesg', name=u'The IESG'),
'iab': IABEntityManager(pk='iab', name=u'The IAB'),
'irtf': IRTFEntityManager(pk='irtf', name=u'The IAB'),
'iabiesg': IAB_IESG_EntityManager(pk='iabiesg', name=u'The IESG and the IAB'),
'area': AreaEntityManager(pk='area', name=u'IETF Areas'),
'wg': WGEntityManager(pk='wg', name=u'IETF Working Groups'),
'sdo': SDOEntityManager(pk='sdo', name=u'Standards Development Organizations'),
@ -430,7 +512,7 @@ class IETFHierarchyManager(object):
def get_all_incoming_entities(self):
entities = []
results = []
for key in ['ietf', 'iesg', 'iab']:
for key in ['ietf', 'iesg', 'iab', 'iabiesg']:
results += self.managers[key].get_managed_list()
entities.append(('Main IETF Entities', results))
entities.append(('IETF Areas', self.managers['area'].get_managed_list()))
@ -445,7 +527,7 @@ class IETFHierarchyManager(object):
def get_entities_for_person(self, person):
entities = []
results = []
for key in ['ietf', 'iesg', 'iab']:
for key in ['ietf', 'iesg', 'iab', 'iabiesg']:
results += self.managers[key].can_send_on_behalf(person)
if results:
entities.append(('Main IETF Entities', results))
@ -459,7 +541,7 @@ class IETFHierarchyManager(object):
def get_all_can_approve_codes(self, person):
entities = []
for key in ['ietf', 'iesg', 'iab']:
for key in ['ietf', 'iesg', 'iab', 'iabiesg']:
entities += self.managers[key].can_approve_list(person)
entities += self.managers['area'].can_approve_list(person)
entities += self.managers['wg'].can_approve_list(person)
@ -467,6 +549,3 @@ class IETFHierarchyManager(object):
IETFHM = IETFHierarchyManager()
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from utilsREDESIGN import *

View file

@ -1,524 +0,0 @@
from ietf.group.models import Group, Role
from ietf.person.models import Person
from ietf.utils.proxy import proxy_personify_role
from ietf.liaisons.accounts import (is_ietfchair, is_iabchair, is_iab_executive_director,
get_ietf_chair, get_iab_chair, get_iab_executive_director,
is_secretariat)
IETFCHAIR = {'name': u'The IETF Chair', 'address': u'chair@ietf.org'}
IESG = {'name': u'The IESG', 'address': u'iesg@ietf.org'}
IAB = {'name': u'The IAB', 'address': u'iab@iab.org'}
IABCHAIR = {'name': u'The IAB Chair', 'address': u'iab-chair@iab.org'}
IABEXECUTIVEDIRECTOR = {'name': u'The IAB Executive Director', 'address': u'execd@iab.org'}
IRTFCHAIR = {'name': u'The IRTF Chair', 'address': u'irtf-chair@irtf.org'}
IESGANDIAB = {'name': u'The IESG and IAB', 'address': u'iesg-iab@ietf.org'}
class FakePerson(object):
def __init__(self, name, address):
self.name = name
self.address = address
def email(self):
return (self.name, self.address)
# the following is a biggish object hierarchy abstracting the entity
# names and auth rules for posting liaison statements in a sort of
# semi-declarational (and perhaps overengineered given the revamped
# schema) way - unfortunately, it's never been strong enough to do so
# fine-grained enough so the form code also has some rules
def all_sdo_managers():
return [proxy_personify_role(r) for r in Role.objects.filter(group__type="sdo", name="liaiman").select_related("person").distinct()]
def role_persons_with_fixed_email(group, role_name):
return [proxy_personify_role(r) for r in Role.objects.filter(group=group, name=role_name).select_related("person").distinct()]
class Entity(object):
poc = []
cc = []
def __init__(self, name, obj=None):
self.name = name
self.obj = obj
def get_poc(self):
if not isinstance(self.poc, list):
return [self.poc]
return self.poc
def get_cc(self, person=None):
if not isinstance(self.cc, list):
return [self.cc]
return self.cc
def get_from_cc(self, person=None):
return []
def needs_approval(self, person=None):
return False
def can_approve(self):
return []
def post_only(self, person, user):
return False
def full_user_list(self):
return False
class IETFEntity(Entity):
poc = FakePerson(**IETFCHAIR)
cc = FakePerson(**IESG)
def get_from_cc(self, person):
result = []
if not is_ietfchair(person):
result.append(self.poc)
result.append(self.cc)
return result
def needs_approval(self, person=None):
if is_ietfchair(person):
return False
return True
def can_approve(self):
return [self.poc]
def full_user_list(self):
result = all_sdo_managers()
result.append(get_ietf_chair())
return result
class IABEntity(Entity):
chair = FakePerson(**IABCHAIR)
director = FakePerson(**IABEXECUTIVEDIRECTOR)
poc = [chair, director]
cc = FakePerson(**IAB)
def get_from_cc(self, person):
result = []
if not is_iabchair(person):
result.append(self.chair)
result.append(self.cc)
if not is_iab_executive_director(person):
result.append(self.director)
return result
def needs_approval(self, person=None):
if is_iabchair(person) or is_iab_executive_director(person):
return False
return True
def can_approve(self):
return [self.chair]
def full_user_list(self):
result = all_sdo_managers()
result += [get_iab_chair(), get_iab_executive_director()]
return result
class IRTFEntity(Entity):
chair = FakePerson(**IRTFCHAIR)
poc = [chair,]
def get_from_cc(self, person):
result = []
return result
def needs_approval(self, person=None):
if is_irtfchair(person):
return False
return True
def can_approve(self):
return [self.chair]
def full_user_list(self):
result = [get_irtf_chair()]
return result
class IAB_IESG_Entity(Entity):
poc = [IABEntity.chair, IABEntity.director, FakePerson(**IETFCHAIR), FakePerson(**IESGANDIAB), ]
cc = [FakePerson(**IAB), FakePerson(**IESG), FakePerson(**IESGANDIAB)]
def __init__(self, name, obj=None):
self.name = name
self.obj = obj
self.iab = IABEntity(name, obj)
self.iesg = IETFEntity(name, obj)
def get_from_cc(self, person):
return list(set(self.iab.get_from_cc(person) + self.iesg.get_from_cc(person)))
def needs_approval(self, person=None):
if not self.iab.needs_approval(person):
return False
if not self.iesg.needs_approval(person):
return False
return True
def can_approve(self):
return list(set(self.iab.can_approve() + self.iesg.can_approve()))
def full_user_list(self):
return [get_ietf_chair(), get_iab_chair(), get_iab_executive_director()]
class AreaEntity(Entity):
def get_poc(self):
return role_persons_with_fixed_email(self.obj, "ad")
def get_cc(self, person=None):
return [FakePerson(**IETFCHAIR)]
def get_from_cc(self, person):
result = [p for p in role_persons_with_fixed_email(self.obj, "ad") if p != person]
result.append(FakePerson(**IETFCHAIR))
return result
def needs_approval(self, person=None):
# Check if person is an area director
if self.obj.role_set.filter(person=person, name="ad"):
return False
return True
def can_approve(self):
return self.get_poc()
def full_user_list(self):
result = all_sdo_managers()
result += self.get_poc()
return result
class WGEntity(Entity):
def get_poc(self):
return role_persons_with_fixed_email(self.obj, "chair")
def get_cc(self, person=None):
if self.obj.parent:
result = [p for p in role_persons_with_fixed_email(self.obj.parent, "ad") if p != person]
else:
result = []
if self.obj.list_subscribe:
result.append(FakePerson(name ='%s Discussion List' % self.obj.name,
address = self.obj.list_subscribe))
return result
def get_from_cc(self, person):
result = [p for p in role_persons_with_fixed_email(self.obj, "chair") if p != person]
result += role_persons_with_fixed_email(self.obj.parent, "ad") if self.obj.parent else []
if self.obj.list_subscribe:
result.append(FakePerson(name ='%s Discussion List' % self.obj.name,
address = self.obj.list_subscribe))
return result
def needs_approval(self, person=None):
# Check if person is director of this wg area
if self.obj.parent and self.obj.parent.role_set.filter(person=person, name="ad"):
return False
return True
def can_approve(self):
return role_persons_with_fixed_email(self.obj.parent, "ad") if self.obj.parent else []
def full_user_list(self):
result = all_sdo_managers()
result += self.get_poc()
return result
class SDOEntity(Entity):
def get_poc(self):
return []
def get_cc(self, person=None):
return role_persons_with_fixed_email(self.obj, "liaiman")
def get_from_cc(self, person=None):
return [p for p in role_persons_with_fixed_email(self.obj, "liaiman") if p != person]
def post_only(self, person, user):
if is_secretariat(user) or self.obj.role_set.filter(person=person, name="auth"):
return False
return True
def full_user_list(self):
result = role_persons_with_fixed_email(self.obj, "liaiman")
result += role_persons_with_fixed_email(self.obj, "auth")
return result
class EntityManager(object):
def __init__(self, pk=None, name=None, queryset=None):
self.pk = pk
self.name = name
self.queryset = queryset
def get_entity(self, pk=None):
return Entity(name=self.name)
def get_managed_list(self):
return [(self.pk, self.name)]
def can_send_on_behalf(self, person):
return []
def can_approve_list(self, person):
return []
class IETFEntityManager(EntityManager):
def __init__(self, *args, **kwargs):
super(IETFEntityManager, self).__init__(*args, **kwargs)
self.entity = IETFEntity(name=self.name)
def get_entity(self, pk=None):
return self.entity
def can_send_on_behalf(self, person):
if is_ietfchair(person):
return self.get_managed_list()
return []
def can_approve_list(self, person):
if is_ietfchair(person):
return self.get_managed_list()
return []
class IABEntityManager(EntityManager):
def __init__(self, *args, **kwargs):
super(IABEntityManager, self).__init__(*args, **kwargs)
self.entity = IABEntity(name=self.name)
def get_entity(self, pk=None):
return self.entity
def can_send_on_behalf(self, person):
if (is_iabchair(person) or
is_iab_executive_director(person)):
return self.get_managed_list()
return []
def can_approve_list(self, person):
if (is_iabchair(person) or
is_iab_executive_director(person)):
return self.get_managed_list()
return []
class IRTFEntityManager(EntityManager):
def __init__(self, *args, **kwargs):
super(IRTFEntityManager, self).__init__(*args, **kwargs)
self.entity = IRTFEntity(name=self.name)
def get_entity(self, pk=None):
return self.entity
def can_send_on_behalf(self, person):
if is_irtfchair(person):
return self.get_managed_list()
return []
def can_approve_list(self, person):
if is_irtfchair(person):
return self.get_managed_list()
return []
class IAB_IESG_EntityManager(EntityManager):
def __init__(self, *args, **kwargs):
super(IAB_IESG_EntityManager, self).__init__(*args, **kwargs)
self.entity = IAB_IESG_Entity(name=self.name)
def get_entity(self, pk=None):
return self.entity
def can_send_on_behalf(self, person):
if (is_iabchair(person) or
is_iab_executive_director(person) or
is_ietfchair(person)):
return self.get_managed_list()
return []
def can_approve_list(self, person):
if (is_iabchair(person) or
is_iab_executive_director(person) or
is_ietfchair(person)):
return self.get_managed_list()
return []
class AreaEntityManager(EntityManager):
def __init__(self, pk=None, name=None, queryset=None):
super(AreaEntityManager, self).__init__(pk, name, queryset)
from ietf.group.proxy import Area
if self.queryset == None:
self.queryset = Area.active_areas()
def get_managed_list(self, query_filter=None):
if not query_filter:
query_filter = {}
return [(u'%s_%s' % (self.pk, i.pk), i.area_acronym.name) for i in self.queryset.filter(**query_filter).order_by('area_acronym__name')]
def get_entity(self, pk=None):
if not pk:
return None
try:
obj = self.queryset.get(pk=pk)
except self.queryset.model.DoesNotExist:
return None
return AreaEntity(name=obj.area_acronym.name, obj=obj)
def can_send_on_behalf(self, person):
query_filter = dict(role__person=person, role__name="ad")
return self.get_managed_list(query_filter)
def can_approve_list(self, person):
query_filter = dict(role__person=person, role__name="ad")
return self.get_managed_list(query_filter)
class WGEntityManager(EntityManager):
def __init__(self, pk=None, name=None, queryset=None):
super(WGEntityManager, self).__init__(pk, name, queryset)
if self.queryset == None:
from ietf.group.proxy import IETFWG, Area
self.queryset = IETFWG.objects.filter(group_type=1, status=IETFWG.ACTIVE, areagroup__area__status=Area.ACTIVE)
def get_managed_list(self, query_filter=None):
if not query_filter:
query_filter = {}
return [(u'%s_%s' % (self.pk, i.pk), '%s - %s' % (i.group_acronym.acronym, i.group_acronym.name)) for i in self.queryset.filter(**query_filter).order_by('group_acronym__acronym')]
def get_entity(self, pk=None):
if not pk:
return None
try:
obj = self.queryset.get(pk=pk)
except self.queryset.model.DoesNotExist:
return None
return WGEntity(name=obj.group_acronym.name, obj=obj)
def can_send_on_behalf(self, person):
wgs = Group.objects.filter(role__person=person, role__name__in=("chair", "secretary")).values_list('pk', flat=True)
query_filter = {'pk__in': wgs}
return self.get_managed_list(query_filter)
def can_approve_list(self, person):
query_filter = dict(parent__role__person=person, parent__role__name="ad")
return self.get_managed_list(query_filter)
class SDOEntityManager(EntityManager):
def __init__(self, pk=None, name=None, queryset=None):
super(SDOEntityManager, self).__init__(pk, name, queryset)
if self.queryset == None:
self.queryset = Group.objects.filter(type="sdo")
def get_managed_list(self):
return [(u'%s_%s' % (self.pk, i.pk), i.name) for i in self.queryset.order_by('name')]
def get_entity(self, pk=None):
if not pk:
return None
try:
obj = self.queryset.get(pk=pk)
except self.queryset.model.DoesNotExist:
return None
return SDOEntity(name=obj.name, obj=obj)
class IETFHierarchyManager(object):
def __init__(self):
self.managers = {'ietf': IETFEntityManager(pk='ietf', name=u'The IETF'),
'iesg': IETFEntityManager(pk='iesg', name=u'The IESG'),
'iab': IABEntityManager(pk='iab', name=u'The IAB'),
'iabiesg': IAB_IESG_EntityManager(pk='iabiesg', name=u'The IESG and the IAB'),
'area': AreaEntityManager(pk='area', name=u'IETF Areas'),
'wg': WGEntityManager(pk='wg', name=u'IETF Working Groups'),
'sdo': SDOEntityManager(pk='sdo', name=u'Standards Development Organizations'),
'othersdo': EntityManager(pk='othersdo', name=u'Other SDOs'),
}
def get_entity_by_key(self, entity_id):
if not entity_id:
return None
id_list = entity_id.split('_', 1)
key = id_list[0]
pk = None
if len(id_list)==2:
pk = id_list[1]
if key not in self.managers.keys():
return None
return self.managers[key].get_entity(pk)
def get_all_entities(self):
entities = []
for manager in self.managers.values():
entities += manager.get_managed_list()
return entities
def get_all_incoming_entities(self):
entities = []
results = []
for key in ['ietf', 'iesg', 'iab', 'iabiesg']:
results += self.managers[key].get_managed_list()
entities.append(('Main IETF Entities', results))
entities.append(('IETF Areas', self.managers['area'].get_managed_list()))
entities.append(('IETF Working Groups', self.managers['wg'].get_managed_list()))
return entities
def get_all_outgoing_entities(self):
entities = [(self.managers['sdo'].name, self.managers['sdo'].get_managed_list())]
entities += [(self.managers['othersdo'].name, self.managers['othersdo'].get_managed_list())]
return entities
def get_entities_for_person(self, person):
entities = []
results = []
for key in ['ietf', 'iesg', 'iab', 'iabiesg']:
results += self.managers[key].can_send_on_behalf(person)
if results:
entities.append(('Main IETF Entities', results))
areas = self.managers['area'].can_send_on_behalf(person)
if areas:
entities.append(('IETF Areas', areas))
wgs = self.managers['wg'].can_send_on_behalf(person)
if wgs:
entities.append(('IETF Working Groups', wgs))
return entities
def get_all_can_approve_codes(self, person):
entities = []
for key in ['ietf', 'iesg', 'iab', 'iabiesg']:
entities += self.managers[key].can_approve_list(person)
entities += self.managers['area'].can_approve_list(person)
entities += self.managers['wg'].can_approve_list(person)
return [i[0] for i in entities]
IETFHM = IETFHierarchyManager()

View file

@ -4,52 +4,49 @@ from email.utils import parseaddr
from django.conf import settings
from django.core.urlresolvers import reverse
from django.db.models import Q
from django.core.validators import email_re
from django.core.validators import validate_email, ValidationError
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden
from django.shortcuts import render_to_response, get_object_or_404
from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.template import RequestContext
from django.utils import simplejson
from django.views.generic.list_detail import object_list, object_detail
from ietf.liaisons.models import LiaisonStatement
from ietf.liaisons.accounts import (get_person_for_user, can_add_outgoing_liaison,
can_add_incoming_liaison, LIAISON_EDIT_GROUPS,
is_ietfchair, is_iabchair, is_iab_executive_director,
can_edit_liaison, is_secretariat)
from ietf.liaisons.decorators import can_submit_liaison
from ietf.liaisons.forms import liaison_form_factory
from ietf.liaisons.models import LiaisonDetail, OutgoingLiaisonApproval
from ietf.liaisons.utils import IETFHM
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.liaisons.proxy import LiaisonDetailProxy as LiaisonDetail
from ietf.liaisons.utils import IETFHM, can_submit_liaison_required, approvable_liaison_statements
from ietf.liaisons.mails import notify_pending_by_email, send_liaison_by_email
@can_submit_liaison
@can_submit_liaison_required
def add_liaison(request, liaison=None):
if request.method == 'POST':
form = liaison_form_factory(request, data=request.POST.copy(),
files = request.FILES, liaison=liaison)
if form.is_valid():
liaison = form.save()
if request.POST.get('send', None):
if liaison.is_pending():
liaison.notify_pending_by_email()
if request.POST.get('send', False):
if not liaison.approved:
notify_pending_by_email(request, liaison)
else:
liaison.send_by_email()
send_liaison_by_email(request, liaison)
return HttpResponseRedirect(reverse('liaison_list'))
else:
form = liaison_form_factory(request, liaison=liaison)
return render_to_response(
'liaisons/liaisondetail_edit.html',
'liaisons/edit.html',
{'form': form,
'liaison': liaison},
context_instance=RequestContext(request),
)
@can_submit_liaison
@can_submit_liaison_required
def get_info(request):
person = get_person_for_user(request.user)
@ -74,143 +71,84 @@ def get_info(request):
result.update({'error': '\n'.join([to_error, from_error])})
else:
result.update({'error': False,
'cc': [i.email() for i in to_entity.get_cc(person=person)] +\
[i.email() for i in from_entity.get_from_cc(person=person)],
'cc': ([i.email() for i in to_entity.get_cc(person=person)] +
[i.email() for i in from_entity.get_from_cc(person=person)]),
'poc': [i.email() for i in to_entity.get_poc()],
'needs_approval': from_entity.needs_approval(person=person),
'post_only': from_entity.post_only(person=person, user=request.user)})
if is_secretariat(request.user):
full_list = [(i.pk, i.email()) for i in set(from_entity.full_user_list())]
full_list.sort(lambda x,y: cmp(x[1], y[1]))
full_list.sort(key=lambda x: x[1])
full_list = [(person.pk, person.email())] + full_list
result.update({'full_list': full_list})
json_result = simplejson.dumps(result)
return HttpResponse(json_result, mimetype='text/javascript')
def normalize_sort(request):
sort = request.GET.get('sort', "")
if sort not in ('submitted', 'deadline', 'title', 'to_name', 'from_name'):
sort = "submitted"
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
def approvable_liaison_statements(group_codes):
# this is a bit complicated because IETFHM encodes the
# groups, it should just give us a list of ids or acronyms
group_acronyms = []
group_ids = []
for x in group_codes:
if "_" in x:
group_ids.append(x.split("_")[1])
else:
group_acronyms.append(x)
# reverse dates
order_by = "-" + sort if sort in ("submitted", "deadline") else sort
return LiaisonDetail.objects.filter(approved=None).filter(Q(from_group__acronym__in=group_acronyms) | Q (from_group__pk__in=group_ids))
return sort, order_by
def liaison_list(request):
user = request.user
can_send_outgoing = can_add_outgoing_liaison(user)
can_send_incoming = can_add_incoming_liaison(user)
can_approve = False
can_edit = False
sort, order_by = normalize_sort(request)
liaisons = LiaisonStatement.objects.exclude(approved=None).order_by(order_by)
person = get_person_for_user(request.user)
if person:
approval_codes = IETFHM.get_all_can_approve_codes(person)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
if is_secretariat(request.user):
can_approve = LiaisonDetail.objects.filter(approved=None).order_by("-submitted").count()
else:
can_approve = approvable_liaison_statements(approval_codes).count()
else:
can_approve = LiaisonDetail.objects.filter(approval__isnull=False, approval__approved=False, from_raw_code__in=approval_codes).count()
can_send_outgoing = can_add_outgoing_liaison(request.user)
can_send_incoming = can_add_incoming_liaison(request.user)
order = request.GET.get('order_by', 'submitted_date')
plain_order = order
reverse_order = order.startswith('-')
if reverse_order:
plain_order = order[1:]
if plain_order not in ('submitted_date', 'deadline_date', 'title', 'to_body', 'from_raw_body'):
order = 'submitted_date'
reverse_order = True
plain_order = 'submitted_date'
elif plain_order in ('submitted_date', 'deadline_date'):
# Reverse order for date fields, humans find it more natural
if reverse_order:
order = plain_order
else:
order = '-%s' % plain_order
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
public_liaisons = LiaisonDetail.objects.exclude(approved=None).order_by(order)
else:
public_liaisons = LiaisonDetail.objects.filter(Q(approval__isnull=True)|Q(approval__approved=True)).order_by(order)
approvable = approvable_liaison_statements(request.user).count()
return object_list(request, public_liaisons,
allow_empty=True,
template_name='liaisons/liaisondetail_list.html',
extra_context={'can_manage': can_approve or can_send_incoming or can_send_outgoing,
'can_approve': can_approve,
'can_edit': can_edit,
'can_send_incoming': can_send_incoming,
'can_send_outgoing': can_send_outgoing,
plain_order: not reverse_order and '-' or None},
)
return render_to_response('liaisons/overview.html', {
"liaisons": liaisons,
"can_manage": approvable or can_send_incoming or can_send_outgoing,
"approvable": approvable,
"can_send_incoming": can_send_incoming,
"can_send_outgoing": can_send_outgoing,
"sort": sort,
}, 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)
@can_submit_liaison
return render_to_response('liaisons/liaison_table.html', {
"liaisons": liaisons,
"sort": sort,
}, context_instance=RequestContext(request))
@can_submit_liaison_required
def liaison_approval_list(request):
if is_secretariat(request.user):
to_approve = LiaisonDetail.objects.filter(approved=None).order_by("-submitted")
else:
person = get_person_for_user(request.user)
approval_codes = IETFHM.get_all_can_approve_codes(person)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
to_approve = approvable_liaison_statements(approval_codes).order_by("-submitted")
else:
to_approve = LiaisonDetail.objects.filter(approval__isnull=False, approval__approved=False, from_raw_code__in=approval_codes).order_by("-submitted_date")
liaisons = approvable_liaison_statements(request.user).order_by("-submitted")
return object_list(request, to_approve,
allow_empty=True,
template_name='liaisons/liaisondetail_approval_list.html',
)
return render_to_response('liaisons/approval_list.html', {
"liaisons": liaisons,
}, context_instance=RequestContext(request))
@can_submit_liaison
@can_submit_liaison_required
def liaison_approval_detail(request, object_id):
if is_secretariat(request.user):
to_approve = LiaisonDetail.objects.filter(approved=None).order_by("-submitted")
else:
person = get_person_for_user(request.user)
approval_codes = IETFHM.get_all_can_approve_codes(person)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
to_approve = approvable_liaison_statements(approval_codes).order_by("-submitted")
else:
to_approve = LiaisonDetail.objects.filter(approval__isnull=False, approval__approved=False, from_raw_code__in=approval_codes).order_by("-submitted_date")
liaison = get_object_or_404(approvable_liaison_statements(request.user), pk=object_id)
if request.method=='POST' and request.POST.get('do_approval', False):
try:
liaison = to_approve.get(pk=object_id)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
liaison.approved = datetime.datetime.now()
liaison.save()
else:
approval = liaison.approval
if not approval:
approval = OutgoingLiaisonApproval.objects.create(approved=True, approval_date=datetime.datetime.now())
liaison.approval = approval
liaison.save()
else:
approval.approved=True
approval.save()
liaison.send_by_email()
except LiaisonDetail.DoesNotExist:
pass
return HttpResponseRedirect(reverse('liaison_list'))
return object_detail(request,
to_approve,
object_id=object_id,
template_name='liaisons/liaisondetail_approval_detail.html',
)
liaison.approved = datetime.datetime.now()
liaison.save()
send_liaison_by_email(request, liaison)
return redirect('liaison_list')
return render_to_response('liaisons/approval_detail.html', {
"liaison": liaison,
}, context_instance=RequestContext(request))
def _can_take_care(liaison, user):
if not liaison.deadline_date or liaison.action_taken:
if not liaison.deadline or liaison.action_taken:
return False
if user.is_authenticated():
@ -224,15 +162,18 @@ def _can_take_care(liaison, user):
def _find_person_in_emails(liaison, person):
if not person:
return False
emails = ','.join([ e for e in [liaison.cc1, liaison.cc2, liaison.to_email,
liaison.to_poc, liaison.submitter_email,
liaison.replyto, liaison.response_contact,
liaison.technical_contact] if e ])
emails = ','.join(e for e in [liaison.cc, liaison.to_contact, liaison.to_name,
liaison.reply_to, liaison.response_contact,
liaison.technical_contact] if e)
for email in emails.split(','):
name, addr = parseaddr(email)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
person.emailaddress_set = person.email_set
if email_re.search(addr) and person.emailaddress_set.filter(address=addr):
try:
validate_email(addr)
except ValidationError:
continue
if person.email_set.filter(address=addr):
return True
elif addr in ('chair@ietf.org', 'iesg@ietf.org') and is_ietfchair(person):
return True
@ -240,67 +181,31 @@ def _find_person_in_emails(liaison, person):
return True
elif addr in ('execd@iab.org', ) and is_iab_executive_director(person):
return True
return False
def liaison_detail(request, object_id):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
qfilter = Q()
public_liaisons = LiaisonDetail.objects.exclude(approved=None).order_by("-submitted_date")
else:
qfilter = Q(approval__isnull=True)|Q(approval__approved=True)
public_liaisons = LiaisonDetail.objects.filter(qfilter).order_by("-submitted_date")
liaison = get_object_or_404(public_liaisons, pk=object_id)
can_edit = False
user = request.user
can_take_care = _can_take_care(liaison, user)
if user.is_authenticated() and can_edit_liaison(user, liaison):
can_edit = True
liaison = get_object_or_404(LiaisonStatement.objects.exclude(approved=None), pk=object_id)
can_edit = request.user.is_authenticated() and can_edit_liaison(request.user, liaison)
can_take_care = _can_take_care(liaison, request.user)
if request.method == 'POST' and request.POST.get('do_action_taken', None) and can_take_care:
liaison.action_taken = True
liaison.save()
can_take_care = False
relations = liaison.liaisondetail_set.filter(qfilter)
return object_detail(request,
public_liaisons,
template_name="liaisons/liaisondetail_detail.html",
object_id=object_id,
extra_context = {'can_edit': can_edit,
'relations': relations,
'can_take_care': can_take_care}
)
relations = liaison.liaisonstatement_set.all()
return render_to_response("liaisons/detail.html", {
"liaison": liaison,
"can_edit": can_edit,
"can_take_care": can_take_care,
"relations": relations,
}, context_instance=RequestContext(request))
def liaison_edit(request, object_id):
liaison = get_object_or_404(LiaisonDetail, pk=object_id)
user = request.user
if not (user.is_authenticated() and can_edit_liaison(user, liaison)):
return HttpResponseForbidden('You have no permission to edit this liaison')
liaison = get_object_or_404(LiaisonStatement, pk=object_id)
if not (request.user.is_authenticated() and can_edit_liaison(request.user, liaison)):
return HttpResponseForbidden('You do not have permission to edit this liaison statement')
return add_liaison(request, liaison=liaison)
def ajax_liaison_list(request):
order = request.GET.get('order_by', 'submitted_date')
plain_order = order
reverse_order = order.startswith('-')
if reverse_order:
plain_order = order[1:]
if plain_order not in ('submitted_date', 'deadline_date', 'title', 'to_body', 'from_raw_body'):
order = 'submitted_date'
reverse_order = True
plain_order = 'submitted_date'
elif plain_order in ('submitted_date', 'deadline_date'):
# Reverse order for date fields, humans find it more natural
if reverse_order:
order = plain_order
else:
order = '-%s' % plain_order
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
public_liaisons = LiaisonDetail.objects.exclude(approved=None).order_by(order)
else:
public_liaisons = LiaisonDetail.objects.filter(Q(approval__isnull=True)|Q(approval__approved=True)).order_by(order)
return object_list(request, public_liaisons,
allow_empty=True,
template_name='liaisons/liaisondetail_simple_list.html',
extra_context={plain_order: not reverse_order and '-' or None}
)

View file

@ -1,8 +1,11 @@
from django.conf import settings
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse as urlreverse
from django.db.models.query import QuerySet
from django.forms.widgets import Select, Widget, TextInput
from django.utils.safestring import mark_safe
from django.utils.html import conditional_escape
from ietf.liaisons.models import LiaisonStatement
class FromWidget(Select):
@ -14,9 +17,9 @@ class FromWidget(Select):
def render(self, name, value, attrs=None, choices=()):
all_choices = list(self.choices) + list(choices)
if len(all_choices)!=1 or \
(isinstance(all_choices[0][1], (list, tuple)) and \
len(all_choices[0][1])!=1):
if (len(all_choices) != 1 or
(isinstance(all_choices[0][1], (list, tuple)) and
len(all_choices[0][1]) != 1)):
base = super(FromWidget, self).render(name, value, attrs, choices)
else:
option = all_choices[0]
@ -24,14 +27,14 @@ class FromWidget(Select):
option = option[1][0]
value = option[0]
text = option[1]
base = u'<input type="hidden" value="%s" id="id_%s" name="%s" />%s' % (value, name, name, text)
base += u' (<a class="from_mailto" href="">' + self.submitter + u'</a>)'
base = u'<input type="hidden" value="%s" id="id_%s" name="%s" />%s' % (conditional_escape(value), conditional_escape(name), conditional_escape(name), conditional_escape(text))
base += u' (<a class="from_mailto" href="">' + conditional_escape(self.submitter) + u'</a>)'
if self.full_power_on:
base += '<div style="display: none;" class="reducedToOptions">'
for from_code in self.full_power_on:
base += '<span class="full_power_on_%s"></span>' % from_code
base += '<span class="full_power_on_%s"></span>' % conditional_escape(from_code)
for to_code in self.reduced_to_set:
base += '<span class="reduced_to_set_%s"></span>' % to_code
base += '<span class="reduced_to_set_%s"></span>' % conditional_escape(to_code)
base += '</div>'
return mark_safe(base)
@ -39,7 +42,7 @@ class FromWidget(Select):
class ReadOnlyWidget(Widget):
def render(self, name, value, attrs=None):
html = u'<div id="id_%s">%s</div>' % (name, value or '')
html = u'<div id="id_%s">%s</div>' % (conditional_escape(name), conditional_escape(value or ''))
return mark_safe(html)
@ -53,14 +56,14 @@ class ButtonWidget(Widget):
super(ButtonWidget, self).__init__(*args, **kwargs)
def render(self, name, value, attrs=None):
html = u'<span style="display: none" class="showAttachsOn">%s</span>' % self.show_on
html += u'<span style="display: none" class="attachEnabledLabel">%s</span>' % self.label
html = u'<span style="display: none" class="showAttachsOn">%s</span>' % conditional_escape(self.show_on)
html += u'<span style="display: none" class="attachEnabledLabel">%s</span>' % conditional_escape(self.label)
if self.require:
for i in self.require:
html += u'<span style="display: none" class="attachRequiredField">%s</span>' % i
required_str = u'Please fill %s to attach a new file' % self.required_label
html += u'<span style="display: none" class="attachDisabledLabel">%s</span>' % required_str
html += u'<input type="button" class="addAttachmentWidget" value="%s" />' % self.label
html += u'<span style="display: none" class="attachRequiredField">%s</span>' % conditional_escape(i)
required_str = u'Please fill in %s to attach a new file' % conditional_escape(self.required_label)
html += u'<span style="display: none" class="attachDisabledLabel">%s</span>' % conditional_escape(required_str)
html += u'<input type="button" class="addAttachmentWidget" value="%s" />' % conditional_escape(self.label)
return mark_safe(html)
@ -71,8 +74,8 @@ class ShowAttachmentsWidget(Widget):
html += u'<span style="display: none" class="showAttachmentsEmpty">No files attached</span>'
html += u'<div class="attachedFiles">'
if value and isinstance(value, QuerySet):
for attach in value:
html += u'<a class="initialAttach" href="%sfile%s%s">%s</a><br />' % (settings.LIAISON_ATTACH_URL, attach.file_id, attach.file_extension, attach.file_title, )
for attachment in value:
html += u'<a class="initialAttach" href="%s%s">%s</a><br />' % (settings.LIAISON_ATTACH_URL, conditional_escape(attachment.external_url), conditional_escape(attachment.title))
else:
html += u'No files attached'
html += u'</div></div>'
@ -88,23 +91,21 @@ class RelatedLiaisonWidget(TextInput):
noliaison = 'inline'
deselect = 'none'
else:
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.liaisons.proxy import LiaisonDetailProxy as LiaisonDetail
liaison = LiaisonDetail.objects.get(pk=value)
liaison = LiaisonStatement.objects.get(pk=value)
title = liaison.title
if not title:
files = liaison.uploads_set.all()
if files:
title = files[0].file_title
attachments = liaison.attachments.all()
if attachments:
title = attachments[0].title
else:
title = 'Liaison #%s' % liaison.pk
noliaison = 'none'
deselect = 'inline'
html = u'<span class="noRelated" style="display: %s;">No liaison selected</span>' % noliaison
html += u'<span class="relatedLiaisonWidgetTitle">%s</span>' % title
html += u'<input type="hidden" name="%s" class="relatedLiaisonWidgetValue" value="%s" /> ' % (name, value)
html += u'<span style="display: none;" class="listURL">%s</span> ' % reverse('ajax_liaison_list')
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'<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" /> ' % name
html += '<input type="button" style="display: %s;" id="id_no_%s" value="Deselect liaison" />' % (deselect, name)
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))
return mark_safe(html)

View file

@ -0,0 +1,10 @@
{% extends "liaisons/detail.html" %}
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% block content %}
{{ block.super }}
<form method="post">
<input type="submit" value="Approve" name='do_approval' />
</form>
{% endblock %}

View file

@ -1,4 +1,4 @@
{% extends "liaisons/liaisondetail_list.html" %}
{% extends "liaisons/overview.html" %}
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% block title %}Pending Liaison Statements{% endblock %}

View file

@ -0,0 +1,120 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% load ietf_filters %}
{% block title %}Liaison Statement: {% include 'liaisons/liaison_title.html' %}{% endblock %}
{% block pagehead %}
<link rel="stylesheet" type="text/css" href="/css/liaisons.css"></link>
<meta name="description" content="Liaison Statement from {{ liaison.from_name }}{% if liaison.from_contact %} to {{ liaison.to_name }}{% endif %} ({{ liaison.submitted|date:"Y" }})" />
{% endblock %}
{% block morecss %}
.ietf-liaison-details tr { vertical-align:top; }
{% endblock morecss %}
{% block content %}
<h1>Liaison Statement: {% include 'liaisons/liaison_title.html' %}</h1>
<table class="ietf-liaison-details">
<tr>
<td style="width:18ex;">Submission Date:</td>
<td>{{ liaison.submitted|date:"Y-m-d" }}</td></tr>
<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>
</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 %}
</td>
</tr>
{% if liaison.from_contact %}
<tr>
<td>Cc:</td><td>{{ liaison.cc|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>
</tr>
<tr>
<td>Technical Contact:</td>
<td>{{ liaison.technical_contact|parse_email_list|make_one_per_line|safe|linebreaksbr }}</td>
</tr>
<tr>
<td>Purpose:</td>
<td>{{ liaison.purpose.name }}</td>
</tr>
{% if liaison.deadline %}
<tr>
<td>Deadline:</td>
<td>{{ liaison.deadline }}
{% if liaison.action_taken %}<span class="actionTaken">Action Taken</span>{% else %}<span class="noActionTaken">Action Needed</span>{% endif %}
{% if can_take_care %}
<form method="post" style="display:inline">
<input type="submit" value="Mark as Action Taken" name='do_action_taken' />
</form>
{% endif %}
</td>
</tr>
{% endif %}
{% endif %}
{% if relations %}
<tr>
<td>Liaisons referring to this one:</td>
<td>
{% for rel in relations %}
<a href="{% url liaison_detail rel.pk %}">{% if rel.title %}{{ rel.title }}{% else %}Liaison #{{ rel.pk }}{% endif %}</a><br />
{% endfor %}
</td>
</tr>
{% endif %}
{% if liaison.related_to %}
<tr>
<td>Referenced liaison:</td>
<td>
<a href="{% url liaison_detail liaison.related_to.pk %}">{% if liaison.related_to.title %}{{ liaison.related_to.title }}{% else %}Liaison #{{ liaison.related_to.pk }}{% endif %}</a>
</td>
</tr>
{% endif %}
<tr>
<td>Attachments:</td>
<td>
{% for doc in liaison.attachments.all %}
<a href="https://datatracker.ietf.org/documents/LIAISON/{{ doc.external_url }}">{{ doc.title }}</a>{% if not forloop.last %}<br/>{% endif %}
{% empty %}
(none)
{% endfor %}
</td>
</tr>
{% if liaison.from_contact and liaison.body %}
<tr>
<td>Body:</td>
<td><pre>{{ liaison.body|wordwrap:"71" }}</pre></td>
</tr>
{% endif %}
</table>
{% if can_edit %}
<p><a href="{% url liaison_edit object_id=liaison.pk %}">Edit Liaison</a></p>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,32 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% load ietf_filters %}
{% block title %}{% if liaison %}Edit liaison: {{ liaison }}{% else %}Send Liaison Statement{% endif %}{% endblock %}
{% block pagehead %}
<link rel="stylesheet" type="text/css" href="/css/liaisons.css"></link>
<link rel="stylesheet" type="text/css" href="/css/jquery-ui-themes/jquery-ui-1.8.11.custom.css"></link>
{% endblock %}
{% block content %}
<h1>{% if liaison %}Edit liaison: {{ liaison }}{% else %}Send Liaison Statement{% endif %}</h1>
<noscript class="js-info">
This page depends on Javascript being enabled to work properly. Please enable Javascript and reload the page.
</noscript>
{% if not liaison %}
<ul>
<li>If you wish to submit your liaison statement by e-mail, then please send it to <a href="mailto:statements@ietf.org">statements@ietf.org</a></li>
<li>Fields marked with <span class="fieldRequired">*</span> are required. For detailed descriptions of the fields see <a href="/liaison/help/fields/">Field help</a></li>
</ul>
{% endif %}
{{ form }}
{% endblock %}
{% 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/liaisons.js"></script>
{% endblock %}

View file

@ -1,19 +1,19 @@
{% load ietf_filters %}{% autoescape off %}Title: {{ liaison.title|clean_whitespace }}
Submission Date: {{ liaison.submitted_date }}
Submission Date: {{ liaison.submitted|date:"Y-m-d" }}
URL of the IETF Web page: {{ url }}
{% if liaison.deadline_date %}Please reply by {{ liaison.deadline_date }}{% endif %}
From: {{ liaison.from_body }} ({{ liaison.person }} <{{ liaison.replyto|default:liaison.from_email }}>)
To: {{ liaison.to_body }} ({{ liaison.to_poc }})
Cc: {{ liaison.cc1 }}
Reponse Contact: {{ liaison.response_contact }}
{% if liaison.deadline %}Please reply by {{ liaison.deadline }}{% endif %}
From: {{ liaison.from_name }} ({{ liaison.from_contact.person }} <{{ liaison.reply_to|default:liaison.from_contact.address }}>)
To: {{ liaison.to_name }} ({{ liaison.to_contact }})
Cc: {{ liaison.cc }}
Response Contact: {{ liaison.response_contact }}
Technical Contact: {{ liaison.technical_contact }}
Purpose: {% if liaison.purpose_text %}{{ liaison.purpose_text }}{% else %}{{ liaison.purpose }}{% endif %}
Purpose: {{ liaison.purpose.name }}
{% if liaison.related_to %}Referenced liaison: {% if liaison.related_to.title %}{{ liaison.related_to.title }}{% else %}Liaison #{{ liaison.related_to.pk }}{% endif %} ({{ referenced_url }}){% endif %}
Body: {{ liaison.body }}
Attachments:
{% for file in liaison.uploads_set.all %}
{{ file.file_title }}
https://datatracker.ietf.org/documents/LIAISON/{{ file.filename }}
{% for doc in liaison.attachments.all %}
{{ doc.title }}
https://datatracker.ietf.org/documents/LIAISON/{{ doc.external_url }}
{% empty %}
No document has been attached
{% endfor %}{% endautoescape %}

View file

@ -1,31 +0,0 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% load ietf_filters %}
{% block title %}{{ liaison.title }}{% endblock %}
{% block content %}
<h1>Simulated Mail for Liaison Statement</h1>
<p>
Demo version of this tool does NOT actually send the liaison statement to the recipients.
</p>
<p>
Rather, the actual email body (including mail header) is displayed below.
</p>
<p>
In production mode, you will not see this screen.
</p>
<pre>
From: {{ message.From }}
To: {{ message.To }}
Cc: {{ message.Cc }}
Bcc: {{ message.Bcc }}
Subject: {{ message.Subject }}
{{ mail.body }}
</pre>
<a href="{% url liaison_list %}">Return to liaison list</a>
{% endblock %}

View file

@ -1,62 +0,0 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% load ietf_filters %}
{% block title %}Liaison Management{% endblock %}
{% block content %}
<h1>Liaison Management</h1>
{% if can_send_incoming or can_send_outgoing %}
<div class="liaison-box">
<h1>Add new liaison</h1>
<ul>
{% if can_send_incoming %}<li><a href="{% url add_liaison %}?incoming">Add new incoming liaison</a></li>{% endif %}
{% if can_send_outgoing %}<li><a href="{% url add_liaison %}">Add new outgoing liaison</a></li>{% endif %}
</div>
{% endif %}
{% if to_aprove %}
<div class="liaison-box pending-for-aproval">
<h1>Liaisons that need your approval</h1>
{% for liaison in to_aprove %}
<a href="">{{ liaison }}</a>
{% endfor %}
</div>
{% endif %}
{% if to_edit %}
<div class="liaison-box edit-liaison-list">
<h1>Liaisons you can edit</h1>
{% for liaison in to_edit %}
<tr class="{% cycle oddrow,evenrow %}">
<td style="white-space:nowrap;">{{ liaison.submitted_date|date:"Y-m-d" }}</td>
<td>{{ liaison.from_body|escape }}</td>
<td>
{% if liaison.by_secretariat %}
{% if liaison.submitter_email %}
<a href="mailto:{{ liaison.submitter_email}}">{{ liaison.submitter_name|escape }}</a>
{% else %}
{{ liaison.submitter_name|escape }}
{% endif %}
{% else %}
{{ liaison.to_body|escape }}
{% endif %}
</td>
<td>
{% if liaison.by_secretariat %}
{% for file in liaison.uploads_set.all %}
<a href="https://datatracker.ietf.org/documents/LIAISON/file{{ file.file_id }}{{ file.file_extension }}">{{ file.file_title|escape }}</a><br/>
{% endfor %}
{% else %}
<a href="{{ liaison.detail_id }}/">{{ liaison.title|escape }}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,39 @@
{% load ietf_filters %}
<table class="ietf-table" width="100%">
<tr>
<th width="9%" class="sort{% if sort == "submitted" %} sorted{% endif %}"><a href="?sort=submitted">Date</a></th>
<th width="15%" class="sort{% if sort == "from_name" %} sorted{% endif %}"><a href="?sort=from_name">From</a></th>
<th width="15%" class="sort{% if sort == "to_name" %} sorted{% endif %}"><a href="?sort=to_name">To</a></th>
<th width="9%" class="sort{% if sort == "deadline" %} sorted{% endif %}"><a href="?sort=deadline">Deadline</a></th>
<th width="50%" class="sort{% if sort == "title" %} sorted{% endif %}"><a href="?sort=title">Title</a></th>
</tr>
{% for liaison in liaisons %}
<tr class="{% cycle oddrow,evenrow %}">
<td style="white-space:nowrap;">{{ liaison.submitted|date:"Y-m-d" }}</td>
<td>{{ liaison.from_name }}</td>
<td>
{% if liaison.from_contact_id %}
{{ liaison.to_name }}
{% else %}
{{ liaison.to_name|strip_email }}
{% endif %}
</td>
<td>
{{ liaison.deadline|default:"-"|date:"Y-m-d" }}
</td>
<td>
{% if not liaison.from_contact_id %}
{% for doc in liaison.attachments.all %}
<a href="https://datatracker.ietf.org/documents/LIAISON/{{ doc.external_url }}">{{ doc.title }}</a><br/>
{% endfor %}
{% else %}
<a href="{% if liaison.approved %}{% url liaison_detail object_id=liaison.pk %}{% else %}{% url liaison_approval_detail object_id=liaison.pk %}{% endif %}">{{ liaison.title }}</a>
{% endif %}
<span style="display: none" class="liaisonPK">{{ liaison.pk }}</span>
</td>
</tr>
{% endfor %}
</table>

View file

@ -1,5 +1,5 @@
{% if object.by_secretariat %}
Liaison statement submitted by email from {{ object.from_body|escape }} to {{ object.submitter_name|escape }} on {{ object.submitted_date }}
{% load ietf_filters %}{% if not liaison.from_contact %}
Liaison statement submitted by email from {{ liaison.from_name }} to {{ liaison.to_name|strip_email }} on {{ liaison.submitted|date:"Y-m-d" }}
{% else %}
{{ object.title }}
{{ liaison.title }}
{% endif %}

View file

@ -1,10 +0,0 @@
{% extends "liaisons/liaisondetail_detail.html" %}
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% block content %}
{{ block.super }}
<form method="post" action="">
<input type="submit" value="Approve" name='do_approval' />
</form>
{% endblock %}

View file

@ -1,110 +0,0 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% load ietf_filters %}
{% block title %}Liaison Statement: {% include 'liaisons/liaison_title.html' %}{% endblock %}
{% block pagehead %}
<link rel="stylesheet" type="text/css" href="/css/liaisons.css"></link>
<meta name="description" content="Liaison Statement from {{ object.from_body }}{% if not object.by_secretariat %} to {{object.to_body}}{% endif %} ({{ object.submitted_date|date:"Y" }})" />
{% endblock %}
{% block morecss %}
.ietf-liaison-details tr { vertical-align:top; }
{% endblock morecss %}
{% block content %}
<h1>Liaison Statement: {% include 'liaisons/liaison_title.html' %}</h1>
<table class="ietf-liaison-details">
<tr>
<td style="width:18ex;">Submission Date:</td><td>{{ object.submitted_date }}</td></tr>
<tr>
<td>From:</td><td>{{ object.from_body }} (<a href="mailto:{{ object.from_email|fix_ampersands }}">{{ object.person }}</a>)</td></tr>
<tr>
<td>To:</td><td>
{% if object.by_secretariat %}
{% if object.submitter_email %}
<a href="mailto:{{ object.submitter_email}}">{{ object.submitter_name }}</a>
{% else %}
{{ object.submitter_name }}
{% endif %}
{% else %}
{{ object.to_body }} ({{ object.to_poc|parse_email_list|safe }})</td></tr>
{% endif %}
{% if not object.by_secretariat %}
<tr>
<td>Cc:</td><td>{{ object.cc1|parse_email_list|make_one_per_line|safe|linebreaksbr }}</td></tr>
<tr>
<td>Response Contact:</td>
<td>
{{ object.response_contact|parse_email_list|make_one_per_line|safe|linebreaksbr }}
</td></tr>
<tr>
<td>Technical Contact:</td>
<td>{{ object.technical_contact|parse_email_list|make_one_per_line|safe|linebreaksbr }}</td>
</tr>
<tr>
<td>Purpose:</td><td>{% if object.purpose_text %}{{ object.purpose_text }}{% else %}{{ object.purpose }}{% endif %}</td></tr>
{% if object.deadline_date %}
<tr>
<td>Deadline:</td>
{% if can_take_care %}
<td><form method="post" action="">
{{ object.deadline_date }}
{% if object.action_taken %}<span class="actionTaken">Action Taken</span>{% else %}<span class="noActionTaken">Action Required</span>{% endif %}
<input type="submit" value="Mark as Action Taken" name='do_action_taken' />
</form></td>
{% else %}
<td>{{ object.deadline_date }}
{% if object.action_taken %}<span class="actionTaken">Action Taken</span>{% else %}<span class="noActionTaken">Action Needed</span>{% endif %}
</td>
{% endif %}
</tr>
{% endif %}
{% endif %}
{% if relations %}
<tr>
<td>Liaisons referring to this one:</td>
<td>
{% for liaison in relations %}
<a href="../{{ liaison.pk }}">{% if liaison.title %}{{ object.title }}{% else %}Liaison #{{ liaison.pk }}{% endif %}</a><br />
{% endfor %}
</td></tr>
{% endif %}
{% if object.related_to %}
<tr>
<td>Referenced liaison:</td>
<td>
<a href="../{{ object.related_to.pk }}">{% if object.related_to.title %}{{ object.related_to.title }}{% else %}Liaison #{{ object.related_to.pk }}{% endif %}</a>
</td></tr>
{% endif %}
<tr>
<td>Attachments:</td>
<td>
{% for file in object.uploads_set.all %}
<a href="https://datatracker.ietf.org/documents/LIAISON/{{ file.filename }}">{{ file.file_title }}</a>{% if not forloop.last %}<br/>{% endif %}
{% empty %}
(none)
{% endfor %}
</td></tr>
{% if not object.by_secretariat and object.body %}
<tr>
<td>Body:</td><td><pre>{{ object.body|wordwrap:"71"|escape }}</pre></td></tr>
{% endif %}
</table>
{% if can_edit %}
<form method="get" action="{% url liaison_edit object.pk %}">
<input type="submit" value="Edit" />
</form>
{% endif %}
{% endblock %}

View file

@ -1,31 +0,0 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% load ietf_filters %}
{% block title %}{% if liaison %}Edit liaison: {{ liaison }}{% else %}Send Liaison Statement{% endif %}{% endblock %}
{% block pagehead %}
{{ form.media }}
{% endblock %}
{% block content %}
<h1>{% if liaison %}Edit liaison: {{ liaison }}{% else %}Send Liaison Statement{% endif %}</h1>
<div class="js-info">
Your browser has Javascript disabled. Please enable javascript and reload the page.
<script type="text/javascript">
(function ($) {
$(".js-info").hide();
})(jQuery);
</script>
</div>
{% if not liaison %}
<ul>
<li>If you wish to submit your liaison statement by e-mail, then please send it to <a href="mailto:statements@ietf.org">statements@ietf.org</a></li>
<li>Fields marked with <span class="fieldRequired">*</span> are required. For detailed descriptions of the fields see <a href="/liaison/help/fields/">Field help</a></li>
</ul>
{% endif %}
{{ form }}
{% endblock %}

View file

@ -1,51 +0,0 @@
<table class="ietf-table" width="100%">
<tr>
<th width="9%" class="orderField orderField{{ submitted_date|yesno:"Active,Inactive,Reversed" }}">
<a href="?order_by={{ submitted_date|default_if_none:"" }}submitted_date">Date</a>
</th>
<th width="15%" class="orderField orderField{{ from_raw_body|yesno:"Active,Inactive,Reversed" }}">
<a href="?order_by={{ from_raw_body|default_if_none:"" }}from_raw_body">From</a>
</th>
<th width="15%" class="orderField orderField{{ to_body|yesno:"Active,Inactive,Reversed" }}">
<a href="?order_by={{ to_body|default_if_none:"" }}to_body">To</a>
</th>
<th width="9%" class="orderField orderField{{ deadline_date|yesno:"Active,Inactive,Reversed" }}">
<a href="?order_by={{ deadline_date|default_if_none:"" }}deadline_date">Deadline</a>
</th>
<th width="50%" class="orderField orderField{{ title|yesno:"Active,Inactive,Reversed" }}">
<a href="?order_by={{ title|default_if_none:"" }}title">Title</a>
</th>
</tr>
{% for liaison in object_list %}
<tr class="{% cycle oddrow,evenrow %}">
<td style="white-space:nowrap;">{{ liaison.submitted_date|date:"Y-m-d" }}</td>
<td>{{ liaison.from_body|escape }}</td>
<td>
{% if liaison.by_secretariat %}
{% if liaison.submitter_email %}
<a href="mailto:{{ liaison.submitter_email}}">{{ liaison.submitter_name|escape }}</a>
{% else %}
{{ liaison.submitter_name|escape }}
{% endif %}
{% else %}
{{ liaison.to_body|escape }}
{% endif %}
</td>
<td>
{{ liaison.deadline_date|default:"--" }}
</td>
<td>
{% if liaison.by_secretariat %}
{% for file in liaison.uploads_set.all %}
<a href="https://datatracker.ietf.org/documents/LIAISON/{{ file.filename }}">{{ file.file_title|escape }}</a><br/>
{% endfor %}
{% else %}
<a href="{{ liaison.detail_id }}/">{{ liaison.title|escape }}</a>
{% endif %}
<span style="display: none" class="liaisonPK">{{ liaison.pk }}</span>
</td>
</tr>
{% endfor %}
</table>

View file

@ -18,12 +18,12 @@
<ul>
{% if can_send_incoming %}<li><a href="{% url add_liaison %}?incoming">Add new incoming liaison</a></li>{% endif %}
{% if can_send_outgoing %}<li><a href="{% url add_liaison %}">Add new outgoing liaison</a></li>{% endif %}
{% if can_approve %}<li><a href="{% url liaison_approval_list %}">You have {{ can_approve }} pending liaison statement{{ can_approve|pluralize }} that need{{ can_approve|pluralize:"s," }} your approval</a></li>{% endif %}
{% if approvable %}<li><a href="{% url liaison_approval_list %}">You have {{ approvable }} pending liaison statement{{ approvable|pluralize }} that need{{ approvable|pluralize:"s," }} your approval</a></li>{% endif %}
</ul>
{% endif %}
{% endblock %}
{% include "liaisons/liaisondetail_simple_list.html" %}
{% include "liaisons/liaison_table.html" %}
{% endblock %}

View file

@ -96,30 +96,17 @@ span.fieldRequired {
background-color: #ffdd88;
}
th.orderField a {
th.sort a {
text-decoration: none;
color: white;
display: block;
padding-right: 20px;
background: url(/images/sort-header-clear.png) no-repeat right center;
}
th.orderFieldReversed a {
background: #2647A0 url(/images/arrow-down.gif) no-repeat left center;
padding-left: 20px;
th.sorted a {
background: url(/images/sort-header-filled.png) no-repeat right center;
}
th.orderFieldActive a {
background: #2647A0 url(/images/arrow-up.gif) no-repeat left center;
padding-left: 20px;
}
.noActionTaken,
.actionTaken {
border: 1px solid green;
padding: 2px 5px;
background-color: #ccffbb;
}
.noActionTaken {
border: 1px solid red;
background-color: #ffccbb;
}
.noActionTaken, .actionTaken { padding: 2px 5px; }
.actionTaken { border: 1px solid green; background-color: #ccffbb; }
.noActionTaken { border: 1px solid red; background-color: #ffccbb; }