Merged in branch/amsl/liaisons/6.4.1@10160 from rcross@amsl.com, bringing in the new liaison tool.
- Legacy-Id: 10161
This commit is contained in:
commit
a7667fb29e
|
@ -54,6 +54,7 @@ def has_role(user, role_names, *args, **kwargs):
|
|||
"IETF Chair": Q(person=person, name="chair", group__acronym="ietf"),
|
||||
"IRTF Chair": Q(person=person, name="chair", group__acronym="irtf"),
|
||||
"IAB Chair": Q(person=person, name="chair", group__acronym="iab"),
|
||||
"IAB Executive Director": Q(person=person, name="execdir", group__acronym="iab"),
|
||||
"IAB Group Chair": Q(person=person, name="chair", group__type="iab", group__state="active"),
|
||||
"WG Chair": Q(person=person,name="chair", group__type="wg", group__state__in=["active","bof", "proposed"]),
|
||||
"WG Secretary": Q(person=person,name="secr", group__type="wg", group__state__in=["active","bof", "proposed"]),
|
||||
|
@ -64,6 +65,8 @@ def has_role(user, role_names, *args, **kwargs):
|
|||
"Nomcom Chair": Q(person=person, name="chair", group__type="nomcom", group__state="active", group__acronym__icontains=kwargs.get('year', '0000')),
|
||||
"Nomcom Advisor": Q(person=person, name="advisor", group__type="nomcom", group__state="active", group__acronym__icontains=kwargs.get('year', '0000')),
|
||||
"Nomcom": Q(person=person, group__type="nomcom", group__state="active", group__acronym__icontains=kwargs.get('year', '0000')),
|
||||
"Liaison Manager": Q(person=person,name="liaiman",group__type="sdo",group__state="active", ),
|
||||
"Authorized Individual": Q(person=person,name="auth",group__type="sdo",group__state="active", ),
|
||||
}
|
||||
|
||||
filter_expr = Q()
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
from ietf.person.models import Person
|
||||
from ietf.group.models import Role
|
||||
|
||||
def proxy_personify_role(role):
|
||||
"""Return person from role with an old-school email() method using
|
||||
email from role."""
|
||||
p = role.person
|
||||
p.email = lambda: (p.plain_name(), role.email.address)
|
||||
return p
|
||||
|
||||
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.person
|
||||
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
|
|
@ -1,10 +1,44 @@
|
|||
from django.contrib import admin
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from ietf.liaisons.models import LiaisonStatement
|
||||
from ietf.liaisons.models import ( LiaisonStatement, LiaisonStatementEvent,
|
||||
LiaisonStatementGroupContacts, RelatedLiaisonStatement, LiaisonStatementAttachment )
|
||||
|
||||
|
||||
class RelatedLiaisonStatementInline(admin.TabularInline):
|
||||
model = RelatedLiaisonStatement
|
||||
fk_name = 'source'
|
||||
raw_id_fields = ['target']
|
||||
extra = 1
|
||||
|
||||
class LiaisonStatementAttachmentInline(admin.TabularInline):
|
||||
model = LiaisonStatementAttachment
|
||||
raw_id_fields = ['document']
|
||||
extra = 1
|
||||
|
||||
class LiaisonStatementAdmin(admin.ModelAdmin):
|
||||
list_display = ['id', 'title', 'from_name', 'to_name', 'submitted', 'purpose', 'related_to']
|
||||
list_display = ['id', 'title', 'submitted', 'from_groups_short_display', 'purpose', 'related_to']
|
||||
list_display_links = ['id', 'title']
|
||||
ordering = ('title', )
|
||||
raw_id_fields = ('from_contact', 'related_to', 'from_group', 'to_group', 'attachments')
|
||||
raw_id_fields = ('from_contact', 'attachments', 'from_groups', 'to_groups')
|
||||
#filter_horizontal = ('from_groups', 'to_groups')
|
||||
inlines = [ RelatedLiaisonStatementInline, LiaisonStatementAttachmentInline ]
|
||||
|
||||
def related_to(self, obj):
|
||||
return '<br />'.join(['<a href="%s">%s</a>' % (reverse('admin:liaisons_liaisonstatement_change', None, (i.target.id, )), str(i.target)) for i in obj.source_of_set.select_related('target').all()])
|
||||
related_to.allow_tags = True
|
||||
|
||||
class LiaisonStatementEventAdmin(admin.ModelAdmin):
|
||||
list_display = ["statement", "type", "by", "time"]
|
||||
search_fields = ["statement__title", "by__name"]
|
||||
raw_id_fields = ["statement", "by"]
|
||||
|
||||
class LiaisonStatementGroupContactsAdmin(admin.ModelAdmin):
|
||||
list_display = ["group", "contacts"]
|
||||
raw_id_fields = ["group"]
|
||||
search_fields = ["group__acronym", "contacts"]
|
||||
ordering = ["group__name"]
|
||||
|
||||
admin.site.register(LiaisonStatement, LiaisonStatementAdmin)
|
||||
admin.site.register(LiaisonStatementEvent, LiaisonStatementEventAdmin)
|
||||
admin.site.register(LiaisonStatementGroupContacts, LiaisonStatementGroupContactsAdmin)
|
||||
|
|
|
@ -16,7 +16,7 @@ from ietf.liaisons.models import LiaisonStatement
|
|||
# to construct a queryset.
|
||||
class LiaisonStatementsFeed(Feed):
|
||||
feed_type = Atom1Feed
|
||||
link = reverse_lazy("liaison_list")
|
||||
link = reverse_lazy("ietf.liaisons.views.liaison_list")
|
||||
description_template = "liaisons/feed_item_description.html"
|
||||
|
||||
def get_object(self, request, kind, search=None):
|
||||
|
@ -24,8 +24,8 @@ class LiaisonStatementsFeed(Feed):
|
|||
|
||||
if kind == 'recent':
|
||||
obj['title'] = 'Recent Liaison Statements'
|
||||
obj['limit'] = 15
|
||||
return obj
|
||||
obj['limit'] = 15
|
||||
return obj
|
||||
|
||||
if kind == 'from':
|
||||
if not search:
|
||||
|
@ -33,7 +33,7 @@ class LiaisonStatementsFeed(Feed):
|
|||
|
||||
try:
|
||||
group = Group.objects.get(acronym=search)
|
||||
obj['filter'] = { 'from_group': group }
|
||||
obj['filter'] = { 'from_groups': group }
|
||||
obj['title'] = u'Liaison Statements from %s' % group.name
|
||||
return obj
|
||||
except Group.DoesNotExist:
|
||||
|
@ -54,46 +54,47 @@ class LiaisonStatementsFeed(Feed):
|
|||
if not search:
|
||||
raise FeedDoesNotExist
|
||||
|
||||
obj['filter'] = dict(to_name__icontains=search)
|
||||
obj['title'] = 'Liaison Statements where to matches %s' % search
|
||||
group = Group.objects.get(acronym=search)
|
||||
obj['filter'] = { 'to_groups': group }
|
||||
obj['title'] = u'Liaison Statements to %s' % group.name
|
||||
return obj
|
||||
|
||||
if kind == 'subject':
|
||||
if kind == 'subject':
|
||||
if not search:
|
||||
raise FeedDoesNotExist
|
||||
raise FeedDoesNotExist
|
||||
|
||||
obj['q'] = [ Q(title__icontains=search) | Q(attachments__title__icontains=search) ]
|
||||
obj['title'] = 'Liaison Statements where subject matches %s' % search
|
||||
return obj
|
||||
|
||||
raise FeedDoesNotExist
|
||||
raise FeedDoesNotExist
|
||||
|
||||
def items(self, obj):
|
||||
qs = LiaisonStatement.objects.all().order_by("-submitted")
|
||||
if obj.has_key('q'):
|
||||
qs = qs.filter(*obj['q'])
|
||||
if obj.has_key('filter'):
|
||||
qs = qs.filter(**obj['filter'])
|
||||
if obj.has_key('limit'):
|
||||
qs = qs[:obj['limit']]
|
||||
return qs
|
||||
qs = LiaisonStatement.objects.all().order_by("-id")
|
||||
if obj.has_key('q'):
|
||||
qs = qs.filter(*obj['q'])
|
||||
if obj.has_key('filter'):
|
||||
qs = qs.filter(**obj['filter'])
|
||||
if obj.has_key('limit'):
|
||||
qs = qs[:obj['limit']]
|
||||
return qs
|
||||
|
||||
def title(self, obj):
|
||||
return obj['title']
|
||||
return obj['title']
|
||||
|
||||
def description(self, obj):
|
||||
return self.title(obj)
|
||||
return self.title(obj)
|
||||
|
||||
def item_title(self, item):
|
||||
return render_to_string("liaisons/liaison_title.html", { 'liaison': item }).strip()
|
||||
|
||||
def item_link(self, item):
|
||||
return urlreverse("liaison_detail", kwargs={ "object_id": item.pk })
|
||||
return urlreverse("ietf.liaisons.views.liaison_detail", kwargs={ "object_id": item.pk })
|
||||
|
||||
def item_pubdate(self, item):
|
||||
# this method needs to return a datetime instance, even
|
||||
# though the database has only date, not time
|
||||
# though the database has only date, not time
|
||||
return item.submitted
|
||||
|
||||
|
||||
def item_author_name(self, item):
|
||||
return item.from_name
|
||||
|
|
|
@ -7,44 +7,65 @@ from django.core.urlresolvers import reverse as urlreverse
|
|||
from ietf.liaisons.models import LiaisonStatement
|
||||
|
||||
def select2_id_liaison_json(objs):
|
||||
return json.dumps([{ "id": o.pk, "text": escape(o.title) } for o in objs])
|
||||
return json.dumps([{ "id": o.pk, "text":u"[{}] {}".format(o.pk, escape(o.title)) } for o in objs])
|
||||
|
||||
class SearchableLiaisonStatementField(forms.IntegerField):
|
||||
def select2_id_group_json(objs):
|
||||
return json.dumps([{ "id": o.pk, "text": escape(o.acronym) } for o in objs])
|
||||
|
||||
class SearchableLiaisonStatementsField(forms.CharField):
|
||||
"""Server-based multi-select field for choosing liaison statements using
|
||||
select2.js."""
|
||||
|
||||
def __init__(self, hint_text="Type in title to search for document", *args, **kwargs):
|
||||
super(SearchableLiaisonStatementField, self).__init__(*args, **kwargs)
|
||||
def __init__(self,
|
||||
max_entries = None,
|
||||
hint_text="Type in title to search for document",
|
||||
model = LiaisonStatement,
|
||||
*args, **kwargs):
|
||||
kwargs["max_length"] = 10000
|
||||
self.model = model
|
||||
self.max_entries = max_entries
|
||||
|
||||
self.widget.attrs["class"] = "select2-field"
|
||||
super(SearchableLiaisonStatementsField, self).__init__(*args, **kwargs)
|
||||
|
||||
self.widget.attrs["class"] = "select2-field form-control"
|
||||
self.widget.attrs["data-placeholder"] = hint_text
|
||||
self.widget.attrs["data-max-entries"] = 1
|
||||
if self.max_entries != None:
|
||||
self.widget.attrs["data-max-entries"] = self.max_entries
|
||||
|
||||
def parse_select2_value(self, value):
|
||||
return [x.strip() for x in value.split(",") if x.strip()]
|
||||
|
||||
def prepare_value(self, value):
|
||||
if not value:
|
||||
value = None
|
||||
elif isinstance(value, LiaisonStatement):
|
||||
value = value
|
||||
else:
|
||||
value = LiaisonStatement.objects.exclude(approved=None).filter(pk=value).first()
|
||||
value = ""
|
||||
if isinstance(value, (int, long)):
|
||||
value = str(value)
|
||||
if isinstance(value, basestring):
|
||||
pks = self.parse_select2_value(value)
|
||||
value = self.model.objects.filter(pk__in=pks)
|
||||
if isinstance(value, LiaisonStatement):
|
||||
value = [value]
|
||||
|
||||
self.widget.attrs["data-pre"] = select2_id_liaison_json([value] if value else [])
|
||||
self.widget.attrs["data-pre"] = select2_id_liaison_json(value)
|
||||
|
||||
# doing this in the constructor is difficult because the URL
|
||||
# patterns may not have been fully constructed there yet
|
||||
self.widget.attrs["data-ajax-url"] = urlreverse("ajax_select2_search_liaison_statements")
|
||||
self.widget.attrs["data-ajax-url"] = urlreverse("ietf.liaisons.views.ajax_select2_search_liaison_statements")
|
||||
|
||||
return value
|
||||
return u",".join(unicode(o.pk) for o in value)
|
||||
|
||||
def clean(self, value):
|
||||
value = super(SearchableLiaisonStatementField, self).clean(value)
|
||||
value = super(SearchableLiaisonStatementsField, self).clean(value)
|
||||
pks = self.parse_select2_value(value)
|
||||
|
||||
if value == None:
|
||||
return None
|
||||
objs = self.model.objects.filter(pk__in=pks)
|
||||
|
||||
obj = LiaisonStatement.objects.filter(pk=value).first()
|
||||
if not obj and self.required:
|
||||
raise forms.ValidationError(u"You must select a value.")
|
||||
found_pks = [str(o.pk) for o in objs]
|
||||
failed_pks = [x for x in pks if x not in found_pks]
|
||||
if failed_pks:
|
||||
raise forms.ValidationError(u"Could not recognize the following groups: {pks}.".format(pks=", ".join(failed_pks)))
|
||||
|
||||
return obj
|
||||
if self.max_entries != None and len(objs) > self.max_entries:
|
||||
raise forms.ValidationError(u"You can select at most %s entries only." % self.max_entries)
|
||||
|
||||
return objs
|
||||
|
|
|
@ -1,43 +1,187 @@
|
|||
import datetime, os
|
||||
import operator
|
||||
import six
|
||||
from email.utils import parseaddr
|
||||
from form_utils.forms import BetterModelForm
|
||||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db.models.query import QuerySet
|
||||
from django.forms.util import ErrorList
|
||||
from django.db.models import Q
|
||||
from django.forms.widgets import RadioFieldRenderer
|
||||
from django.core.validators import validate_email, ValidationError
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.html import format_html
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
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)
|
||||
from ietf.liaisons.models import LiaisonStatement, LiaisonStatementPurposeName
|
||||
from ietf.liaisons.fields import SearchableLiaisonStatementField
|
||||
from ietf.group.models import Group, Role
|
||||
from ietf.person.models import Person, Email
|
||||
from ietf.ietfauth.utils import has_role
|
||||
from ietf.name.models import DocRelationshipName
|
||||
from ietf.liaisons.utils import get_person_for_user,is_authorized_individual
|
||||
from ietf.liaisons.widgets import ButtonWidget,ShowAttachmentsWidget
|
||||
from ietf.liaisons.models import (LiaisonStatement,
|
||||
LiaisonStatementEvent,LiaisonStatementAttachment,LiaisonStatementPurposeName)
|
||||
from ietf.liaisons.fields import SearchableLiaisonStatementsField
|
||||
from ietf.group.models import Group
|
||||
from ietf.person.models import Email
|
||||
from ietf.person.fields import SearchableEmailField
|
||||
from ietf.doc.models import Document
|
||||
from ietf.utils.fields import DatepickerDateField
|
||||
|
||||
'''
|
||||
NOTES:
|
||||
Authorized individuals are people (in our Person table) who are authorized to send
|
||||
messages on behalf of some other group - they have a formal role in the other group,
|
||||
whereas the liasion manager has a formal role with the IETF (or more correctly,
|
||||
with the IAB).
|
||||
'''
|
||||
|
||||
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()
|
||||
related_to = SearchableLiaisonStatementField(label=u'Related Liaison Statement', required=False)
|
||||
deadline_date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label='Deadline', required=True)
|
||||
|
||||
# -------------------------------------------------
|
||||
# Helper Functions
|
||||
# -------------------------------------------------
|
||||
def liaison_manager_sdos(person):
|
||||
return Group.objects.filter(type="sdo", state="active", role__person=person, role__name="liaiman").distinct()
|
||||
|
||||
def get_internal_choices(user):
|
||||
'''Returns the set of internal IETF groups the user has permissions for, as a list
|
||||
of choices suitable for use in a select widget. If user == None, all active internal
|
||||
groups are included.'''
|
||||
choices = []
|
||||
groups = get_groups_for_person(user.person if user else None)
|
||||
main = [ (g.pk, 'The {}'.format(g.acronym.upper())) for g in groups.filter(acronym__in=('ietf','iesg','iab')) ]
|
||||
areas = [ (g.pk, '{} - {}'.format(g.acronym,g.name)) for g in groups.filter(type='area') ]
|
||||
wgs = [ (g.pk, '{} - {}'.format(g.acronym,g.name)) for g in groups.filter(type='wg') ]
|
||||
choices.append(('Main IETF Entities', main))
|
||||
choices.append(('IETF Areas', areas))
|
||||
choices.append(('IETF Working Groups', wgs ))
|
||||
return choices
|
||||
|
||||
def get_groups_for_person(person):
|
||||
'''Returns queryset of internal Groups the person has interesting roles in.
|
||||
This is a refactor of IETFHierarchyManager.get_entities_for_person(). If Person
|
||||
is None or Secretariat or Liaison Manager all internal IETF groups are returned.
|
||||
'''
|
||||
if person == None or has_role(person.user, "Secretariat") or has_role(person.user, "Liaison Manager"):
|
||||
# collect all internal IETF groups
|
||||
queries = [Q(acronym__in=('ietf','iesg','iab')),
|
||||
Q(type='area',state='active'),
|
||||
Q(type='wg',state='active')]
|
||||
else:
|
||||
# Interesting roles, as Group queries
|
||||
queries = [Q(role__person=person,role__name='chair',acronym='ietf'),
|
||||
Q(role__person=person,role__name__in=('chair','execdir'),acronym='iab'),
|
||||
Q(role__person=person,role__name='ad',type='area',state='active'),
|
||||
Q(role__person=person,role__name__in=('chair','secretary'),type='wg',state='active')]
|
||||
return Group.objects.filter(reduce(operator.or_,queries)).order_by('acronym').distinct()
|
||||
|
||||
def liaison_form_factory(request, type=None, **kwargs):
|
||||
"""Returns appropriate Liaison entry form"""
|
||||
user = request.user
|
||||
if kwargs.get('instance',None):
|
||||
return EditLiaisonForm(user, **kwargs)
|
||||
elif type == 'incoming':
|
||||
return IncomingLiaisonForm(user, **kwargs)
|
||||
elif type == 'outgoing':
|
||||
return OutgoingLiaisonForm(user, **kwargs)
|
||||
return None
|
||||
|
||||
def validate_emails(value):
|
||||
'''Custom validator for emails'''
|
||||
value = value.strip() # strip whitespace
|
||||
if '\r\n' in value: # cc_contacts has newlines
|
||||
value = value.replace('\r\n',',')
|
||||
emails = value.split(',')
|
||||
for email in emails:
|
||||
name, addr = parseaddr(email)
|
||||
try:
|
||||
validate_email(addr)
|
||||
except ValidationError:
|
||||
raise forms.ValidationError('Invalid email address: %s' % addr)
|
||||
try:
|
||||
addr.encode('ascii')
|
||||
except UnicodeEncodeError as e:
|
||||
raise forms.ValidationError('Invalid email address: %s (check character %d)' % (addr,e.start))
|
||||
|
||||
# -------------------------------------------------
|
||||
# Form Classes
|
||||
# -------------------------------------------------
|
||||
|
||||
class RadioRenderer(RadioFieldRenderer):
|
||||
def render(self):
|
||||
output = []
|
||||
for widget in self:
|
||||
output.append(format_html(force_text(widget)))
|
||||
return mark_safe('\n'.join(output))
|
||||
|
||||
|
||||
class SearchLiaisonForm(forms.Form):
|
||||
text = forms.CharField(required=False)
|
||||
scope = forms.ChoiceField(choices=(("all", "All text fields"), ("title", "Title field")), required=False, initial='title', widget=forms.RadioSelect(renderer=RadioRenderer))
|
||||
source = forms.CharField(required=False)
|
||||
destination = forms.CharField(required=False)
|
||||
start_date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label='Start date', required=False)
|
||||
end_date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label='End date', required=False)
|
||||
|
||||
def get_results(self):
|
||||
results = LiaisonStatement.objects.filter(state__slug='posted')
|
||||
if self.is_bound:
|
||||
query = self.cleaned_data.get('text')
|
||||
if query:
|
||||
q = (Q(title__icontains=query) | Q(other_identifiers__icontains=query) | Q(body__icontains=query) |
|
||||
Q(attachments__title__icontains=query,liaisonstatementattachment__removed=False) |
|
||||
Q(technical_contacts__icontains=query) | Q(action_holder_contacts__icontains=query) |
|
||||
Q(cc_contacts=query) | Q(response_contacts__icontains=query))
|
||||
results = results.filter(q)
|
||||
|
||||
source = self.cleaned_data.get('source')
|
||||
if source:
|
||||
results = results.filter(Q(from_groups__name__icontains=source) | Q(from_groups__acronym__iexact=source))
|
||||
|
||||
destination = self.cleaned_data.get('destination')
|
||||
if destination:
|
||||
results = results.filter(Q(to_groups__name__icontains=destination) | Q(to_groups__acronym__iexact=destination))
|
||||
|
||||
start_date = self.cleaned_data.get('start_date')
|
||||
end_date = self.cleaned_data.get('end_date')
|
||||
events = None
|
||||
if start_date:
|
||||
events = LiaisonStatementEvent.objects.filter(type='posted', time__gte=start_date)
|
||||
if end_date:
|
||||
events = events.filter(time__lte=end_date)
|
||||
elif end_date:
|
||||
events = LiaisonStatementEvent.objects.filter(type='posted', time__lte=end_date)
|
||||
if events:
|
||||
results = results.filter(liaisonstatementevent__in=events)
|
||||
|
||||
results = results.distinct().order_by('title')
|
||||
return results
|
||||
|
||||
|
||||
class CustomModelMultipleChoiceField(forms.ModelMultipleChoiceField):
|
||||
'''If value is a QuerySet, return it as is (for use in widget.render)'''
|
||||
def prepare_value(self, value):
|
||||
if isinstance(value, QuerySet):
|
||||
return value
|
||||
if (hasattr(value, '__iter__') and
|
||||
not isinstance(value, six.text_type) and
|
||||
not hasattr(value, '_meta')):
|
||||
return [super(forms.ModelMultipleChoiceField, self).prepare_value(v) for v in value]
|
||||
return super(forms.ModelMultipleChoiceField, self).prepare_value(value)
|
||||
|
||||
|
||||
class LiaisonModelForm(BetterModelForm):
|
||||
'''Specify fields which require a custom widget or that are not part of the model'''
|
||||
from_groups = forms.ModelMultipleChoiceField(queryset=Group.objects.all(),label=u'Groups')
|
||||
from_contact = forms.EmailField()
|
||||
to_groups = forms.ModelMultipleChoiceField(queryset=Group.objects,label=u'Groups')
|
||||
deadline = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label='Deadline', required=True)
|
||||
related_to = SearchableLiaisonStatementsField(label=u'Related Liaison Statement', required=False)
|
||||
submitted_date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label='Submission date', required=True, 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)
|
||||
attachments = CustomModelMultipleChoiceField(queryset=Document.objects,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='',
|
||||
|
@ -45,100 +189,62 @@ class LiaisonForm(forms.Form):
|
|||
require=['id_attach_title', 'id_attach_file'],
|
||||
required_label='title and file'),
|
||||
required=False)
|
||||
|
||||
fieldsets = [('From', ('from_field', 'replyto')),
|
||||
('To', ('organization', 'to_poc')),
|
||||
('Other email addresses', ('response_contact', 'technical_contact', 'cc1')),
|
||||
('Purpose', ('purpose', 'deadline_date')),
|
||||
('Reference', ('related_to', )),
|
||||
('Liaison Statement', ('title', 'submitted_date', 'body', 'attachments')),
|
||||
('Add attachment', ('attach_title', 'attach_file', 'attach_button')),
|
||||
]
|
||||
class Meta:
|
||||
model = LiaisonStatement
|
||||
exclude = ('attachments','state','from_name','to_name')
|
||||
fieldsets = [('From', {'fields': ['from_groups','from_contact', 'response_contacts'], 'legend': ''}),
|
||||
('To', {'fields': ['to_groups','to_contacts'], 'legend': ''}),
|
||||
('Other email addresses', {'fields': ['technical_contacts','action_holder_contacts','cc_contacts'], 'legend': ''}),
|
||||
('Purpose', {'fields':['purpose', 'deadline'], 'legend': ''}),
|
||||
('Reference', {'fields': ['other_identifiers','related_to'], 'legend': ''}),
|
||||
('Liaison Statement', {'fields': ['title', 'submitted_date', 'body', 'attachments'], 'legend': ''}),
|
||||
('Add attachment', {'fields': ['attach_title', 'attach_file', 'attach_button'], 'legend': ''})]
|
||||
|
||||
def __init__(self, user, *args, **kwargs):
|
||||
super(LiaisonModelForm, self).__init__(*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.is_new = not self.instance.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
|
||||
if "approved" in self.fields:
|
||||
self.initial["approved"] = bool(self.instance.approved)
|
||||
self.fields["from_groups"].widget.attrs["placeholder"] = "Type in name to search for group"
|
||||
self.fields["to_groups"].widget.attrs["placeholder"] = "Type in name to search for group"
|
||||
self.fields["to_contacts"].label = 'Contacts'
|
||||
|
||||
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()
|
||||
# add email validators
|
||||
for field in ['from_contact','to_contacts','technical_contacts','action_holder_contacts','cc_contacts']:
|
||||
if field in self.fields:
|
||||
self.fields[field].validators.append(validate_emails)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.as_div()
|
||||
self.set_from_fields()
|
||||
self.set_to_fields()
|
||||
|
||||
def get_post_only(self):
|
||||
return False
|
||||
def clean_from_contact(self):
|
||||
contact = self.cleaned_data.get('from_contact')
|
||||
try:
|
||||
email = Email.objects.get(address=contact)
|
||||
except ObjectDoesNotExist:
|
||||
raise forms.ValidationError('Email address does not exist')
|
||||
return email
|
||||
|
||||
def set_required_fields(self):
|
||||
purpose = self.data.get('purpose', None)
|
||||
if purpose in ['1', '2']:
|
||||
self.fields['deadline_date'].required = True
|
||||
else:
|
||||
self.fields['deadline_date'].required = False
|
||||
def clean_cc_contacts(self):
|
||||
'''Return a comma separated list of addresses'''
|
||||
cc_contacts = self.cleaned_data.get('cc_contacts')
|
||||
return cc_contacts.replace('\r\n',',')
|
||||
|
||||
def reset_required_fields(self):
|
||||
self.fields['deadline_date'].required = True
|
||||
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'])
|
||||
|
||||
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:
|
||||
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
|
||||
# if purpose=response there must be a related statement
|
||||
purpose = LiaisonStatementPurposeName.objects.get(slug='response')
|
||||
if self.cleaned_data.get('purpose') == purpose and not self.cleaned_data.get('related_to'):
|
||||
self._errors['related_to'] = ErrorList([u'You must provide a related statement when purpose is In Response'])
|
||||
return self.cleaned_data
|
||||
|
||||
def full_clean(self):
|
||||
self.set_required_fields()
|
||||
super(LiaisonForm, self).full_clean()
|
||||
super(LiaisonModelForm, self).full_clean()
|
||||
self.reset_required_fields()
|
||||
|
||||
def has_attachments(self):
|
||||
|
@ -147,120 +253,41 @@ class LiaisonForm(forms.Form):
|
|||
return True
|
||||
return False
|
||||
|
||||
def check_email(self, value):
|
||||
if not value:
|
||||
return
|
||||
emails = value.split(',')
|
||||
for email in emails:
|
||||
name, addr = parseaddr(email)
|
||||
try:
|
||||
validate_email(addr)
|
||||
except ValidationError:
|
||||
raise forms.ValidationError('Invalid email address: %s' % addr)
|
||||
try:
|
||||
addr.encode('ascii')
|
||||
except UnicodeEncodeError as e:
|
||||
raise forms.ValidationError('Invalid email address: %s (check character %d)' % (addr,e.start))
|
||||
|
||||
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):
|
||||
return self.cleaned_data.get('cc1', '')
|
||||
def is_approved(self):
|
||||
assert NotImplemented
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
l = self.instance
|
||||
if not l:
|
||||
l = LiaisonStatement()
|
||||
super(LiaisonModelForm, self).save(*args,**kwargs)
|
||||
|
||||
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
|
||||
# set state for new statements
|
||||
if self.is_new:
|
||||
self.instance.change_state(state_id='pending',person=self.person)
|
||||
if self.is_approved():
|
||||
self.instance.change_state(state_id='posted',person=self.person)
|
||||
else:
|
||||
# create modified event
|
||||
LiaisonStatementEvent.objects.create(
|
||||
type_id='modified',
|
||||
by=self.person,
|
||||
statement=self.instance,
|
||||
desc='Statement Modified'
|
||||
)
|
||||
|
||||
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
|
||||
self.save_related_liaisons()
|
||||
self.save_attachments()
|
||||
|
||||
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', '-time')
|
||||
if e:
|
||||
liaison.from_contact = e[0]
|
||||
return self.instance
|
||||
|
||||
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()
|
||||
def save_attachments(self):
|
||||
'''Saves new attachments.
|
||||
Files come in with keys like "attach_file_N" where N is index of attachments
|
||||
displayed in the form. The attachment title is in the corresponding
|
||||
request.POST[attach_title_N]
|
||||
'''
|
||||
written = self.instance.attachments.all().count()
|
||||
for key in self.files.keys():
|
||||
title_key = key.replace('file', 'title')
|
||||
attachment_title = self.data.get(title_key)
|
||||
if not key.startswith('attach_file_') or not title_key in self.data.keys():
|
||||
continue
|
||||
attached_file = self.files.get(key)
|
||||
|
@ -270,205 +297,193 @@ class LiaisonForm(forms.Form):
|
|||
else:
|
||||
extension = ''
|
||||
written += 1
|
||||
name = instance.name() + ("-attachment-%s" % written)
|
||||
name = self.instance.name() + ("-attachment-%s" % written)
|
||||
attach, _ = Document.objects.get_or_create(
|
||||
name = name,
|
||||
defaults=dict(
|
||||
title = self.data.get(title_key),
|
||||
title = attachment_title,
|
||||
type_id = "liai-att",
|
||||
external_url = name + extension, # strictly speaking not necessary, but just for the time being ...
|
||||
)
|
||||
)
|
||||
instance.attachments.add(attach)
|
||||
LiaisonStatementAttachment.objects.create(statement=self.instance,document=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}
|
||||
if not self.is_new:
|
||||
# create modified event
|
||||
LiaisonStatementEvent.objects.create(
|
||||
type_id='modified',
|
||||
by=self.person,
|
||||
statement=self.instance,
|
||||
desc='Added attachment: {}'.format(attachment_title)
|
||||
)
|
||||
|
||||
def save_related_liaisons(self):
|
||||
rel = DocRelationshipName.objects.get(slug='refold')
|
||||
new_related = self.cleaned_data.get('related_to', [])
|
||||
# add new ones
|
||||
for stmt in new_related:
|
||||
self.instance.source_of_set.get_or_create(target=stmt,relationship=rel)
|
||||
# delete removed ones
|
||||
for related in self.instance.source_of_set.all():
|
||||
if related.target not in new_related:
|
||||
related.delete()
|
||||
|
||||
def set_from_fields(self):
|
||||
assert NotImplemented
|
||||
|
||||
def set_required_fields(self):
|
||||
purpose = self.data.get('purpose', None)
|
||||
if purpose in ['action', 'comment']:
|
||||
self.fields['deadline'].required = True
|
||||
else:
|
||||
exclude_filter = {}
|
||||
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
|
||||
self.fields['deadline'].required = False
|
||||
|
||||
def reset_required_fields(self):
|
||||
self.fields['deadline'].required = True
|
||||
|
||||
class IncomingLiaisonForm(LiaisonForm):
|
||||
def set_to_fields(self):
|
||||
assert NotImplemented
|
||||
|
||||
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)
|
||||
class IncomingLiaisonForm(LiaisonModelForm):
|
||||
def clean(self):
|
||||
if 'send' in self.data.keys() and self.get_post_only():
|
||||
raise forms.ValidationError('As an IETF Liaison Manager you can not send incoming liaison statements, you only can post them')
|
||||
return super(IncomingLiaisonForm, self).clean()
|
||||
|
||||
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 is_approved(self):
|
||||
'''Incoming Liaison Statements do not required approval'''
|
||||
return True
|
||||
|
||||
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"):
|
||||
from_groups = self.cleaned_data.get('from_groups')
|
||||
if has_role(self.user, "Secretariat") or is_authorized_individual(self.user,from_groups):
|
||||
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 set_from_fields(self):
|
||||
'''Set from_groups and from_contact options and initial value based on user
|
||||
accessing the form.'''
|
||||
if has_role(self.user, "Secretariat"):
|
||||
queryset = Group.objects.filter(type="sdo", state="active").order_by('name')
|
||||
else:
|
||||
queryset = Group.objects.filter(type="sdo", state="active", role__person=self.person, role__name__in=("liaiman", "auth")).distinct().order_by('name')
|
||||
self.fields['from_contact'].initial = self.person.role_set.filter(group=queryset[0]).first().email.address
|
||||
self.fields['from_contact'].widget.attrs['readonly'] = True
|
||||
self.fields['from_groups'].queryset = queryset
|
||||
self.fields['from_groups'].widget.submitter = unicode(self.person)
|
||||
|
||||
# if there's only one possibility make it the default
|
||||
if len(queryset) == 1:
|
||||
self.fields['from_groups'].initial = queryset
|
||||
|
||||
def set_to_fields(self):
|
||||
'''Set to_groups and to_contacts options and initial value based on user
|
||||
accessing the form. For incoming Liaisons, to_groups choices is the full set.
|
||||
'''
|
||||
self.fields['to_groups'].choices = get_internal_choices(None)
|
||||
|
||||
|
||||
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)
|
||||
class OutgoingLiaisonForm(LiaisonModelForm):
|
||||
from_contact = SearchableEmailField(only_users=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 which 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', 'body',
|
||||
'deadline_date', 'purpose', 'replyto', 'related_to')
|
||||
model = LiaisonStatement
|
||||
exclude = ('attachments','state','from_name','to_name','action_holder_contacts')
|
||||
# add approved field, no action_holder_contacts
|
||||
fieldsets = [('From', {'fields': ['from_groups','from_contact','response_contacts','approved'], 'legend': ''}),
|
||||
('To', {'fields': ['to_groups','to_contacts'], 'legend': ''}),
|
||||
('Other email addresses', {'fields': ['technical_contacts','cc_contacts'], 'legend': ''}),
|
||||
('Purpose', {'fields':['purpose', 'deadline'], 'legend': ''}),
|
||||
('Reference', {'fields': ['other_identifiers','related_to'], 'legend': ''}),
|
||||
('Liaison Statement', {'fields': ['title', 'submitted_date', 'body', 'attachments'], 'legend': ''}),
|
||||
('Add attachment', {'fields': ['attach_title', 'attach_file', 'attach_button'], 'legend': ''})]
|
||||
|
||||
def is_approved(self):
|
||||
return self.cleaned_data['approved']
|
||||
|
||||
def set_from_fields(self):
|
||||
'''Set from_groups and from_contact options and initial value based on user
|
||||
accessing the form'''
|
||||
self.fields['from_groups'].choices = get_internal_choices(self.user)
|
||||
if has_role(self.user, "Secretariat"):
|
||||
return
|
||||
|
||||
if self.person.role_set.filter(name='liaiman',group__state='active'):
|
||||
email = self.person.role_set.filter(name='liaiman',group__state='active').first().email.address
|
||||
elif self.person.role_set.filter(name__in=('ad','chair'),group__state='active'):
|
||||
email = self.person.role_set.filter(name__in=('ad','chair'),group__state='active').first().email.address
|
||||
else:
|
||||
email = self.person.email_address()
|
||||
self.fields['from_contact'].initial = email
|
||||
self.fields['from_contact'].widget.attrs['readonly'] = True
|
||||
|
||||
def set_to_fields(self):
|
||||
'''Set to_groups and to_contacts options and initial value based on user
|
||||
accessing the form'''
|
||||
# set options. if the user is a Liaison Manager and nothing more, reduce set to his SDOs
|
||||
if has_role(self.user, "Liaison Manager") and not self.person.role_set.filter(name__in=('ad','chair'),group__state='active'):
|
||||
queryset = Group.objects.filter(type="sdo", state="active", role__person=self.person, role__name="liaiman").distinct().order_by('name')
|
||||
else:
|
||||
# get all outgoing entities
|
||||
queryset = Group.objects.filter(type="sdo", state="active").order_by('name')
|
||||
|
||||
self.fields['to_groups'].queryset = queryset
|
||||
|
||||
# set initial
|
||||
if has_role(self.user, "Liaison Manager"):
|
||||
self.fields['to_groups'].initial = [queryset.first()]
|
||||
|
||||
class EditLiaisonForm(LiaisonModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(EditLiaisonForm, self).__init__(*args, **kwargs)
|
||||
self.edit = True
|
||||
self.fields['attachments'].initial = self.instance.liaisonstatementattachment_set.exclude(removed=True)
|
||||
related = [ str(x.pk) for x in self.instance.source_of_set.all() ]
|
||||
self.fields['related_to'].initial = ','.join(related)
|
||||
self.fields['submitted_date'].initial = self.instance.submitted
|
||||
|
||||
def set_from_field(self):
|
||||
self.fields['from_field'].initial = self.instance.from_name
|
||||
def save(self, *args, **kwargs):
|
||||
super(EditLiaisonForm, self).save(*args,**kwargs)
|
||||
if self.has_changed() and 'submitted_date' in self.changed_data:
|
||||
event = self.instance.liaisonstatementevent_set.filter(type='submitted').first()
|
||||
event.time = self.cleaned_data.get('submitted_date')
|
||||
event.save()
|
||||
|
||||
def set_replyto_field(self):
|
||||
self.fields['replyto'].initial = self.instance.reply_to
|
||||
return self.instance
|
||||
|
||||
def set_organization_field(self):
|
||||
self.fields['organization'].initial = self.instance.to_name
|
||||
def set_from_fields(self):
|
||||
'''Set from_groups and from_contact options and initial value based on user
|
||||
accessing the form.'''
|
||||
if self.instance.is_outgoing():
|
||||
self.fields['from_groups'].choices = get_internal_choices(self.user)
|
||||
else:
|
||||
if has_role(self.user, "Secretariat"):
|
||||
queryset = Group.objects.filter(type="sdo", state="active").order_by('name')
|
||||
else:
|
||||
queryset = Group.objects.filter(type="sdo", state="active", role__person=self.person, role__name__in=("liaiman", "auth")).distinct().order_by('name')
|
||||
self.fields['from_contact'].widget.attrs['readonly'] = True
|
||||
self.fields['from_groups'].queryset = queryset
|
||||
|
||||
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']
|
||||
def set_to_fields(self):
|
||||
'''Set to_groups and to_contacts options and initial value based on user
|
||||
accessing the form. For incoming Liaisons, to_groups choices is the full set.
|
||||
'''
|
||||
if self.instance.is_outgoing():
|
||||
# if the user is a Liaison Manager and nothing more, reduce to set to his SDOs
|
||||
if has_role(self.user, "Liaison Manager") and not self.person.role_set.filter(name__in=('ad','chair'),group__state='active'):
|
||||
queryset = Group.objects.filter(type="sdo", state="active", role__person=self.person, role__name="liaiman").distinct().order_by('name')
|
||||
else:
|
||||
# get all outgoing entities
|
||||
queryset = Group.objects.filter(type="sdo", state="active").order_by('name')
|
||||
self.fields['to_groups'].queryset = queryset
|
||||
else:
|
||||
self.fields['to_groups'].choices = get_internal_choices(None)
|
||||
|
||||
def liaison_form_factory(request, **kwargs):
|
||||
user = request.user
|
||||
force_incoming = 'incoming' in request.GET.keys()
|
||||
liaison = kwargs.pop('liaison', None)
|
||||
if liaison:
|
||||
return EditLiaisonForm(user, instance=liaison, **kwargs)
|
||||
if not force_incoming and can_add_outgoing_liaison(user):
|
||||
return OutgoingLiaisonForm(user, **kwargs)
|
||||
elif can_add_incoming_liaison(user):
|
||||
return IncomingLiaisonForm(user, **kwargs)
|
||||
return None
|
||||
|
||||
class EditAttachmentForm(forms.Form):
|
||||
title = forms.CharField(max_length=255)
|
||||
|
||||
|
|
|
@ -2,72 +2,56 @@ import datetime
|
|||
|
||||
from django.conf import settings
|
||||
from django.template.loader import render_to_string
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
|
||||
from ietf.utils.mail import send_mail_text
|
||||
from ietf.liaisons.utils import role_persons_with_fixed_email
|
||||
from ietf.liaisons.utils import approval_roles
|
||||
from ietf.group.models import Role
|
||||
|
||||
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_contact.split(',')
|
||||
cc = liaison.cc.split(',')
|
||||
if liaison.technical_contact:
|
||||
cc += liaison.technical_contact.split(',')
|
||||
if liaison.response_contact:
|
||||
cc += liaison.response_contact.split(',')
|
||||
to_email = liaison.to_contacts.split(',')
|
||||
cc = liaison.cc_contacts.split(',')
|
||||
if liaison.technical_contacts:
|
||||
cc += liaison.technical_contacts.split(',')
|
||||
if liaison.response_contacts:
|
||||
cc += liaison.response_contacts.split(',')
|
||||
bcc = ['statements@ietf.org']
|
||||
body = render_to_string('liaisons/liaison_mail.txt', dict(
|
||||
liaison=liaison,
|
||||
url=settings.IDTRACKER_BASE_URL + urlreverse("liaison_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,
|
||||
))
|
||||
body = render_to_string('liaisons/liaison_mail.txt', dict(liaison=liaison))
|
||||
|
||||
send_mail_text(request, to_email, from_email, subject, body, cc=", ".join(cc), bcc=", ".join(bcc))
|
||||
|
||||
def notify_pending_by_email(request, liaison):
|
||||
'''Send mail requesting approval of pending liaison statement. Send mail to
|
||||
the intersection of approvers for all from_groups
|
||||
'''
|
||||
approval_set = set(approval_roles(liaison.from_groups.first()))
|
||||
if liaison.from_groups.count() > 1:
|
||||
for group in liaison.from_groups.all():
|
||||
approval_set.intersection_update(approval_roles(group))
|
||||
to_emails = [ r.email.address for r in approval_set ]
|
||||
|
||||
# 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
|
||||
# Bug 880: https://trac.tools.ietf.org/tools/ietfdb/ticket/880
|
||||
#
|
||||
# from ietf.liaisons.utils import IETFHM
|
||||
#
|
||||
# from_entity = IETFHM.get_entity_by_key(liaison.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 = u'New Liaison Statement, "%s" needs your approval' % (liaison.title)
|
||||
from_email = settings.LIAISON_UNIVERSAL_FROM
|
||||
body = render_to_string('liaisons/pending_liaison_mail.txt', dict(
|
||||
liaison=liaison,
|
||||
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(request, to_email, from_email, subject, body)
|
||||
send_mail_text(request, ['statements@ietf.org'], from_email, subject, body)
|
||||
body = render_to_string('liaisons/pending_liaison_mail.txt', dict(liaison=liaison))
|
||||
send_mail_text(request, to_emails, from_email, subject, body)
|
||||
|
||||
def send_sdo_reminder(sdo):
|
||||
roles = Role.objects.filter(name="liaiman", group=sdo)
|
||||
if not roles: # no manager to contact
|
||||
return None
|
||||
|
||||
manager_role = roles[0]
|
||||
|
||||
|
||||
subject = 'Request for update of list of authorized individuals'
|
||||
to_email = manager_role.email.address
|
||||
name = manager_role.person.plain_name()
|
||||
|
||||
authorized_list = role_persons_with_fixed_email(sdo, "auth")
|
||||
authorized_list = Role.objects.filter(group=sdo, name='auth').select_related("person").distinct()
|
||||
body = render_to_string('liaisons/sdo_reminder.txt', dict(
|
||||
manager_name=name,
|
||||
sdo_name=sdo.name,
|
||||
individuals=authorized_list,
|
||||
))
|
||||
|
||||
|
||||
send_mail_text(None, to_email, settings.LIAISON_UNIVERSAL_FROM, subject, body)
|
||||
|
||||
return body
|
||||
|
@ -82,11 +66,11 @@ def possibly_send_deadline_reminder(liaison):
|
|||
1: 'tomorrow',
|
||||
0: 'today'
|
||||
}
|
||||
|
||||
|
||||
days_to_go = (liaison.deadline - datetime.date.today()).days
|
||||
if not (days_to_go < 0 or days_to_go in PREVIOUS_DAYS.keys()):
|
||||
return None # no reminder
|
||||
|
||||
|
||||
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)
|
||||
|
@ -95,20 +79,16 @@ def possibly_send_deadline_reminder(liaison):
|
|||
days_msg = 'expires %s' % PREVIOUS_DAYS[days_to_go]
|
||||
|
||||
from_email = settings.LIAISON_UNIVERSAL_FROM
|
||||
to_email = liaison.to_contact.split(',')
|
||||
cc = liaison.cc.split(',')
|
||||
if liaison.technical_contact:
|
||||
cc += liaison.technical_contact.split(',')
|
||||
if liaison.response_contact:
|
||||
cc += liaison.response_contact.split(',')
|
||||
to_email = liaison.to_contacts.split(',')
|
||||
cc = liaison.cc_contacts.split(',')
|
||||
if liaison.technical_contacts:
|
||||
cc += liaison.technical_contacts.split(',')
|
||||
if liaison.response_contacts:
|
||||
cc += liaison.response_contacts.split(',')
|
||||
bcc = 'statements@ietf.org'
|
||||
body = render_to_string('liaisons/liaison_deadline_mail.txt',
|
||||
dict(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,
|
||||
))
|
||||
|
||||
dict(liaison=liaison,days_msg=days_msg,))
|
||||
|
||||
send_mail_text(None, to_email, from_email, subject, body, cc=cc, bcc=bcc)
|
||||
|
||||
return body
|
||||
|
|
|
@ -11,10 +11,12 @@ class Command(BaseCommand):
|
|||
|
||||
def handle(self, *args, **options):
|
||||
today = datetime.date.today()
|
||||
|
||||
cutoff = today - datetime.timedelta(14)
|
||||
|
||||
for l in LiaisonStatement.objects.filter(action_taken=False, deadline__gte=cutoff).exclude(deadline=None):
|
||||
msgs = []
|
||||
for l in LiaisonStatement.objects.filter(deadline__gte=cutoff).exclude(tags__slug='taken'):
|
||||
r = possibly_send_deadline_reminder(l)
|
||||
if r:
|
||||
print 'Liaison %05s#: Deadline reminder sent!' % l.pk
|
||||
msgs.append('Liaison %05s#: Deadline reminder sent!' % l.pk)
|
||||
|
||||
return '\n'.join(msgs)
|
|
@ -10,10 +10,9 @@ class Command(BaseCommand):
|
|||
help = (u"Send a remind to each SDO Liaison Manager to update the list of persons authorized to send liaison statements on behalf of his SDO")
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option('-s', '--sdo-pk', dest='sdo_pk',
|
||||
help='Send the reminder to the SDO whith this primary key. If not provided reminder will be sent to all SDOs'),
|
||||
help='Send the reminder to the SDO with this primary key. If not provided reminder will be sent to all SDOs'),
|
||||
)
|
||||
|
||||
|
||||
def handle(self, *args, **options):
|
||||
sdo_pk = options.get('sdo_pk', None)
|
||||
return_output = options.get('return_output', False)
|
||||
|
@ -21,7 +20,6 @@ class Command(BaseCommand):
|
|||
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):
|
||||
sdos = Group.objects.filter(type="sdo").order_by('pk')
|
||||
if sdo_pk:
|
||||
|
@ -29,7 +27,7 @@ def send_reminders_to_sdos(sdo_pk=None):
|
|||
|
||||
if not sdos:
|
||||
print "No SDOs found!"
|
||||
|
||||
|
||||
msgs = []
|
||||
for sdo in sdos:
|
||||
body = send_sdo_reminder(sdo)
|
||||
|
@ -38,10 +36,6 @@ def send_reminders_to_sdos(sdo_pk=None):
|
|||
msg = u'%05s#: %s has no liaison manager' % (sdo.pk, sdo.name)
|
||||
else:
|
||||
msg = u'%05s#: %s mail sent!' % (sdo.pk, sdo.name)
|
||||
|
||||
print msg
|
||||
msgs.append(msg)
|
||||
|
||||
return msgs
|
||||
|
||||
|
||||
return msgs
|
|
@ -1,264 +1,45 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from south.v2 import SchemaMigration
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'LiaisonStatement'
|
||||
# db.create_table(u'liaisons_liaisonstatement', (
|
||||
# (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
# ('title', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
|
||||
# ('purpose', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.LiaisonStatementPurposeName'])),
|
||||
# ('body', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
# ('deadline', self.gf('django.db.models.fields.DateField')(null=True, blank=True)),
|
||||
# ('related_to', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['liaisons.LiaisonStatement'], null=True, blank=True)),
|
||||
# ('from_group', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='liaisonstatement_from_set', null=True, to=orm['group.Group'])),
|
||||
# ('from_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
|
||||
# ('from_contact', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['person.Email'], null=True, blank=True)),
|
||||
# ('to_group', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='liaisonstatement_to_set', null=True, to=orm['group.Group'])),
|
||||
# ('to_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
|
||||
# ('to_contact', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
|
||||
# ('reply_to', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
|
||||
# ('response_contact', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
|
||||
# ('technical_contact', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
|
||||
# ('cc', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
# ('submitted', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
|
||||
# ('modified', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
|
||||
# ('approved', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
|
||||
# ('action_taken', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
# ))
|
||||
# db.send_create_signal(u'liaisons', ['LiaisonStatement'])
|
||||
dependencies = [
|
||||
('doc', '0002_auto_20141222_1749'),
|
||||
('person', '0001_initial'),
|
||||
('name', '0007_populate_liaison_names'),
|
||||
]
|
||||
|
||||
# # Adding M2M table for field attachments on 'LiaisonStatement'
|
||||
# m2m_table_name = db.shorten_name(u'liaisons_liaisonstatement_attachments')
|
||||
# db.create_table(m2m_table_name, (
|
||||
# ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
||||
# ('liaisonstatement', models.ForeignKey(orm[u'liaisons.liaisonstatement'], null=False)),
|
||||
# ('document', models.ForeignKey(orm[u'doc.document'], null=False))
|
||||
# ))
|
||||
# db.create_unique(m2m_table_name, ['liaisonstatement_id', 'document_id'])
|
||||
pass
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# # Deleting model 'LiaisonStatement'
|
||||
# db.delete_table(u'liaisons_liaisonstatement')
|
||||
|
||||
# # Removing M2M table for field attachments on 'LiaisonStatement'
|
||||
# db.delete_table(db.shorten_name(u'liaisons_liaisonstatement_attachments'))
|
||||
pass
|
||||
|
||||
models = {
|
||||
u'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'auth.permission': {
|
||||
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
u'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
|
||||
},
|
||||
u'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
u'doc.document': {
|
||||
'Meta': {'object_name': 'Document'},
|
||||
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': u"orm['person.Person']"}),
|
||||
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['person.Email']", 'symmetrical': 'False', 'through': u"orm['doc.DocumentAuthor']", 'blank': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
|
||||
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
|
||||
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
|
||||
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': u"orm['person.Email']"}),
|
||||
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
|
||||
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
u'doc.documentauthor': {
|
||||
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
|
||||
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']"}),
|
||||
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
|
||||
},
|
||||
u'doc.state': {
|
||||
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': u"orm['doc.State']"}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.StateType']"}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'doc.statetype': {
|
||||
'Meta': {'object_name': 'StateType'},
|
||||
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
|
||||
},
|
||||
u'group.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']", 'null': 'True', 'blank': 'True'}),
|
||||
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': u"orm['doc.Document']"}),
|
||||
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
|
||||
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
|
||||
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupStateName']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupTypeName']", 'null': 'True'}),
|
||||
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'liaisons.liaisonstatement': {
|
||||
'Meta': {'object_name': 'LiaisonStatement'},
|
||||
'action_taken': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'approved': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'attachments': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.Document']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'body': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'cc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'deadline': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'from_contact': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']", 'null': 'True', 'blank': 'True'}),
|
||||
'from_group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'liaisonstatement_from_set'", 'null': 'True', 'to': u"orm['group.Group']"}),
|
||||
'from_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'purpose': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.LiaisonStatementPurposeName']"}),
|
||||
'related_to': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['liaisons.LiaisonStatement']", 'null': 'True', 'blank': 'True'}),
|
||||
'reply_to': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'response_contact': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'submitted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'technical_contact': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'to_contact': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'to_group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'liaisonstatement_to_set'", 'null': 'True', 'to': u"orm['group.Group']"}),
|
||||
'to_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
|
||||
},
|
||||
u'name.doctagname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.doctypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.groupstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.grouptypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.intendedstdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.liaisonstatementpurposename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.stdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.streamname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'person.email': {
|
||||
'Meta': {'object_name': 'Email'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
|
||||
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
|
||||
},
|
||||
u'person.person': {
|
||||
'Meta': {'object_name': 'Person'},
|
||||
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['liaisons']
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='LiaisonStatement',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('title', models.CharField(max_length=255, blank=True)),
|
||||
('body', models.TextField(blank=True)),
|
||||
('deadline', models.DateField(null=True, blank=True)),
|
||||
('from_name', models.CharField(help_text=b'Name of the sender body', max_length=255)),
|
||||
('to_name', models.CharField(help_text=b'Name of the recipient body', max_length=255)),
|
||||
('to_contact', models.CharField(help_text=b'Contacts at recipient body', max_length=255, blank=True)),
|
||||
('reply_to', models.CharField(max_length=255, blank=True)),
|
||||
('response_contact', models.CharField(max_length=255, blank=True)),
|
||||
('technical_contact', models.CharField(max_length=255, blank=True)),
|
||||
('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(to='doc.Document', blank=True)),
|
||||
('from_contact', models.ForeignKey(blank=True, to='person.Email', null=True)),
|
||||
('from_group', models.ForeignKey(related_name='liaisonstatement_from_set', blank=True, to='group.Group', help_text=b'Sender group, if it exists', null=True)),
|
||||
('purpose', models.ForeignKey(to='name.LiaisonStatementPurposeName')),
|
||||
('related_to', models.ForeignKey(blank=True, to='liaisons.LiaisonStatement', null=True)),
|
||||
('to_group', models.ForeignKey(related_name='liaisonstatement_to_set', blank=True, to='group.Group', help_text=b'Recipient group, if it exists', null=True)),
|
||||
],
|
||||
options={
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,229 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from south.v2 import DataMigration
|
||||
|
||||
class Migration(DataMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
for l in orm.LiaisonStatement.objects.filter(title=""):
|
||||
a = l.attachments.all().first()
|
||||
if a:
|
||||
l.title = a.title
|
||||
l.save()
|
||||
|
||||
def backwards(self, orm):
|
||||
pass
|
||||
|
||||
models = {
|
||||
u'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'auth.permission': {
|
||||
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
u'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
|
||||
},
|
||||
u'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
u'doc.document': {
|
||||
'Meta': {'object_name': 'Document'},
|
||||
'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': u"orm['person.Person']"}),
|
||||
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['person.Email']", 'symmetrical': 'False', 'through': u"orm['doc.DocumentAuthor']", 'blank': 'True'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
|
||||
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
|
||||
'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
|
||||
'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
|
||||
'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': u"orm['person.Email']"}),
|
||||
'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
|
||||
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
|
||||
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
u'doc.documentauthor': {
|
||||
'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
|
||||
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']"}),
|
||||
'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
|
||||
},
|
||||
u'doc.state': {
|
||||
'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': u"orm['doc.State']"}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.StateType']"}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'doc.statetype': {
|
||||
'Meta': {'object_name': 'StateType'},
|
||||
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
|
||||
},
|
||||
u'group.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}),
|
||||
'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']", 'null': 'True', 'blank': 'True'}),
|
||||
'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': u"orm['doc.Document']"}),
|
||||
'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
|
||||
'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
|
||||
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}),
|
||||
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupStateName']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupTypeName']", 'null': 'True'}),
|
||||
'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
u'liaisons.liaisonstatement': {
|
||||
'Meta': {'object_name': 'LiaisonStatement'},
|
||||
'action_taken': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'approved': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'attachments': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.Document']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'body': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'cc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'deadline': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'from_contact': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']", 'null': 'True', 'blank': 'True'}),
|
||||
'from_group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'liaisonstatement_from_set'", 'null': 'True', 'to': u"orm['group.Group']"}),
|
||||
'from_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'purpose': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.LiaisonStatementPurposeName']"}),
|
||||
'related_to': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['liaisons.LiaisonStatement']", 'null': 'True', 'blank': 'True'}),
|
||||
'reply_to': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'response_contact': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'submitted': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'technical_contact': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'to_contact': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'to_group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'liaisonstatement_to_set'", 'null': 'True', 'to': u"orm['group.Group']"}),
|
||||
'to_name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
|
||||
},
|
||||
u'name.doctagname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.doctypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.groupstatename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.grouptypename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.intendedstdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.liaisonstatementpurposename': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.stdlevelname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'name.streamname': {
|
||||
'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
|
||||
'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}),
|
||||
'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
|
||||
},
|
||||
u'person.email': {
|
||||
'Meta': {'object_name': 'Email'},
|
||||
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
|
||||
'person': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']", 'null': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
|
||||
},
|
||||
u'person.person': {
|
||||
'Meta': {'object_name': 'Person'},
|
||||
'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
|
||||
'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['liaisons']
|
||||
symmetrical = True
|
123
ietf/liaisons/migrations/0002_schema_changes.py
Normal file
123
ietf/liaisons/migrations/0002_schema_changes.py
Normal file
|
@ -0,0 +1,123 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('group', '0003_auto_20150304_0743'),
|
||||
('person', '0001_initial'),
|
||||
('doc', '0002_auto_20141222_1749'),
|
||||
('name', '0007_populate_liaison_names'),
|
||||
('liaisons', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='LiaisonStatementEvent',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('time', models.DateTimeField(auto_now_add=True)),
|
||||
('desc', models.TextField()),
|
||||
('by', models.ForeignKey(to='person.Person')),
|
||||
('statement', models.ForeignKey(to='liaisons.LiaisonStatement')),
|
||||
('type', models.ForeignKey(to='name.LiaisonStatementEventTypeName')),
|
||||
],
|
||||
options={'ordering': ['-time', '-id']},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='LiaisonStatementGroupContacts',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('contacts', models.CharField(max_length=255,blank=True)),
|
||||
('cc_contacts', models.CharField(max_length=255,blank=True)),
|
||||
('group', models.ForeignKey(to='group.Group', unique=True)),
|
||||
],
|
||||
options={
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='RelatedLiaisonStatement',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('relationship', models.ForeignKey(to='name.DocRelationshipName')),
|
||||
('source', models.ForeignKey(related_name='source_of_set', to='liaisons.LiaisonStatement')),
|
||||
('target', models.ForeignKey(related_name='target_of_set', to='liaisons.LiaisonStatement')),
|
||||
],
|
||||
options={
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='liaisonstatement',
|
||||
old_name='cc',
|
||||
new_name='cc_contacts',
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='liaisonstatement',
|
||||
old_name='to_contact',
|
||||
new_name='to_contacts',
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='liaisonstatement',
|
||||
old_name='technical_contact',
|
||||
new_name='technical_contacts',
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='liaisonstatement',
|
||||
old_name='response_contact',
|
||||
new_name='response_contacts',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='liaisonstatement',
|
||||
name='action_holder_contacts',
|
||||
field=models.CharField(help_text=b'Who makes sure action is completed', max_length=255, blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='liaisonstatement',
|
||||
name='from_groups',
|
||||
field=models.ManyToManyField(related_name='liaisonsatement_from_set', to='group.Group', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='liaisonstatement',
|
||||
name='other_identifiers',
|
||||
field=models.TextField(null=True, blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='liaisonstatement',
|
||||
name='state',
|
||||
field=models.ForeignKey(default=b'pending', to='name.LiaisonStatementState'),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='liaisonstatement',
|
||||
name='tags',
|
||||
field=models.ManyToManyField(to='name.LiaisonStatementTagName', null=True, blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='liaisonstatement',
|
||||
name='to_groups',
|
||||
field=models.ManyToManyField(related_name='liaisonsatement_to_set', to='group.Group', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='liaisonstatement',
|
||||
name='response_contacts',
|
||||
field=models.CharField(help_text=b'Where to send a response', max_length=255, blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='liaisonstatement',
|
||||
name='technical_contacts',
|
||||
field=models.CharField(help_text=b'Who to contact for clarification', max_length=255, blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
103
ietf/liaisons/migrations/0003_migrate_general.py
Normal file
103
ietf/liaisons/migrations/0003_migrate_general.py
Normal file
|
@ -0,0 +1,103 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
def migrate_tags(apps, schema_editor):
|
||||
LiaisonStatement = apps.get_model("liaisons", "LiaisonStatement")
|
||||
for s in LiaisonStatement.objects.filter(action_taken=True):
|
||||
s.tags.add('taken')
|
||||
|
||||
def migrate_state(apps, schema_editor):
|
||||
LiaisonStatement = apps.get_model("liaisons", "LiaisonStatement")
|
||||
for s in LiaisonStatement.objects.all():
|
||||
if s.approved:
|
||||
s.state_id='posted'
|
||||
else:
|
||||
s.state_id='pending'
|
||||
s.save()
|
||||
|
||||
def create_events(apps, schema_editor):
|
||||
LiaisonStatement = apps.get_model("liaisons", "LiaisonStatement")
|
||||
LiaisonStatementEvent = apps.get_model("liaisons", "LiaisonStatementEvent")
|
||||
Person = apps.get_model("person","Person")
|
||||
system = Person.objects.get(name="(system)")
|
||||
for s in LiaisonStatement.objects.all():
|
||||
if s.submitted:
|
||||
event = LiaisonStatementEvent.objects.create(
|
||||
type_id='submitted',
|
||||
by=system,
|
||||
statement=s,
|
||||
desc='Statement Submitted')
|
||||
event.time=s.submitted
|
||||
event.save()
|
||||
if s.approved:
|
||||
# create posted event
|
||||
event = LiaisonStatementEvent.objects.create(
|
||||
type_id='posted',
|
||||
by=system,
|
||||
statement=s,
|
||||
desc='Statement Posted')
|
||||
event.time=s.approved
|
||||
event.save()
|
||||
|
||||
# create approved event for outgoing only
|
||||
if s.approved != s.submitted:
|
||||
event = LiaisonStatementEvent.objects.create(
|
||||
type_id='approved',
|
||||
by=system,
|
||||
statement=s,
|
||||
desc='Statement Approved')
|
||||
event.time=s.approved
|
||||
event.save()
|
||||
|
||||
if s.modified and ( s.modified != s.submitted and s.modified != s.approved ):
|
||||
event = LiaisonStatementEvent.objects.create(
|
||||
type_id='modified',
|
||||
by=system,
|
||||
statement=s,
|
||||
desc='Statement Modified')
|
||||
event.time=s.modified
|
||||
event.save()
|
||||
|
||||
def migrate_relations(apps, schema_editor):
|
||||
LiaisonStatement = apps.get_model("liaisons", "LiaisonStatement")
|
||||
RelatedLiaisonStatement = apps.get_model("liaisons", "RelatedLiaisonStatement")
|
||||
for liaison in LiaisonStatement.objects.filter(related_to__isnull=False):
|
||||
RelatedLiaisonStatement.objects.create(
|
||||
source=liaison,
|
||||
target=liaison.related_to,
|
||||
relationship_id='refold')
|
||||
|
||||
# XXX: Now done in migrate_groups
|
||||
def merge_reply_to(apps, schema_editor):
|
||||
"""Merge contents of reply_to field into response_contact and create comment Event"""
|
||||
LiaisonStatement = apps.get_model("liaisons", "LiaisonStatement")
|
||||
LiaisonStatementEvent = apps.get_model("liaisons", "LiaisonStatementEvent")
|
||||
Person = apps.get_model("person","Person")
|
||||
system = Person.objects.get(name="(system)")
|
||||
for liaison in LiaisonStatement.objects.exclude(reply_to=''):
|
||||
if liaison.reply_to in liaison.response_contacts:
|
||||
continue
|
||||
LiaisonStatementEvent.objects.create(
|
||||
type_id='comment',
|
||||
statement=liaison,
|
||||
desc='Merged reply_to field into response_contacts\nOriginal reply_to: %s\nOriginal response_contacts: %s' % (liaison.reply_to, liaison.response_contacts),
|
||||
by=system
|
||||
)
|
||||
liaison.response_contacts += ',%s' % liaison.reply_to
|
||||
liaison.save()
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('liaisons', '0002_schema_changes'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrate_tags),
|
||||
migrations.RunPython(migrate_state),
|
||||
migrations.RunPython(create_events),
|
||||
migrations.RunPython(migrate_relations),
|
||||
#migrations.RunPython(merge_reply_to),
|
||||
]
|
49
ietf/liaisons/migrations/0004_migrate_attachments.py
Normal file
49
ietf/liaisons/migrations/0004_migrate_attachments.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
# This migration handles converting a standard Many-to-Many field to one
|
||||
# with a through table
|
||||
|
||||
def copy_attachments(apps, schema_editor):
|
||||
LiaisonStatement = apps.get_model("liaisons", "LiaisonStatement")
|
||||
LiaisonStatementAttachment = apps.get_model("liaisons", "LiaisonStatementAttachment")
|
||||
for liaison in LiaisonStatement.objects.all():
|
||||
for doc in liaison.attachments.all():
|
||||
LiaisonStatementAttachment.objects.create(
|
||||
statement=liaison,
|
||||
document=doc,
|
||||
removed=False)
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('liaisons', '0003_migrate_general'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='LiaisonStatementAttachment',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('removed', models.BooleanField(default=False)),
|
||||
('document', models.ForeignKey(to='doc.Document')),
|
||||
('statement', models.ForeignKey(to='liaisons.LiaisonStatement')),
|
||||
],
|
||||
options={
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.RunPython(copy_attachments),
|
||||
migrations.RemoveField(
|
||||
model_name='liaisonstatement',
|
||||
name='attachments',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='liaisonstatement',
|
||||
name='attachments',
|
||||
field=models.ManyToManyField(to='doc.Document', through='liaisons.LiaisonStatementAttachment', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
792
ietf/liaisons/migrations/0005_migrate_groups.py
Normal file
792
ietf/liaisons/migrations/0005_migrate_groups.py
Normal file
|
@ -0,0 +1,792 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
def create_new_groups(apps, schema_editor):
|
||||
Group = apps.get_model("group","Group")
|
||||
for group in NEW_GROUPS:
|
||||
if group[2]:
|
||||
#print "Get parent: {}".format(group[2])
|
||||
parent = Group.objects.get(acronym=group[2])
|
||||
else:
|
||||
parent = None
|
||||
Group.objects.create(
|
||||
acronym=group[0],
|
||||
name=group[1],
|
||||
parent=parent,
|
||||
type_id='sdo',
|
||||
state_id=group[3])
|
||||
|
||||
def change_acronyms(apps, schema_editor):
|
||||
'''Modify some existing groups'''
|
||||
Group = apps.get_model("group","Group")
|
||||
for old,new in CHANGE_ACRONYM:
|
||||
group = Group.objects.get(acronym=old)
|
||||
group.acronym = new
|
||||
group.save()
|
||||
|
||||
def set_parents(apps, schema_editor):
|
||||
'''Modify some existing groups'''
|
||||
Group = apps.get_model("group","Group")
|
||||
for child_acronym,parent_acronym in SET_PARENT:
|
||||
#print "Setting parent {}:{}".format(child_acronym,parent_acronym)
|
||||
child = Group.objects.get(acronym=child_acronym)
|
||||
parent = Group.objects.get(acronym=parent_acronym)
|
||||
child.parent = parent
|
||||
child.save()
|
||||
|
||||
def reassign_groups(apps,schema_editor):
|
||||
'''For Statements that have a multi to_group assignment, remove the group
|
||||
assignment and populate the to_name field for conversion to multiple groups
|
||||
in later function'''
|
||||
LiaisonStatement = apps.get_model("liaisons", "LiaisonStatement")
|
||||
for acronym,name in MULTI_TO_GROUPS:
|
||||
for stmt in LiaisonStatement.objects.filter(to_group__acronym=acronym):
|
||||
stmt.to_name=name
|
||||
stmt.to_group=None
|
||||
stmt.save()
|
||||
|
||||
def cleanup_groups(apps, schema_editor):
|
||||
Group = apps.get_model("group","Group")
|
||||
for group,x in MULTI_TO_GROUPS:
|
||||
Group.objects.get(acronym=group).delete()
|
||||
|
||||
def copy_to_group(apps, schema_editor):
|
||||
'''For this migration we are favoring the value in to_name over to_group. Based
|
||||
on observation there are statements with multiple groups in the to_name but
|
||||
restricted to one to_group.'''
|
||||
LiaisonStatement = apps.get_model("liaisons", "LiaisonStatement")
|
||||
Group = apps.get_model("group","Group")
|
||||
for s in LiaisonStatement.objects.all():
|
||||
if s.to_name and s.to_name in TO_NAME_MAPPING:
|
||||
if TO_NAME_MAPPING[s.to_name]:
|
||||
got_exception = False
|
||||
for acronym in TO_NAME_MAPPING[s.to_name]:
|
||||
try:
|
||||
s.to_groups.add(Group.objects.get(acronym=acronym))
|
||||
except Group.DoesNotExist:
|
||||
print "Group Does Not Exist: {},{},{}".format(s.pk,s.to_name,acronym)
|
||||
got_exception = True
|
||||
if not got_exception:
|
||||
s.to_name = ''
|
||||
s.save()
|
||||
else:
|
||||
print "{}:{} empty to_group mapping".format(s.pk,s.to_name)
|
||||
|
||||
elif s.to_group:
|
||||
s.to_groups.add(s.to_group)
|
||||
s.to_name = ''
|
||||
s.save()
|
||||
|
||||
def copy_from_group(apps, schema_editor):
|
||||
LiaisonStatement = apps.get_model("liaisons", "LiaisonStatement")
|
||||
Group = apps.get_model("group","Group")
|
||||
for s in LiaisonStatement.objects.all():
|
||||
if s.from_name and s.from_name in FROM_NAME_MAPPING:
|
||||
if FROM_NAME_MAPPING[s.from_name]:
|
||||
got_exception = False
|
||||
for acronym in FROM_NAME_MAPPING[s.from_name]:
|
||||
try:
|
||||
s.from_groups.add(Group.objects.get(acronym=acronym))
|
||||
except Group.DoesNotExist:
|
||||
print "Group Does Not Exist: {}".format(acronym)
|
||||
got_exception = True
|
||||
if not got_exception:
|
||||
s.from_name = ''
|
||||
s.save()
|
||||
else:
|
||||
print "{}:{} empty from_group mapping".format(s.pk,s.from_name)
|
||||
elif s.from_group:
|
||||
s.from_groups.add(s.from_group)
|
||||
s.from_name = ''
|
||||
s.save()
|
||||
else:
|
||||
print "from_name not mapped and no from_group {}".format(s.pk)
|
||||
|
||||
# set from_contact
|
||||
#if s.from_contact:
|
||||
# for fg in s.fromgroup_set.all():
|
||||
# fg.contact = s.from_contact
|
||||
# fg.save()
|
||||
|
||||
def set_default_poc(apps, schema_editor):
|
||||
"""Set default group POC if there is only one unique value"""
|
||||
LiaisonStatementGroupContacts = apps.get_model("liaisons", "LiaisonStatementGroupContacts")
|
||||
Group = apps.get_model("group", "Group")
|
||||
for group in Group.objects.filter(liaisonstatement_to_set__isnull=False).distinct():
|
||||
contacts = set()
|
||||
for stmt in group.liaisonstatement_to_set.all():
|
||||
if stmt.to_contacts:
|
||||
contacts.add(stmt.to_contacts)
|
||||
if len(contacts) == 1:
|
||||
LiaisonStatementGroupContacts.objects.create(group=group,contacts=contacts.pop())
|
||||
|
||||
# do explicit mappings
|
||||
for acronym,contacts in DEFAULT_POC.items():
|
||||
group = Group.objects.get(acronym=acronym)
|
||||
try:
|
||||
lsgc = LiaisonStatementGroupContacts.objects.get(group=group)
|
||||
lsgc.contacts = contacts
|
||||
lsgc.save()
|
||||
except LiaisonStatementGroupContacts.DoesNotExist:
|
||||
LiaisonStatementGroupContacts.objects.create(group=group,contacts=contacts)
|
||||
|
||||
|
||||
def set_cc_contacts(apps, schema_editor):
|
||||
"""Set initial LiaisonStatementGroupContacts.cc_contacts"""
|
||||
LiaisonStatementGroupContacts = apps.get_model("liaisons", "LiaisonStatementGroupContacts")
|
||||
Group = apps.get_model("group", "Group")
|
||||
cc_contacts = 'itu-t-liaison@iab.org'
|
||||
for group in Group.objects.filter(acronym__startswith='itu'):
|
||||
lsgc = group.liaisonstatementgroupcontacts_set.first()
|
||||
if lsgc:
|
||||
lsgc.cc_contacts = cc_contacts
|
||||
lsgc.save()
|
||||
else:
|
||||
LiaisonStatementGroupContacts.objects.create(group=group,cc_contacts=cc_contacts)
|
||||
|
||||
def explicit_mappings(apps, schema_editor):
|
||||
"""In some cases the to_name cannot be mapped one-to-one with a group. The
|
||||
following liaison statements are modified individually
|
||||
"""
|
||||
#LiaisonStatementFromGroup = apps.get_model("liaisons", "LiaisonStatmentFromGroup")
|
||||
LiaisonStatement = apps.get_model("liaisons", "LiaisonStatement")
|
||||
Group = apps.get_model("group", "Group")
|
||||
|
||||
def _setgroup(to=None,frm=None,pks=None):
|
||||
for pk in pks:
|
||||
s = LiaisonStatement.objects.get(pk=pk)
|
||||
if to:
|
||||
s.to_groups.add(*Group.objects.filter(acronym__in=to))
|
||||
s.to_name = ''
|
||||
if frm:
|
||||
#for acronym in frm:
|
||||
# LiaisonStatementFromGroup.objects.create(statement=s,group=Group.objects.get(acronym=acronym))
|
||||
s.from_groups.add(*Group.objects.filter(acronym__in=frm))
|
||||
s.from_name = ''
|
||||
s.save()
|
||||
|
||||
_setgroup(to=['ietf'],pks=[116,782,796,797,823,835,836,837,840])
|
||||
_setgroup(to=['sipping'],pks=[809])
|
||||
_setgroup(to=['ieprep'],pks=[810])
|
||||
_setgroup(to=['atm-forum'],frm=['megaco'],pks=[816])
|
||||
_setgroup(to=['ccamp'],pks=[827,829])
|
||||
_setgroup(to=['sub','tsv'],pks=[828])
|
||||
_setgroup(to=['sigtran'],pks=[830])
|
||||
_setgroup(to=['irtf'],pks=[831,832,833,834])
|
||||
_setgroup(to=['rmt'],pks=[838,839])
|
||||
_setgroup(to=['ietf','iana'],pks=[841])
|
||||
_setgroup(to=['isoc','iana'],pks=[842])
|
||||
_setgroup(to=['ietf','avt'],pks=[811,812])
|
||||
_setgroup(to=['avt'],pks=[822])
|
||||
# 821 / 824
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('liaisons', '0004_migrate_attachments'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(change_acronyms),
|
||||
migrations.RunPython(create_new_groups),
|
||||
migrations.RunPython(set_parents),
|
||||
migrations.RunPython(reassign_groups),
|
||||
migrations.RunPython(copy_to_group),
|
||||
migrations.RunPython(copy_from_group),
|
||||
migrations.RunPython(set_default_poc),
|
||||
migrations.RunPython(set_cc_contacts),
|
||||
migrations.RunPython(cleanup_groups),
|
||||
migrations.RunPython(explicit_mappings),
|
||||
]
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# x_name to group mappings
|
||||
# -----------------------------------------------------------
|
||||
NEW_GROUPS = [
|
||||
('3gpp-tsgsa','SGPP TSG SA','3gpp','active'),
|
||||
('3gpp-tsgsa-sa2','3GPP TSG SA WG2','3gpp-tsgsa','active'),
|
||||
('3gpp-tsgsa-sa3','3GPP TSG SA WG3','3gpp-tsgsa','active'),
|
||||
('3gpp-tsgct','SGPP TSG CT','3gpp','active'),
|
||||
('3gpp-tsgct-ct1','3GPP TSG CT WG1','3gpp-tsgct','active'),
|
||||
('3gpp-tsgct-ct4','3GPP TSG CT WG4','3gpp-tsgct','active'),
|
||||
('3gpp-tsgran','SGPP TSG RAN','3gpp','active'),
|
||||
('3gpp-tsgran-ran2','3GPP TSG RAN WG2','3gpp-tsgran','active'),
|
||||
('3gpp-tsgt-wg2','3GPP-TSGT-WG2','3gpp','active'),
|
||||
('acif','Australian Communications Industry Forum',None,'active'),
|
||||
('arib','Association of Radio Industries and Business',None,'active'),
|
||||
('ashrae','American Society of Heating, Refrigerating, and Air-Conditioning Engineers',None,'active'),
|
||||
('atis','ATIS',None,'active'),
|
||||
('atm-forum','ATM Forum',None,'active'),
|
||||
('ccsa','China Communications Standards Association',None,'active'),
|
||||
('dlna','Digital Living Network Alliance',None,'active'),
|
||||
('dsl-forum','DSL Forum',None,'active'),
|
||||
('dsl-forum-twg','DSL Forum Architecture & Transport Working Group','dsl-forum','active'),
|
||||
('dvb-tm-ipi','DVB TM-IPI',None,'active'),
|
||||
('epcglobal','EPCGlobal',None,'active'),
|
||||
('etsi','ETSI',None,'active'),
|
||||
('etsi-at-digital','ETSI AT Digital','etsi','active'),
|
||||
('etsi-bran','ETSI BRAN','etsi','active'),
|
||||
('etsi-dect','ETSI DECT','etsi','active'),
|
||||
('etsi-emtel','ETSI EMTEL','etsi','active'),
|
||||
('etsi-tc-hf','ETSI TC HF','etsi','active'),
|
||||
('etsi-tispan','ETSI TISPAN','etsi','active'),
|
||||
('etsi-tispan-wg4','ETSI TISPAN WG4','etsi-tispan','active'),
|
||||
('etsi-tispan-wg5','ETSI TISPAN WG5','etsi-tispan','active'),
|
||||
('femto-forum','Femto Forum',None,'active'),
|
||||
('gsma','GSMA',None,'active'),
|
||||
('gsma-wlan','GSMA WLAN','gsma','active'),
|
||||
('incits-t11-5','INCITS T11.5',None,'active'),
|
||||
('isma','Internet Streaming Media Alliance',None,'active'),
|
||||
('itu','ITU',None,'active'),
|
||||
('itu-r-wp5a','ITU-R-WP5A','itu-r','active'),
|
||||
('itu-r-wp5d','ITU-R-WP5D','itu-r','active'),
|
||||
('itu-r-wp8a','ITU-R-WP8A','itu-r','active'),
|
||||
('itu-r-wp8f','ITU-R-WP8F','itu-r','active'),
|
||||
('itu-t-ipv6-group','ITU-T-IPV6-GROUP','itu-t','active'),
|
||||
('itu-t-fg-cloud','ITU-T-FG-CLOUD','itu-t','conclude'),
|
||||
('itu-t-fg-iptv','ITU-T-FG-IPTV','itu-t','conclude'),
|
||||
('itu-t-fg-ngnm','ITU-T-FG-NGNM','itu-t','conclude'),
|
||||
('itu-t-jca-idm','ITU-T-JCA-IDM','itu-t','active'),
|
||||
('itu-t-ngnmfg','ITU-T-NGNMFG','itu-t','active'),
|
||||
('itu-t-sg-4','ITU-T-SG-4','itu-t','conclude'),
|
||||
('itu-t-sg-6','ITU-T-SG-6','itu-t','conclude'),
|
||||
('itu-t-sg-7','ITU-T-SG-7','itu-t','conclude'),
|
||||
('itu-t-sg-8','ITU-T-SG-8','itu-t','conclude'),
|
||||
('itu-t-sg-9','ITU-T-SG-9','itu-t','active'),
|
||||
('itu-t-sg-2-q1','ITU-T-SG-2-Q1','itu-t-sg-2','active'),
|
||||
('itu-t-sg-11-q5','ITU-T-SG-11-Q5','itu-t-sg-11','active'),
|
||||
('itu-t-sg-11-wp2','ITU-T-SG-11-WP2','itu-t-sg-11','active'),
|
||||
('itu-t-sg-12-q12','ITU-T-SG-12-Q12','itu-t-sg-12','active'),
|
||||
('itu-t-sg-12-q17','ITU-T-SG-12-Q17','itu-t-sg-12','active'),
|
||||
('itu-t-sg-13-q3','ITU-T-SG-13-Q3','itu-t-sg-13','active'),
|
||||
('itu-t-sg-13-q5','ITU-T-SG-13-Q5','itu-t-sg-13','active'),
|
||||
('itu-t-sg-13-q7','ITU-T-SG-13-Q7','itu-t-sg-13','active'),
|
||||
('itu-t-sg-13-q9','ITU-T-SG-13-Q9','itu-t-sg-13','active'),
|
||||
('itu-t-sg-13-q11','ITU-T-SG-13-Q11','itu-t-sg-13','active'),
|
||||
('itu-t-sg-13-wp3','ITU-T-SG-13-WP3','itu-t-sg-13','conclude'),
|
||||
('itu-t-sg-13-wp4','ITU-T-SG-13-WP4','itu-t-sg-13','conclude'),
|
||||
('itu-t-sg-13-wp5','ITU-T-SG-13-WP5','itu-t-sg-13','conclude'),
|
||||
('itu-t-sg-14','ITU-T-SG-14','itu-t','active'),
|
||||
('itu-t-sg-15-q1','ITU-T-SG-15-Q1','itu-t-sg-15','active'),
|
||||
('itu-t-sg-15-q3','ITU-T-SG-15-Q3','itu-t-sg-15','active'),
|
||||
('itu-t-sg-15-q4','ITU-T-SG-15-Q4','itu-t-sg-15','active'),
|
||||
('itu-t-sg-15-q6','ITU-T-SG-15-Q6','itu-t-sg-15','active'),
|
||||
('itu-t-sg-15-q9','ITU-T-SG-15-Q9','itu-t-sg-15','active'),
|
||||
('itu-t-sg-15-q10','ITU-T-SG-15-Q10','itu-t-sg-15','active'),
|
||||
('itu-t-sg-15-q11','ITU-T-SG-15-Q11','itu-t-sg-15','active'),
|
||||
('itu-t-sg-15-q12','ITU-T-SG-15-Q12','itu-t-sg-15','active'),
|
||||
('itu-t-sg-15-q14','ITU-T-SG-15-Q14','itu-t-sg-15','active'),
|
||||
('itu-t-sg-15-q15','ITU-T-SG-15-Q15','itu-t-sg-15','active'),
|
||||
('itu-t-sg-15-wp1','ITU-T-SG-15-WP1','itu-t-sg-15','active'),
|
||||
('itu-t-sg-15-wp3','ITU-T-SG-15-WP3','itu-t-sg-15','active'),
|
||||
('itu-t-sg-16-q8','ITU-T-SG-16-Q8','itu-t-sg-16','active'),
|
||||
('itu-t-sg-16-q9','ITU-T-SG-16-Q9','itu-t-sg-16','active'),
|
||||
('itu-t-sg-16-q10','ITU-T-SG-16-Q10','itu-t-sg-16','active'),
|
||||
#('itu-t-sg-17-tsb','ITU-T-SG-17-TSB','itu-t-sg-17','active'),
|
||||
('itu-t-sg-17-q2','ITU-T-SG-17-Q2','itu-t-sg-17','active'),
|
||||
#('itu-t-sg-17-q4','ITU-T-SG-17-Q4','itu-t-sg-17','active'),
|
||||
('itu-t-sg-20','ITU-T-SG-20','itu-t','active'),
|
||||
('ieee','IEEE',None,'active'),
|
||||
('ieee-802','IEEE 802','ieee','active'),
|
||||
('ieee-802-ec','IEEE 802 Executive Committee','ieee','active'),
|
||||
('ieee-802-21','IEEE 802.21','ieee-802','active'),
|
||||
('ieee-sa-ngson','IEEE SA NGSON','ieee-sa','active'),
|
||||
('iso-iec-jtc1','ISO/IEC JTC1',None,'active'),
|
||||
('iso-iec-jtc1-sc29-wg1','ISO/IEC JTC1 SC29 WG1','iso-iec-jtc1-sc29','active'),
|
||||
('iso-iec-jtc1-sc31','ISO/IEC JTC1 SC31','iso-iec-jtc1','active'),
|
||||
('iso-iec-jtc1-sc31-wg4','ISO/IEC JTC1 SC31 WG4','iso-iec-jtc1-sc31','active'),
|
||||
('iso-iec-jtc1-sgsn','ISO/IEC JTC1 SGSN','iso-iec-jtc1','active'),
|
||||
('iso-iec-jtc1-wg7','ISO/IEC JTC1 WG7','iso-iec-jtc1','active'),
|
||||
('mead','IETF MEAD Team','rtg','active'),
|
||||
('mfa-forum','MFA Forum',None,'active'),
|
||||
('mpeg','MPEG',None,'active'),
|
||||
('mpls-forum','MPLS Forum',None,'active'),
|
||||
('mfa','MPLS and Frame Relay Alliance',None,'active'),
|
||||
('nanc-lnpa-wg','NANC LNPA WG',None,'active'),
|
||||
('nmnro','National, Multi-National or Regional Organizations',None,'active'),
|
||||
('oma','OMA',None,'active'),
|
||||
('oma-bcast','OMA BCAST','oma','active'),
|
||||
('oma-com-cab','OMA COM CAB','oma','active'),
|
||||
('oma-com-cpm','OMA COM CPM','oma','active'),
|
||||
('oma-mwg','OMA MWG','oma','active'),
|
||||
('oma-mwg-mem','OMA MWG-MEM','oma-mwg','active'),
|
||||
('oma-pag-wg','OMA PAG WG','oma','active'),
|
||||
('oma-tp','OMA TP','oma','active'),
|
||||
('opif','Open IPTV Forum',None,'active'),
|
||||
('t1m1','T1M1',None,'active'),
|
||||
('t1s1','T1S1',None,'active'),
|
||||
('t1x1','T1X1',None,'active'),
|
||||
('tia','TIA',None,'active'),
|
||||
('tmoc','TMOC',None,'active'),
|
||||
('w3c-geolocation-wg','W3C Geolocation WG','w3c','active'),
|
||||
('w3c-mmi','W3C MMI','w3c','active'),
|
||||
('wifi-alliance','Wifi Alliance',None,'active'),
|
||||
('wig','WIG',None,'active'),
|
||||
]
|
||||
|
||||
CHANGE_ACRONYM = [
|
||||
('ieee-8021','ieee-802-1'),
|
||||
('ieee-8023','ieee-802-3'),
|
||||
('ieee-80211','ieee-802-11'),
|
||||
('ieee-80216','ieee-802-16'),
|
||||
('ieee-80223','ieee-802-23'),
|
||||
('isoiec-jtc1-sc2','iso-iec-jtc1-sc2'),
|
||||
('isoiec-jtc1-sc6','iso-iec-jtc1-sc6'),
|
||||
('isoiec-jtc1-sc29','iso-iec-jtc1-sc29'),
|
||||
('isoiec-jtc-1sc-29wg-11','iso-iec-jtc1-sc29-wg11'),
|
||||
('itu-t-fgd','itu-t-fg-dist'),
|
||||
('itu-t-sg17-q4','itu-t-sg-17-q4'),
|
||||
('itu-t-sg17-tsb','itu-t-sg-17-tsb'),
|
||||
('ITU-T-SG5','itu-t-sg-5'),
|
||||
('3GPP-TSG-SA-WG4','3gpp-tsgsa-sa4'),
|
||||
('IEEE-802-OmniRAN','ieee-802-ec-omniran'),
|
||||
]
|
||||
|
||||
SET_PARENT = [
|
||||
('itu-t','itu'),
|
||||
('itu-r','itu'),
|
||||
('itu-t-jca-cloud','itu-t'),
|
||||
('itu-t-jca-cop','itu-t'),
|
||||
('itu-t-jca-sdn','itu-t'),
|
||||
('itu-t-mpls','itu-t'),
|
||||
('itu-t-sg-2','itu-t'),
|
||||
('itu-t-sg-3','itu-t'),
|
||||
('itu-t-sg-11','itu-t'),
|
||||
('itu-t-sg-12','itu-t'),
|
||||
('itu-t-sg-13','itu-t'),
|
||||
('itu-t-sg-15','itu-t'),
|
||||
('itu-t-sg-16','itu-t'),
|
||||
('itu-t-sg-17','itu-t'),
|
||||
('itu-t-tsag','itu-t'),
|
||||
('ieee-sa','ieee'),
|
||||
('ieee-802-1','ieee-802'),
|
||||
('ieee-802-3','ieee-802'),
|
||||
('ieee-802-11','ieee-802'),
|
||||
('ieee-802-16','ieee-802'),
|
||||
('ieee-802-23','ieee-802'),
|
||||
('ieee-802-ec-omniran','ieee-802-ec'),
|
||||
('iso-iec-jtc1-sc2','iso-iec-jtc1'),
|
||||
('iso-iec-jtc1-sc6','iso-iec-jtc1'),
|
||||
('iso-iec-jtc1-sc7','iso-iec-jtc1'),
|
||||
('iso-iec-jtc1-sc27','iso-iec-jtc1'),
|
||||
('iso-iec-jtc1-sc29','iso-iec-jtc1'),
|
||||
('iso-iec-jtc1-sc29-wg11','iso-iec-jtc1-sc29'),
|
||||
]
|
||||
|
||||
MULTI_TO_GROUPS = [
|
||||
('itu-t-sg15-q9-q10-q12-and-q14','ITU-T SG 15 Q9, Q10, Q12 and Q14'),
|
||||
('itu-t-sg12-q-12-17','ITU-T SG 12, Q12, Q17'),
|
||||
]
|
||||
|
||||
TO_NAME_MAPPING = {
|
||||
u'(bwijnen@lucent.com) Bert Wijnen': [u'sming'],
|
||||
u'(lyong@ciena.com)Lyndon Ong': [u'itu-t-sg-15'],
|
||||
#u'(sob@harvard.edu) Scott Bradner': None, # this is a bunch (explicit)
|
||||
u'(sob@harvard.edu)Scott Bradner': ['irtf'], # this is 833
|
||||
u'3GPP SA WG4': [u'3gpp-tsgsa-sa4'],
|
||||
u'3GPP SA2': [u'3gpp-tsgsa-sa2'],
|
||||
u'3GPP TSG CT WG4': [u'3gpp-tsgct-ct4'],
|
||||
u'3GPP TSG RAN WG2': [u'3gpp-tsgran-ran2'],
|
||||
u'3GPP TSG SA WG4': [u'3gpp-tsgsa-sa4'],
|
||||
u'3GPP, 3GPP2, ARIB, ATIS, CCSA, ETSI, ETSI-DECT, ETSI-BRAN, IEEE, IETF,': [u'ietf'],
|
||||
u'3GPP/IETF and 3GPP/ITU-T Co-ordinator': ['3gpp-tsgct-ct1'],
|
||||
u'ACIF, ARIB, ATIS, CCSA, ETSI, IEEE, IETF, ISACC, TIA, TTA, TTC': ['ietf'],
|
||||
u'ASON-related Work': ['ccamp'],
|
||||
u'ATIS': ['atis'],
|
||||
u'American Society of Heating, Refrigerating, and Air-Conditioning Engineers': ['ashrae'],
|
||||
u'BBF': ['broadband-forum'],
|
||||
u'BMWG': [u'bmwg'],
|
||||
u'Bert Wijnen and the IETF O & M Area': [u'ops'],
|
||||
u'Bert Wijnen, Bernard Aboba and the IETF': [u'ietf'],
|
||||
u'CCAMP WG co-chairs and IEEE-IETF': ['ccamp'],
|
||||
u'CCAMP WG co-chairs and IEEE-IETF liaisons': ['ccamp'],
|
||||
u'Completes action above Scott Bradner, Area co-Director (sob@harvard.edu)': ['tsv'],
|
||||
u'DLNA': ['dlna'],
|
||||
#u'DONE': None, # this one is explicitly mapped
|
||||
u'DSL Forum': [u'dsl-forum'],
|
||||
u'DSL Forum Architecture & Transport Working Group': ['dsl-forum-twg'],
|
||||
u'DVB IPI': ['dvb-tm-ipi'],
|
||||
u'DVB TM-IPI, ETSI TISPAN, ATIS IIF, IETF RMT, IETF FECFRAME': ['fecframe','rmt'],
|
||||
u'EAP Method Update Working Group': ['emu'],
|
||||
u'ETSI AT working group Digital': ['etsi-at-digital'],
|
||||
u'ETSI TC HF': ['etsi-tc-hf'],
|
||||
u'ETSI TISPAN': ['etsi-tispan'],
|
||||
u'G.7712 Editor, ITU-T SG15Q14 Rapporteur, ITU-T SG15': ['itu-t-sg-15'],
|
||||
u'Generic EAP Encapsulation': ['int'],
|
||||
u'Harald Alvestrand': ['avt'], # placeholder for explicit (2)
|
||||
u'IAB and IETF Routing Area Directors': [u'iab', 'rtg'],
|
||||
u'IAB, IESG': [u'iab', 'iesg'],
|
||||
u'IANA': [u'iana'],
|
||||
u'ICANN, IETF/IAB, NRO and ACSIS': ['ietf','iab'],
|
||||
u'IEEE 802': [u'ieee-802'],
|
||||
u'IEEE NGSON Study Group': ['ieee-sa-ngson'],
|
||||
u'IEEE802.1': [u'ieee-802-1'],
|
||||
u'IESG members, IAB members': [u'iesg', u'iab'],
|
||||
u'IESG, IAB, IETF MPLS WG': ['iesg','iab','mpls'],
|
||||
u'IESG, IETF-RAI': [u'iesg', u'rai'],
|
||||
u'IESG/IAB Chair': [u'iesg', u'iab'],
|
||||
u'IETF PWE3 and TICTOC': [u'pwe3', u'tictoc'],
|
||||
u'IETF (CCAMP, PCE and MPLS WGs)': [u'ccamp', u'pce', u'mpls'],
|
||||
u'IETF (Management)': ['iesg'],
|
||||
u'IETF (SAVI and V6OPS WGS, OPS Area and INT Area)': [u'savi', u'v6ops', u'ops', u'int'],
|
||||
u'IETF (Sub-IP & Transport Areas)': [u'sub', u'tsv'],
|
||||
u'IETF (and others)': [u'ietf'],
|
||||
u'IETF (ccamp, pce and mpls WGs)': [u'ccamp', u'pce', u'mpls'],
|
||||
u'IETF 6MAN WG, IETF Internet Area': [u'6man', u'int'],
|
||||
u'IETF AVT WG, ITU-T SG11': [u'avt', u'itu-t-sg-11'],
|
||||
#u'IETF CCAMP WG and Routing Area Directors': [u'ccamp', u'rtg'],
|
||||
#u'IETF CCAMP WG and Sub IP Directors': [u'ccamp','sub'],
|
||||
#u'IETF CCAMP WG and Sub-IP Area Directors': [u'ccamp','sub'],
|
||||
u'IETF CCAMP WG, CC: IETF OSPF WG': [u'ospf','ccamp'],
|
||||
u'IETF CCAMP WG, Routing Area Directors': [u'ccamp', u'rtg'],
|
||||
u'IETF CCAMP and MPLS WGs': [u'ccamp', u'mpls'],
|
||||
u'IETF CCAMP and MPLS WGs and the Routing Area Directors of the IETF': [u'ccamp', u'mpls', u'rtg'],
|
||||
u'IETF CCAMP and PCE WGs': [u'ccamp', u'pce'],
|
||||
u'IETF CCAMP, IETF Routing Area Directors': [u'ccamp', u'rtg'],
|
||||
u'IETF CCAMP, PCE and MPLS WGs': [u'ccamp', u'pce', u'mpls'],
|
||||
u'IETF Charter group on Authority to Citizen Alert (ATOCA)': [u'atoca'],
|
||||
u'IETF DNSOP WG, SAAG, IAB': [u'dnsop', u'saag', u'iab'],
|
||||
u'IETF IAB, IETF IESG': [u'iab', u'ietf', u'iesg'],
|
||||
u'IETF IESG, IAB, PWE3 WG, MPLS WG, routing and internet Area Directors': [u'iesg', u'iab', u'pwe3', u'mpls', u'rtg', u'int'],
|
||||
u'IETF IESG, IETF MPLS WG': [u'mpls','iesg'],
|
||||
#u'IETF ISIS WG and Routing Area Directors': [u'isis','rtg'],
|
||||
u'IETF IPPM, IETF AVT': [u'ippm', u'avt'],
|
||||
u'IETF Internet Area; IETF MIF WG; IETF v6ops WG; IETF 6man WG; IETF softwire WG; IETF Operations and Management Area': [u'int', u'mif', u'v6ops', u'6man', u'softwire', u'ops'],
|
||||
u'IETF Liaison to the ITU on MPLS and PWE3 WG Co-Chair': [u'itu-t-mpls', u'pwe3'],
|
||||
u'IETF MEAD Team': [u'mead'],
|
||||
u'IETF MEAD team': [u'mead'],
|
||||
u'IETF MEXT WG': ['mext'],
|
||||
u'IETF MIPSHOP-WG': [u'mipshop'],
|
||||
u'IETF MMUSIC WG,IETF SIPPING WG': [u'sipping','mmusic'],
|
||||
u'IETF MPLS & PWE3': [u'mpls', u'pwe3'],
|
||||
u'IETF MPLS WG, CC: IETF CCAMP and PWE3 WGs': [u'mpls', u'ccamp', u'pwe3'],
|
||||
u'IETF MPLS WG, CC: MFA Forum': ['mpls','mfa-forum'],
|
||||
u'IETF MPLS WG, IAB, IESG': [u'mpls', u'iab', u'iesg'],
|
||||
u'IETF MPLS WG, IETF IAB and IESG': [u'mpls', u'iab', u'iesg'],
|
||||
u'IETF MPLS WG, IETF PWE3 WG, Broadband Forum': [u'mpls', u'pwe3', u'broadband-forum'],
|
||||
u'IETF MPLS WG Co Chairs (Info: CCAMP WG Co Chairs, MEAD team)': [u'mpls','ccamp','mead'],
|
||||
u'IETF MPLS WG Co-Chairs, CC: IETF MEAD team': [u'mpls','mead'],
|
||||
u'IETF MPLS WG and OPSA WG': [u'mpls','opsawg'],
|
||||
u'IETF MPLS WG and PEW3 WG': [u'mpls','pwe3'],
|
||||
u'IETF MPLS WG, PWE3 WG': [u'pwe3','mpls'],
|
||||
u'IETF MPLS and GMPLS': ['mpls'],
|
||||
u'IETF MPLS and PWE3 WG, MFA Forum, ITU-T Q7/13': ['mpls','pwe3','mfa-forum','itu-t-sg-13-q7'],
|
||||
u'IETF MPLS liaison representative': [u'mpls'],
|
||||
u'IETF MPLS, CCAMP and PWE3 WGs': [u'mpls', u'ccamp', u'pwe3'],
|
||||
u'IETF MPLS, CCAMP, PWE3 and L2VPN': [u'mpls', u'ccamp', u'pwe3', u'l2vpn'],
|
||||
u'IETF MPLS, PWE WGs (Info: IETF MEAD team)': ['mpls','pwe3','mead'],
|
||||
u'IETF Mead Team': [u'mead'],
|
||||
u'IETF NSIS WG Chairs, IETF TSV Area Directors, IESG members, IAB members': [u'nsis', u'tsv', u'iesg', u'iab'],
|
||||
u'IETF PWE3 and L2VPN': [u'pwe3', u'l2vpn'],
|
||||
u'IETF PWE3 and L2VPN Working Groups': [u'pwe3', u'l2vpn'],
|
||||
u'IETF PWE3 and MPLS WG': [u'mpls',u'pwe3'],
|
||||
u'IETF PWE3 and MPLS WGs': [u'pwe3', u'mpls'],
|
||||
u'IETF PWE3 and MPLS Working Groups': [u'pwe3', u'mpls'],
|
||||
u'IETF PWE3, IETF L2VPN WG': ['pwe3',u'l2vpn'],
|
||||
u'IETF PWE3, MPLS working groups': [u'pwe3', u'mpls'],
|
||||
u'IETF RAI and IESG': [u'rai', 'iesg'],
|
||||
u'IETF Real-time Applications and Infrastructure Area Director': [u'rai'],
|
||||
u'IETF Routing Area, the MPLS and CCAMP working groups': [u'rtg', u'mpls', u'ccamp'],
|
||||
u'IETF Routing Area (CCAMP WG) and Internet Area (L2VPN WG and L3VPN WG)': ['ccamp','l2vpn','l3vpn'],
|
||||
u'IETF Routing Area Directors and IAB (CC: CCAMP WG)': ['ccamp',u'rtg','iab'],
|
||||
u'IETF Routing Area Directors and IS-IS WG': ['isis'],
|
||||
u'IETF Routing and Transport areas': [u'rtg', u'tsv'],
|
||||
u'IETF Security Area Directors, CC: IETF CCAMP WG': [u'sec','ccamp'],
|
||||
u'IETF SIP related Working Groups and IESG': ['iesg','rai'],
|
||||
u'IETF Transport and Internat Areas': [u'tsv', u'int'],
|
||||
u'IETF Transport Area Directors, PCN Working Group Chairs': [u'pcn'],
|
||||
u'IETF WG MPLS': [u'mpls'],
|
||||
u'IETF Working Groups IEPREP, TSV, NSIS': [u'ieprep', u'tsv', u'nsis'],
|
||||
u'IETF and Harald Alvestrand': ['ietf'],
|
||||
u'IETF and IAB': [u'ietf', u'iab'],
|
||||
u'IETF avt and mmusic WG': [u'mmusic','avt'],
|
||||
u'IETF ccamp and pce WG': ['ccamp',u'pce'],
|
||||
u'IETF mobileip WG and mpls WG': [u'mobileip','mpls'],
|
||||
u'IETF mpls WG, CC: IETF pwe3 WG': ['mpls',u'pwe3'],
|
||||
u'IETF mpls and ccamp WG': ['mpls',u'ccamp'],
|
||||
u'IETF pwe3 WG, CC: mpls WG': [u'mpls','pwe3'],
|
||||
u'IETF pwe3, mpls WGs': [u'pwe3', u'mpls'],
|
||||
u'IETF pwe3 and mpls WG': [u'mpls','pwe3'],
|
||||
u'IETF re RoHC': [u'rohc'],
|
||||
u'IETF \u2013 Internet Area Directors, Internet Area Working Groups': [u'int'],
|
||||
u'IETF: Transport Area Directors, PCN Working Group Chairs': [u'pcn'],
|
||||
u'IETF, IAB': [u'ietf', u'iab'],
|
||||
u'IETF/IAB': [u'ietf', u'iab'],
|
||||
u'IETF/IAB, NRO, ICANN and ACSIS': ['ietf','iab'],
|
||||
u'IETF/IAB/IESG': [u'ietf', u'iab', u'iesg'],
|
||||
u'IETF/PWE3 and L2VPN WGs': [u'pwe3', u'l2vpn'],
|
||||
u'ISIS': [u'isis'],
|
||||
u'ISMA': ['isma'],
|
||||
u'ISO/IEC JTC': [u'iso-iec-jtc1'],
|
||||
u'ISO/IEC JTC 1/SC 29/WG 1': [u'iso-iec-jtc1-sc29-wg1'],
|
||||
u'ISOC': [u'isoc'],
|
||||
u'ISOC/IAB Liaison': [u'isoc', 'iab'],
|
||||
u'ITU': [u'itu'],
|
||||
u'ITU IPv6 Group': [u'itu-t-ipv6-group'],
|
||||
u'ITU Q12/15 and Q14/15': [u'itu-t-sg-15-q12',u'itu-t-sg-15-q14'],
|
||||
u'ITU SG 16 Q8, 9, 10/16': [u'itu-t-sg-16-q8',u'itu-t-sg-16-q9',u'itu-t-sg-16-q10'],
|
||||
u'ITU SG13': [u'itu-t-sg-13'],
|
||||
u'ITU SG15': [u'itu-t-sg-15'],
|
||||
u'ITU-R': [u'itu-r'],
|
||||
u'ITU-R WP8F & IETF': [u'itu-r-wp8f',u'ietf'],
|
||||
u'ITU-SG15': [u'itu-t-sg-15'],
|
||||
u'ITU-SG2': [u'itu-t-sg-2'],
|
||||
u'ITU-T JCA-IdM': [u'itu-t-jca-idm'],
|
||||
u'ITU-T Q1/SG15': [u'itu-t-sg-15-q1'],
|
||||
u'ITU-T Q10/15': [u'itu-t-sg-15-q10'],
|
||||
u'ITU-T Q12/15 and Q14/15': [u'itu-t-sg-15-q12',u'itu-t-sg-15-q14'],
|
||||
u'ITU-T Q14/15': [u'itu-t-sg-15-q14'],
|
||||
u'ITU-T Q14/15 - Mr. Kam Lam, Rapporteur': [u'itu-t-sg-15-q14'],
|
||||
u'ITU-T Q14/15, ITU-T Q11/15': [u'itu-t-sg-15-q11',u'itu-t-sg-15-q14'],
|
||||
u'ITU-T Q3/15': [u'itu-t-sg-15-q3'],
|
||||
u'ITU-T Q5/13 (recently renamed ITU-T Q7/13)': [u'itu-t-sg-13-q7'],
|
||||
u'ITU-T Q7/SG13': [u'itu-t-sg-13-q7'],
|
||||
u'ITU-T Question 14/15': [u'itu-t-sg-15-q14'],
|
||||
u'ITU-T Question 3/15': [u'itu-t-sg-15-q3'],
|
||||
u'ITU-T SG 11 and ITU-T TSAG': [u'itu-t-sg-11',u'itu-t-tsag'],
|
||||
u'ITU-T SG 11, ITU-T Q.5/11, ITU-T WP 2/11': [u'itu-t-sg-11',u'itu-t-sg-11-q5',u'itu-t-sg-11-wp2'],
|
||||
u'ITU-T SG 12, Q12, Q17': [u'itu-t-sg12-q-12-17'],
|
||||
u'ITU-T SG 13 (ITU-T SG 11 and SG 12 for information)': [u'itu-t-sg-13',u'itu-t-sg-12',u'itu-t-sg-12'],
|
||||
u'ITU-T SG 13 (ITU-T SG 11 for information)': [u'itu-t-sg-13',u'itu-t-sg-11'],
|
||||
u'ITU-T SG 13, SG 15': [u'itu-t-sg-13', u'itu-t-sg-15'],
|
||||
u'ITU-T SG 15 <tsbsg15@itu.int, greg.jones@itu.int>': [u'itu-t-sg-15'],
|
||||
u'ITU-T SG 15 Q9, Q10, Q12 and Q14': [u'itu-t-sg-15-q9',u'itu-t-sg-15-q10',u'itu-t-sg-15-q12',u'itu-t-sg-15-q14'],
|
||||
u'ITU-T SG 15, Q.14/15': [u'itu-t-sg-15-q14'],
|
||||
u'ITU-T SG 15, Q9, Q11, Q12, Q14': [u'itu-t-sg-15-q9',u'itu-t-sg-15-q10',u'itu-t-sg-15-q12',u'itu-t-sg-15-q14'],
|
||||
u'ITU-T SG 17 Q.2/17': [u'itu-t-sg-17-q2'],
|
||||
u'ITU-T SG 4': [u'itu-t-sg-4'],
|
||||
u'ITU-T SG 4, 9, 11, 13, 16 and IETF': [u'itu-t-sg-4',u'itu-t-sg-9',u'itu-t-sg-11',u'itu-t-sg-13',u'itu-t-sg-16',u'ietf'],
|
||||
u'ITU-T SG-15': [u'itu-t-sg-15'],
|
||||
u'ITU-T SG-2': [u'itu-t-sg-2'],
|
||||
u'ITU-T SG11': [u'itu-t-sg-11'],
|
||||
u'ITU-T SG12, SG13, ATIS, TIA, IEC, IETF ccamp WG, IEEE 802.1, 802.3, OIF, Metro Ethernet Forum, ATM Forum': ['ccamp'],
|
||||
u'ITU-T SG13': [u'itu-t-sg-13'],
|
||||
u'ITU-T SG13 and SG15': [u'itu-t-sg-13', u'itu-t-sg-15'],
|
||||
u'ITU-T SG15': [u'itu-t-sg-15'],
|
||||
u'ITU-T SG15 (Optical Control Plane)': [u'itu-t-sg-15'],
|
||||
u'ITU-T SG15 Q10': [u'itu-t-sg-15-q10'],
|
||||
u'ITU-T SG15 Q10, Q12': [u'itu-t-sg-15-q10',u'itu-t-sg-15-q12'],
|
||||
u'ITU-T SG15 Q12': [u'itu-t-sg-15-q12'],
|
||||
u'ITU-T SG15 Q14': [u'itu-t-sg-15-q14'],
|
||||
u'ITU-T SG15 Q6': [u'itu-t-sg-15-q6'],
|
||||
u'ITU-T SG15 Q9, Q10, Q12 and Q14': [u'itu-t-sg-15-q9',u'itu-t-sg-15-q10',u'itu-t-sg-15-q12',u'itu-t-sg-15-q14'],
|
||||
u'ITU-T SG15 Q9, Q11, Q12 and Q14': [u'itu-t-sg-15-q9',u'itu-t-sg-15-q11',u'itu-t-sg-15-q12',u'itu-t-sg-15-q14'],
|
||||
u'ITU-T SG15 Question 12': [u'itu-t-sg-15-q12'],
|
||||
u'ITU-T SG15 Question 3': [u'itu-t-sg-15-q3'],
|
||||
u'ITU-T SG15 Question 6': [u'itu-t-sg-15-q6'],
|
||||
u'ITU-T SG15 Question 6, Question 12, and Question 14': [u'itu-t-sg-15-q6',u'itu-t-sg-15-q12',u'itu-t-sg-15-q14'],
|
||||
u'ITU-T SG15 Question 9': [u'itu-t-sg-15-q9'],
|
||||
u'ITU-T SG15 Questions 12 and 14': [u'itu-t-sg-15-q12',u'itu-t-sg-15-q14'],
|
||||
u'ITU-T SG15 and Q14/15': [u'itu-t-sg-15-q14'],
|
||||
u'ITU-T SG15, Q 9/15, Q 10/15, Q 12/15 and Q 14/15': [u'itu-t-sg-15-q9',u'itu-t-sg-15-q10',u'itu-t-sg-15-q12',u'itu-t-sg-15-q14'],
|
||||
u'ITU-T SG15, Q 9/15, Q10/15, Q12/15 and Q14/15': [u'itu-t-sg-15-q9',u'itu-t-sg-15-q10',u'itu-t-sg-15-q12',u'itu-t-sg-15-q14'],
|
||||
u'ITU-T SG15, Q9, Q11, Q12 and Q14': [u'itu-t-sg-15-q9',u'itu-t-sg-15-q11',u'itu-t-sg-15-q12',u'itu-t-sg-15-q14'],
|
||||
u'ITU-T SG16': [u'itu-t-sg-16'],
|
||||
u'ITU-T SG17': [u'itu-t-sg-17'],
|
||||
u'ITU-T SG17 TSB': [u'itu-t-sg-17-tsb'],
|
||||
u'ITU-T SG2': [u'itu-t-sg-2'],
|
||||
u'ITU-T SG2 <tsbsg2@itu.int>': [u'itu-t-sg-2'],
|
||||
u'ITU-T SG2 Q 1/2': [u'itu-t-sg-2-q1'],
|
||||
u'ITU-T SG4': [u'itu-t-sg-4'],
|
||||
u'ITU-T SG4, ITU-T SG15, ITU-T NGNM Focus group, 3GPP SA5, 3GPP2, ATIS/TMOC, TMF, IETF Management, ETSI BRAN': ['iesg'],
|
||||
u'ITU-T SGs, ITU-R WGs, ITU-D SG2 and the IETF': ['ietf'],
|
||||
u'ITU-T SGs: 2 (info), 4, 9, 11, 12, 13, 17, 19; ITU-R SGs: 1, 4, 5, 6; ITU-D SG 2; Focus Group on \u2018From/In/To Cars II\u2019 (ITU-T SG 12); ISO TC 22 SC3 and TC 204 ; IEEE 802, 802.11 (WiFi), 802.15.1 (Bluetooth); AUTOSAR WPII-1.1, OSGi VEG, IrDA and JSR298 Tele': ['ietf'],
|
||||
u'ITU-T SQ15 Question 14': [u'itu-t-sg-15-q14'],
|
||||
u'ITU-T Study Group 11': [u'itu-t-sg-11'],
|
||||
u'ITU-T Study Group 11 <tsg11gen@itu.int>': [u'itu-t-sg-11'],
|
||||
u'ITU-T Study Group 13': [u'itu-t-sg-13'],
|
||||
u'ITU-T Study Group 15': [u'itu-t-sg-15'],
|
||||
u'ITU-T Study Group 15 <greg.jones@itu.int>': [u'itu-t-sg-15'],
|
||||
u'ITU-T Study Group 15 Q4 <rlstuart@ieee.org>': [u'itu-t-sg-15-q4'],
|
||||
u'ITU-T Study Group 15 Question 14': [u'itu-t-sg-15-q14'],
|
||||
u'ITU-T Study Group 15 Question 3': [u'itu-t-sg-15-q3'],
|
||||
u'ITU-T Study Group 15 Question 6': [u'itu-t-sg-15-q6'],
|
||||
u'ITU-T TSAG External Relations Group': [u'itu-t-tsag'],
|
||||
u'ITU-T Working Party 3/13 and ITU-T Question 11/13': [u'itu-t-sg-13-wp3',u'itu-t-sg-13-q11'],
|
||||
u'ITU-T and ITU-T Study Group 13': [u'itu-t', u'itu-t-sg-13'],
|
||||
u'ITU-T, ITU SG13': [u'itu-t', u'itu-t-sg-13'],
|
||||
u'ITU-T-SG13': [u'itu-t-sg-13'],
|
||||
u'ITU-T/FG Cloud': ['itu-t-fg-cloud'],
|
||||
u'ITU-T/SG11': [u'itu-t-sg-11'],
|
||||
u'ITU-T/Study Group 11': [u'itu-t-sg-11'],
|
||||
u'Kam Lam, Rapporteur for Question 14 of ITU-T SG15': [u'itu-t-sg-15-q14'],
|
||||
u'Kam Lam, Rapporteur for Question 14 of ITU-T Study Group 15': [u'itu-t-sg-15-q14'],
|
||||
u'Lyndon Ong (lyong@ciena.com)': [u'sigtran'],
|
||||
u'MFA Forum': ['mfa-forum'],
|
||||
u'MPLS and Frame Relay Alliance': ['mfa'],
|
||||
u'Mr. Kam Lam, Rapporteur for Question 14 of ITU-T Study Group 15': [u'itu-t-sg-15-q14'],
|
||||
u'National, Multi-National or Regional Organizations': ['nmnro'],
|
||||
u'OMA': [u'oma'],
|
||||
u'OMA MEM': [u'oma-mwg-mem'],
|
||||
u'OMA MWG': [u'oma-mwg'],
|
||||
u'OMA MWG MEM Sub Working Group': [u'oma-mwg-mem'],
|
||||
u'OMA TP': [u'oma-tp'],
|
||||
u'OPS ADs (Randy Bush and Bert Wijnen)': [u'ops'],
|
||||
u'OPS Area Director Bert Wijnen': [u'ops'],
|
||||
u'Open IPTV Forum': ['opif'],
|
||||
u'Open Mobile Alliance Broadcasting Working Group': [u'oma-bcast'],
|
||||
u'Open Mobile Alliance, PAG Working Group': [u'oma-pag-wg'],
|
||||
u'PDNR ITU-R M.[IP CHAR]': ['ietf'], # pending robert
|
||||
u'PWE WG': ['pwe3'],
|
||||
u'Phase 1 report to SG 4': ['ops'],
|
||||
u'Q7/13': [u'itu-t-sg-13-q7'],
|
||||
u'Rao Cherukuri, Chair MPLS and Frame Relay Alliance Technical Committee': ['mfa'],
|
||||
u'Rao Cherukuri, Chairman, MPLS and Frame Relay Alliance Technical Committee': ['mfa'],
|
||||
u'SA2, T2, OMA TP, S3': ['3gpp-tsgsa-sa2','3gpp-tsgt-wg2',u'oma-tp','3gpp-tsgsa-sa3'],
|
||||
u'SAVI WG, V6OPS WG, OPS AREA, INT AREA': [u'savi', u'v6ops', u'ops', u'int'],
|
||||
u'SC 29/WG11': [u'iso-iec-jtc1-sc29-wg11'],
|
||||
u'SC29/WG11': [u'iso-iec-jtc1-sc29-wg11'],
|
||||
u'SG 15,Questions 3,9, 11,12, 14 and WP 3/15': [u'itu-t-sg-15-q3',u'itu-t-sg-15-q9',u'itu-t-sg-15-q11',u'itu-t-sg-15-q12',u'itu-t-sg-15-q14',u'itu-t-sg-15-wp3'],
|
||||
u'SG-13, Q.3/13, Q.9/13 and TSAG': [u'itu-t-sg-13-q3',u'itu-t-sg-13-q9',u'itu-t-tsag'],
|
||||
u'SG13, SG13 WP4': [u'itu-t-sg-13',u'itu-t-sg-13-wp4'],
|
||||
u'SG15 Q9': [u'itu-t-sg-15-q9'],
|
||||
u'SG15, Q9, Q10, Q12 and Q14': [u'itu-t-sg-15-q9',u'itu-t-sg-15-q10',u'itu-t-sg-15-q12',u'itu-t-sg-15-q14'],
|
||||
u'SG15, Q9, Q10, Q12, Q14': [u'itu-t-sg-15-q9',u'itu-t-sg-15-q10',u'itu-t-sg-15-q12',u'itu-t-sg-15-q14'],
|
||||
u'SG15, Q9, Q10, Q12, and Q14': [u'itu-t-sg-15-q9',u'itu-t-sg-15-q10',u'itu-t-sg-15-q12',u'itu-t-sg-15-q14'],
|
||||
u'SG15, Q9, Q11, Q12 and Q14': [u'itu-t-sg-15-q9',u'itu-t-sg-15-q11',u'itu-t-sg-15-q12',u'itu-t-sg-15-q14'],
|
||||
u'SG17, SG13, SG11, JCA-NID, ETSI TISPAN WG4, 3GPP TSG CT4, IESG': ['iesg'],
|
||||
u'SG4': [u'itu-t-sg-4'],
|
||||
u'SIP aand SIPPING WGs': [u'sip', u'sipping'],
|
||||
u'SIP, SIPPING, SIMPLE WGs': [u'sip', u'sipping', u'simple'],
|
||||
u'SUB-IP and Transport Areas': [u'sub', u'tsv'],
|
||||
u'Scott Bradner': ['ccamp','isis','sigtran'],
|
||||
u'Scott Bradner (sob@harvard.edu)': ['iesg'], # placeholder for explicit mappings
|
||||
u'Scott Bradner (sob@harvard.edu) Done': ['mpls'],
|
||||
u'SubIP ADs (sob@harvard.edu,bwijnen@lucent.com)': [u'sub'],
|
||||
u'TEWG, MPLS, CCAMP WGs': [u'tewg', u'mpls', u'ccamp'],
|
||||
u'TRILL WG co-chairs and IEEE-IETF liaisons': ['trill'],
|
||||
u'TRILL WG co-chairs, ADs, and IEEE-IETF liaisons': ['trill'],
|
||||
u'TSG-X Corr to IETF re MIP6 Bootstrapping': ['int'],
|
||||
u'The IAB': [u'iab'],
|
||||
u'The IESG': [u'iesg'],
|
||||
u'The IESG and the IAB': [u'iesg', u'iab'],
|
||||
u'The IETF': [u'ietf'],
|
||||
u'Tom Taylor (taylor@nortelnetworks.com), Megaco WG Chair': [u'megaco'],
|
||||
u'Transport ADs (Allison Mankin and Scott Bradner)': [u'tsv'],
|
||||
u'Transport Area Directors': [u'tsv'],
|
||||
u'Unicode Consortium': ['unicode'],
|
||||
u'Unicode Technical Committee': ['unicode'],
|
||||
u'Various IETF WGs': ['mobileip','pppext','avt'],
|
||||
u'W3C Geolocation WG': ['w3c-geolocation-wg'],
|
||||
u'W3C Geolocation Working Group': ['w3c-geolocation-wg'],
|
||||
u'W3C Multimedia Interaction Work Group': ['w3c-mmi'],
|
||||
u'WiFi Alliance and Wireless Broadband Alliance': ['wifi-alliance','wba'],
|
||||
u'chair@ietf.org': [u'ietf'],
|
||||
u'gonzalo.camarillo@ericsson.com': ['ietf'],
|
||||
u'tsbdir@itu.int': ['itu-t']
|
||||
}
|
||||
|
||||
FROM_NAME_MAPPING = {
|
||||
u'3GPP TSG RAN WG2': ['3gpp-tsgran-ran2'],
|
||||
u'<unknown body 0>': ['itu-t-sg-13'],
|
||||
u'ATIS': ['atis'],
|
||||
u'ATM Forum': [u'atm-forum'],
|
||||
u'ATM Forum AIC WG': [u'afic'],
|
||||
u'BBF': [u'broadband-forum'],
|
||||
u'DSL Forum': [u'dsl-forum'],
|
||||
u'EPCGlobal': [u'epcglobal'],
|
||||
u'ETSI': ['etsi'],
|
||||
u'ETSI EMTEL': ['etsi-emtel'],
|
||||
u'ETSI TC HF': ['etsi-tc-hf'],
|
||||
u'ETSI TISPAN': ['etsi-tispan'],
|
||||
u'ETSI TISPAN WG5': ['etsi-tispan-wg5'],
|
||||
u'Femto Forum': ['femto-forum'],
|
||||
u'GSMA WLAN': ['gsma-wlan'],
|
||||
u'IEEE 802': [u'ieee-802'],
|
||||
u'IEEE 802.11': [u'ieee-802-11'],
|
||||
u'IEEE 802.21': [u'ieee-802-21'],
|
||||
u'IETF ADSL MIB': [u'adslmib'],
|
||||
u'IETF MEAD Team': [u'mead'],
|
||||
u'IETF Mead Team': [u'mead'],
|
||||
u'IETF liaison on MPLS': [u'mpls'],
|
||||
u'INCITS T11.5': ['incits-t11-5'],
|
||||
u'ISO/IEC JTC 1 SC 29/WG 11': [u'iso-iec-jtc1-sc29-wg11'],
|
||||
u'ISO/IEC JTC 1 SGSN': ['iso-iec-jtc1-sgsn'],
|
||||
u'ISO/IEC JTC 1/SC31/WG 4/SG 1': ['iso-iec-jtc1-sc31-wg4'],
|
||||
u'ISO/IEC JTC 1/WG 7': [u'iso-iec-jtc1-wg7'],
|
||||
u'ISO/IEC JTC SC 29/WG1': [u'iso-iec-jtc1-sc29-wg1'],
|
||||
u'ISO/IEC JTC SC 29/WG11': [u'iso-iec-jtc1-sc29-wg11'],
|
||||
u'ISO/IEC JTC1/SC29/WG11': [u'iso-iec-jtc1-sc29-wg11'],
|
||||
u'ISO/IEC JTC1/SC6': [u'iso-iec-jtc1-sc6'],
|
||||
u'ITU': [u'itu'],
|
||||
u'ITU IPv6 Group': [u'itu-t-ipv6-group'],
|
||||
u'ITU-Q.14/15': [u'itu-t-sg-15-q14'],
|
||||
u'ITU-R WP 5A': [u'itu-r-wp5a'],
|
||||
u'ITU-R WP 5D': [u'itu-r-wp5d'],
|
||||
u'ITU-R WP8A': [u'itu-r-wp8a'],
|
||||
u'ITU-R WP8F': [u'itu-r-wp8f'],
|
||||
u'ITU-SC29': ['iso-iec-jtc1-sc29-wg1'],
|
||||
u'ITU-SG 15': [u'itu-t-sg-15'],
|
||||
u'ITU-SG 7': [u'itu-t-sg-7'],
|
||||
u'ITU-SG 8': [u'itu-t-sg-8'],
|
||||
u'ITU-T FG Cloud': [u'itu-t-fg-cloud'],
|
||||
u'ITU-T FG IPTV': [u'itu-t-fg-iptv'],
|
||||
u'ITU-T Q.5/13': [u'itu-t-sg-13-q5'],
|
||||
u'ITU-T SG 15 Q14/15': [u'itu-t-sg-15-q14'],
|
||||
u'ITU-T SG 15 WP 1': [u'itu-t-sg-15-wp1'],
|
||||
u'ITU-T SG 15, Q.11': [u'itu-t-sg-15-q11'],
|
||||
u'ITU-T SG 15, Q.14/15': [u'itu-t-sg-15-q14'],
|
||||
u'ITU-T SG 4': [u'itu-t-sg-4'],
|
||||
u'ITU-T SG 6': [u'itu-t-sg-6'],
|
||||
u'ITU-T SG 7': [u'itu-t-sg-7'],
|
||||
u'ITU-T SG 9': [u'itu-t-sg-9'],
|
||||
u'ITUT-T SG 16': [u'itu-t-sg-16'],
|
||||
u'JCA-IdM': [u'itu-t-jca-idm'],
|
||||
u'MFA Forum': ['mfa-forum'],
|
||||
u'MPEG': ['mpeg'],
|
||||
u'MPLS Forum': ['mpls-forum'],
|
||||
u'MPLS and FR Alliance': ['mfa'],
|
||||
u'MPLS and Frame Relay Alliance': ['mfa'],
|
||||
u'NANP LNPA WG': ['nanc-lnpa-wg'],
|
||||
u'NGN Management Focus Group': ['itu-t-ngnmfg'],
|
||||
u'OMA': [u'oma'],
|
||||
u'OMA COM-CAB SWG': [u'oma-com-cab'],
|
||||
u'OMA COM-CPM Group': [u'oma-com-cpm'],
|
||||
u'Open IPTV Forum': ['opif'],
|
||||
u'SC 29/WG 1': [u'iso-iec-jtc1-sc29-wg1'],
|
||||
u'SC 29/WG 11': [u'iso-iec-jtc1-sc29-wg11'],
|
||||
u'SC29 4559': [u'iso-iec-jtc1-sc29-wg11'],
|
||||
u'SC29 4561': [u'iso-iec-jtc1-sc29-wg11'],
|
||||
u'SIP, SIPPING, SIMPLE WGs': [u'sip', u'sipping', u'simple'],
|
||||
u'T1M1': ['t1m1'],
|
||||
u'T1S1': ['t1s1'],
|
||||
u'T1X1 cc: ITU-T Q. 14/15 (for info)': ['t1x1','itu-t-sg-15-q14'],
|
||||
u'TIA': ['tia'],
|
||||
u'TMOC': ['tmoc'],
|
||||
u'The IAB': [u'iab'],
|
||||
u'The IESG': [u'iesg'],
|
||||
u'The IESG and the IAB': [u'iesg', u'iab'],
|
||||
u'The IETF': [u'ietf'],
|
||||
u'W3C Geolocation WG': ['w3c-geolocation-wg'],
|
||||
u'WIG': ['wig']
|
||||
}
|
||||
|
||||
DEFAULT_POC = {
|
||||
'3gpp':'georg.mayer.huawei@gmx.com,3GPPLiaison@etsi.org',
|
||||
'3gpp-tsgct':'georg.mayer.huawei@gmx.com,3GPPLiaison@etsi.org',
|
||||
'3gpp-tsgct-ct1':'georg.mayer.huawei@gmx.com,3GPPLiaison@etsi.org',
|
||||
'3gpp-tsgct-ct4':'georg.mayer.huawei@gmx.com,3GPPLiaison@etsi.org',
|
||||
'3gpp-tsgran':'georg.mayer.huawei@gmx.com,3GPPLiaison@etsi.org',
|
||||
'3gpp-tsgran-ran2':'georg.mayer.huawei@gmx.com,3GPPLiaison@etsi.org',
|
||||
'3gpp-tsgsa':'georg.mayer.huawei@gmx.com,3GPPLiaison@etsi.org',
|
||||
'3gpp-tsgsa-sa2':'georg.mayer.huawei@gmx.com,3GPPLiaison@etsi.org',
|
||||
'3gpp-tsgsa-sa3':'georg.mayer.huawei@gmx.com,3GPPLiaison@etsi.org',
|
||||
'3gpp-tsgsa-sa4':'georg.mayer.huawei@gmx.com,3GPPLiaison@etsi.org',
|
||||
'3gpp-tsgt-wg2':'georg.mayer.huawei@gmx.com,3GPPLiaison@etsi.org',
|
||||
'ieee-802':'Paul Nikolich <p.nikolich@ieee.org>,Pat Thaler <pthaler@broadcom.com>',
|
||||
'ieee-802-1':'Paul Nikolich <p.nikolich@ieee.org>,Glen Parsons <glenn.parsons@ericsson.com>,John Messenger <jmessenger@advaoptical.com>',
|
||||
'ieee-802-11':'Dorothy Stanley <dstanley@agere.com>, Adrian Stephens <adrian.p.stephens@intel.com>',
|
||||
'cablelabs':'Greg White <g.white@CableLabs.com>',
|
||||
'iso-iec-jtc1-sc29':'Watanabe Shinji <watanabe@itscj.ipsj.or.jp>',
|
||||
'iso-iec-jtc1-sc29-wg1':'Watanabe Shinji <watanabe@itscj.ipsj.or.jp>',
|
||||
'iso-iec-jtc1-sc29-wg11':'Watanabe Shinji <watanabe@itscj.ipsj.or.jp>',
|
||||
'unicode':'Richard McGowan <rick@unicode.org>',
|
||||
'isotc46':'sabine.donnardcusse@afnor.org',
|
||||
'w3c':u'Wendy Seltzer <wseltzer@w3.org>,Philippe Le Hégaret <plh@w3.org>',
|
||||
# change to m3aawg
|
||||
'maawg':'Mike Adkins <madkins@fb.com>,technical-chair@mailman.m3aawg.org',
|
||||
'ecma-tc39':'John Neuman <johnneumann.openstrat@gmail.com>,Istvan Sebestyen <istvan@ecma-interational.org>',
|
||||
}
|
||||
|
50
ietf/liaisons/migrations/0006_remove_fields.py
Normal file
50
ietf/liaisons/migrations/0006_remove_fields.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('liaisons', '0005_migrate_groups'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='liaisonstatement',
|
||||
name='action_taken',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='liaisonstatement',
|
||||
name='approved',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='liaisonstatement',
|
||||
name='from_group',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='liaisonstatement',
|
||||
name='modified',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='liaisonstatement',
|
||||
name='related_to',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='liaisonstatement',
|
||||
name='reply_to',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='liaisonstatement',
|
||||
name='submitted',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='liaisonstatement',
|
||||
name='to_group',
|
||||
),
|
||||
#migrations.RemoveField(
|
||||
# model_name='liaisonstatement',
|
||||
# name='from_contact',
|
||||
#),
|
||||
]
|
26
ietf/liaisons/migrations/0007_auto_20151009_1220.py
Normal file
26
ietf/liaisons/migrations/0007_auto_20151009_1220.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('liaisons', '0006_remove_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='liaisonstatement',
|
||||
name='from_groups',
|
||||
field=models.ManyToManyField(related_name='liaisonstatement_from_set', to='group.Group', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='liaisonstatement',
|
||||
name='to_groups',
|
||||
field=models.ManyToManyField(related_name='liaisonstatement_to_set', to='group.Group', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
|
@ -1,52 +1,207 @@
|
|||
# Copyright The IETF Trust 2007, All Rights Reserved
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
from django.db import models
|
||||
from django.utils.text import slugify
|
||||
|
||||
from ietf.name.models import LiaisonStatementPurposeName
|
||||
from ietf.person.models import Email, Person
|
||||
from ietf.name.models import (LiaisonStatementPurposeName, LiaisonStatementState,
|
||||
LiaisonStatementEventTypeName, LiaisonStatementTagName,
|
||||
DocRelationshipName)
|
||||
from ietf.doc.models import Document
|
||||
from ietf.person.models import Email
|
||||
from ietf.group.models import Group
|
||||
|
||||
|
||||
# maps (previous state id, new state id) to event type id
|
||||
STATE_EVENT_MAPPING = {
|
||||
(u'pending','approved'):'approved',
|
||||
(u'pending','dead'):'killed',
|
||||
(u'pending','posted'):'posted',
|
||||
(u'approved','posted'):'posted',
|
||||
(u'dead','pending'):'resurrected',
|
||||
(u'pending','pending'):'submitted'
|
||||
}
|
||||
|
||||
|
||||
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_groups = models.ManyToManyField(Group, blank=True, related_name='liaisonstatement_from_set')
|
||||
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.")
|
||||
to_groups = models.ManyToManyField(Group, blank=True, related_name='liaisonstatement_to_set')
|
||||
to_contacts = models.CharField(blank=True, max_length=255, help_text="Contacts at recipient body")
|
||||
|
||||
reply_to = models.CharField(blank=True, max_length=255)
|
||||
response_contacts = models.CharField(blank=True, max_length=255, help_text="Where to send a response") # RFC4053
|
||||
technical_contacts = models.CharField(blank=True, max_length=255, help_text="Who to contact for clarification") # RFC4053
|
||||
action_holder_contacts = models.CharField(blank=True, max_length=255, help_text="Who makes sure action is completed") # incoming only?
|
||||
cc_contacts = models.TextField(blank=True)
|
||||
|
||||
response_contact = models.CharField(blank=True, max_length=255)
|
||||
technical_contact = models.CharField(blank=True, max_length=255)
|
||||
cc = models.TextField(blank=True)
|
||||
purpose = models.ForeignKey(LiaisonStatementPurposeName)
|
||||
deadline = models.DateField(null=True, blank=True)
|
||||
other_identifiers = models.TextField(blank=True, null=True) # Identifiers from other bodies
|
||||
body = 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)
|
||||
tags = models.ManyToManyField(LiaisonStatementTagName, blank=True, null=True)
|
||||
attachments = models.ManyToManyField(Document, through='LiaisonStatementAttachment', blank=True)
|
||||
state = models.ForeignKey(LiaisonStatementState, default='pending')
|
||||
|
||||
action_taken = models.BooleanField(default=False)
|
||||
# remove these fields post upgrade
|
||||
from_name = models.CharField(max_length=255, help_text="Name of the sender body")
|
||||
to_name = models.CharField(max_length=255, help_text="Name of the recipient body")
|
||||
|
||||
attachments = models.ManyToManyField(Document, blank=True)
|
||||
def __unicode__(self):
|
||||
return self.title or u"<no title>"
|
||||
|
||||
def change_state(self,state_id=None,person=None):
|
||||
'''Helper function to change state of liaison statement and create appropriate
|
||||
event'''
|
||||
previous_state_id = self.state_id
|
||||
self.set_state(state_id)
|
||||
event_type_id = STATE_EVENT_MAPPING[(previous_state_id,state_id)]
|
||||
LiaisonStatementEvent.objects.create(
|
||||
type_id=event_type_id,
|
||||
by=person,
|
||||
statement=self,
|
||||
desc='Statement {}'.format(event_type_id.capitalize())
|
||||
)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return settings.IDTRACKER_BASE_URL + urlreverse('ietf.liaisons.views.liaison_detail',kwargs={'object_id':self.id})
|
||||
|
||||
def is_outgoing(self):
|
||||
return self.to_groups.first().type_id == 'sdo'
|
||||
|
||||
def latest_event(self, *args, **filter_args):
|
||||
"""Get latest event of optional Python type and with filter
|
||||
arguments, e.g. d.latest_event(type="xyz") returns an LiaisonStatementEvent
|
||||
while d.latest_event(WriteupDocEvent, type="xyz") returns a
|
||||
WriteupDocEvent event."""
|
||||
model = args[0] if args else LiaisonStatementEvent
|
||||
e = model.objects.filter(statement=self).filter(**filter_args).order_by('-time', '-id')[:1]
|
||||
return e[0] if e else None
|
||||
|
||||
def name(self):
|
||||
if self.from_group:
|
||||
frm = self.from_group.acronym or self.from_group.name
|
||||
if self.from_groups.count():
|
||||
frm = ', '.join([i.acronym or i.name for i in self.from_groups.all()])
|
||||
else:
|
||||
frm = self.from_name
|
||||
if self.to_group:
|
||||
to = self.to_group.acronym or self.to_group.name
|
||||
if self.to_groups.count():
|
||||
to = ', '.join([i.acronym or i.name for i in self.to_groups.all()])
|
||||
else:
|
||||
to = self.to_name
|
||||
return slugify("liaison" + " " + self.submitted.strftime("%Y-%m-%d") + " " + frm[:50] + " " + to[:50] + " " + self.title[:115])
|
||||
|
||||
@property
|
||||
def posted(self):
|
||||
event = self.latest_event(type='posted')
|
||||
if event:
|
||||
return event.time
|
||||
return None
|
||||
|
||||
@property
|
||||
def submitted(self):
|
||||
event = self.latest_event(type='submitted')
|
||||
if event:
|
||||
return event.time
|
||||
return None
|
||||
|
||||
@property
|
||||
def sort_date(self):
|
||||
"""Returns the date to use for sorting, for posted statements this is post date,
|
||||
for pending statements this is submitted date"""
|
||||
if self.state_id == 'posted':
|
||||
return self.posted
|
||||
elif self.state_id == 'pending':
|
||||
return self.submitted
|
||||
|
||||
@property
|
||||
def modified(self):
|
||||
event = self.liaisonstatementevent_set.all().order_by('-time').first()
|
||||
if event:
|
||||
return event.time
|
||||
return None
|
||||
|
||||
@property
|
||||
def approved(self):
|
||||
return self.state_id in ('approved','posted')
|
||||
|
||||
@property
|
||||
def action_taken(self):
|
||||
return self.tags.filter(slug='taken').exists()
|
||||
|
||||
def active_attachments(self):
|
||||
'''Returns attachments with removed ones filtered out'''
|
||||
return self.attachments.exclude(liaisonstatementattachment__removed=True)
|
||||
|
||||
@property
|
||||
def awaiting_action(self):
|
||||
if getattr(self, '_awaiting_action', None) != None:
|
||||
return bool(self._awaiting_action)
|
||||
return self.tags.filter(slug='awaiting').exists()
|
||||
|
||||
@property
|
||||
def from_groups_display(self):
|
||||
'''Returns comma separated list of from_group names'''
|
||||
groups = self.from_groups.order_by('name').values_list('name',flat=True)
|
||||
return ', '.join(groups)
|
||||
|
||||
@property
|
||||
def to_groups_display(self):
|
||||
'''Returns comma separated list of to_group names'''
|
||||
groups = self.to_groups.order_by('name').values_list('name',flat=True)
|
||||
return ', '.join(groups)
|
||||
|
||||
def from_groups_short_display(self):
|
||||
'''Returns comma separated list of from_group acronyms. For use in admin
|
||||
interface'''
|
||||
groups = self.to_groups.order_by('acronym').values_list('acronym',flat=True)
|
||||
return ', '.join(groups)
|
||||
from_groups_short_display.short_description = 'From Groups'
|
||||
|
||||
def set_state(self,slug):
|
||||
try:
|
||||
state = LiaisonStatementState.objects.get(slug=slug)
|
||||
except LiaisonStatementState.DoesNotExist:
|
||||
return
|
||||
self.state = state
|
||||
self.save()
|
||||
|
||||
|
||||
class LiaisonStatementAttachment(models.Model):
|
||||
statement = models.ForeignKey(LiaisonStatement)
|
||||
document = models.ForeignKey(Document)
|
||||
removed = models.BooleanField(default=False)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.title
|
||||
return self.document.name
|
||||
|
||||
|
||||
class RelatedLiaisonStatement(models.Model):
|
||||
source = models.ForeignKey(LiaisonStatement, related_name='source_of_set')
|
||||
target = models.ForeignKey(LiaisonStatement, related_name='target_of_set')
|
||||
relationship = models.ForeignKey(DocRelationshipName)
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s %s %s" % (self.source.title, self.relationship.name.lower(), self.target.title)
|
||||
|
||||
|
||||
class LiaisonStatementGroupContacts(models.Model):
|
||||
group = models.ForeignKey(Group, unique=True)
|
||||
contacts = models.CharField(max_length=255,blank=True)
|
||||
cc_contacts = models.CharField(max_length=255,blank=True)
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s" % self.group.name
|
||||
|
||||
|
||||
class LiaisonStatementEvent(models.Model):
|
||||
time = models.DateTimeField(auto_now_add=True)
|
||||
type = models.ForeignKey(LiaisonStatementEventTypeName)
|
||||
by = models.ForeignKey(Person)
|
||||
statement = models.ForeignKey(LiaisonStatement)
|
||||
desc = models.TextField()
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s %s by %s at %s" % (self.statement.title, self.type.slug, self.by.plain_name(), self.time)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-time', '-id']
|
||||
|
|
|
@ -1,23 +1,24 @@
|
|||
# Autogenerated by the mkresources management command 2014-11-13 23:53
|
||||
# Autogenerated by the makeresources management command 2015-08-27 10:22 PDT
|
||||
from tastypie.resources import ModelResource
|
||||
from tastypie.fields import ToOneField, ToManyField
|
||||
from tastypie.constants import ALL, ALL_WITH_RELATIONS
|
||||
from tastypie.fields import ToOneField, ToManyField # pyflakes:ignore
|
||||
from tastypie.constants import ALL, ALL_WITH_RELATIONS # pyflakes:ignore
|
||||
|
||||
from ietf import api
|
||||
|
||||
from ietf.liaisons.models import * # pyflakes:ignore
|
||||
from ietf.liaisons.models import * # pyflakes:ignore
|
||||
|
||||
|
||||
from ietf.group.resources import GroupResource
|
||||
from ietf.doc.resources import DocumentResource
|
||||
from ietf.name.resources import LiaisonStatementPurposeNameResource
|
||||
from ietf.person.resources import EmailResource
|
||||
from ietf.group.resources import GroupResource
|
||||
from ietf.name.resources import LiaisonStatementPurposeNameResource, LiaisonStatementTagNameResource, LiaisonStatementStateResource
|
||||
from ietf.doc.resources import DocumentResource
|
||||
class LiaisonStatementResource(ModelResource):
|
||||
purpose = ToOneField(LiaisonStatementPurposeNameResource, 'purpose')
|
||||
related_to = ToOneField('ietf.liaisons.resources.LiaisonStatementResource', 'related_to', null=True)
|
||||
from_group = ToOneField(GroupResource, 'from_group', null=True)
|
||||
from_contact = ToOneField(EmailResource, 'from_contact', null=True)
|
||||
to_group = ToOneField(GroupResource, 'to_group', null=True)
|
||||
purpose = ToOneField(LiaisonStatementPurposeNameResource, 'purpose')
|
||||
state = ToOneField(LiaisonStatementStateResource, 'state')
|
||||
from_groups = ToManyField(GroupResource, 'from_groups', null=True)
|
||||
to_groups = ToManyField(GroupResource, 'to_groups', null=True)
|
||||
tags = ToManyField(LiaisonStatementTagNameResource, 'tags', null=True)
|
||||
attachments = ToManyField(DocumentResource, 'attachments', null=True)
|
||||
class Meta:
|
||||
queryset = LiaisonStatement.objects.all()
|
||||
|
@ -26,25 +27,86 @@ class LiaisonStatementResource(ModelResource):
|
|||
filtering = {
|
||||
"id": ALL,
|
||||
"title": ALL,
|
||||
"body": ALL,
|
||||
"to_contacts": ALL,
|
||||
"response_contacts": ALL,
|
||||
"technical_contacts": ALL,
|
||||
"action_holder_contacts": ALL,
|
||||
"cc_contacts": ALL,
|
||||
"deadline": ALL,
|
||||
"other_identifiers": ALL,
|
||||
"body": ALL,
|
||||
"from_name": ALL,
|
||||
"to_name": ALL,
|
||||
"to_contact": ALL,
|
||||
"reply_to": ALL,
|
||||
"response_contact": ALL,
|
||||
"technical_contact": ALL,
|
||||
"cc": ALL,
|
||||
"submitted": ALL,
|
||||
"modified": ALL,
|
||||
"approved": ALL,
|
||||
"action_taken": ALL,
|
||||
"purpose": ALL_WITH_RELATIONS,
|
||||
"related_to": ALL_WITH_RELATIONS,
|
||||
"from_group": ALL_WITH_RELATIONS,
|
||||
"from_contact": ALL_WITH_RELATIONS,
|
||||
"to_group": ALL_WITH_RELATIONS,
|
||||
"purpose": ALL_WITH_RELATIONS,
|
||||
"state": ALL_WITH_RELATIONS,
|
||||
"from_groups": ALL_WITH_RELATIONS,
|
||||
"to_groups": ALL_WITH_RELATIONS,
|
||||
"tags": ALL_WITH_RELATIONS,
|
||||
"attachments": ALL_WITH_RELATIONS,
|
||||
}
|
||||
api.liaisons.register(LiaisonStatementResource())
|
||||
|
||||
from ietf.group.resources import GroupResource
|
||||
class LiaisonStatementGroupContactsResource(ModelResource):
|
||||
group = ToOneField(GroupResource, 'group')
|
||||
class Meta:
|
||||
queryset = LiaisonStatementGroupContacts.objects.all()
|
||||
#resource_name = 'liaisonstatementgroupcontacts'
|
||||
filtering = {
|
||||
"id": ALL,
|
||||
"contacts": ALL,
|
||||
"group": ALL_WITH_RELATIONS,
|
||||
}
|
||||
api.liaisons.register(LiaisonStatementGroupContactsResource())
|
||||
|
||||
from ietf.person.resources import PersonResource
|
||||
from ietf.name.resources import LiaisonStatementEventTypeNameResource
|
||||
class LiaisonStatementEventResource(ModelResource):
|
||||
type = ToOneField(LiaisonStatementEventTypeNameResource, 'type')
|
||||
by = ToOneField(PersonResource, 'by')
|
||||
statement = ToOneField(LiaisonStatementResource, 'statement')
|
||||
class Meta:
|
||||
queryset = LiaisonStatementEvent.objects.all()
|
||||
#resource_name = 'liaisonstatementevent'
|
||||
filtering = {
|
||||
"id": ALL,
|
||||
"time": ALL,
|
||||
"desc": ALL,
|
||||
"type": ALL_WITH_RELATIONS,
|
||||
"by": ALL_WITH_RELATIONS,
|
||||
"statement": ALL_WITH_RELATIONS,
|
||||
}
|
||||
api.liaisons.register(LiaisonStatementEventResource())
|
||||
|
||||
from ietf.doc.resources import DocumentResource
|
||||
class LiaisonStatementAttachmentResource(ModelResource):
|
||||
statement = ToOneField(LiaisonStatementResource, 'statement')
|
||||
document = ToOneField(DocumentResource, 'document')
|
||||
class Meta:
|
||||
queryset = LiaisonStatementAttachment.objects.all()
|
||||
#resource_name = 'liaisonstatementattachment'
|
||||
filtering = {
|
||||
"id": ALL,
|
||||
"removed": ALL,
|
||||
"statement": ALL_WITH_RELATIONS,
|
||||
"document": ALL_WITH_RELATIONS,
|
||||
}
|
||||
api.liaisons.register(LiaisonStatementAttachmentResource())
|
||||
|
||||
from ietf.name.resources import DocRelationshipNameResource
|
||||
class RelatedLiaisonStatementResource(ModelResource):
|
||||
source = ToOneField(LiaisonStatementResource, 'source')
|
||||
target = ToOneField(LiaisonStatementResource, 'target')
|
||||
relationship = ToOneField(DocRelationshipNameResource, 'relationship')
|
||||
class Meta:
|
||||
queryset = RelatedLiaisonStatement.objects.all()
|
||||
#resource_name = 'relatedliaisonstatement'
|
||||
filtering = {
|
||||
"id": ALL,
|
||||
"source": ALL_WITH_RELATIONS,
|
||||
"target": ALL_WITH_RELATIONS,
|
||||
"relationship": ALL_WITH_RELATIONS,
|
||||
}
|
||||
api.liaisons.register(RelatedLiaisonStatementResource())
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,20 +4,34 @@ from django.conf.urls import patterns, url
|
|||
from django.views.generic import RedirectView, TemplateView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^help/$', TemplateView.as_view(template_name='liaisons/help.html')),
|
||||
url(r'^help/fields/$', TemplateView.as_view(template_name='liaisons/field_help.html'), name="liaisons_field_help"),
|
||||
(r'^help/from_ietf/$', TemplateView.as_view(template_name='liaisons/guide_from_ietf.html')),
|
||||
(r'^help/to_ietf/$', TemplateView.as_view(template_name='liaisons/guide_to_ietf.html')),
|
||||
(r'^managers/$', RedirectView.as_view(url='https://www.ietf.org/liaison/managers.html')),
|
||||
(r'^help/$', TemplateView.as_view(template_name='liaisons/help.html')),
|
||||
url(r'^help/fields/$', TemplateView.as_view(template_name='liaisons/field_help.html'), name='liaisons_field_help'),
|
||||
(r'^help/from_ietf/$', TemplateView.as_view(template_name='liaisons/guide_from_ietf.html')),
|
||||
(r'^help/to_ietf/$', TemplateView.as_view(template_name='liaisons/guide_to_ietf.html')),
|
||||
(r'^managers/$', RedirectView.as_view(url='https://www.ietf.org/liaison/managers.html')),
|
||||
)
|
||||
|
||||
# AJAX views
|
||||
urlpatterns += patterns('ietf.liaisons.views',
|
||||
url(r'^$', 'liaison_list', name='liaison_list'),
|
||||
url(r'^(?P<object_id>\d+)/$', 'liaison_detail', name='liaison_detail'),
|
||||
url(r'^(?P<object_id>\d+)/edit/$', 'liaison_edit', name='liaison_edit'),
|
||||
url(r'^for_approval/$', 'liaison_approval_list', name='liaison_approval_list'),
|
||||
url(r'^for_approval/(?P<object_id>\d+)/$', 'liaison_approval_detail', name='liaison_approval_detail'),
|
||||
url(r'^add/$', 'add_liaison', name='add_liaison'),
|
||||
url(r'^ajax/get_info/$', 'ajax_get_liaison_info'),
|
||||
url(r'^ajax/select2search/$', 'ajax_select2_search_liaison_statements', name='ajax_select2_search_liaison_statements'),
|
||||
(r'^ajax/get_info/$', 'ajax_get_liaison_info'),
|
||||
(r'^ajax/select2search/$', 'ajax_select2_search_liaison_statements'),
|
||||
)
|
||||
|
||||
# Views
|
||||
urlpatterns += patterns('ietf.liaisons.views',
|
||||
(r'^$', 'liaison_list'),
|
||||
(r'^(?P<state>(posted|pending|dead))/$', 'liaison_list'),
|
||||
(r'^(?P<object_id>\d+)/$', 'liaison_detail'),
|
||||
(r'^(?P<object_id>\d+)/edit/$', 'liaison_edit'),
|
||||
(r'^(?P<object_id>\d+)/edit-attachment/(?P<doc_id>[A-Za-z0-9._+-]+)$', 'liaison_edit_attachment'),
|
||||
(r'^(?P<object_id>\d+)/delete-attachment/(?P<attach_id>[A-Za-z0-9._+-]+)$', 'liaison_delete_attachment'),
|
||||
(r'^(?P<object_id>\d+)/history/$', 'liaison_history'),
|
||||
(r'^(?P<object_id>\d+)/reply/$', 'liaison_reply'),
|
||||
(r'^(?P<object_id>\d+)/resend/$', 'liaison_resend'),
|
||||
(r'^add/(?P<type>(incoming|outgoing))/$', 'liaison_add'),
|
||||
|
||||
# Redirects for backwards compatibility
|
||||
(r'^add/$', 'redirect_add'),
|
||||
(r'^for_approval/$', 'redirect_for_approval'),
|
||||
(r'^for_approval/(?P<object_id>\d+)/$', 'redirect_for_approval'),
|
||||
)
|
||||
|
|
|
@ -1,548 +1,82 @@
|
|||
from django.db.models import Q
|
||||
from itertools import chain
|
||||
|
||||
from ietf.group.models import Group, Role
|
||||
from ietf.liaisons.models import LiaisonStatement
|
||||
from ietf.ietfauth.utils import has_role, passes_test_decorator
|
||||
|
||||
from ietf.liaisons.accounts import (is_ietfchair, is_iabchair, is_iab_executive_director, is_irtfchair,
|
||||
get_ietf_chair, get_iab_chair, get_iab_executive_director, get_irtf_chair,
|
||||
is_secretariat, can_add_liaison, get_person_for_user, proxy_personify_role)
|
||||
# a list of tuples, group query kwargs, role query kwargs
|
||||
GROUP_APPROVAL_MAPPING = [
|
||||
({'acronym':'ietf'},{'name':'chair'}),
|
||||
({'acronym':'iab'},{'name':'chair'}),
|
||||
({'type':'area'},{'name':'ad'}),
|
||||
({'type':'wg'},{'name':'ad'})]
|
||||
|
||||
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 approval_roles(group):
|
||||
'''Returns roles that have approval authority for group'''
|
||||
for group_kwargs,role_kwargs in GROUP_APPROVAL_MAPPING:
|
||||
if group in Group.objects.filter(**group_kwargs):
|
||||
# TODO is there a cleaner way?
|
||||
if group.type == 'wg':
|
||||
return Role.objects.filter(group=group.parent,**role_kwargs)
|
||||
else:
|
||||
return Role.objects.filter(group=group,**role_kwargs)
|
||||
|
||||
def approvable_liaison_statements(user):
|
||||
liaisons = LiaisonStatement.objects.filter(approved=None)
|
||||
'''Returns a queryset of Liaison Statements in pending state that user has authority
|
||||
to approve'''
|
||||
liaisons = LiaisonStatement.objects.filter(state__slug='pending')
|
||||
person = get_person_for_user(user)
|
||||
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])
|
||||
approvable_liaisons = []
|
||||
for liaison in liaisons:
|
||||
for group in liaison.from_groups.all():
|
||||
if person not in [ r.person for r in approval_roles(group) ]:
|
||||
break
|
||||
else:
|
||||
group_acronyms.append(x)
|
||||
approvable_liaisons.append(liaison.pk)
|
||||
|
||||
return liaisons.filter(Q(from_group__acronym__in=group_acronyms) | Q(from_group__pk__in=group_ids))
|
||||
return liaisons.filter(id__in=approvable_liaisons)
|
||||
|
||||
|
||||
# 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'}
|
||||
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)
|
||||
|
||||
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
|
||||
def can_edit_liaison(user, liaison):
|
||||
'''Return True if user is Secretariat or Liaison Manager of all SDO groups involved'''
|
||||
if has_role(user, "Secretariat"):
|
||||
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]
|
||||
if has_role(user, "Liaison Manager"):
|
||||
person = get_person_for_user(user)
|
||||
for group in chain(liaison.from_groups.filter(type_id='sdo'),liaison.to_groups.filter(type_id='sdo')):
|
||||
if not person.role_set.filter(group=group,name='liaiman'):
|
||||
return False
|
||||
else:
|
||||
result = []
|
||||
if self.obj.list_email:
|
||||
result.append(FakePerson(name ='%s Discussion List' % self.obj.name,
|
||||
address = self.obj.list_email))
|
||||
return result
|
||||
return True
|
||||
|
||||
def get_from_cc(self, person):
|
||||
result = [p for p in role_persons_with_fixed_email(self.obj, "chair") if p != person]
|
||||
if self.obj.parent:
|
||||
result += role_persons_with_fixed_email(self.obj.parent, "ad")
|
||||
if self.obj.list_email:
|
||||
result.append(FakePerson(name ='%s Discussion List' % self.obj.name,
|
||||
address = self.obj.list_email))
|
||||
return result
|
||||
return False
|
||||
|
||||
def needs_approval(self, person=None):
|
||||
# Check if person is AD of area
|
||||
if self.obj.parent and self.obj.parent.role_set.filter(person=person, name="ad"):
|
||||
def get_person_for_user(user):
|
||||
try:
|
||||
return user.person
|
||||
except:
|
||||
return None
|
||||
|
||||
def can_add_outgoing_liaison(user):
|
||||
return has_role(user, ["Area Director","WG Chair","WG Secretary","IETF Chair","IAB Chair",
|
||||
"IAB Executive Director","Liaison Manager","Secretariat"])
|
||||
|
||||
def can_add_incoming_liaison(user):
|
||||
return has_role(user, ["Liaison Manager","Authorized Individual","Secretariat"])
|
||||
|
||||
def can_add_liaison(user):
|
||||
return can_add_incoming_liaison(user) or can_add_outgoing_liaison(user)
|
||||
|
||||
def is_authorized_individual(user, groups):
|
||||
'''Returns True if the user has authorized_individual role for each of the groups'''
|
||||
for group in groups:
|
||||
if not Role.objects.filter(person=user.person, group=group, name="auth"):
|
||||
return False
|
||||
return True
|
||||
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)
|
||||
if self.queryset == None:
|
||||
self.queryset = Group.objects.filter(type="area", state="active")
|
||||
|
||||
def get_managed_list(self, query_filter=None):
|
||||
if not query_filter:
|
||||
query_filter = {}
|
||||
return [(u'%s_%s' % (self.pk, i.pk), i.name) for i in self.queryset.filter(**query_filter).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 AreaEntity(name=obj.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:
|
||||
self.queryset = Group.objects.filter(type="wg", state="active", parent__state="active").select_related("parent")
|
||||
|
||||
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.acronym, i.name)) for i in self.queryset.filter(**query_filter).order_by('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.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()
|
||||
|
|
|
@ -1,179 +1,69 @@
|
|||
# Copyright The IETF Trust 2007, All Rights Reserved
|
||||
import datetime
|
||||
import json
|
||||
from email.utils import parseaddr
|
||||
|
||||
from django.contrib import messages
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
from django.core.validators import validate_email, ValidationError
|
||||
from django.db.models import Q
|
||||
from django.http import HttpResponse, HttpResponseForbidden
|
||||
from django.shortcuts import render_to_response, get_object_or_404, redirect
|
||||
from django.shortcuts import render, render_to_response, get_object_or_404, redirect
|
||||
from django.template import RequestContext
|
||||
|
||||
from ietf.liaisons.models import LiaisonStatement
|
||||
from ietf.liaisons.accounts import (get_person_for_user, can_add_outgoing_liaison,
|
||||
can_add_incoming_liaison,
|
||||
is_ietfchair, is_iabchair, is_iab_executive_director,
|
||||
can_edit_liaison, is_secretariat)
|
||||
from ietf.liaisons.forms import liaison_form_factory
|
||||
from ietf.liaisons.utils import IETFHM, can_submit_liaison_required, approvable_liaison_statements
|
||||
from ietf.doc.models import Document
|
||||
from ietf.ietfauth.utils import role_required, has_role
|
||||
from ietf.group.models import Group, Role
|
||||
from ietf.liaisons.models import (LiaisonStatement,LiaisonStatementEvent,
|
||||
LiaisonStatementAttachment)
|
||||
from ietf.liaisons.utils import (get_person_for_user, can_add_outgoing_liaison,
|
||||
can_add_incoming_liaison, can_edit_liaison,can_submit_liaison_required,
|
||||
can_add_liaison)
|
||||
from ietf.liaisons.forms import liaison_form_factory, SearchLiaisonForm, EditAttachmentForm
|
||||
from ietf.liaisons.mails import notify_pending_by_email, send_liaison_by_email
|
||||
from ietf.liaisons.fields import select2_id_liaison_json
|
||||
|
||||
EMAIL_ALIASES = {
|
||||
'IETFCHAIR':'The IETF Chair <chair@ietf.org>',
|
||||
'IESG':'The IESG <iesg@ietf.org>',
|
||||
'IAB':'The IAB <iab@iab.org>',
|
||||
'IABCHAIR':'The IAB Chair <iab-chair@iab.org>',
|
||||
'IABEXECUTIVEDIRECTOR':'The IAB Executive Director <execd@iab.org>'}
|
||||
|
||||
|
||||
@can_submit_liaison_required
|
||||
def add_liaison(request, liaison=None):
|
||||
if 'incoming' in request.GET.keys() and not can_add_incoming_liaison(request.user):
|
||||
return HttpResponseForbidden("Restricted to users who are authorized to submit incoming liaison statements")
|
||||
|
||||
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', False):
|
||||
if not liaison.approved:
|
||||
notify_pending_by_email(request, liaison)
|
||||
else:
|
||||
send_liaison_by_email(request, liaison)
|
||||
return redirect('liaison_list')
|
||||
else:
|
||||
form = liaison_form_factory(request, liaison=liaison)
|
||||
|
||||
return render_to_response(
|
||||
'liaisons/edit.html',
|
||||
{'form': form,
|
||||
'liaison': liaison},
|
||||
context_instance=RequestContext(request),
|
||||
)
|
||||
|
||||
|
||||
@can_submit_liaison_required
|
||||
def ajax_get_liaison_info(request):
|
||||
person = get_person_for_user(request.user)
|
||||
|
||||
to_entity_id = request.GET.get('to_entity_id', None)
|
||||
from_entity_id = request.GET.get('from_entity_id', None)
|
||||
|
||||
result = {'poc': [], 'cc': [], 'needs_approval': False, 'post_only': False, 'full_list': []}
|
||||
|
||||
to_error = 'Invalid TO entity id'
|
||||
if to_entity_id:
|
||||
to_entity = IETFHM.get_entity_by_key(to_entity_id)
|
||||
if to_entity:
|
||||
to_error = ''
|
||||
|
||||
from_error = 'Invalid FROM entity id'
|
||||
if from_entity_id:
|
||||
from_entity = IETFHM.get_entity_by_key(from_entity_id)
|
||||
if from_entity:
|
||||
from_error = ''
|
||||
|
||||
if to_error or from_error:
|
||||
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)]),
|
||||
'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(key=lambda x: x[1])
|
||||
full_list = [(person.pk, person.email())] + full_list
|
||||
result.update({'full_list': full_list})
|
||||
|
||||
json_result = json.dumps(result)
|
||||
return HttpResponse(json_result, content_type='text/javascript')
|
||||
|
||||
def normalize_sort(request):
|
||||
sort = request.GET.get('sort', "")
|
||||
if sort not in ('submitted', 'deadline', 'title', 'to_name', 'from_name'):
|
||||
sort = "submitted"
|
||||
|
||||
# reverse dates
|
||||
order_by = "-" + sort if sort in ("submitted", "deadline") else sort
|
||||
|
||||
return sort, order_by
|
||||
|
||||
def liaison_list(request):
|
||||
sort, order_by = normalize_sort(request)
|
||||
liaisons = LiaisonStatement.objects.exclude(approved=None).order_by(order_by).prefetch_related("attachments")
|
||||
|
||||
can_send_outgoing = can_add_outgoing_liaison(request.user)
|
||||
can_send_incoming = can_add_incoming_liaison(request.user)
|
||||
|
||||
approvable = approvable_liaison_statements(request.user).count()
|
||||
|
||||
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_select2_search_liaison_statements(request):
|
||||
q = [w.strip() for w in request.GET.get('q', '').split() if w.strip()]
|
||||
|
||||
if not q:
|
||||
objs = LiaisonStatement.objects.none()
|
||||
else:
|
||||
qs = LiaisonStatement.objects.exclude(approved=None).all()
|
||||
|
||||
for t in q:
|
||||
qs = qs.filter(title__icontains=t)
|
||||
|
||||
objs = qs.distinct().order_by("-id")[:20]
|
||||
|
||||
return HttpResponse(select2_id_liaison_json(objs), content_type='application/json')
|
||||
|
||||
@can_submit_liaison_required
|
||||
def liaison_approval_list(request):
|
||||
liaisons = approvable_liaison_statements(request.user).order_by("-submitted")
|
||||
|
||||
return render_to_response('liaisons/approval_list.html', {
|
||||
"liaisons": liaisons,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
@can_submit_liaison_required
|
||||
def liaison_approval_detail(request, object_id):
|
||||
liaison = get_object_or_404(approvable_liaison_statements(request.user), pk=object_id)
|
||||
|
||||
if request.method == 'POST' and request.POST.get('do_approval', False):
|
||||
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,
|
||||
"is_approving": True,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
# -------------------------------------------------
|
||||
# Helper Functions
|
||||
# -------------------------------------------------
|
||||
def _can_reply(liaison, user):
|
||||
'''Returns true if the user can send a reply to this liaison'''
|
||||
if user.is_authenticated():
|
||||
person = get_person_for_user(user)
|
||||
if has_role(user, "Secretariat"):
|
||||
return True
|
||||
if liaison.is_outgoing() and Role.objects.filter(group__in=liaison.to_groups.all(),person=person,name='auth'):
|
||||
return True
|
||||
if not liaison.is_outgoing() and Role.objects.filter(group__in=liaison.from_groups.all(),person=person,name='liaiman'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _can_take_care(liaison, user):
|
||||
'''Returns True if user can take care of awaiting actions associated with this liaison'''
|
||||
if not liaison.deadline or liaison.action_taken:
|
||||
return False
|
||||
|
||||
if user.is_authenticated():
|
||||
if is_secretariat(user):
|
||||
if has_role(user, "Secretariat"):
|
||||
return True
|
||||
else:
|
||||
return _find_person_in_emails(liaison, get_person_for_user(user))
|
||||
return False
|
||||
|
||||
|
||||
def _find_person_in_emails(liaison, person):
|
||||
'''Returns true if person corresponds with any of the email addresses associated
|
||||
with this liaison statement'''
|
||||
if not person:
|
||||
return False
|
||||
|
||||
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)
|
||||
emails = ','.join(e for e in [liaison.response_contacts, liaison.cc_contacts,
|
||||
liaison.to_contacts,liaison.technical_contacts] if e)
|
||||
for email in emails.split(','):
|
||||
name, addr = parseaddr(email)
|
||||
try:
|
||||
|
@ -183,37 +73,445 @@ def _find_person_in_emails(liaison, person):
|
|||
|
||||
if person.email_set.filter(address=addr):
|
||||
return True
|
||||
elif addr in ('chair@ietf.org', 'iesg@ietf.org') and is_ietfchair(person):
|
||||
elif addr in ('chair@ietf.org', 'iesg@ietf.org') and has_role(person.user, "IETF Chair"):
|
||||
return True
|
||||
elif addr in ('iab@iab.org', 'iab-chair@iab.org') and is_iabchair(person):
|
||||
elif addr in ('iab@iab.org', 'iab-chair@iab.org') and has_role(person.user, "IAB Chair"):
|
||||
return True
|
||||
elif addr in ('execd@iab.org', ) and is_iab_executive_director(person):
|
||||
elif addr in ('execd@iab.org', ) and has_role(person.user, "IAB Executive Director"):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def contacts_from_roles(roles):
|
||||
'''Returns contact string for given roles'''
|
||||
emails = [ '{} <{}>'.format(r.person.plain_name(),r.email.address) for r in roles ]
|
||||
return ','.join(emails)
|
||||
|
||||
def get_cc(group):
|
||||
'''Returns list of emails to use as CC for group. Simplified refactor of IETFHierarchy
|
||||
get_cc() and get_from_cc()
|
||||
'''
|
||||
emails = []
|
||||
|
||||
# role based CCs
|
||||
if group.acronym in ('ietf','iesg'):
|
||||
emails.append(EMAIL_ALIASES['IESG'])
|
||||
emails.append(EMAIL_ALIASES['IETFCHAIR'])
|
||||
elif group.acronym in ('iab'):
|
||||
emails.append(EMAIL_ALIASES['IAB'])
|
||||
emails.append(EMAIL_ALIASES['IABCHAIR'])
|
||||
emails.append(EMAIL_ALIASES['IABEXECUTIVEDIRECTOR'])
|
||||
elif group.type_id == 'area':
|
||||
emails.append(EMAIL_ALIASES['IETFCHAIR'])
|
||||
ad_roles = group.role_set.filter(name='ad')
|
||||
emails.extend([ '{} <{}>'.format(r.person.plain_name(),r.email.address) for r in ad_roles ])
|
||||
elif group.type_id == 'wg':
|
||||
ad_roles = group.parent.role_set.filter(name='ad')
|
||||
emails.extend([ '{} <{}>'.format(r.person.plain_name(),r.email.address) for r in ad_roles ])
|
||||
chair_roles = group.role_set.filter(name='chair')
|
||||
emails.extend([ '{} <{}>'.format(r.person.plain_name(),r.email.address) for r in chair_roles ])
|
||||
if group.list_email:
|
||||
emails.append('{} Discussion List <{}>'.format(group.name,group.list_email))
|
||||
elif group.type_id == 'sdo':
|
||||
liaiman_roles = group.role_set.filter(name='liaiman')
|
||||
emails.extend([ '{} <{}>'.format(r.person.plain_name(),r.email.address) for r in liaiman_roles ])
|
||||
|
||||
# explicit CCs
|
||||
if group.liaisonstatementgroupcontacts_set.exists() and group.liaisonstatementgroupcontacts_set.first().cc_contacts:
|
||||
emails = emails + group.liaisonstatementgroupcontacts_set.first().cc_contacts.split(',')
|
||||
|
||||
return emails
|
||||
|
||||
def get_contacts_for_group(group):
|
||||
'''Returns default contacts for groups as a comma separated string'''
|
||||
contacts = []
|
||||
|
||||
# use explicit default contacts if defined
|
||||
if group.liaisonstatementgroupcontacts_set.first():
|
||||
contacts.append(group.liaisonstatementgroupcontacts_set.first().contacts)
|
||||
|
||||
# otherwise construct based on group type
|
||||
elif group.type_id == 'area':
|
||||
roles = group.role_set.filter(name='ad')
|
||||
contacts.append(contacts_from_roles(roles))
|
||||
elif group.type_id == 'wg':
|
||||
roles = group.role_set.filter(name='chair')
|
||||
contacts.append(contacts_from_roles(roles))
|
||||
elif group.acronym == 'ietf':
|
||||
contacts.append(EMAIL_ALIASES['IETFCHAIR'])
|
||||
elif group.acronym == 'iab':
|
||||
contacts.append(EMAIL_ALIASES['IABCHAIR'])
|
||||
contacts.append(EMAIL_ALIASES['IABEXECUTIVEDIRECTOR'])
|
||||
elif group.acronym == 'iesg':
|
||||
contacts.append(EMAIL_ALIASES['IESG'])
|
||||
|
||||
return ','.join(contacts)
|
||||
|
||||
def get_details_tabs(stmt, selected):
|
||||
return [
|
||||
t + (t[0].lower() == selected.lower(),)
|
||||
for t in [
|
||||
('Statement', urlreverse('ietf.liaisons.views.liaison_detail', kwargs={ 'object_id': stmt.pk })),
|
||||
('History', urlreverse('ietf.liaisons.views.liaison_history', kwargs={ 'object_id': stmt.pk }))
|
||||
]]
|
||||
|
||||
def needs_approval(group,person):
|
||||
'''Returns True if the person does not have authority to send a Liaison Statement
|
||||
from group. For outgoing Liaison Statements only'''
|
||||
user = person.user
|
||||
if group.acronym in ('ietf','iesg') and has_role(user, 'IETF Chair'):
|
||||
return False
|
||||
if group.acronym == 'iab' and (has_role(user,'IAB Chair') or has_role(user,'IAB Executive Director')):
|
||||
return False
|
||||
if group.type_id == 'area' and group.role_set.filter(name='ad',person=person):
|
||||
return False
|
||||
if group.type_id == 'wg' and group.parent and group.parent.role_set.filter(name='ad',person=person):
|
||||
return False
|
||||
return True
|
||||
|
||||
def normalize_sort(request):
|
||||
sort = request.GET.get('sort', "")
|
||||
if sort not in ('date', 'deadline', 'title', 'to_groups', 'from_groups'):
|
||||
sort = "date"
|
||||
|
||||
# reverse dates
|
||||
order_by = "-" + sort if sort in ("date", "deadline") else sort
|
||||
|
||||
return sort, order_by
|
||||
|
||||
def post_only(group,person):
|
||||
'''Returns true if the user is restricted to post_only (vs. post_and_send) for this
|
||||
group. This is for incoming liaison statements.
|
||||
- Secretariat have full access.
|
||||
- Authorized Individuals have full access for the group they are associated with
|
||||
- Liaison Managers can post only
|
||||
'''
|
||||
if group.type_id == 'sdo' and ( not(has_role(person.user,"Secretariat") or group.role_set.filter(name='auth',person=person)) ):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# -------------------------------------------------
|
||||
# Ajax Functions
|
||||
# -------------------------------------------------
|
||||
@can_submit_liaison_required
|
||||
def ajax_get_liaison_info(request):
|
||||
'''Returns dictionary of info to update entry form given the groups
|
||||
that have been selected
|
||||
'''
|
||||
person = get_person_for_user(request.user)
|
||||
|
||||
from_groups = request.GET.getlist('from_groups', None)
|
||||
if not any(from_groups):
|
||||
from_groups = []
|
||||
to_groups = request.GET.getlist('to_groups', None)
|
||||
if not any(to_groups):
|
||||
to_groups = []
|
||||
from_groups = [ Group.objects.get(id=id) for id in from_groups ]
|
||||
to_groups = [ Group.objects.get(id=id) for id in to_groups ]
|
||||
|
||||
cc = []
|
||||
does_need_approval = []
|
||||
can_post_only = []
|
||||
to_contacts = []
|
||||
response_contacts = []
|
||||
result = {'response_contacts':[],'to_contacts': [], 'cc': [], 'needs_approval': False, 'post_only': False, 'full_list': []}
|
||||
|
||||
for group in from_groups:
|
||||
cc.extend(get_cc(group))
|
||||
does_need_approval.append(needs_approval(group,person))
|
||||
can_post_only.append(post_only(group,person))
|
||||
response_contacts.append(get_contacts_for_group(group))
|
||||
|
||||
for group in to_groups:
|
||||
cc.extend(get_cc(group))
|
||||
to_contacts.append(get_contacts_for_group(group))
|
||||
|
||||
# if there are from_groups and any need approval
|
||||
if does_need_approval:
|
||||
if any(does_need_approval):
|
||||
does_need_approval = True
|
||||
else:
|
||||
does_need_approval = False
|
||||
else:
|
||||
does_need_approval = True
|
||||
|
||||
result.update({'error': False,
|
||||
'cc': list(set(cc)),
|
||||
'response_contacts':list(set(response_contacts)),
|
||||
'to_contacts': list(set(to_contacts)),
|
||||
'needs_approval': does_need_approval,
|
||||
'post_only': any(can_post_only)})
|
||||
|
||||
json_result = json.dumps(result)
|
||||
return HttpResponse(json_result, content_type='text/javascript')
|
||||
|
||||
def ajax_select2_search_liaison_statements(request):
|
||||
query = [w.strip() for w in request.GET.get('q', '').split() if w.strip()]
|
||||
|
||||
if not query:
|
||||
objs = LiaisonStatement.objects.none()
|
||||
else:
|
||||
qs = LiaisonStatement.objects.filter(state='posted')
|
||||
|
||||
for term in query:
|
||||
if term.isdigit():
|
||||
q = Q(title__icontains=term)|Q(pk=term)
|
||||
else:
|
||||
q = Q(title__icontains=term)
|
||||
qs = qs.filter(q)
|
||||
|
||||
objs = qs.distinct().order_by("-id")[:20]
|
||||
|
||||
return HttpResponse(select2_id_liaison_json(objs), content_type='application/json')
|
||||
|
||||
|
||||
# -------------------------------------------------
|
||||
# Redirects for backwards compatibility
|
||||
# -------------------------------------------------
|
||||
|
||||
def redirect_add(request):
|
||||
"""Redirects old add urls"""
|
||||
if 'incoming' in request.GET.keys():
|
||||
return redirect('ietf.liaisons.views.liaison_add', type='incoming')
|
||||
else:
|
||||
return redirect('ietf.liaisons.views.liaison_add', type='outgoing')
|
||||
|
||||
def redirect_for_approval(request, object_id=None):
|
||||
"""Redirects old approval urls"""
|
||||
if object_id:
|
||||
return redirect('ietf.liaisons.views.liaison_detail', object_id=object_id)
|
||||
else:
|
||||
return redirect('ietf.liaisons.views.liaison_list', state='pending')
|
||||
|
||||
# -------------------------------------------------
|
||||
# View Functions
|
||||
# -------------------------------------------------
|
||||
|
||||
@can_submit_liaison_required
|
||||
def liaison_add(request, type=None, **kwargs):
|
||||
if type == 'incoming' and not can_add_incoming_liaison(request.user):
|
||||
return HttpResponseForbidden("Restricted to users who are authorized to submit incoming liaison statements")
|
||||
if type == 'outgoing' and not can_add_outgoing_liaison(request.user):
|
||||
return HttpResponseForbidden("Restricted to users who are authorized to submit outgoing liaison statements")
|
||||
|
||||
if request.method == 'POST':
|
||||
form = liaison_form_factory(request, data=request.POST.copy(),
|
||||
files=request.FILES, type=type, **kwargs)
|
||||
|
||||
if form.is_valid():
|
||||
liaison = form.save()
|
||||
|
||||
# notifications
|
||||
if 'send' in request.POST and liaison.state.slug == 'posted':
|
||||
send_liaison_by_email(request, liaison)
|
||||
messages.success(request, 'The statement has been sent and posted')
|
||||
elif liaison.state.slug == 'pending':
|
||||
notify_pending_by_email(request, liaison)
|
||||
messages.success(request, 'The statement has been submitted and is awaiting approval')
|
||||
|
||||
return redirect('ietf.liaisons.views.liaison_detail', object_id=liaison.pk)
|
||||
|
||||
else:
|
||||
form = liaison_form_factory(request,type=type,**kwargs)
|
||||
|
||||
return render_to_response(
|
||||
'liaisons/edit.html',
|
||||
{'form': form,
|
||||
'liaison': kwargs.get('instance')},
|
||||
context_instance=RequestContext(request),
|
||||
)
|
||||
|
||||
def liaison_history(request, object_id):
|
||||
"""Show the history for a specific liaison statement"""
|
||||
liaison = get_object_or_404(LiaisonStatement, id=object_id)
|
||||
events = liaison.liaisonstatementevent_set.all().order_by("-time", "-id").select_related("by")
|
||||
|
||||
return render(request, "liaisons/detail_history.html", {
|
||||
'events':events,
|
||||
'liaison': liaison,
|
||||
'tabs': get_details_tabs(liaison, 'History'),
|
||||
'selected_tab_entry':'history'
|
||||
})
|
||||
|
||||
def liaison_delete_attachment(request, object_id, attach_id):
|
||||
liaison = get_object_or_404(LiaisonStatement, pk=object_id)
|
||||
attach = get_object_or_404(LiaisonStatementAttachment, pk=attach_id)
|
||||
if not ( request.user.is_authenticated() and can_edit_liaison(request.user, liaison) ):
|
||||
return HttpResponseForbidden("You are not authorized for this action")
|
||||
|
||||
attach.removed = True
|
||||
attach.save()
|
||||
|
||||
# create event
|
||||
LiaisonStatementEvent.objects.create(
|
||||
type_id='modified',
|
||||
by=get_person_for_user(request.user),
|
||||
statement=liaison,
|
||||
desc='Attachment Removed: {}'.format(attach.document.title)
|
||||
)
|
||||
messages.success(request, 'Attachment Deleted')
|
||||
return redirect('ietf.liaisons.views.liaison_detail', object_id=liaison.pk)
|
||||
|
||||
def liaison_detail(request, object_id):
|
||||
liaison = get_object_or_404(LiaisonStatement.objects.exclude(approved=None), pk=object_id)
|
||||
liaison = get_object_or_404(LiaisonStatement, 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)
|
||||
can_reply = _can_reply(liaison, request.user)
|
||||
person = get_person_for_user(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.liaisonstatement_set.exclude(approved=None)
|
||||
if request.method == 'POST':
|
||||
if request.POST.get('approved'):
|
||||
liaison.change_state(state_id='approved',person=person)
|
||||
liaison.change_state(state_id='posted',person=person)
|
||||
send_liaison_by_email(request, liaison)
|
||||
messages.success(request,'Liaison Statement Approved and Posted')
|
||||
elif request.POST.get('dead'):
|
||||
liaison.change_state(state_id='dead',person=person)
|
||||
messages.success(request,'Liaison Statement Killed')
|
||||
elif request.POST.get('resurrect'):
|
||||
liaison.change_state(state_id='pending',person=person)
|
||||
messages.success(request,'Liaison Statement Resurrected')
|
||||
elif request.POST.get('do_action_taken') and can_take_care:
|
||||
liaison.tags.remove('required')
|
||||
liaison.tags.add('taken')
|
||||
can_take_care = False
|
||||
messages.success(request,'Action handled')
|
||||
|
||||
relations_by = [i.target for i in liaison.source_of_set.filter(target__state__slug='posted')]
|
||||
relations_to = [i.source for i in liaison.target_of_set.filter(source__state__slug='posted')]
|
||||
|
||||
return render_to_response("liaisons/detail.html", {
|
||||
"liaison": liaison,
|
||||
'tabs': get_details_tabs(liaison, 'Statement'),
|
||||
"can_edit": can_edit,
|
||||
"can_take_care": can_take_care,
|
||||
"relations": relations,
|
||||
"can_reply": can_reply,
|
||||
"relations_to": relations_to,
|
||||
"relations_by": relations_by,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
def liaison_edit(request, object_id):
|
||||
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)
|
||||
return liaison_add(request, instance=liaison)
|
||||
|
||||
def liaison_edit_attachment(request, object_id, doc_id):
|
||||
'''Edit the Liaison Statement attachment title'''
|
||||
liaison = get_object_or_404(LiaisonStatement, pk=object_id)
|
||||
doc = get_object_or_404(Document, pk=doc_id)
|
||||
if not ( request.user.is_authenticated() and can_edit_liaison(request.user, liaison) ):
|
||||
return HttpResponseForbidden("You are not authorized for this action")
|
||||
|
||||
if request.method == 'POST':
|
||||
form = EditAttachmentForm(request.POST)
|
||||
if form.is_valid():
|
||||
title = form.cleaned_data.get('title')
|
||||
doc.title = title
|
||||
doc.save()
|
||||
|
||||
# create event
|
||||
LiaisonStatementEvent.objects.create(
|
||||
type_id='modified',
|
||||
by=get_person_for_user(request.user),
|
||||
statement=liaison,
|
||||
desc='Attachment Title changed to {}'.format(title)
|
||||
)
|
||||
messages.success(request,'Attachment title changed')
|
||||
return redirect('ietf.liaisons.views.liaison_detail', object_id=liaison.pk)
|
||||
|
||||
else:
|
||||
form = EditAttachmentForm(initial={'title':doc.title})
|
||||
|
||||
return render_to_response(
|
||||
'liaisons/edit_attachment.html',
|
||||
{'form': form,
|
||||
'liaison': liaison},
|
||||
context_instance=RequestContext(request),
|
||||
)
|
||||
|
||||
def liaison_list(request, state='posted'):
|
||||
"""A generic list view with tabs for different states: posted, pending, dead"""
|
||||
liaisons = LiaisonStatement.objects.filter(state=state)
|
||||
|
||||
# check authorization for pending and dead tabs
|
||||
if state in ('pending','dead') and not can_add_liaison(request.user):
|
||||
msg = "Restricted to participants who are authorized to submit liaison statements on behalf of the various IETF entities"
|
||||
return HttpResponseForbidden(msg)
|
||||
|
||||
# perform search / filter
|
||||
if 'text' in request.GET:
|
||||
form = SearchLiaisonForm(data=request.GET)
|
||||
search_conducted = True
|
||||
if form.is_valid():
|
||||
results = form.get_results()
|
||||
liaisons = results
|
||||
else:
|
||||
form = SearchLiaisonForm()
|
||||
search_conducted = False
|
||||
|
||||
# perform sort
|
||||
sort, order_by = normalize_sort(request)
|
||||
if sort == 'date':
|
||||
liaisons = sorted(liaisons, key=lambda a: a.sort_date, reverse=True)
|
||||
if sort == 'from_groups':
|
||||
liaisons = sorted(liaisons, key=lambda a: a.from_groups_display)
|
||||
if sort == 'to_groups':
|
||||
liaisons = sorted(liaisons, key=lambda a: a.to_groups_display)
|
||||
if sort == 'deadline':
|
||||
liaisons = liaisons.order_by('-deadline')
|
||||
if sort == 'title':
|
||||
liaisons = liaisons.order_by('title')
|
||||
|
||||
# add menu entries
|
||||
entries = []
|
||||
entries.append(("Posted", urlreverse("ietf.liaisons.views.liaison_list", kwargs={'state':'posted'})))
|
||||
if can_add_liaison(request.user):
|
||||
entries.append(("Pending", urlreverse("ietf.liaisons.views.liaison_list", kwargs={'state':'pending'})))
|
||||
entries.append(("Dead", urlreverse("ietf.liaisons.views.liaison_list", kwargs={'state':'dead'})))
|
||||
|
||||
# add menu actions
|
||||
actions = []
|
||||
if can_add_incoming_liaison(request.user):
|
||||
actions.append(("New incoming liaison", urlreverse("ietf.liaisons.views.liaison_add", kwargs={'type':'incoming'})))
|
||||
if can_add_outgoing_liaison(request.user):
|
||||
actions.append(("New outgoing liaison", urlreverse("ietf.liaisons.views.liaison_add", kwargs={'type':'outgoing'})))
|
||||
|
||||
return render(request, 'liaisons/liaison_base.html', {
|
||||
'liaisons':liaisons,
|
||||
'selected_menu_entry':state,
|
||||
'menu_entries':entries,
|
||||
'menu_actions':actions,
|
||||
'sort':sort,
|
||||
'form':form,
|
||||
'with_search':True,
|
||||
'search_conducted':search_conducted,
|
||||
'state':state,
|
||||
})
|
||||
|
||||
def liaison_reply(request,object_id):
|
||||
'''Returns a new liaison form with initial data to reply to the given liaison'''
|
||||
liaison = get_object_or_404(LiaisonStatement, pk=object_id)
|
||||
if liaison.is_outgoing():
|
||||
reply_type = 'incoming'
|
||||
else:
|
||||
reply_type = 'outgoing'
|
||||
initial = dict(
|
||||
to_groups=[ x.pk for x in liaison.from_groups.all() ],
|
||||
from_groups=[ x.pk for x in liaison.to_groups.all() ],
|
||||
related_to=str(liaison.pk))
|
||||
|
||||
return liaison_add(request,type=reply_type,initial=initial)
|
||||
|
||||
@role_required('Secretariat',)
|
||||
def liaison_resend(request, object_id):
|
||||
'''Resend the liaison statement notification email'''
|
||||
liaison = get_object_or_404(LiaisonStatement, pk=object_id)
|
||||
person = get_person_for_user(request.user)
|
||||
send_liaison_by_email(request,liaison)
|
||||
LiaisonStatementEvent.objects.create(type_id='resent',by=person,statement=liaison,desc='Statement Resent')
|
||||
messages.success(request,'Liaison Statement resent')
|
||||
return redirect('ietf.liaisons.views.liaison_list')
|
||||
|
||||
|
|
|
@ -1,49 +1,12 @@
|
|||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
from django.db.models.query import QuerySet
|
||||
from django.forms.widgets import Select, Widget
|
||||
from django.forms.widgets import Widget
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.html import conditional_escape
|
||||
|
||||
|
||||
class FromWidget(Select):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(FromWidget, self).__init__(*args, **kwargs)
|
||||
self.full_power_on = []
|
||||
self.reduced_to_set = []
|
||||
|
||||
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)):
|
||||
base = super(FromWidget, self).render(name, value, attrs, choices)
|
||||
else:
|
||||
option = all_choices[0]
|
||||
if isinstance(option[1], (list, tuple)):
|
||||
option = option[1][0]
|
||||
value = option[0]
|
||||
text = option[1]
|
||||
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 form-control" 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>' % conditional_escape(from_code)
|
||||
for to_code in self.reduced_to_set:
|
||||
base += '<span class="reduced_to_set_%s"></span>' % conditional_escape(to_code)
|
||||
base += '</div>'
|
||||
return mark_safe(base)
|
||||
|
||||
|
||||
class ReadOnlyWidget(Widget):
|
||||
def render(self, name, value, attrs=None):
|
||||
html = u'<div id="id_%s" class="form-control widget">%s</div>' % (conditional_escape(name), conditional_escape(value or ''))
|
||||
return mark_safe(html)
|
||||
|
||||
|
||||
class ButtonWidget(Widget):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.label = kwargs.pop('label', None)
|
||||
self.show_on = kwargs.pop('show_on', None)
|
||||
|
@ -64,15 +27,17 @@ class ButtonWidget(Widget):
|
|||
|
||||
|
||||
class ShowAttachmentsWidget(Widget):
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
html = u'<div id="id_%s">' % name
|
||||
html += u'<span style="display: none" class="showAttachmentsEmpty form-control widget">No files attached</span>'
|
||||
html += u'<div class="attachedFiles form-control widget">'
|
||||
if value and isinstance(value, QuerySet):
|
||||
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))
|
||||
html += u'<a class="initialAttach" href="%s%s">%s</a> ' % (settings.LIAISON_ATTACH_URL, conditional_escape(attachment.document.external_url), conditional_escape(attachment.document.title))
|
||||
html += u'<a class="btn btn-default btn-xs" href="{}">Edit</a> '.format(urlreverse("ietf.liaisons.views.liaison_edit_attachment", kwargs={'object_id':attachment.statement.pk,'doc_id':attachment.document.pk}))
|
||||
html += u'<a class="btn btn-default btn-xs" href="{}">Delete</a> '.format(urlreverse("ietf.liaisons.views.liaison_delete_attachment", kwargs={'object_id':attachment.statement.pk,'attach_id':attachment.pk}))
|
||||
html += u'<br />'
|
||||
else:
|
||||
html += u'No files attached'
|
||||
html += u'</div></div>'
|
||||
return mark_safe(html)
|
||||
return mark_safe(html)
|
|
@ -1530,6 +1530,66 @@
|
|||
"model": "name.liaisonstatementpurposename",
|
||||
"pk": "response"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"order": 1,
|
||||
"used": true,
|
||||
"name": "Required",
|
||||
"desc": ""
|
||||
},
|
||||
"model": "name.liaisonstatementtagname",
|
||||
"pk": "required"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"order": 2,
|
||||
"used": true,
|
||||
"name": "Taken",
|
||||
"desc": ""
|
||||
},
|
||||
"model": "name.liaisonstatementtagname",
|
||||
"pk": "taken"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"order": 1,
|
||||
"used": true,
|
||||
"name": "Pending",
|
||||
"desc": ""
|
||||
},
|
||||
"model": "name.liaisonstatementstate",
|
||||
"pk": "pending"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"order": 2,
|
||||
"used": true,
|
||||
"name": "Approved",
|
||||
"desc": ""
|
||||
},
|
||||
"model": "name.liaisonstatementstate",
|
||||
"pk": "approved"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"order": 3,
|
||||
"used": true,
|
||||
"name": "Posted",
|
||||
"desc": ""
|
||||
},
|
||||
"model": "name.liaisonstatementstate",
|
||||
"pk": "posted"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"order": 4,
|
||||
"used": true,
|
||||
"name": "Dead",
|
||||
"desc": ""
|
||||
},
|
||||
"model": "name.liaisonstatementstate",
|
||||
"pk": "dead"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"order": 0,
|
||||
|
|
59
ietf/name/migrations/0006_add_liaison_names.py
Normal file
59
ietf/name/migrations/0006_add_liaison_names.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('name', '0005_add_sug_replaces'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='LiaisonStatementEventTypeName',
|
||||
fields=[
|
||||
('slug', models.CharField(max_length=32, serialize=False, primary_key=True)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('desc', models.TextField(blank=True)),
|
||||
('used', models.BooleanField(default=True)),
|
||||
('order', models.IntegerField(default=0)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['order'],
|
||||
'abstract': False,
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='LiaisonStatementState',
|
||||
fields=[
|
||||
('slug', models.CharField(max_length=32, serialize=False, primary_key=True)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('desc', models.TextField(blank=True)),
|
||||
('used', models.BooleanField(default=True)),
|
||||
('order', models.IntegerField(default=0)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['order'],
|
||||
'abstract': False,
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='LiaisonStatementTagName',
|
||||
fields=[
|
||||
('slug', models.CharField(max_length=32, serialize=False, primary_key=True)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('desc', models.TextField(blank=True)),
|
||||
('used', models.BooleanField(default=True)),
|
||||
('order', models.IntegerField(default=0)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['order'],
|
||||
'abstract': False,
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
39
ietf/name/migrations/0007_populate_liaison_names.py
Normal file
39
ietf/name/migrations/0007_populate_liaison_names.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
def populate_names(apps, schema_editor):
|
||||
# LiaisonStatementState: Pending, Approved, Dead
|
||||
LiaisonStatementState = apps.get_model("name", "LiaisonStatementState")
|
||||
LiaisonStatementState.objects.create(slug="pending", order=1, name="Pending")
|
||||
LiaisonStatementState.objects.create(slug="approved", order=2, name="Approved")
|
||||
LiaisonStatementState.objects.create(slug="posted", order=3, name="Posted")
|
||||
LiaisonStatementState.objects.create(slug="dead", order=4, name="Dead")
|
||||
|
||||
# LiaisonStatementEventTypeName: Submitted, Modified, Approved, Posted, Killed, Resurrected, MsgIn, MsgOut, Comment
|
||||
LiaisonStatementEventTypeName = apps.get_model("name", "LiaisonStatementEventTypeName")
|
||||
LiaisonStatementEventTypeName.objects.create(slug="submitted", order=1, name="Submitted")
|
||||
LiaisonStatementEventTypeName.objects.create(slug="modified", order=2, name="Modified")
|
||||
LiaisonStatementEventTypeName.objects.create(slug="approved", order=3, name="Approved")
|
||||
LiaisonStatementEventTypeName.objects.create(slug="posted", order=4, name="Posted")
|
||||
LiaisonStatementEventTypeName.objects.create(slug="killed", order=5, name="Killed")
|
||||
LiaisonStatementEventTypeName.objects.create(slug="resurrected", order=6, name="Resurrected")
|
||||
LiaisonStatementEventTypeName.objects.create(slug="msgin", order=7, name="MsgIn")
|
||||
LiaisonStatementEventTypeName.objects.create(slug="msgout", order=8, name="MsgOut")
|
||||
LiaisonStatementEventTypeName.objects.create(slug="comment", order=9, name="Comment")
|
||||
|
||||
#LiaisonStatementTagName: Action Required, Action Taken
|
||||
LiaisonStatementTagName = apps.get_model("name", "LiaisonStatementTagName")
|
||||
LiaisonStatementTagName.objects.create(slug="required", order=1, name="Action Required")
|
||||
LiaisonStatementTagName.objects.create(slug="taken", order=2, name="Action Taken")
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('name', '0006_add_liaison_names'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(populate_names),
|
||||
]
|
|
@ -80,4 +80,9 @@ class IprLicenseTypeName(NameModel):
|
|||
class IprEventTypeName(NameModel):
|
||||
"""submitted,posted,parked,removed,rejected,msgin,msgoutcomment,private_comment,
|
||||
legacy,update_notify,change_disclosure"""
|
||||
|
||||
class LiaisonStatementState(NameModel):
|
||||
"Pending, Approved, Dead"
|
||||
class LiaisonStatementEventTypeName(NameModel):
|
||||
"Submitted, Modified, Approved, Posted, Killed, Resurrected, MsgIn, MsgOut, Comment"
|
||||
class LiaisonStatementTagName(NameModel):
|
||||
"Action Required, Action Taken"
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
# Autogenerated by the mkresources management command 2014-11-13 23:53
|
||||
# Autogenerated by the makeresources management command 2015-08-27 11:01 PDT
|
||||
from tastypie.resources import ModelResource
|
||||
from tastypie.fields import ToManyField
|
||||
from tastypie.constants import ALL, ALL_WITH_RELATIONS
|
||||
from tastypie.fields import ToOneField, ToManyField # pyflakes:ignore
|
||||
from tastypie.constants import ALL, ALL_WITH_RELATIONS # pyflakes:ignore
|
||||
|
||||
from ietf import api
|
||||
|
||||
from ietf.name.models import * # pyflakes:ignore
|
||||
from ietf.name.models import * # pyflakes:ignore
|
||||
|
||||
|
||||
class TimeSlotTypeNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = TimeSlotTypeName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'timeslottypename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -25,7 +24,6 @@ api.name.register(TimeSlotTypeNameResource())
|
|||
class GroupStateNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = GroupStateName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'groupstatename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -39,7 +37,6 @@ api.name.register(GroupStateNameResource())
|
|||
class DocTagNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = DocTagName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'doctagname'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -53,7 +50,6 @@ api.name.register(DocTagNameResource())
|
|||
class IntendedStdLevelNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = IntendedStdLevelName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'intendedstdlevelname'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -67,7 +63,6 @@ api.name.register(IntendedStdLevelNameResource())
|
|||
class LiaisonStatementPurposeNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = LiaisonStatementPurposeName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'liaisonstatementpurposename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -82,7 +77,6 @@ class DraftSubmissionStateNameResource(ModelResource):
|
|||
next_states = ToManyField('ietf.name.resources.DraftSubmissionStateNameResource', 'next_states', null=True)
|
||||
class Meta:
|
||||
queryset = DraftSubmissionStateName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'draftsubmissionstatename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -97,7 +91,6 @@ api.name.register(DraftSubmissionStateNameResource())
|
|||
class DocTypeNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = DocTypeName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'doctypename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -111,7 +104,6 @@ api.name.register(DocTypeNameResource())
|
|||
class RoleNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = RoleName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'rolename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -122,10 +114,22 @@ class RoleNameResource(ModelResource):
|
|||
}
|
||||
api.name.register(RoleNameResource())
|
||||
|
||||
class IprDisclosureStateNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = IprDisclosureStateName.objects.all()
|
||||
#resource_name = 'iprdisclosurestatename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
"name": ALL,
|
||||
"desc": ALL,
|
||||
"used": ALL,
|
||||
"order": ALL,
|
||||
}
|
||||
api.name.register(IprDisclosureStateNameResource())
|
||||
|
||||
class StdLevelNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = StdLevelName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'stdlevelname'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -136,10 +140,22 @@ class StdLevelNameResource(ModelResource):
|
|||
}
|
||||
api.name.register(StdLevelNameResource())
|
||||
|
||||
class LiaisonStatementEventTypeNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = LiaisonStatementEventTypeName.objects.all()
|
||||
#resource_name = 'liaisonstatementeventtypename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
"name": ALL,
|
||||
"desc": ALL,
|
||||
"used": ALL,
|
||||
"order": ALL,
|
||||
}
|
||||
api.name.register(LiaisonStatementEventTypeNameResource())
|
||||
|
||||
class GroupTypeNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = GroupTypeName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'grouptypename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -150,10 +166,22 @@ class GroupTypeNameResource(ModelResource):
|
|||
}
|
||||
api.name.register(GroupTypeNameResource())
|
||||
|
||||
class IprEventTypeNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = IprEventTypeName.objects.all()
|
||||
#resource_name = 'ipreventtypename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
"name": ALL,
|
||||
"desc": ALL,
|
||||
"used": ALL,
|
||||
"order": ALL,
|
||||
}
|
||||
api.name.register(IprEventTypeNameResource())
|
||||
|
||||
class GroupMilestoneStateNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = GroupMilestoneStateName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'groupmilestonestatename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -167,7 +195,6 @@ api.name.register(GroupMilestoneStateNameResource())
|
|||
class SessionStatusNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = SessionStatusName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'sessionstatusname'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -181,7 +208,6 @@ api.name.register(SessionStatusNameResource())
|
|||
class DocReminderTypeNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = DocReminderTypeName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'docremindertypename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -195,7 +221,6 @@ api.name.register(DocReminderTypeNameResource())
|
|||
class ConstraintNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = ConstraintName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'constraintname'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -210,7 +235,6 @@ api.name.register(ConstraintNameResource())
|
|||
class MeetingTypeNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = MeetingTypeName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'meetingtypename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -224,7 +248,6 @@ api.name.register(MeetingTypeNameResource())
|
|||
class DocRelationshipNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = DocRelationshipName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'docrelationshipname'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -239,7 +262,6 @@ api.name.register(DocRelationshipNameResource())
|
|||
class RoomResourceNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = RoomResourceName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
resource_name = 'roomresourcename' # Needed because tastypie otherwise removes 'resource' from the name
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -250,10 +272,35 @@ class RoomResourceNameResource(ModelResource):
|
|||
}
|
||||
api.name.register(RoomResourceNameResource())
|
||||
|
||||
class IprLicenseTypeNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = IprLicenseTypeName.objects.all()
|
||||
#resource_name = 'iprlicensetypename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
"name": ALL,
|
||||
"desc": ALL,
|
||||
"used": ALL,
|
||||
"order": ALL,
|
||||
}
|
||||
api.name.register(IprLicenseTypeNameResource())
|
||||
|
||||
class LiaisonStatementTagNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = LiaisonStatementTagName.objects.all()
|
||||
#resource_name = 'liaisonstatementtagname'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
"name": ALL,
|
||||
"desc": ALL,
|
||||
"used": ALL,
|
||||
"order": ALL,
|
||||
}
|
||||
api.name.register(LiaisonStatementTagNameResource())
|
||||
|
||||
class FeedbackTypeNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = FeedbackTypeName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'feedbacktypename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -264,10 +311,22 @@ class FeedbackTypeNameResource(ModelResource):
|
|||
}
|
||||
api.name.register(FeedbackTypeNameResource())
|
||||
|
||||
class LiaisonStatementStateResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = LiaisonStatementState.objects.all()
|
||||
#resource_name = 'liaisonstatementstate'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
"name": ALL,
|
||||
"desc": ALL,
|
||||
"used": ALL,
|
||||
"order": ALL,
|
||||
}
|
||||
api.name.register(LiaisonStatementStateResource())
|
||||
|
||||
class StreamNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = StreamName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'streamname'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -281,7 +340,6 @@ api.name.register(StreamNameResource())
|
|||
class BallotPositionNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = BallotPositionName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'ballotpositionname'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -296,7 +354,6 @@ api.name.register(BallotPositionNameResource())
|
|||
class DBTemplateTypeNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = DBTemplateTypeName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'dbtemplatetypename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -310,7 +367,6 @@ api.name.register(DBTemplateTypeNameResource())
|
|||
class NomineePositionStateNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = NomineePositionStateName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'nomineepositionstatename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
|
@ -321,47 +377,3 @@ class NomineePositionStateNameResource(ModelResource):
|
|||
}
|
||||
api.name.register(NomineePositionStateNameResource())
|
||||
|
||||
|
||||
|
||||
class IprDisclosureStateNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = IprDisclosureStateName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'iprdisclosurestatename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
"name": ALL,
|
||||
"desc": ALL,
|
||||
"used": ALL,
|
||||
"order": ALL,
|
||||
}
|
||||
api.name.register(IprDisclosureStateNameResource())
|
||||
|
||||
class IprEventTypeNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = IprEventTypeName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'ipreventtypename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
"name": ALL,
|
||||
"desc": ALL,
|
||||
"used": ALL,
|
||||
"order": ALL,
|
||||
}
|
||||
api.name.register(IprEventTypeNameResource())
|
||||
|
||||
class IprLicenseTypeNameResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = IprLicenseTypeName.objects.all()
|
||||
serializer = api.Serializer()
|
||||
#resource_name = 'iprlicensetypename'
|
||||
filtering = {
|
||||
"slug": ALL,
|
||||
"name": ALL,
|
||||
"desc": ALL,
|
||||
"used": ALL,
|
||||
"order": ALL,
|
||||
}
|
||||
api.name.register(IprLicenseTypeNameResource())
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.db.models import Q
|
|||
from ietf.group.models import Group, GroupMilestone, Role
|
||||
from ietf.name.models import GroupStateName, GroupTypeName, RoleName
|
||||
from ietf.person.models import Person, Email
|
||||
|
||||
from ietf.liaisons.models import LiaisonStatementGroupContacts
|
||||
|
||||
|
||||
# ---------------------------------------------
|
||||
|
@ -69,6 +69,7 @@ class GroupModelForm(forms.ModelForm):
|
|||
parent = forms.ModelChoiceField(queryset=Group.objects.filter(Q(type='area',state='active')|Q(acronym='irtf')),required=False)
|
||||
ad = forms.ModelChoiceField(queryset=Person.objects.filter(role__name='ad',role__group__state='active',role__group__type='area'),required=False)
|
||||
state = forms.ModelChoiceField(queryset=GroupStateName.objects.exclude(slug__in=('dormant','unknown')),empty_label=None)
|
||||
liaison_contacts = forms.CharField(max_length=255,required=False,label='Default Liaison Contacts')
|
||||
|
||||
class Meta:
|
||||
model = Group
|
||||
|
@ -82,7 +83,12 @@ class GroupModelForm(forms.ModelForm):
|
|||
self.fields['ad'].label = 'Area Director'
|
||||
self.fields['comments'].widget.attrs['rows'] = 3
|
||||
self.fields['parent'].label = 'Area'
|
||||
|
||||
|
||||
if self.instance.pk:
|
||||
lsgc = self.instance.liaisonstatementgroupcontacts_set.first() # there can only be one
|
||||
if lsgc:
|
||||
self.fields['liaison_contacts'].initial = lsgc.contacts
|
||||
|
||||
def clean_parent(self):
|
||||
parent = self.cleaned_data['parent']
|
||||
type = self.cleaned_data['type']
|
||||
|
@ -111,7 +117,24 @@ class GroupModelForm(forms.ModelForm):
|
|||
raise forms.ValidationError('You must choose "active" or "concluded" for research group state')
|
||||
|
||||
return self.cleaned_data
|
||||
|
||||
def save(self, force_insert=False, force_update=False, commit=True):
|
||||
obj = super(GroupModelForm, self).save(commit=False)
|
||||
if commit:
|
||||
obj.save()
|
||||
contacts = self.cleaned_data.get('liaison_contacts')
|
||||
if contacts:
|
||||
try:
|
||||
lsgc = LiaisonStatementGroupContacts.objects.get(group=self.instance)
|
||||
lsgc.contacts = contacts
|
||||
lsgc.save()
|
||||
except LiaisonStatementGroupContacts.DoesNotExist:
|
||||
LiaisonStatementGroupContacts.objects.create(group=self.instance,contacts=contacts)
|
||||
elif LiaisonStatementGroupContacts.objects.filter(group=self.instance):
|
||||
LiaisonStatementGroupContacts.objects.filter(group=self.instance).delete()
|
||||
|
||||
return obj
|
||||
|
||||
class RoleForm(forms.Form):
|
||||
name = forms.ModelChoiceField(RoleName.objects.filter(slug__in=('chair','editor','secr','techadv')),empty_label=None)
|
||||
person = forms.CharField(max_length=50,widget=forms.TextInput(attrs={'class':'name-autocomplete'}),help_text="To see a list of people type the first name, or last name, or both.")
|
||||
|
|
|
@ -45,6 +45,7 @@ def add_legacy_fields(group):
|
|||
group.techadvisors = group.role_set.filter(name="techadv")
|
||||
group.editors = group.role_set.filter(name="editor")
|
||||
group.secretaries = group.role_set.filter(name="secr")
|
||||
group.liaison_contacts = group.liaisonstatementgroupcontacts_set.first()
|
||||
|
||||
#fill_in_charter_info(group)
|
||||
|
||||
|
|
|
@ -46,6 +46,9 @@
|
|||
<tr><td>Email Address:</td><td>{{ group.list_email }}</td></tr>
|
||||
<tr><td>Email Subscription:</td><td>{{ group.list_subscribe }}</td></tr>
|
||||
<tr><td>Email Archive:</td><td>{{ group.list_archive }}</td></tr>
|
||||
{% if group.liaison_contacts %}
|
||||
<tr><td>Default Liaison Contacts:</td><td>{{ group.liaison_contacts.contacts }}</td></tr>
|
||||
{% endif %}
|
||||
{% if group.features.has_chartering_process %}
|
||||
<tr><td>Charter:</td><td><a href="{% url "groups_charter" acronym=group.acronym %}">View Charter</a></td></tr>
|
||||
{% else %}
|
||||
|
|
|
@ -399,6 +399,8 @@ DOC_HREFS = {
|
|||
"slides": "https://www.ietf.org/slides/{doc.name}-{doc.rev}",
|
||||
"conflrev": "https://www.ietf.org/cr/{doc.name}-{doc.rev}.txt",
|
||||
"statchg": "https://www.ietf.org/sc/{doc.name}-{doc.rev}.txt",
|
||||
"liaison": "/documents/LIAISON/{doc.external_url}",
|
||||
"liai-att": "/documents/LIAISON/{doc.external_url}",
|
||||
}
|
||||
|
||||
MEETING_DOC_HREFS = {
|
||||
|
|
131
ietf/static/ietf/css/liaisons.css
Normal file
131
ietf/static/ietf/css/liaisons.css
Normal file
|
@ -0,0 +1,131 @@
|
|||
.baseform {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.baseform .fieldset {
|
||||
margin: 1em 0px;
|
||||
border: none;
|
||||
border: 1px solid #8899dd;
|
||||
background-color: #edf5ff;
|
||||
}
|
||||
|
||||
.baseform .fieldset h2 {
|
||||
background-color: #2647a0;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
padding: 5px 10px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.baseform .field {
|
||||
padding: 0.5em 10px;
|
||||
}
|
||||
|
||||
.baseform .field label {
|
||||
display: block;
|
||||
width: 150px;
|
||||
float: left;
|
||||
clear: left;
|
||||
}
|
||||
|
||||
.baseform .field .endfield {
|
||||
clear: left;
|
||||
}
|
||||
|
||||
.baseform .fieldWidget {
|
||||
margin-left: 150px;
|
||||
}
|
||||
|
||||
.baseform #baseform-fieldname-purpose_text,
|
||||
.baseform #baseform-fieldname-deadline_date {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.baseform select,
|
||||
.baseform textarea,
|
||||
.baseform input {
|
||||
border: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
.baseform input {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#id_title,
|
||||
.baseformedit #id_from_field,
|
||||
.baseform #id_organization,
|
||||
.baseform #id_to_poc,
|
||||
.baseform #id_response_contacts,
|
||||
.baseform #id_technical_contact,
|
||||
.baseform #id_cc1,
|
||||
.attach_titleField input,
|
||||
.baseform textarea {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
#id_purpose_text {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
#id_body {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.baseform input.disabledAddAttachment {
|
||||
border: none;
|
||||
padding: none;
|
||||
background: none;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
span.fieldRequired {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.fieldError {
|
||||
background-color: #ffcc66;
|
||||
}
|
||||
|
||||
th.sort {
|
||||
background-image: url(/images/sort-header-clear.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: right center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
th.headerSortUp {
|
||||
background-image: url(/images/sort-header-up-filled.png);
|
||||
}
|
||||
|
||||
th.headerSortDown {
|
||||
background-image: url(/images/sort-header-filled.png);
|
||||
}
|
||||
|
||||
td span.awaiting {
|
||||
background-color: #ffcc33;
|
||||
border-radius: 3px;
|
||||
float: right;
|
||||
width: 35px;
|
||||
padding: 4px 8px;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.noActionTaken, .actionTaken { padding: 2px 5px; }
|
||||
.actionTaken { border: 1px solid green; background-color: #ccffbb; }
|
||||
.noActionTaken { border: 1px solid red; background-color: #ffccbb; }
|
||||
|
||||
input[id$='DELETE'] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#id_from_groups + span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#id_to_groups + span {
|
||||
display: none;
|
||||
}
|
|
@ -1,319 +1,311 @@
|
|||
$(document).ready(function () {
|
||||
function setupAttachmentWidget() {
|
||||
var button = $(this);
|
||||
var config = {};
|
||||
var count = 0;
|
||||
|
||||
var readConfig = function() {
|
||||
var buttonFormGroup = button.parents('.form-group');
|
||||
var disabledLabel = buttonFormGroup.find('.attachDisabledLabel');
|
||||
var attachmentWidget = {
|
||||
button : null,
|
||||
config : {},
|
||||
count : 0,
|
||||
|
||||
if (disabledLabel.length) {
|
||||
config.disabledLabel = disabledLabel.html();
|
||||
var required = [];
|
||||
buttonFormGroup.find('.attachRequiredField').each(function(index, field) {
|
||||
required.push('#' + $(field).text());
|
||||
});
|
||||
config.basefields = $(required.join(","));
|
||||
}
|
||||
readConfig : function() {
|
||||
var buttonFormGroup = attachmentWidget.button.parents('.form-group');
|
||||
var disabledLabel = buttonFormGroup.find('.attachDisabledLabel');
|
||||
|
||||
config.showOn = $('#' + buttonFormGroup.find('.showAttachsOn').html());
|
||||
config.showOnDisplay = config.showOn.find('.attachedFiles');
|
||||
count = config.showOnDisplay.find('.initialAttach').length;
|
||||
config.showOnEmpty = config.showOn.find('.showAttachmentsEmpty').html();
|
||||
config.enabledLabel = buttonFormGroup.find('.attachEnabledLabel').html();
|
||||
};
|
||||
|
||||
var setState = function() {
|
||||
var enabled = true;
|
||||
config.fields.each(function() {
|
||||
if (!$(this).val()) {
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
if (disabledLabel.length) {
|
||||
attachmentWidget.config.disabledLabel = disabledLabel.html();
|
||||
var required = [];
|
||||
buttonFormGroup.find('.attachRequiredField').each(function(index, field) {
|
||||
required.push('#' + $(field).text());
|
||||
});
|
||||
if (enabled) {
|
||||
button.removeAttr('disabled').removeClass('disabledAddAttachment');
|
||||
button.val(config.enabledLabel);
|
||||
attachmentWidget.config.basefields = $(required.join(","));
|
||||
}
|
||||
|
||||
attachmentWidget.config.showOn = $('#' + buttonFormGroup.find('.showAttachsOn').html());
|
||||
attachmentWidget.config.showOnDisplay = attachmentWidget.config.showOn.find('.attachedFiles');
|
||||
attachmentWidget.count = attachmentWidget.config.showOnDisplay.find('.initialAttach').length;
|
||||
attachmentWidget.config.showOnEmpty = attachmentWidget.config.showOn.find('.showAttachmentsEmpty').html();
|
||||
attachmentWidget.config.enabledLabel = buttonFormGroup.find('.attachEnabledLabel').html();
|
||||
},
|
||||
|
||||
setState : function() {
|
||||
var enabled = true;
|
||||
attachmentWidget.config.fields.each(function() {
|
||||
if (!$(this).val()) {
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
});
|
||||
if (enabled) {
|
||||
attachmentWidget.button.removeAttr('disabled').removeClass('disabledAddAttachment');
|
||||
attachmentWidget.button.val(attachmentWidget.config.enabledLabel);
|
||||
} else {
|
||||
attachmentWidget.button.attr('disabled', 'disabled').addClass('disabledAddAttachment');
|
||||
attachmentWidget.button.val(attachmentWidget.config.disabledLabel);
|
||||
}
|
||||
},
|
||||
|
||||
cloneFields : function() {
|
||||
var html = '<div class="attachedFileInfo">';
|
||||
if (attachmentWidget.count) {
|
||||
html = attachmentWidget.config.showOnDisplay.html() + html;
|
||||
}
|
||||
attachmentWidget.config.fields.each(function() {
|
||||
var field = $(this);
|
||||
var container= $(this).parents('.form-group');
|
||||
if (container.find(':file').length) {
|
||||
html += ' (' + field.val() + ')';
|
||||
} else {
|
||||
button.attr('disabled', 'disabled').addClass('disabledAddAttachment');
|
||||
button.val(config.disabledLabel);
|
||||
html += ' ' + field.val();
|
||||
}
|
||||
};
|
||||
html += '<span style="display: none;" class="removeField">';
|
||||
html += container.attr('id');
|
||||
html += '</span>';
|
||||
container.hide();
|
||||
});
|
||||
//html += ' <a href="" class="removeAttach glyphicon glyphicon-remove text-danger"></a>';
|
||||
html += ' <a href="" class="removeAttach btn btn-default btn-xs">Delete</a>';
|
||||
html += '</div>';
|
||||
attachmentWidget.config.showOnDisplay.html(html);
|
||||
attachmentWidget.count += 1;
|
||||
attachmentWidget.initFileInput();
|
||||
},
|
||||
|
||||
var cloneFields = function() {
|
||||
var html = '<div class="attachedFileInfo">';
|
||||
if (count) {
|
||||
html = config.showOnDisplay.html() + html;
|
||||
doAttach : function() {
|
||||
attachmentWidget.cloneFields();
|
||||
attachmentWidget.setState();
|
||||
},
|
||||
|
||||
removeAttachment : function() {
|
||||
var link = $(this);
|
||||
var attach = $(this).parent('.attachedFileInfo');
|
||||
var fields = attach.find('.removeField');
|
||||
fields.each(function() {
|
||||
$('#' + $(this).html()).remove();
|
||||
});
|
||||
attach.remove();
|
||||
if (!attachmentWidget.config.showOnDisplay.html()) {
|
||||
attachmentWidget.config.showOnDisplay.html(attachmentWidget.config.showOnEmpty);
|
||||
attachmentWidget.count = 0;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
initTriggers : function() {
|
||||
attachmentWidget.config.showOnDisplay.on('click', 'a.removeAttach', attachmentWidget.removeAttachment);
|
||||
attachmentWidget.button.click(attachmentWidget.doAttach);
|
||||
},
|
||||
|
||||
initFileInput : function() {
|
||||
var fieldids = [];
|
||||
attachmentWidget.config.basefields.each(function(i) {
|
||||
var field = $(this);
|
||||
var oldcontainer= $(this).parents('.form-group');
|
||||
var newcontainer= oldcontainer.clone();
|
||||
var newfield = newcontainer.find('#' + field.attr('id'));
|
||||
newfield.attr('name', newfield.attr('name') + '_' + attachmentWidget.count);
|
||||
newfield.attr('id', newfield.attr('id') + '_' + attachmentWidget.count);
|
||||
newcontainer.attr('id', 'container_id_' + newfield.attr('name'));
|
||||
oldcontainer.after(newcontainer);
|
||||
oldcontainer.hide();
|
||||
newcontainer.show();
|
||||
fieldids.push('#' + newfield.attr('id'));
|
||||
});
|
||||
attachmentWidget.config.fields = $(fieldids.join(","));
|
||||
attachmentWidget.config.fields.change(attachmentWidget.setState);
|
||||
attachmentWidget.config.fields.keyup(attachmentWidget.setState);
|
||||
},
|
||||
|
||||
initWidget : function() {
|
||||
attachmentWidget.button = $(this);
|
||||
attachmentWidget.readConfig();
|
||||
attachmentWidget.initFileInput();
|
||||
attachmentWidget.initTriggers();
|
||||
attachmentWidget.setState();
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
var liaisonForm = {
|
||||
initVariables : function() {
|
||||
liaisonForm.from_groups = liaisonForm.form.find('#id_from_groups');
|
||||
liaisonForm.from_contact = liaisonForm.form.find('#id_from_contact');
|
||||
liaisonForm.response_contacts = liaisonForm.form.find('#id_response_contacts');
|
||||
liaisonForm.to_groups = liaisonForm.form.find('#id_to_groups');
|
||||
liaisonForm.to_contacts = liaisonForm.form.find('#id_to_contacts');
|
||||
liaisonForm.cc = liaisonForm.form.find('#id_cc_contacts');
|
||||
liaisonForm.purpose = liaisonForm.form.find('#id_purpose');
|
||||
liaisonForm.deadline = liaisonForm.form.find('#id_deadline');
|
||||
liaisonForm.submission_date = liaisonForm.form.find('#id_submitted_date');
|
||||
liaisonForm.approval = liaisonForm.form.find('#id_approved');
|
||||
liaisonForm.initial_approval_label = liaisonForm.form.find("label[for='id_approved']").text();
|
||||
liaisonForm.cancel = liaisonForm.form.find('#id_cancel');
|
||||
liaisonForm.cancel_dialog = liaisonForm.form.find('#cancel-dialog');
|
||||
liaisonForm.config = {};
|
||||
liaisonForm.related_trigger = liaisonForm.form.find('.id_related_to');
|
||||
liaisonForm.related_url = liaisonForm.form.find('#id_related_to').parent().find('.listURL').text();
|
||||
liaisonForm.related_dialog = liaisonForm.form.find('#related-dialog');
|
||||
liaisonForm.unrelate_trigger = liaisonForm.form.find('.id_no_related_to');
|
||||
},
|
||||
|
||||
render_mails_into : function(container, person_list, as_html) {
|
||||
var html='';
|
||||
|
||||
$.each(person_list, function(index, person) {
|
||||
if (as_html) {
|
||||
html += person[0] + ' <<a href="mailto:'+person[1]+'">'+person[1]+'</a>><br />';
|
||||
} else {
|
||||
//html += person[0] + ' <'+person[1]+'>\n';
|
||||
html += person + '\n';
|
||||
}
|
||||
config.fields.each(function() {
|
||||
var field = $(this);
|
||||
var container= $(this).parents('.form-group');
|
||||
if (container.find(':file').length) {
|
||||
html += ' (' + field.val() + ')';
|
||||
} else {
|
||||
html += ' ' + field.val();
|
||||
});
|
||||
container.html(html);
|
||||
},
|
||||
|
||||
toggleApproval : function(needed) {
|
||||
if (!liaisonForm.approval.length) {
|
||||
return;
|
||||
}
|
||||
if (!needed) {
|
||||
liaisonForm.approval.prop('checked',true);
|
||||
liaisonForm.approval.hide();
|
||||
//$("label[for='id_approved']").text("Approval not required");
|
||||
var nodes = $("label[for='id_approved']:not(.control-label)")[0].childNodes;
|
||||
nodes[nodes.length-1].nodeValue= 'Approval not required';
|
||||
return;
|
||||
}
|
||||
if ( needed && !$('#id_approved').is(':visible') ) {
|
||||
liaisonForm.approval.prop('checked',false);
|
||||
liaisonForm.approval.show();
|
||||
//$("label[for='id_approved']").text(initial_approval_label);
|
||||
var nodes = $("label[for='id_approved']:not(.control-label)")[0].childNodes;
|
||||
nodes[nodes.length-1].nodeValue=initial_approval_label;
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
checkPostOnly : function(post_only) {
|
||||
if (post_only) {
|
||||
$("button[name=send]").hide();
|
||||
} else {
|
||||
$("button[name=send]").show();
|
||||
}
|
||||
},
|
||||
|
||||
updateInfo : function(first_time, sender) {
|
||||
var from_ids = liaisonForm.from_groups.val();
|
||||
var to_ids = liaisonForm.to_groups.val();
|
||||
var url = liaisonForm.form.data("ajaxInfoUrl");
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'GET',
|
||||
cache: false,
|
||||
async: true,
|
||||
dataType: 'json',
|
||||
data: {from_groups: from_ids,
|
||||
to_groups: to_ids},
|
||||
success: function(response){
|
||||
if (!response.error) {
|
||||
if (!first_time || !liaisonForm.cc.text()) {
|
||||
liaisonForm.render_mails_into(liaisonForm.cc, response.cc, false);
|
||||
}
|
||||
//render_mails_into(poc, response.poc, false);
|
||||
if ( sender.attr('id') == 'id_to_groups' ) {
|
||||
liaisonForm.to_contacts.val(response.to_contacts);
|
||||
}
|
||||
if ( sender.attr('id') == 'id_from_groups' ) {
|
||||
liaisonForm.toggleApproval(response.needs_approval);
|
||||
liaisonForm.response_contacts.val(response.response_contacts);
|
||||
}
|
||||
liaisonForm.checkPostOnly(response.post_only);
|
||||
}
|
||||
html += '<span style="display: none;" class="removeField">';
|
||||
html += container.attr('id');
|
||||
html += '</span>';
|
||||
container.hide();
|
||||
});
|
||||
html += ' <a href="" class="removeAttach glyphicon glyphicon-remove text-danger"></a>';
|
||||
html += '</div>';
|
||||
config.showOnDisplay.html(html);
|
||||
count += 1;
|
||||
initFileInput();
|
||||
};
|
||||
|
||||
var doAttach = function() {
|
||||
cloneFields();
|
||||
setState();
|
||||
};
|
||||
|
||||
var removeAttachment = function() {
|
||||
var link = $(this);
|
||||
var attach = $(this).parent('.attachedFileInfo');
|
||||
var fields = attach.find('.removeField');
|
||||
fields.each(function() {
|
||||
$('#' + $(this).html()).remove();
|
||||
});
|
||||
attach.remove();
|
||||
if (!config.showOnDisplay.html()) {
|
||||
config.showOnDisplay.html(config.showOnEmpty);
|
||||
count = 0;
|
||||
}
|
||||
});
|
||||
return false;
|
||||
},
|
||||
|
||||
updatePurpose : function() {
|
||||
var deadlinecontainer = liaisonForm.deadline.closest('.form-group');
|
||||
var value = liaisonForm.purpose.val();
|
||||
|
||||
if (value == 'action' || value == 'comment') {
|
||||
liaisonForm.deadline.prop('required',true);
|
||||
deadlinecontainer.show();
|
||||
} else {
|
||||
liaisonForm.deadline.prop('required',false);
|
||||
deadlinecontainer.hide();
|
||||
liaisonForm.deadline.val('');
|
||||
}
|
||||
},
|
||||
|
||||
cancelForm : function() {
|
||||
liaisonForm.cancel_dialog.dialog("open");
|
||||
},
|
||||
|
||||
checkSubmissionDate : function() {
|
||||
var date_str = liaisonForm.submission_date.val();
|
||||
if (date_str) {
|
||||
var sdate = new Date(date_str);
|
||||
var today = new Date();
|
||||
if (Math.abs(today-sdate) > 2592000000) { // 2592000000 = 30 days in milliseconds
|
||||
return confirm('Submission date ' + date_str + ' differ more than 30 days.\n\nDo you want to continue and post this liaison using that submission date?\n');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
};
|
||||
},
|
||||
|
||||
var initTriggers = function() {
|
||||
config.showOnDisplay.on('click', 'a.removeAttach', removeAttachment);
|
||||
button.click(doAttach);
|
||||
};
|
||||
init : function() {
|
||||
liaisonForm.form = $(this);
|
||||
liaisonForm.initVariables();
|
||||
$('#id_from_groups').select2();
|
||||
$('#id_to_groups').select2();
|
||||
liaisonForm.to_groups.change(function() { liaisonForm.updateInfo(false,$(this)); });
|
||||
liaisonForm.from_groups.change(function() { liaisonForm.updateInfo(false,$(this)); });
|
||||
liaisonForm.purpose.change(liaisonForm.updatePurpose);
|
||||
liaisonForm.form.submit(liaisonForm.checkSubmissionDate);
|
||||
$('.addAttachmentWidget').each(attachmentWidget.initWidget);
|
||||
|
||||
liaisonForm.updatePurpose();
|
||||
if($('#id_to_groups').val()) {
|
||||
$('#id_to_groups').trigger('change');
|
||||
}
|
||||
if($('#id_from_groups').val()) {
|
||||
$('#id_from_groups').trigger('change');
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var initFileInput = function() {
|
||||
var fieldids = [];
|
||||
config.basefields.each(function(i) {
|
||||
var field = $(this);
|
||||
var oldcontainer= $(this).parents('.form-group');
|
||||
var newcontainer= oldcontainer.clone();
|
||||
var newfield = newcontainer.find('#' + field.attr('id'));
|
||||
newfield.attr('name', newfield.attr('name') + '_' + count);
|
||||
newfield.attr('id', newfield.attr('id') + '_' + count);
|
||||
newcontainer.attr('id', 'container_id_' + newfield.attr('name'));
|
||||
oldcontainer.after(newcontainer);
|
||||
oldcontainer.hide();
|
||||
newcontainer.show();
|
||||
fieldids.push('#' + newfield.attr('id'));
|
||||
|
||||
var searchForm = {
|
||||
// search form, based on doc search feature
|
||||
init : function() {
|
||||
searchForm.form = $(this);
|
||||
searchForm.form.find(".search_field input,select").change(searchForm.toggleSubmit).click(searchForm.toggleSubmit).keyup(searchForm.toggleSubmit);
|
||||
},
|
||||
|
||||
anyAdvancedActive : function() {
|
||||
var advanced = false;
|
||||
var by = searchForm.form.find("input[name=by]:checked");
|
||||
|
||||
if (by.length > 0) {
|
||||
by.closest(".search_field").find("input,select").not("input[name=by]").each(function () {
|
||||
if ($.trim(this.value)) {
|
||||
advanced = true;
|
||||
}
|
||||
});
|
||||
config.fields = $(fieldids.join(","));
|
||||
config.fields.change(setState);
|
||||
config.fields.keyup(setState);
|
||||
};
|
||||
|
||||
var initWidget = function() {
|
||||
readConfig();
|
||||
|
||||
initFileInput();
|
||||
initTriggers();
|
||||
|
||||
setState();
|
||||
};
|
||||
|
||||
initWidget();
|
||||
}
|
||||
return advanced;
|
||||
},
|
||||
|
||||
toggleSubmit : function() {
|
||||
var textSearch = $.trim($("#id_text").val());
|
||||
searchForm.form.find("button[type=submit]").get(0).disabled = !textSearch && !searchForm.anyAdvancedActive();
|
||||
}
|
||||
|
||||
$('form.liaisons').each(function() {
|
||||
var form = $(this);
|
||||
var organization = form.find('#id_organization');
|
||||
var from = form.find('#id_from_field');
|
||||
var poc = form.find('#id_to_poc');
|
||||
var cc = form.find('#id_cc1');
|
||||
var reply = form.find('#id_replyto');
|
||||
var purpose = form.find('#id_purpose');
|
||||
var other_purpose = form.find('#id_purpose_text');
|
||||
var deadline = form.find('#id_deadline_date');
|
||||
var submission_date = form.find('#id_submitted_date');
|
||||
var other_organization = form.find('#id_other_organization');
|
||||
var approval = form.find('#id_approved');
|
||||
|
||||
var render_mails_into = function(container, person_list, as_html) {
|
||||
var html='';
|
||||
|
||||
$.each(person_list, function(index, person) {
|
||||
if (as_html) {
|
||||
html += person[0] + ' <<a href="mailto:'+person[1]+'">'+person[1]+'</a>><br />';
|
||||
} else {
|
||||
html += person[0] + ' <'+person[1]+'>\n';
|
||||
}
|
||||
});
|
||||
container.html(html);
|
||||
};
|
||||
|
||||
var toggleApproval = function(needed) {
|
||||
if (!approval.length) {
|
||||
return;
|
||||
}
|
||||
if (needed) {
|
||||
approval.removeAttr('disabled');
|
||||
approval.removeAttr('checked');
|
||||
} else {
|
||||
approval.attr('checked','checked');
|
||||
approval.attr('disabled','disabled');
|
||||
}
|
||||
};
|
||||
|
||||
var checkPostOnly = function(post_only) {
|
||||
if (post_only) {
|
||||
$("input[name=send]").hide();
|
||||
} else {
|
||||
$("input[name=send]").show();
|
||||
}
|
||||
};
|
||||
|
||||
var updateReplyTo = function() {
|
||||
var select = form.find('select[name=from_fake_user]');
|
||||
var option = select.find('option:selected');
|
||||
reply.val(option.attr('title'));
|
||||
updateFrom();
|
||||
};
|
||||
|
||||
var userSelect = function(user_list) {
|
||||
if (!user_list || !user_list.length) {
|
||||
return;
|
||||
}
|
||||
var link = form.find('a.from_mailto');
|
||||
var select = form.find('select[name=from_fake_user]');
|
||||
var options = '';
|
||||
link.hide();
|
||||
$.each(user_list, function(index, person) {
|
||||
options += '<option value="' + person[0] + '" title="' + person[1][1] + '">'+ person[1][0] + ' <' + person[1][1] + '></option>';
|
||||
});
|
||||
select.remove();
|
||||
link.after('<select name="from_fake_user" class="form-control" style="margin-top: 0.5em;">' + options +'</select>');
|
||||
form.find('select[name=from_fake_user]').change(updateReplyTo);
|
||||
updateReplyTo();
|
||||
};
|
||||
|
||||
var updateInfo = function(first_time, sender) {
|
||||
var entity = organization;
|
||||
var to_entity = from;
|
||||
if (!entity.is('select') || !to_entity.is('select')) {
|
||||
return false;
|
||||
}
|
||||
var url = form.data("ajaxInfoUrl");
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'GET',
|
||||
cache: false,
|
||||
async: true,
|
||||
dataType: 'json',
|
||||
data: {to_entity_id: organization.val(),
|
||||
from_entity_id: to_entity.val()},
|
||||
success: function(response){
|
||||
if (!response.error) {
|
||||
if (!first_time || !cc.text()) {
|
||||
render_mails_into(cc, response.cc, false);
|
||||
}
|
||||
render_mails_into(poc, response.poc, true);
|
||||
toggleApproval(response.needs_approval);
|
||||
checkPostOnly(response.post_only);
|
||||
if (sender == 'from') {
|
||||
userSelect(response.full_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
};
|
||||
|
||||
var updateFrom = function() {
|
||||
var reply_to = reply.val();
|
||||
form.find('a.from_mailto').attr('href', 'mailto:' + reply_to);
|
||||
};
|
||||
|
||||
var updatePurpose = function() {
|
||||
var deadlinecontainer = deadline.closest('.form-group');
|
||||
var othercontainer = other_purpose.closest('.form-group');
|
||||
|
||||
var selected_id = purpose.val();
|
||||
|
||||
if (selected_id == '1' || selected_id == '2') {
|
||||
deadline.prop('required',true);
|
||||
deadlinecontainer.show();
|
||||
} else {
|
||||
deadline.prop('required',false);
|
||||
deadlinecontainer.hide();
|
||||
deadline.val('');
|
||||
}
|
||||
};
|
||||
|
||||
var checkOtherSDO = function() {
|
||||
var entity = organization.val();
|
||||
if (entity=='othersdo') {
|
||||
other_organization.closest('.form-group').show();
|
||||
other_organization.prop("required", true);
|
||||
} else {
|
||||
other_organization.closest('.form-group').hide();
|
||||
other_organization.prop("required", false);
|
||||
}
|
||||
};
|
||||
|
||||
var checkFrom = function(first_time) {
|
||||
var reduce_options = form.find('.reducedToOptions');
|
||||
if (!reduce_options.length) {
|
||||
updateInfo(first_time, 'from');
|
||||
return;
|
||||
}
|
||||
var to_select = organization;
|
||||
var from_entity = from.val();
|
||||
if (!reduce_options.find('.full_power_on_' + from_entity).length) {
|
||||
to_select.find('optgroup').eq(1).hide();
|
||||
to_select.find('option').each(function() {
|
||||
if (!reduce_options.find('.reduced_to_set_' + $(this).val()).length) {
|
||||
$(this).hide();
|
||||
} else {
|
||||
$(this).show();
|
||||
}
|
||||
});
|
||||
if (!to_select.find('option:selected').is(':visible')) {
|
||||
to_select.find('option:selected').removeAttr('selected');
|
||||
}
|
||||
} else {
|
||||
to_select.find('optgroup').show();
|
||||
to_select.find('option').show();
|
||||
}
|
||||
updateInfo(first_time, 'from');
|
||||
};
|
||||
|
||||
var checkSubmissionDate = function() {
|
||||
var date_str = submission_date.val();
|
||||
if (date_str) {
|
||||
var sdate = new Date(date_str);
|
||||
var today = new Date();
|
||||
if (Math.abs(today-sdate) > 2592000000) { // 2592000000 = 30 days in milliseconds
|
||||
return confirm('Submission date ' + date_str + ' differ more than 30 days.\n\nDo you want to continue and post this liaison using that submission date?\n');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// init form
|
||||
organization.change(function() { updateInfo(false, 'to'); });
|
||||
organization.change(checkOtherSDO);
|
||||
from.change(function() { checkFrom(false); });
|
||||
reply.keyup(updateFrom);
|
||||
purpose.change(updatePurpose);
|
||||
form.submit(checkSubmissionDate);
|
||||
|
||||
updateFrom();
|
||||
checkFrom(true);
|
||||
updatePurpose();
|
||||
checkOtherSDO();
|
||||
|
||||
form.find('.addAttachmentWidget').each(setupAttachmentWidget);
|
||||
});
|
||||
$(document).ready(function () {
|
||||
// use traditional style URL parameters
|
||||
$.ajaxSetup({ traditional: true });
|
||||
|
||||
$('form.liaisons-form').each(liaisonForm.init);
|
||||
$('#search_form').each(searchForm.init);
|
||||
});
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
{% extends "liaisons/detail.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
{{ block.super }}
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<input class="btn btn-primary" type="submit" value="Approve" name='do_approval' />
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -1,15 +0,0 @@
|
|||
{% extends "liaisons/overview.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
|
||||
{% block title %}Pending liaison statements{% endblock %}
|
||||
|
||||
{% block content-title %}
|
||||
<h1>Pending liaison statements</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block management-links %}
|
||||
<p>
|
||||
<a class="btn btn-default" href="{% url "liaison_list" %}">Return to liaison list</a>
|
||||
</p>
|
||||
{% endblock %}
|
|
@ -13,65 +13,68 @@
|
|||
Liaison statement<br><small>{% include 'liaisons/liaison_title.html' %}</small>
|
||||
</h1>
|
||||
|
||||
<table class="table table-condensed table-striped">
|
||||
<tr>
|
||||
<th class="text-nowrap">Submission date</th>
|
||||
<td>{{ liaison.submitted|date:"Y-m-d" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-nowrap">From</th>
|
||||
<td>
|
||||
{{ liaison.from_name }}
|
||||
{% if liaison.from_contact %}(<a href="mailto:{{ liaison.from_contact.address }}">{{ liaison.from_contact.person }}</a>)
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-nowrap">To</th>
|
||||
<td>
|
||||
{% if liaison.from_contact %}
|
||||
{{ liaison.to_name }} ({{ liaison.to_contact|parse_email_list }})
|
||||
{% else %}
|
||||
{{ liaison.to_name|urlize }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% include "liaisons/detail_tabs.html" %}
|
||||
|
||||
<table class="table table-condensed table-striped">
|
||||
<tr>
|
||||
<th>State</th>
|
||||
<td>{{ liaison.state }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-nowrap">Submission Date</th>
|
||||
<td>{{ liaison.submitted|date:"Y-m-d" }}</td></tr>
|
||||
|
||||
{% if liaison.from_contact %}
|
||||
<tr>
|
||||
<th class="text-nowrap">Cc</th>
|
||||
<td>
|
||||
{{ liaison.cc|parse_email_list }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if liaison.from_contact %}
|
||||
<tr>
|
||||
<th class="text-nowrap">Sender</th>
|
||||
<td>
|
||||
<a href="mailto:{{ liaison.from_contact.address }}">{{ liaison.from_contact.person }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if liaison.response_contact %}
|
||||
<tr>
|
||||
<th class="text-nowrap">Response contact</th>
|
||||
<td>
|
||||
{{ liaison.response_contact|parse_email_list }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th class="text-nowrap">From</th>
|
||||
<td>{{ liaison.from_groups_display }}</td>
|
||||
</tr>
|
||||
|
||||
{% if liaison.technical_contact %}
|
||||
<tr>
|
||||
<th class="text-nowrap">Technical contact</th>
|
||||
<td>
|
||||
{{ liaison.technical_contact|parse_email_list }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th class="text-nowrap">To</th>
|
||||
<td>{{ liaison.to_groups_display }}</td>
|
||||
</tr>
|
||||
|
||||
{% if liaison.purpose %}
|
||||
<tr>
|
||||
<th class="text-nowrap">Purpose</th>
|
||||
<td>{{ liaison.purpose.name }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if liaison.cc_contacts %}
|
||||
<tr>
|
||||
<th class="text-nowrap">Cc</th><td>{{ liaison.cc_contacts|parse_email_list|make_one_per_line|safe|linebreaksbr }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if liaison.response_contacts %}
|
||||
<tr>
|
||||
<th class="text-nowrap">Response Contact</th>
|
||||
<td>{{ liaison.response_contacts|parse_email_list|make_one_per_line|safe|linebreaksbr }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if liaison.technical_contacts %}
|
||||
<tr>
|
||||
<th class="text-nowrap">Technical Contact</th>
|
||||
<td>{{ liaison.technical_contacts|parse_email_list|make_one_per_line|safe|linebreaksbr }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if liaison.action_holder_contacts %}
|
||||
<tr>
|
||||
<th class="text-nowrap">Action Holder Contacts</th>
|
||||
<td>{{ liaison.action_holder_contacts|parse_email_list|make_one_per_line|safe|linebreaksbr }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
<tr>
|
||||
<th class="text-nowrap">Purpose</th>
|
||||
<td>{{ liaison.purpose.name }}</td>
|
||||
</tr>
|
||||
|
||||
{% if liaison.deadline %}
|
||||
<tr>
|
||||
<th class="text-nowrap">Deadline</th>
|
||||
|
@ -97,7 +100,7 @@
|
|||
<th class="text-nowrap">Liaisons referring to this</th>
|
||||
<td>
|
||||
{% for rel in relations %}
|
||||
<a href="{% url "liaison_detail" rel.pk %}">
|
||||
<a href="{% url "ietf.liaisons.views.liaison_detail" rel.pk %}">
|
||||
{% if rel.title %}{{ rel.title }}{% else %}Liaison #{{ rel.pk }}{% endif %}
|
||||
</a>
|
||||
<br>
|
||||
|
@ -111,7 +114,7 @@
|
|||
<tr>
|
||||
<th class="text-nowrap">Referenced liaison</th>
|
||||
<td>
|
||||
<a href="{% url "liaison_detail" liaison.related_to.pk %}">
|
||||
<a href="{% url "ietf.liaisons.views.liaison_detail" liaison.related_to.pk %}">
|
||||
{% if liaison.related_to.title %}{{ liaison.related_to.title }}{% else %}Liaison #{{ liaison.related_to.pk }}{% endif %}
|
||||
</a>
|
||||
</td>
|
||||
|
@ -119,11 +122,18 @@
|
|||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if liaison.other_identifiers %}
|
||||
<tr>
|
||||
<th class="text-nowrap">Other Identifiers</th>
|
||||
<td>{{ liaison.other_identifiers }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
<tr>
|
||||
<th class="text-nowrap">Attachments</th>
|
||||
<td>
|
||||
{% for doc in liaison.attachments.all %}
|
||||
<a href="https://datatracker.ietf.org/documents/LIAISON/{{ doc.external_url }}">{{ doc.title }}</a>
|
||||
{% for doc in liaison.active_attachments.all %}
|
||||
<a href="{{ doc.href }}">{{ doc.title }}</a>
|
||||
{% if not forloop.last %}<br>{% endif %}
|
||||
{% empty %}
|
||||
(None)
|
||||
|
@ -131,6 +141,28 @@
|
|||
</td>
|
||||
</tr>
|
||||
|
||||
{% if relations_by %}
|
||||
<tr>
|
||||
<th class="text-nowrap">Liaisons referred by this one</th>
|
||||
<td>
|
||||
{% for rel in relations_by %}
|
||||
<a href="{% url "ietf.liaisons.views.liaison_detail" rel.pk %}">{% if rel.title %}{{ rel.title }}{% else %}Liaison #{{ rel.pk }}{% endif %}</a><br />
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if relations_to %}
|
||||
<tr>
|
||||
<th class="text-nowrap">Liaisons referring to this one</th>
|
||||
<td>
|
||||
{% for rel in relations_to %}
|
||||
<a href="{% url "ietf.liaisons.views.liaison_detail" rel.pk %}">{% if rel.title %}{{ rel.title }}{% else %}Liaison #{{ rel.pk }}{% endif %}</a><br />
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if liaison.from_contact and liaison.body %}
|
||||
<tr>
|
||||
<th class="text-nowrap">Body</th>
|
||||
|
@ -139,12 +171,29 @@
|
|||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
|
||||
</table>
|
||||
|
||||
{% if can_edit %}
|
||||
<p>
|
||||
<a class="btn btn-default" href="{% url "liaison_edit" object_id=liaison.pk %}">Edit liaison</a>
|
||||
</p>
|
||||
<p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% if liaison.state.slug != 'dead' and can_edit %}
|
||||
<a class="btn btn-default" href="{% url "ietf.liaisons.views.liaison_edit" object_id=liaison.pk %}">Edit liaison</a>
|
||||
{% endif %}
|
||||
{% if liaison.state.slug != 'dead' and can_reply %}
|
||||
<a class="btn btn-default" href="{% url "ietf.liaisons.views.liaison_reply" object_id=liaison.pk %}">Reply to liaison</a>
|
||||
{% endif %}
|
||||
{% if liaison.state.slug == 'pending' and can_edit %}
|
||||
<input class="btn btn-default" type="submit" value="Approve" name='approved' />
|
||||
<input class="btn btn-default" type="submit" value="Mark as Dead" name='dead' />
|
||||
{% endif %}
|
||||
{% if liaison.state.slug == 'posted' and user|has_role:"Secretariat" %}
|
||||
<a class="btn btn-default" href="{% url "ietf.liaisons.views.liaison_resend" object_id=liaison.pk %}">Resend statement</a>
|
||||
{% endif %}
|
||||
{% if liaison.state.slug == 'dead' and can_edit %}
|
||||
<input class="btn btn-default" type="submit" value="Resurrect" name='resurrect' />
|
||||
{% endif %}
|
||||
</form>
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
||||
|
|
47
ietf/templates/liaisons/detail_history.html
Normal file
47
ietf/templates/liaisons/detail_history.html
Normal file
|
@ -0,0 +1,47 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% load future %}
|
||||
{% load ietf_filters %}
|
||||
|
||||
{% block title %}History for Liaison Statement - {{ liaison.title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>History for Liaison Statement<br><small>{{ liaison.title }}</small></h1>
|
||||
|
||||
{% include "liaisons/detail_tabs.html" %}
|
||||
|
||||
{% comment %}
|
||||
{% if user|has_role:"Area Director,Secretariat,IANA,RFC Editor" %}
|
||||
<p class="buttonlist">
|
||||
<a class="btn btn-default" href="{% url "ipr_add_comment" id=ipr.id %}" title="Add comment to history">Add comment</a>
|
||||
<a class="btn btn-default" href="{% url "ipr_add_email" id=ipr.id %}" title="Add email to history">Add email</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endcomment %}
|
||||
|
||||
<table class="table table-condensed table-striped history">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Type</th>
|
||||
<th>By</th>
|
||||
<th>Text</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for e in events %}
|
||||
<tr>
|
||||
<td class="text-nowrap">{{ e.time|date:"Y-m-d" }}</td>
|
||||
<td>{{ e.type }}
|
||||
{% if e.response_due and e.response_past_due %}
|
||||
<span class="glyphicon glyphicon-exclamation-sign" title="Response overdue"></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ e.by }}</td>
|
||||
<td>{{ e.desc|format_history_text }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock content %}
|
5
ietf/templates/liaisons/detail_tabs.html
Normal file
5
ietf/templates/liaisons/detail_tabs.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<ul class="nav nav-tabs" role="tablist">
|
||||
{% for name, link, selected in tabs %}
|
||||
<li {% if selected %}class="active"{% endif %}><a href="{{ link }}">{{ name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
|
@ -2,7 +2,8 @@
|
|||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load staticfiles %}
|
||||
{% load bootstrap3 %}
|
||||
{% load ietf_filters %}
|
||||
{% load bootstrap3 widget_tweaks %}
|
||||
|
||||
{% block title %}{% if liaison %}Edit liaison: {{ liaison }}{% else %}Send Liaison Statement{% endif %}{% endblock %}
|
||||
|
||||
|
@ -10,6 +11,7 @@
|
|||
<link rel="stylesheet" href="{% static 'bootstrap-datepicker/css/datepicker3.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'select2/select2.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'select2-bootstrap-css/select2-bootstrap.min.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'ietf/css/liaisons.css' %}">
|
||||
{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
|
@ -41,18 +43,22 @@
|
|||
<p class="help-block">Fields marked with <label class="required"></label> are required. For detailed descriptions of the fields see the <a href="{% url "liaisons_field_help" %}">field help</a>.</p>
|
||||
{% endif %}
|
||||
|
||||
<form class="liaisons form-horizontal show-required" method="post" enctype="multipart/form-data" data-ajax-info-url="{% url "ietf.liaisons.views.ajax_get_liaison_info" %}">{% csrf_token %}
|
||||
<form role="form" class="liaisons-form form-horizontal show-required" method="post" enctype="multipart/form-data" data-ajax-info-url="{% url "ietf.liaisons.views.ajax_get_liaison_info" %}">{% csrf_token %}
|
||||
|
||||
{% for fieldset in form.get_fieldsets %}
|
||||
<h2>{{ fieldset.name }}</h2>
|
||||
|
||||
{% for field in fieldset.fields %}
|
||||
{% for fieldset in form.fieldsets %}
|
||||
{% if forloop.first and user|has_role:"Secretariat" %}
|
||||
<h2><div class="col-md-2">{{ fieldset.name }}</div><div class="col-md-10"><a class="small" target="_blank" href="{% url "groups_add" %}">Add new group >></a></div></h2>
|
||||
{% else %}
|
||||
<h2>{{ fieldset.name }}</h2>
|
||||
{% endif %}
|
||||
|
||||
{% for field in fieldset %}
|
||||
{% bootstrap_field field layout="horizontal" %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
{% buttons %}
|
||||
<a class="btn btn-danger pull-right" href="{% url "liaison_list" %}">Cancel</a>
|
||||
<a class="btn btn-danger pull-right" href="{% if liaison %}{% url "ietf.liaisons.views.liaison_detail" object_id=liaison.pk %}{% else %}{% url "ietf.liaisons.views.liaison_list" %}{% endif %}">Cancel</a>
|
||||
|
||||
{% if not liaison %}
|
||||
<button name="send" type="submit" class="btn btn-primary">Send and post</button>
|
||||
|
|
44
ietf/templates/liaisons/edit_attachment.html
Normal file
44
ietf/templates/liaisons/edit_attachment.html
Normal file
|
@ -0,0 +1,44 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load bootstrap3 widget_tweaks %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block title %}Edit liaison attachment{% endblock %}
|
||||
|
||||
{% block pagehead %}
|
||||
<link rel="stylesheet" href="{% static 'ietf/css/liaisons.css' %}">
|
||||
{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
.widget { height: auto; min-height: 34px; }
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
|
||||
<h1>Edit liaison attachment</h1>
|
||||
|
||||
{% bootstrap_messages %}
|
||||
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger">
|
||||
<p>There were errors in the submitted form -- see below. Please correct these and resubmit.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% bootstrap_form_errors form %}
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
{% buttons %}
|
||||
<a class="btn btn-danger pull-right" href="{% url "ietf.liaisons.views.liaison_edit" object_id=liaison.pk %}">Cancel</a>
|
||||
<button name="save" type="submit" class="btn btn-primary">Save</button>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
{% if attachments %}
|
||||
<ul>
|
||||
{% for doc in attachments %}
|
||||
<li><a href="https://datatracker.ietf.org/documents/LIAISON/{{ doc.external_url }}">{{ doc.title }}</a><br>
|
||||
<li><a href="{{ doc.href }}">{{ doc.title }}</a><br>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
|
|
|
@ -20,12 +20,12 @@
|
|||
<ul>
|
||||
<li>
|
||||
<a href="/liaison/help/to_ietf/">
|
||||
Liaison statements to the IETF: guidelines for completing the "To:" and "Cc:" fields
|
||||
Liaison statements to the IETF: guidelines for completing the "To:" and "Cc:" fields
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/liaison/help/from_ietf/">
|
||||
Liaison statements from the IETF: guidelines for completing the "Cc:" field
|
||||
Liaison statements from the IETF: guidelines for completing the "Cc:" field
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -47,65 +47,65 @@
|
|||
<table class="table table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Sub-field</th>
|
||||
<th>Description</th>
|
||||
<th>Comments</th>
|
||||
<th>Fieldset</th>
|
||||
<th>Field</th>
|
||||
<th>Description</th>
|
||||
<th>Comments</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr>
|
||||
<th>From</th>
|
||||
<th>Organization</th>
|
||||
<td>The organization submitting the liaison statement.</td>
|
||||
<td>The field is filled in automatically.</td>
|
||||
<th>Groups</th>
|
||||
<td>The organization(s) submitting the liaison statement.</td>
|
||||
<td>Use arrows to select or type name to search</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>Submitter</th>
|
||||
<th>From Contact</th>
|
||||
<td>
|
||||
The name and e-mail address of the person submitting the liaison statement.
|
||||
The e-mail address of the person submitting the liaison statement.
|
||||
</td>
|
||||
<td>The field is filled in automatically.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>Reply to</th>
|
||||
<th>Response Contacts</th>
|
||||
<td>
|
||||
The e-mail address(es) that will be inserted in the "To" field when a recipient hits the "reply" button on the message.
|
||||
The e-mail address(es) to which any response should be sent, separated by commas.
|
||||
</td>
|
||||
<td>Mandatory format: Name <e-mail address></td>
|
||||
</tr
|
||||
<tr>
|
||||
<th>To</th>
|
||||
<th>Organization</th>
|
||||
<th>Groups</th>
|
||||
<td>
|
||||
The name of the organization (and sub-group, if applicable) to which the liaison statement is being sent.
|
||||
The organization(s) (and sub-group, if applicable) to which the liaison statement is being sent.
|
||||
</td>
|
||||
<td>
|
||||
Drop-down menu with available choices. If an SDO is not listed, please contact statements@ietf.org
|
||||
Drop-down menu with available choices. If an SDO is not listed, please contact statements@ietf.org
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>POC(s)</th>
|
||||
<th>Contacts</th>
|
||||
<td>
|
||||
The e-mail address(es) of the recipient(s) of the liaison statement, separated by commas.
|
||||
The e-mail address(es) of the recipient(s) of the liaison statement, separated by commas.
|
||||
</td>
|
||||
<td>The field is filled in automatically.</td>
|
||||
<td>The field may be filled in automatically.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Other email addresses</th>
|
||||
<th>Response contact</th>
|
||||
<th>Technical contact</th>
|
||||
<td>
|
||||
The e-mail address(es) to which any response should be sent, separated by commas.
|
||||
The e-mail address(es) of individuals or lists that may be contacted for clarification of the liaison statement, separated by commas.
|
||||
</td>
|
||||
<td>Optional. Suggested format: Name <e-mail address></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>Technical contact</th>
|
||||
<th>Action Holder Contacts</th>
|
||||
<td>
|
||||
The e-mail address(es) of individuals or lists that may be contacted for clarification of the liaison statement, separated by commas.
|
||||
The e-mail address(es) of the persons responsible for acting on the statement.
|
||||
</td>
|
||||
<td>Optional. Suggested format: Name <e-mail address></td>
|
||||
</tr>
|
||||
|
@ -113,7 +113,7 @@
|
|||
<td></td>
|
||||
<th>Cc</th>
|
||||
<td>
|
||||
The e-mail address(es) of the copy recipient(s) of the liaison statement, one on each line.
|
||||
The e-mail address(es) of the copy recipient(s) of the liaison statement, one on each line.
|
||||
</td>
|
||||
<td>Optional. Suggested format: Name <e-mail address></td>
|
||||
</tr>
|
||||
|
@ -121,10 +121,10 @@
|
|||
<th>Purpose</th>
|
||||
<th>Purpose</th>
|
||||
<td>
|
||||
The intent of the liaison statement. Normally, one of four choices: (a) For action, (b) For comment, (c) For information, (d) In Response.
|
||||
The intent of the liaison statement. One of four choices: (a) For action, (b) For comment, (c) For information, (d) In Response.
|
||||
</td>
|
||||
<td>
|
||||
The submitter selects one of the four choices, or selects "other" and indicates the intent.
|
||||
The submitter selects one of the four choices.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -132,7 +132,7 @@
|
|||
<th>Deadline</th>
|
||||
<td>The date by which a comment or action is required.</td>
|
||||
<td>
|
||||
Mandatory if the purpose is "For comment" or "For action." Otherwise, optional.
|
||||
Mandatory if the purpose is "For comment" or "For action."
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
@ -153,7 +153,7 @@
|
|||
<th>Body</th>
|
||||
<td>The text of the liaison statement.</td>
|
||||
<td>
|
||||
Mandatory if there are no attachments. Optional if the text of the liaison statement is provided in an attachment.
|
||||
Mandatory if there are no attachments. Optional if the text of the liaison statement is provided in an attachment.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
@ -162,7 +162,7 @@
|
|||
<th>Title</th>
|
||||
<td>The title of the attachment.</td>
|
||||
<td>
|
||||
Optional if there is text in the body, mandatory if there is not.
|
||||
Optional if there is text in the body, mandatory if there is not.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -170,7 +170,7 @@
|
|||
<th>File</th>
|
||||
<td>Browse to find the attachment.</td>
|
||||
<td>
|
||||
Optional if there is text in the body, mandatory if there is not.
|
||||
Optional if there is text in the body, mandatory if there is not.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
55
ietf/templates/liaisons/liaison_base.html
Normal file
55
ietf/templates/liaisons/liaison_base.html
Normal file
|
@ -0,0 +1,55 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load ietf_filters %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block pagehead %}
|
||||
<link rel="stylesheet" href="{% static 'bootstrap-datepicker/css/datepicker3.css' %}">
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}Liaison Statements - {{ selected_menu_entry|capfirst }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
|
||||
<h1>Liaison Statements</h1>
|
||||
|
||||
{% if with_search %}
|
||||
<div class="ietf-box search-form-box">
|
||||
{% include "liaisons/search_form.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
{% for name, url in menu_entries %}
|
||||
<li {% if selected_menu_entry == name.lower %}class="active"{% endif %}>
|
||||
<a href="{{ url }}">{{ name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% if menu_actions %}
|
||||
<div class="buttonlist">
|
||||
{% for name, url in menu_actions %}
|
||||
<a class="btn btn-default" href="{{ url }}">{{ name }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% block group_content %}
|
||||
|
||||
{% if search_conducted and not liaisons %}
|
||||
<div class="alert alert-info">No statements match your query.</div>
|
||||
{% else %}
|
||||
{% include "liaisons/liaison_table.html" %}
|
||||
{% endif %}
|
||||
|
||||
{% endblock group_content %}
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{% static 'bootstrap-datepicker/js/bootstrap-datepicker.js' %}"></script>
|
||||
<script src="{% static 'ietf/js/liaisons.js' %}"></script>
|
||||
{% endblock %}
|
|
@ -1,19 +1,21 @@
|
|||
{% load ietf_filters %}{% autoescape off %}Title: {{ liaison.title|clean_whitespace }}
|
||||
Submission Date: {{ liaison.submitted|date:"Y-m-d" }}
|
||||
URL of the IETF Web page: {{ url }}
|
||||
URL of the IETF Web page: {{ liaison.get_absolute_url }}
|
||||
{% 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 }})
|
||||
From: {{ liaison.from_name }} ({{ liaison.from_contact.person }} <{% if liaison.from_contact %}{{ liaison.from_contact.address }}{% endif %}>)
|
||||
To: {{ liaison.to_name }} ({{ liaison.to_contacts }})
|
||||
Cc: {{ liaison.cc }}
|
||||
Response Contact: {{ liaison.response_contact }}
|
||||
Technical Contact: {{ liaison.technical_contact }}
|
||||
Response Contacts: {{ liaison.response_contacts }}
|
||||
Technical Contacts: {{ liaison.technical_contacts }}
|
||||
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 %}
|
||||
{% for related in liaison.source_of_set.all %}
|
||||
Referenced liaison: {% if related.target.title %}{{ related.target.title }}{% else %}Liaison #{{ related.target.pk }}{% endif %} ({{ related.target.get_absolute_url }})
|
||||
{% endfor %}
|
||||
Body: {{ liaison.body }}
|
||||
Attachments:
|
||||
{% for doc in liaison.attachments.all %}
|
||||
{{ doc.title }}
|
||||
https://datatracker.ietf.org/documents/LIAISON/{{ doc.external_url }}
|
||||
{{ doc.href }}
|
||||
{% empty %}
|
||||
No document has been attached
|
||||
{% endfor %}{% endautoescape %}
|
||||
|
|
46
ietf/templates/liaisons/liaison_table.html
Normal file
46
ietf/templates/liaisons/liaison_table.html
Normal file
|
@ -0,0 +1,46 @@
|
|||
{# Copyright The IETF Trust 2007, All Rights Reserved #}
|
||||
{% load ietf_filters %}
|
||||
{% load future %}
|
||||
|
||||
<table class="table table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a href="?sort=date">Date {% if sort == "date" %}<span class="fa fa-caret-down"></span>{% endif %}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="?sort=from_groups">From {% if sort == "from_groups" %}<span class="fa fa-caret-down"></span>{% endif %}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="?sort=to_groups">To {% if sort == "to_groups" %}<span class="fa fa-caret-down"></span>{% endif %}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="?sort=deadline">Deadline {% if sort == "deadline" %}<span class="fa fa-caret-down"></span>{% endif %}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="?sort=title">Title {% if sort == "title" %}<span class="fa fa-caret-down"></span>{% endif %}</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for liaison in liaisons %}
|
||||
<tr>
|
||||
<td class="text-nowrap">{{ liaison.sort_date|date:"Y-m-d" }}</td>
|
||||
<td>{{ liaison.from_groups_display }}</td>
|
||||
<td>{{ liaison.to_groups_display }}</td>
|
||||
<td class="text-nowrap">{{ liaison.deadline|default:"-"|date:"Y-m-d" }}{% if liaison.deadline and not liaison.action_taken %}<br><span class="label label-warning">Action Needed</span>{% endif %}</td>
|
||||
<td>
|
||||
{% if not liaison.from_contact_id %}
|
||||
{% for doc in liaison.attachments.all %}
|
||||
<a href="{{ doc.href }}">{{ doc.title }}</a>
|
||||
<br>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<a href="{% url "ietf.liaisons.views.liaison_detail" object_id=liaison.pk %}">{{ liaison.title }}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
|
@ -2,7 +2,7 @@
|
|||
{% 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" }}
|
||||
Liaison statement submitted by email from {{ liaison.from_groups.first.name }} to {{ liaison.to_groups.first.name }} on {{ liaison.submitted|date:"Y-m-d" }}
|
||||
{% else %}
|
||||
{{ liaison.title }}
|
||||
{% endif %}
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
|
||||
{% block title %}Liaison statements{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
|
||||
{% block content-title %}
|
||||
<h1>Liaison statements</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block management-links %}
|
||||
{% if can_manage %}
|
||||
<p class="buttonlist">
|
||||
{% if can_send_incoming %}
|
||||
<a class="btn btn-default" href="{% url "add_liaison" %}?incoming">New incoming liaison</a>
|
||||
{% endif %}
|
||||
{% if can_send_outgoing %}
|
||||
<a class="btn btn-default" href="{% url "add_liaison" %}">New outgoing liaison</a>
|
||||
{% endif %}
|
||||
{% if approvable %}
|
||||
<a class="btn btn-default" href="{% url "liaison_approval_list" %}">Approve pending liaison statement{{ approvable|pluralize }} <span class="badge">{{ approvable }}</span></a>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% load ietf_filters %}
|
||||
<table class="table table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a href="?sort=submitted">Date {% if sort == "submitted" %}<span class="fa fa-caret-down"></span>{% endif %}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="?sort=from_name">From {% if sort == "from_name" %}<span class="fa fa-caret-down"></span>{% endif %}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="?sort=to_name">To {% if sort == "to_name" %}<span class="fa fa-caret-down"></span>{% endif %}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="?sort=deadline">Deadline {% if sort == "deadline" %}<span class="fa fa-caret-down"></span>{% endif %}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="?sort=title">Title {% if sort == "title" %}<span class="fa fa-caret-down"></span>{% endif %}</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for liaison in liaisons %}
|
||||
<tr>
|
||||
<td class="text-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 class="text-nowrap">{{ 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
|
|
@ -2,4 +2,4 @@ The following liaison statement will remain pending (and not public available) i
|
|||
|
||||
{% include "liaisons/liaison_mail.txt" %}
|
||||
|
||||
Please visit {{ url }} in order to approve the liaison statement.
|
||||
Please visit {{ liaison.get_absolute_url }} in order to approve the liaison statement.
|
||||
|
|
|
@ -8,8 +8,8 @@ As liaison manager of {{ sdo_name }} you have to provide an updated list of pers
|
|||
Current list in our system for {{ sdo_name }} is:
|
||||
|
||||
------
|
||||
{% for person in individuals %}
|
||||
{{ person.email.0 }} <{{ person.email.1 }}>
|
||||
{% for role in individuals %}
|
||||
{{ role.person.plain_name }} <{{ role.email.address }}>
|
||||
{% endfor %}
|
||||
------
|
||||
|
||||
|
|
87
ietf/templates/liaisons/search_form.html
Normal file
87
ietf/templates/liaisons/search_form.html
Normal file
|
@ -0,0 +1,87 @@
|
|||
{# Copyright The IETF Trust 2015, All Rights Reserved #}{% load origin %}{% origin %}
|
||||
{% load widget_tweaks %}
|
||||
{% load ietf_filters %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
<form id="search_form" class="form-horizontal" action="{% url "ietf.liaisons.views.liaison_list" state=state %}" method="get">
|
||||
|
||||
<div class="input-group search_field">
|
||||
{{ form.text|add_class:"form-control"|attr:"placeholder:Title, body, identifiers, etc." }}
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<span class="fa fa-search"></span>
|
||||
Search
|
||||
</button>
|
||||
</span>
|
||||
</div> <!-- search_field -->
|
||||
|
||||
{{ form.sort }} {# hidden field #}
|
||||
|
||||
<div class="panel-group" id="accordion1">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<p class="panel-title toggle_advanced">
|
||||
<a data-toggle="collapse" data-parent="#accordion1" href="#searchcollapse">
|
||||
<span class="fa fa-caret-down"></span> Additional search criteria
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div id="searchcollapse" class="panel-collapse collapse visible-nojs">
|
||||
<div class="panel-body">
|
||||
|
||||
|
||||
<div class="form-group search_field">
|
||||
<div class="col-sm-4">
|
||||
<label for="id_source" class="control-label">Source</label>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
{{ form.source|add_class:"form-control" }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group search_field">
|
||||
<div class="col-sm-4">
|
||||
<label for="id_destination" class="control-label">Destination</label>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
{{ form.destination|add_class:"form-control" }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group search_field">
|
||||
<div class="col-sm-4">
|
||||
<label for="id_start_date" class="control-label">Start Date</label>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
{{ form.start_date|add_class:"form-control" }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group search_field">
|
||||
<div class="col-sm-4">
|
||||
<label for="id_end_date" class="control-label">End Date</label>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
{{ form.end_date|add_class:"form-control" }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="form-group search_field">
|
||||
<div class="col-md-offset-4 col-sm-4">
|
||||
<button class="btn btn-default btn-block" type="reset">Clear</button>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<button class="btn btn-primary btn-block" type="submit">
|
||||
<span class="fa fa-search"></span>
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- accordian1 -->
|
||||
</form>
|
||||
|
Loading…
Reference in a new issue