'
- html += 'Changed doc from stream %s to %s' % (self.from_stream, self.to_stream)
- html += '
'
- return html
-
-
-class StateDescription(models.Model):
- state = models.ForeignKey(State)
- definition = models.TextField()
- order = models.PositiveIntegerField()
-
- class Meta:
- ordering = ('order', )
-
- def __unicode__(self):
- return unicode(self.state)
-
-
-class AnnotationTag(models.Model):
- name = models.CharField(_(u"Name"), max_length=100)
- workflow = models.ForeignKey(Workflow, verbose_name=_(u"Workflow"), related_name="annotation_tags")
- permission = models.ForeignKey(Permission, verbose_name=_(u"Permission"), blank=True, null=True)
-
- class Meta:
- ordering = ('name', )
-
- def __unicode__(self):
- return self.name
-
-
-class AnnotationTagObjectRelation(models.Model):
- content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"), related_name="annotation_tags", blank=True, null=True)
- content_id = models.PositiveIntegerField(_(u"Content id"), blank=True, null=True)
- content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id")
-
- annotation_tag = models.ForeignKey(AnnotationTag, verbose_name=_(u"Annotation tag"))
-
-
-class StateObjectRelationMetadata(models.Model):
- relation = models.ForeignKey(StateObjectRelation)
- from_date = models.DateTimeField(_('Initial date'), blank=True, null=True)
- estimated_date = models.DateTimeField(_('Estimated date'), blank=True, null=True)
-
-
-class WGWorkflow(Workflow):
- selected_states = models.ManyToManyField(State, blank=True, null=True)
- selected_tags = models.ManyToManyField(AnnotationTag, blank=True, null=True)
-
- class Meta:
- verbose_name = 'IETF Workflow'
- verbose_name_plural = 'IETF Workflows'
-
- def get_tags(self):
- tags = self.annotation_tags.all()
- if tags.count():
- return tags
- else:
- return self.selected_tags.all()
-
- def get_states(self):
- states = self.states.all()
- if states.count():
- return states
- else:
- return self.selected_states.all()
-
-
-class Stream(models.Model):
- name = models.CharField(_(u"Name"), max_length=100)
- document_group_attribute = models.CharField(_(u'Document group attribute'), max_length=255, blank=True, null=True)
- group_chair_attribute = models.CharField(_(u'Group chair attribute'), max_length=255, blank=True, null=True)
- workflow = models.ForeignKey(WGWorkflow)
-
- def __unicode__(self):
- return u'%s stream' % self.name
- workflow_link = admin_link('workflow')
-
- def _irtf_group(self, document):
- filename = document.filename.split('-')
- if len(filename) > 2 and filename[0] == 'draft' and filename[1] == 'irtf':
- try:
- return IRTF.objects.get(acronym=filename[2])
- except IRTF.DoesNotExist:
- return None
- return None
-
- def _irtf_chairs_for_document(self, document):
- group = self._irtf_group(document)
- if not group:
- return []
- chairs = [i.person for i in group.chairs()]
- chairs.append(Role.objects.get(pk=Role.IRTF_CHAIR).person)
- return chairs
-
- def _ietf_delegates_for_document(self, document):
- group = self.get_group_for_document(document)
- if not group:
- return False
- return [i.person for i in group.wgdelegate_set.all()]
-
- def get_group_for_document(self, document):
- if hasattr(self, '_%s_group' % self.name.lower()):
- return getattr(self, '_%s_group' % self.name.lower())(document)
-
- if not self.document_group_attribute:
- return None
- attr = None
- obj = document
- for attr_name in self.document_group_attribute.split('.'):
- attr = getattr(obj, attr_name, None)
- if not attr:
- return None
- if callable(attr):
- attr = attr()
- obj = attr
- return attr
-
- def get_chairs_for_document(self, document):
- if hasattr(self, '_%s_chairs_for_document' % self.name.lower()):
- return getattr(self, '_%s_chairs_for_document' % self.name.lower())(document)
-
- group = self.get_group_for_document(document)
- if not group or not self.group_chair_attribute:
- return []
- attr = None
- obj = group
- for attr_name in self.group_chair_attribute.split('.'):
- attr = getattr(obj, attr_name, None)
- if not attr:
- return None
- if callable(attr):
- attr = attr()
- obj = attr
- return attr
-
- def get_delegates_for_document(self, document):
- delegates = []
- if hasattr(self, '_%s_delegates_for_document' % self.name.lower()):
- delegates = getattr(self, '_%s_delegates_for_document' % self.name.lower())(document)
- delegates += [i.person for i in self.streamdelegate_set.all()]
- return delegates
-
- def _ise_chairs_for_document(self, document):
- return self._ise_stream_chairs()
-
- def _ise_stream_chairs(self):
- chairs = []
- try:
- chairs.append(Role.objects.get(role_name='ISE').person)
- except Role.DoesNotExist:
- pass
- return chairs
-
- def get_chairs(self):
- chairs = []
- if hasattr(self, '_%s_stream_chairs' % self.name.lower()):
- chairs += list(getattr(self, '_%s_stream_chairs' % self.name.lower())())
-
- role_key = getattr(Role, '%s_CHAIR' % self.name.upper(), None)
- if role_key:
- try:
- chairs.append(Role.objects.get(pk=role_key).person)
- except Role.DoesNotExist:
- pass
- return list(set(chairs))
-
- def get_delegates(self):
- delegates = []
- if hasattr(self, '_%s_stream_delegates' % self.name.lower()):
- delegates += list(getattr(self, '_%s_stream_delegates' % self.name.lower())())
- delegates += [i.person for i in StreamDelegate.objects.filter(stream=self)]
- return list(set(delegates))
-
- def check_chair(self, person):
- return person in self.get_chairs()
-
- def check_delegate(self, person):
- return person in self.get_delegates()
-
-
-class StreamedID(models.Model):
- draft = models.OneToOneField(InternetDraft)
- stream = models.ForeignKey(Stream, blank=True, null=True)
-
- def get_group(self):
- return self.stream.get_group_for_document(self.draft)
-
-
-class StreamDelegate(models.Model):
- stream = models.ForeignKey(Stream)
- person = models.ForeignKey(PersonOrOrgInfo)
-
-if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- from ietf.name.proxy import StreamProxy as Stream
diff --git a/ietf/ietfworkflows/streams.py b/ietf/ietfworkflows/streams.py
deleted file mode 100644
index 9540a2825..000000000
--- a/ietf/ietfworkflows/streams.py
+++ /dev/null
@@ -1,102 +0,0 @@
-from django.db import models
-from django.conf import settings
-from ietf.idtracker.models import InternetDraft
-
-from ietf.idrfc.idrfc_wrapper import IdRfcWrapper, IdWrapper
-from ietf.ietfworkflows.models import StreamedID, Stream
-
-
-def get_streamed_draft(draft):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- class Dummy: pass
- o = Dummy()
- o.draft = draft
- o.stream = super(InternetDraft, draft).stream
- o.group = draft.group
- o.get_group = lambda: draft.group
- return o
-
- if not draft:
- return None
- try:
- return draft.streamedid
- except StreamedID.DoesNotExist:
- return None
-
-
-def get_stream_from_draft(draft):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- s = super(InternetDraft, draft).stream
- if s:
- s.with_groups = s.slug in ["ietf", "irtf"]
- return s
-
- streamedid = get_streamed_draft(draft)
- if streamedid:
- return streamedid.stream
- return False
-
-
-def get_stream_by_name(stream_name):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- raise NotImplementedError
-
- try:
- return Stream.objects.get(name=stream_name)
- except Stream.DoesNotExist:
- return None
-
-
-def get_stream_from_id(stream_id):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- raise NotImplementedError
-
- try:
- return Stream.objects.get(id=stream_id)
- except Stream.DoesNotExist:
- return None
-
-
-def _set_stream_automatically(draft, stream):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- raise NotImplementedError
- (streamed, created) = StreamedID.objects.get_or_create(draft=draft)
- if created:
- streamed.stream = stream
- streamed.save()
- return
-
-
-def get_stream_from_wrapper(idrfc_wrapper):
- idwrapper = None
- if isinstance(idrfc_wrapper, IdRfcWrapper):
- idwrapper = idrfc_wrapper.id
- elif isinstance(idrfc_wrapper, IdWrapper):
- idwrapper = idrfc_wrapper
- if not idwrapper:
- return None
- draft = idwrapper._draft
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- return super(InternetDraft, draft).stream
-
- stream = get_stream_from_draft(draft)
- if stream == False:
- stream_id = idwrapper.stream_id()
- stream = get_stream_from_id(stream_id)
- _set_stream_automatically(draft, stream)
- return stream
- else:
- return stream
- return None
-
-
-def set_stream_for_draft(draft, stream):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- raise NotImplementedError
-
- (streamed, created) = StreamedID.objects.get_or_create(draft=draft)
- if streamed.stream != stream:
- streamed.stream = stream
- streamed.group = None
- streamed.save()
- return streamed.stream
diff --git a/ietf/ietfworkflows/templatetags/.gitignore b/ietf/ietfworkflows/templatetags/.gitignore
deleted file mode 100644
index a74b07aee..000000000
--- a/ietf/ietfworkflows/templatetags/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/*.pyc
diff --git a/ietf/ietfworkflows/templatetags/__init__.py b/ietf/ietfworkflows/templatetags/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/ietf/ietfworkflows/templatetags/ietf_streams.py b/ietf/ietfworkflows/templatetags/ietf_streams.py
deleted file mode 100644
index f9e69803f..000000000
--- a/ietf/ietfworkflows/templatetags/ietf_streams.py
+++ /dev/null
@@ -1,114 +0,0 @@
-from django import template
-from django.conf import settings
-from django.core.urlresolvers import reverse as urlreverse
-
-from ietf.idrfc.idrfc_wrapper import IdRfcWrapper, IdWrapper
-from ietf.ietfworkflows.utils import (get_workflow_for_draft,
- get_state_for_draft)
-from ietf.wgchairs.accounts import (can_manage_shepherd_of_a_document,
- can_manage_writeup_of_a_document)
-from ietf.ietfworkflows.streams import get_stream_from_wrapper
-from ietf.ietfworkflows.models import Stream
-from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream,
- is_chair_of_stream, can_adopt)
-
-register = template.Library()
-
-
-@register.inclusion_tag('ietfworkflows/stream_state.html', takes_context=True)
-def stream_state(context, doc):
- data = {}
- stream = get_stream_from_wrapper(doc)
- data.update({'stream': stream})
- if not stream:
- return data
-
- idwrapper = None
- if isinstance(doc, IdRfcWrapper):
- idwrapper = doc.id
- elif isinstance(doc, IdWrapper):
- idwrapper = doc
- if not idwrapper:
- return data
-
- draft = getattr(idwrapper, '_draft', None)
- if not draft:
- return data
-
- workflow = get_workflow_for_draft(draft)
- state = get_state_for_draft(draft)
-
- data.update({'workflow': workflow,
- 'draft': draft,
- 'state': state,
- 'milestones': draft.groupmilestone_set.filter(state="active")
- })
-
- return data
-
-
-@register.inclusion_tag('ietfworkflows/workflow_history_entry.html', takes_context=True)
-def workflow_history_entry(context, entry):
- real_entry = entry.get_real_instance()
- return {'entry': real_entry,
- 'entry_class': real_entry.__class__.__name__.lower()}
-
-
-@register.inclusion_tag('ietfworkflows/edit_actions.html', takes_context=True)
-def edit_actions(context, wrapper):
- request = context.get('request', None)
- user = request and request.user
- if not user:
- return {}
- idwrapper = None
- if isinstance(wrapper, IdRfcWrapper):
- idwrapper = wrapper.id
- elif isinstance(wrapper, IdWrapper):
- idwrapper = wrapper
- if not idwrapper:
- return None
- doc = wrapper
- draft = wrapper._draft
-
- actions = []
- if can_adopt(user, draft):
- actions.append(("Adopt in WG", urlreverse('edit_adopt', kwargs=dict(name=doc.draft_name))))
-
- if can_edit_state(user, draft):
- actions.append(("Change stream state", urlreverse('edit_state', kwargs=dict(name=doc.draft_name))))
- if draft.stream_id in ("iab", "ise", "irtf"):
- actions.append(("Request publication", urlreverse('doc_request_publication', kwargs=dict(name=doc.draft_name))))
-
- return dict(actions=actions)
-
-
-class StreamListNode(template.Node):
-
- def __init__(self, user, var_name):
- self.user = user
- self.var_name = var_name
-
- def render(self, context):
- user = self.user.resolve(context)
- streams = []
- for i in Stream.objects.all():
- if "Legacy" in i.name:
- continue
- if is_chair_of_stream(user, i):
- streams.append(i)
- context.update({self.var_name: streams})
- return ''
-
-
-@register.tag
-def get_user_managed_streams(parser, token):
- firstbits = token.contents.split(None, 2)
- if len(firstbits) != 3:
- raise template.TemplateSyntaxError("'get_user_managed_streams' tag takes three arguments")
- user = parser.compile_filter(firstbits[1])
- lastbits_reversed = firstbits[2][::-1].split(None, 2)
- if lastbits_reversed[1][::-1] != 'as':
- raise template.TemplateSyntaxError("next-to-last argument to 'get_user_managed_stream' tag must"
- " be 'as'")
- var_name = lastbits_reversed[0][::-1]
- return StreamListNode(user, var_name)
diff --git a/ietf/ietfworkflows/templatetags/ietf_streams_redesign.py b/ietf/ietfworkflows/templatetags/ietf_streams_redesign.py
deleted file mode 100644
index 978e99e74..000000000
--- a/ietf/ietfworkflows/templatetags/ietf_streams_redesign.py
+++ /dev/null
@@ -1,104 +0,0 @@
-from django import template
-from django.conf import settings
-from django.core.urlresolvers import reverse as urlreverse
-
-from ietf.idrfc.idrfc_wrapper import IdRfcWrapper, IdWrapper
-from ietf.ietfworkflows.utils import (get_workflow_for_draft,
- get_state_for_draft)
-from ietf.wgchairs.accounts import (can_manage_shepherd_of_a_document,
- can_manage_writeup_of_a_document)
-from ietf.ietfworkflows.streams import get_stream_from_wrapper
-from ietf.ietfworkflows.models import Stream
-from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream,
- is_chair_of_stream, can_adopt)
-
-register = template.Library()
-
-
-@register.inclusion_tag('ietfworkflows/stream_state_redesign.html', takes_context=True)
-def stream_state(context, doc):
- data = {}
- stream = doc.stream
- data.update({'stream': stream})
- if not stream:
- return data
-
- if doc.type.slug != 'draft':
- return data
-
- state = get_state_for_draft(doc)
-
- data.update({
- 'state': state,
- 'doc': doc,
- })
-
- return data
-
-
-@register.inclusion_tag('ietfworkflows/workflow_history_entry.html', takes_context=True)
-def workflow_history_entry(context, entry):
- real_entry = entry.get_real_instance()
- return {'entry': real_entry,
- 'entry_class': real_entry.__class__.__name__.lower()}
-
-
-@register.inclusion_tag('ietfworkflows/edit_actions.html', takes_context=True)
-def edit_actions(context, wrapper):
- request = context.get('request', None)
- user = request and request.user
- if not user:
- return {}
- idwrapper = None
- if isinstance(wrapper, IdRfcWrapper):
- idwrapper = wrapper.id
- elif isinstance(wrapper, IdWrapper):
- idwrapper = wrapper
- if not idwrapper:
- return None
- doc = wrapper
- draft = wrapper._draft
-
- actions = []
- if can_adopt(user, draft):
- actions.append(("Adopt in WG", urlreverse('edit_adopt', kwargs=dict(name=doc.draft_name))))
-
- if can_edit_state(user, draft):
- actions.append(("Change stream state", urlreverse('edit_state', kwargs=dict(name=doc.draft_name))))
-
- if can_edit_stream(user, draft):
- actions.append(("Change stream", urlreverse('edit_stream', kwargs=dict(name=doc.draft_name))))
-
- return dict(actions=actions)
-
-
-class StreamListNode(template.Node):
-
- def __init__(self, user, var_name):
- self.user = user
- self.var_name = var_name
-
- def render(self, context):
- user = self.user.resolve(context)
- streams = []
- for i in Stream.objects.all():
- if "Legacy" in i.name:
- continue
- if is_chair_of_stream(user, i):
- streams.append(i)
- context.update({self.var_name: streams})
- return ''
-
-
-@register.tag
-def get_user_managed_streams(parser, token):
- firstbits = token.contents.split(None, 2)
- if len(firstbits) != 3:
- raise template.TemplateSyntaxError("'get_user_managed_streams' tag takes three arguments")
- user = parser.compile_filter(firstbits[1])
- lastbits_reversed = firstbits[2][::-1].split(None, 2)
- if lastbits_reversed[1][::-1] != 'as':
- raise template.TemplateSyntaxError("next-to-last argument to 'get_user_managed_stream' tag must"
- " be 'as'")
- var_name = lastbits_reversed[0][::-1]
- return StreamListNode(user, var_name)
diff --git a/ietf/ietfworkflows/tests.py b/ietf/ietfworkflows/tests.py
deleted file mode 100644
index 522afc907..000000000
--- a/ietf/ietfworkflows/tests.py
+++ /dev/null
@@ -1,189 +0,0 @@
-import datetime, os, shutil
-
-from django.conf import settings
-from django.contrib.auth.models import User
-from django.core.urlresolvers import reverse as urlreverse
-from StringIO import StringIO
-from pyquery import PyQuery
-
-from ietf.utils.test_utils import login_testing_unauthorized
-from ietf.utils.test_data import make_test_data
-from ietf.utils.mail import outbox
-from ietf.utils import TestCase
-
-if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- from ietf.person.models import Person, Email
- from ietf.group.models import Group, Role
- from ietf.doc.models import Document, State
- from ietf.doc.utils import *
- from ietf.name.models import DocTagName
-
-class EditStreamInfoTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
- def test_adopt_document(self):
- draft = make_test_data()
- draft.stream = None
- draft.group = Group.objects.get(type="individ")
- draft.save()
- draft.unset_state("draft-stream-ietf")
-
- url = urlreverse('edit_adopt', kwargs=dict(name=draft.name))
- login_testing_unauthorized(self, "marschairman", url)
-
- # get
- r = self.client.get(url)
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- self.assertEquals(len(q('form input[type=submit][value*=adopt]')), 1)
- self.assertEquals(len(q('form select[name="group"] option')), 1) # we can only select "mars"
-
- # adopt in mars WG
- mailbox_before = len(outbox)
- events_before = draft.docevent_set.count()
- r = self.client.post(url,
- dict(comment="some comment",
- group=Group.objects.get(acronym="mars").pk,
- weeks="10"))
- self.assertEquals(r.status_code, 302)
-
- draft = Document.objects.get(pk=draft.pk)
- self.assertEquals(draft.group.acronym, "mars")
- self.assertEquals(draft.stream_id, "ietf")
- self.assertEquals(draft.docevent_set.count() - events_before, 4)
- self.assertEquals(len(outbox), mailbox_before + 1)
- self.assertTrue("state changed" in outbox[-1]["Subject"].lower())
- self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1]))
- self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1]))
-
- def test_set_tags(self):
- draft = make_test_data()
- draft.tags = DocTagName.objects.filter(slug="w-expert")
- draft.group.unused_tags.add("w-refdoc")
-
- url = urlreverse('edit_state', kwargs=dict(name=draft.name))
- login_testing_unauthorized(self, "marschairman", url)
-
- # get
- r = self.client.get(url)
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- # make sure the unused tags are hidden
- unused = draft.group.unused_tags.values_list("slug", flat=True)
- for t in q("input[name=tags]"):
- self.assertTrue(t.attrib["value"] not in unused)
-
- # set tags
- mailbox_before = len(outbox)
- events_before = draft.docevent_set.count()
- r = self.client.post(url,
- dict(comment="some comment",
- weeks="10",
- tags=["need-aut", "sheph-u"],
- only_tags="1",
- # unused but needed for validation
- new_state=draft.get_state("draft-stream-%s" % draft.stream_id).pk,
- ))
- self.assertEquals(r.status_code, 302)
-
- draft = Document.objects.get(pk=draft.pk)
- self.assertEquals(draft.tags.count(), 2)
- self.assertEquals(draft.tags.filter(slug="w-expert").count(), 0)
- self.assertEquals(draft.tags.filter(slug="need-aut").count(), 1)
- self.assertEquals(draft.tags.filter(slug="sheph-u").count(), 1)
- self.assertEquals(draft.docevent_set.count() - events_before, 2)
- self.assertEquals(len(outbox), mailbox_before + 1)
- self.assertTrue("tags changed" in outbox[-1]["Subject"].lower())
- self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1]))
- self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1]))
- self.assertTrue("plain@example.com" in unicode(outbox[-1]))
-
- def test_set_state(self):
- draft = make_test_data()
-
- url = urlreverse('edit_state', kwargs=dict(name=draft.name))
- login_testing_unauthorized(self, "marschairman", url)
-
- # get
- r = self.client.get(url)
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- # make sure the unused states are hidden
- unused = draft.group.unused_states.values_list("pk", flat=True)
- for t in q("select[name=new_state]").find("option[name=tags]"):
- self.assertTrue(t.attrib["value"] not in unused)
- self.assertEquals(len(q('select[name=new_state]')), 1)
-
- old_state = draft.get_state("draft-stream-%s" % draft.stream_id )
- new_state = State.objects.get(used=True, type="draft-stream-%s" % draft.stream_id, slug="parked")
- self.assertTrue(old_state!=new_state)
- mailbox_before = len(outbox)
- events_before = draft.docevent_set.count()
-
- # First make sure cancel doesn't change anything
- r = self.client.post(url,
- dict(comment="some comment",
- weeks="10",
- tags=[x.pk for x in draft.tags.filter(slug__in=get_tags_for_stream_id(draft.stream_id))],
- new_state=new_state.pk,
- cancel="1",
- ))
- self.assertEquals(r.status_code, 302)
-
- draft = Document.objects.get(pk=draft.pk)
- self.assertEquals(draft.get_state("draft-stream-%s" % draft.stream_id), old_state)
-
- # Set new state
- r = self.client.post(url,
- dict(comment="some comment",
- weeks="10",
- tags=[x.pk for x in draft.tags.filter(slug__in=get_tags_for_stream_id(draft.stream_id))],
- new_state=new_state.pk,
- ))
- self.assertEquals(r.status_code, 302)
-
- draft = Document.objects.get(pk=draft.pk)
- self.assertEquals(draft.get_state("draft-stream-%s" % draft.stream_id), new_state)
- self.assertEquals(draft.docevent_set.count() - events_before, 2)
- reminder = DocReminder.objects.filter(event__doc=draft, type="stream-s")
- self.assertEquals(len(reminder), 1)
- due = datetime.datetime.now() + datetime.timedelta(weeks=10)
- self.assertTrue(due - datetime.timedelta(days=1) <= reminder[0].due <= due + datetime.timedelta(days=1))
- self.assertEquals(len(outbox), mailbox_before + 1)
- self.assertTrue("state changed" in outbox[-1]["Subject"].lower())
- self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1]))
- self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1]))
-
- def test_manage_stream_delegates(self):
- make_test_data()
-
- url = urlreverse('stream_delegates', kwargs=dict(stream_name="IETF"))
- login_testing_unauthorized(self, "secretary", url)
-
- # get
- r = self.client.get(url)
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- self.assertEquals(len(q('input[type=submit][value*=Add]')), 1)
-
- delegate = Email.objects.get(address="plain@example.com")
-
- # add delegate
- r = self.client.post(url,
- dict(email=delegate.address))
- self.assertEquals(r.status_code, 200)
-
- self.assertEquals(Role.objects.filter(group__acronym="ietf", name="delegate", person__email__address=delegate.address).count(), 1)
-
- # remove delegate again
- r = self.client.post(url,
- dict(remove_delegate=[delegate.person.pk],
- delete="1"))
- self.assertEquals(r.status_code, 200)
-
- self.assertEquals(Role.objects.filter(group__acronym="ietf", name="delegate", person__email__address=delegate.address).count(), 0)
-
-if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
- # the above tests only work with the new schema
- del EditStreamInfoTestCase
diff --git a/ietf/ietfworkflows/urls.py b/ietf/ietfworkflows/urls.py
deleted file mode 100644
index af7a885d4..000000000
--- a/ietf/ietfworkflows/urls.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright The IETF Trust 2008, All Rights Reserved
-
-from django.conf.urls.defaults import patterns, url
-from django.views.generic.simple import redirect_to
-
-urlpatterns = patterns('ietf.ietfworkflows.views',
- url(r'^(?P[^/]+)/history/$', 'stream_history', name='stream_history'),
- url(r'^(?P[^/]+)/edit/adopt/$', 'edit_adopt', name='edit_adopt'),
- # FIXME: the name edit_state is far too generic
- url(r'^(?P[^/]+)/edit/state/$', 'edit_state', name='edit_state'),
- url(r'^(?P[^/]+)/edit/stream/$', redirect_to, { 'url': '/doc/%(name)s/edit/info/'}) ,
- url(r'^delegates/(?P[^/]+)/$', 'stream_delegates', name='stream_delegates'),
-)
diff --git a/ietf/ietfworkflows/utils.py b/ietf/ietfworkflows/utils.py
deleted file mode 100644
index 137abb765..000000000
--- a/ietf/ietfworkflows/utils.py
+++ /dev/null
@@ -1,471 +0,0 @@
-import copy
-import datetime
-
-from django.conf import settings
-from django.contrib.contenttypes.models import ContentType
-from django.core.mail import EmailMessage
-from django.template.loader import render_to_string
-from django.template.defaultfilters import pluralize
-
-from workflows.models import State, StateObjectRelation
-from workflows.utils import (get_workflow_for_object, set_workflow_for_object,
- get_state, set_state)
-
-from ietf.ietfworkflows.streams import (get_streamed_draft, get_stream_from_draft,
- set_stream_for_draft)
-from ietf.ietfworkflows.models import (WGWorkflow, AnnotationTagObjectRelation,
- AnnotationTag, ObjectAnnotationTagHistoryEntry,
- ObjectHistoryEntry, StateObjectRelationMetadata,
- ObjectWorkflowHistoryEntry, ObjectStreamHistoryEntry)
-from ietf.idtracker.models import InternetDraft
-from ietf.utils.mail import send_mail
-from ietf.doc.models import Document, DocEvent, save_document_in_history, DocReminder, DocReminderTypeName
-from ietf.group.models import Role
-
-WAITING_WRITEUP = 'WG Consensus: Waiting for Write-Up'
-FOLLOWUP_TAG = 'Doc Shepherd Follow-up Underway'
-
-
-def get_default_workflow_for_wg():
- try:
- workflow = WGWorkflow.objects.get(name='Default WG Workflow')
- return workflow
- except WGWorkflow.DoesNotExist:
- return None
-
-
-def clone_transition(transition):
- new = copy.copy(transition)
- new.pk = None
- new.save()
-
- # Reference original initial states
- for state in transition.states.all():
- new.states.add(state)
- return new
-
-
-def clone_workflow(workflow, name):
- new = WGWorkflow.objects.create(name=name, initial_state=workflow.initial_state)
-
- # Reference default states
- for state in workflow.states.all():
- new.selected_states.add(state)
-
- # Reference default annotation tags
- for tag in workflow.annotation_tags.all():
- new.selected_tags.add(tag)
-
- # Reference cloned transitions
- for transition in workflow.transitions.all():
- new.transitions.add(clone_transition(transition))
- return new
-
-
-def get_workflow_for_wg(wg, default=None):
- workflow = get_workflow_for_object(wg)
- try:
- workflow = workflow and workflow.wgworkflow
- except WGWorkflow.DoesNotExist:
- workflow = None
- if not workflow:
- if default:
- workflow = default
- else:
- workflow = get_default_workflow_for_wg()
- if not workflow:
- return None
- workflow = clone_workflow(workflow, name='%s workflow' % wg)
- set_workflow_for_object(wg, workflow)
- return workflow
-
-
-def get_workflow_for_draft(draft):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- return True if get_streamed_draft(draft) else None
-
- workflow = get_workflow_for_object(draft)
- try:
- workflow = workflow and workflow.wgworkflow
- except WGWorkflow.DoesNotExist:
- workflow = None
- if not workflow:
- streamed_draft = get_streamed_draft(draft)
- if not streamed_draft or not streamed_draft.stream:
- return None
- stream = streamed_draft.stream
- if stream.document_group_attribute:
- group = streamed_draft.get_group()
- if not group:
- return None
- else:
- workflow = get_workflow_for_wg(group, streamed_draft.stream.workflow)
- else:
- workflow = stream.workflow
- set_workflow_for_object(draft, workflow)
- return workflow
-
-
-def get_workflow_history_for_draft(draft, entry_type=None):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- from ietf.doc.proxy import ObjectHistoryEntryProxy
- return ObjectHistoryEntryProxy.objects.filter(doc=draft).order_by('-time', '-id').select_related('by')
-
- ctype = ContentType.objects.get_for_model(draft)
- filter_param = {'content_type': ctype,
- 'content_id': draft.pk}
- if entry_type:
- filter_param.update({'%s__isnull' % entry_type: False})
- history = ObjectHistoryEntry.objects.filter(**filter_param).\
- select_related('objectworkflowhistoryentry', 'objectannotationtaghistoryentry',
- 'objectstreamhistoryentry')
- return history
-
-
-def get_annotation_tags_for_draft(draft):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- from ietf.name.proxy import AnnotationTagObjectRelationProxy
- from ietf.doc.utils import get_tags_for_stream_id
- return AnnotationTagObjectRelationProxy.objects.filter(document=draft.pk, slug__in=get_tags_for_stream_id(draft.stream_id))
-
- ctype = ContentType.objects.get_for_model(draft)
- tags = AnnotationTagObjectRelation.objects.filter(content_type=ctype, content_id=draft.pk)
- return tags
-
-
-def get_state_for_draft(draft):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- return draft.get_state("draft-stream-%s" % draft.stream_id)
- return get_state(draft)
-
-
-def get_state_by_name(state_name):
- try:
- return State.objects.get(used=True, name=state_name)
- except State.DoesNotExist:
- return None
-
-
-def get_annotation_tag_by_name(tag_name):
- try:
- return AnnotationTag.objects.get(name=tag_name)
- except AnnotationTag.DoesNotExist:
- return None
-
-
-def set_tag(obj, tag):
- ctype = ContentType.objects.get_for_model(obj)
- (relation, created) = AnnotationTagObjectRelation.objects.get_or_create(
- content_type=ctype,
- content_id=obj.pk,
- annotation_tag=tag)
- return relation
-
-
-def set_tag_by_name(obj, tag_name):
- try:
- tag = AnnotationTag.objects.get(name=tag_name)
- return set_tag(obj, tag)
- except AnnotationTag.DoesNotExist:
- return None
-
-
-def reset_tag(obj, tag):
- ctype = ContentType.objects.get_for_model(obj)
- try:
- tag_relation = AnnotationTagObjectRelation.objects.get(
- content_type=ctype,
- content_id=obj.pk,
- annotation_tag=tag)
- tag_relation.delete()
- return True
- except AnnotationTagObjectRelation.DoesNotExist:
- return False
-
-
-def reset_tag_by_name(obj, tag_name):
- try:
- tag = AnnotationTag.objects.get(name=tag_name)
- return reset_tag(obj, tag)
- except AnnotationTag.DoesNotExist:
- return False
-
-
-def set_state_for_draft(draft, state, estimated_date=None):
- workflow = get_workflow_for_draft(draft)
- if state in workflow.get_states():
- set_state(draft, state)
- relation = StateObjectRelation.objects.get(
- content_type=ContentType.objects.get_for_model(draft),
- content_id=draft.pk)
- metadata = StateObjectRelationMetadata.objects.get_or_create(relation=relation)[0]
- metadata.from_date = datetime.date.today()
- metadata.to_date = estimated_date
- metadata.save()
- return state
- return False
-
-
-def notify_entry(entry, template, extra_notify=[]):
- doc = entry.content
- wg = doc.group.ietfwg
- mail_list = set(['%s <%s>' % i.person.email() for i in wg.wgchair_set.all() if i.person.email()])
- mail_list = mail_list.union(['%s <%s>' % i.person.email() for i in wg.wgdelegate_set.all() if i.person.email()])
- mail_list = mail_list.union(['%s <%s>' % i.person.email() for i in doc.authors.all() if i.person.email()])
- mail_list = mail_list.union(extra_notify)
- mail_list = list(mail_list)
-
- subject = 'Annotation tags have changed for draft %s' % doc
- body = render_to_string(template, {'doc': doc,
- 'entry': entry,
- })
- mail = EmailMessage(subject=subject,
- body=body,
- to=mail_list,
- from_email=settings.DEFAULT_FROM_EMAIL)
- # Only send emails if we are not debug mode
- if not settings.DEBUG:
- mail.send()
- return mail
-
-
-def notify_tag_entry(entry, extra_notify=[]):
- return notify_entry(entry, 'ietfworkflows/annotation_tags_updated_mail.txt', extra_notify)
-
-
-def notify_state_entry(entry, extra_notify=[]):
- return notify_entry(entry, 'ietfworkflows/state_updated_mail.txt', extra_notify)
-
-
-def notify_stream_entry(entry, extra_notify=[]):
- return notify_entry(entry, 'ietfworkflows/stream_updated_mail.txt', extra_notify)
-
-def get_notification_receivers(doc, extra_notify):
- persons = set()
- res = []
- for r in Role.objects.filter(group=doc.group, name__in=("chair", "delegate")):
- res.append(u'"%s" <%s>' % (r.person.plain_name(), r.email.address))
- persons.add(r.person)
-
- for email in doc.authors.all():
- if email.person not in persons:
- res.append(email.formatted_email())
- persons.add(email.person)
-
- for x in extra_notify:
- if not x in res:
- res.append(x)
-
- return res
-
-def get_pubreq_receivers(doc, extra_notify):
- res = [u'"IESG Secretary" ', ]
-
- for r in Role.objects.filter(person=doc.group.ad,name__slug='ad'):
- res.append(u'"%s" <%s>' % (r.person.plain_name(), r.email.address))
-
- for x in extra_notify:
- if not x in res:
- res.append(x)
-
- return res
-
-def get_pubreq_cc_receivers(doc):
- res = []
-
- for r in Role.objects.filter(group=doc.group, name__in=("chair", "delegate")):
- res.append(u'"%s" <%s>' % (r.person.plain_name(), r.email.address))
-
- return res
-
-def update_tags(request, obj, comment, person, set_tags=[], reset_tags=[], extra_notify=[], send_email=True):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- doc = Document.objects.get(pk=obj.pk)
- save_document_in_history(doc)
-
- obj.tags.remove(*reset_tags)
- obj.tags.add(*set_tags)
-
- doc.time = datetime.datetime.now()
-
- e = DocEvent(type="changed_document", time=doc.time, by=person, doc=doc)
- l = []
- if set_tags:
- l.append(u"Annotation tag%s %s set." % (pluralize(set_tags), ", ".join(x.name for x in set_tags)))
- if reset_tags:
- l.append(u"Annotation tag%s %s cleared." % (pluralize(reset_tags), ", ".join(x.name for x in reset_tags)))
- e.desc = " ".join(l)
- e.save()
-
- if send_email:
- receivers = get_notification_receivers(doc, extra_notify)
- send_mail(request, receivers, settings.DEFAULT_FROM_EMAIL,
- u"Annotations tags changed for draft %s" % doc.name,
- 'ietfworkflows/annotation_tags_updated_mail.txt',
- dict(doc=doc,
- entry=dict(setted=", ".join(x.name for x in set_tags),
- unsetted=", ".join(x.name for x in reset_tags),
- change_date=doc.time,
- person=person,
- comment=comment)))
- return
-
- ctype = ContentType.objects.get_for_model(obj)
- setted = []
- resetted = []
- for tag in set_tags:
- if isinstance(tag, basestring):
- if set_tag_by_name(obj, tag):
- setted.append(tag)
- else:
- if set_tag(obj, tag):
- setted.append(tag.name)
- for tag in reset_tags:
- if isinstance(tag, basestring):
- if reset_tag_by_name(obj, tag):
- resetted.append(tag)
- else:
- if reset_tag(obj, tag):
- resetted.append(tag.name)
- entry = ObjectAnnotationTagHistoryEntry.objects.create(
- content_type=ctype,
- content_id=obj.pk,
- setted=','.join(setted),
- unsetted=','.join(resetted),
- date=datetime.datetime.now(),
- comment=comment,
- person=person)
- notify_tag_entry(entry, extra_notify)
-
-
-def update_state(request, doc, comment, person, to_state, added_tags, removed_tags, estimated_date=None, extra_notify=[]):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- doc = Document.objects.get(pk=doc.pk)
- save_document_in_history(doc)
-
- doc.time = datetime.datetime.now()
- from_state = doc.get_state("draft-stream-%s" % doc.stream_id)
- doc.set_state(to_state)
-
- e = DocEvent(type="changed_document", time=doc.time, by=person, doc=doc)
- e.desc = u"%s changed to %s from %s" % (to_state.type.label, to_state, from_state)
- e.save()
-
- # reminder
- reminder_type = DocReminderTypeName.objects.get(slug="stream-s")
- try:
- reminder = DocReminder.objects.get(event__doc=doc, type=reminder_type,
- active=True)
- except DocReminder.DoesNotExist:
- reminder = None
-
- if estimated_date:
- if not reminder:
- reminder = DocReminder(type=reminder_type)
-
- reminder.event = e
- reminder.due = estimated_date
- reminder.active = True
- reminder.save()
- elif reminder:
- reminder.active = False
- reminder.save()
-
- set_tags=", ".join(x.name for x in added_tags)
- reset_tags=", ".join(x.name for x in removed_tags)
- receivers = get_notification_receivers(doc, extra_notify)
- send_mail(request, receivers, settings.DEFAULT_FROM_EMAIL,
- u"State changed for draft %s" % doc.name,
- 'ietfworkflows/state_updated_mail.txt',
- dict(doc=doc,
- entry=dict(from_state=from_state,
- to_state=to_state,
- transition_date=doc.time,
- person=person,
- comment=comment,
- set_tags=set_tags,
- reset_tags=reset_tags)))
-
- if (to_state.slug=='sub-pub'):
- receivers = get_pubreq_receivers(doc, extra_notify)
- cc_receivers = get_pubreq_cc_receivers(doc)
-
- send_mail(request, receivers, settings.DEFAULT_FROM_EMAIL,
- u"Publication has been requested for draft %s" % doc.name,
- 'ietfworkflows/state_updated_mail.txt',
- dict(doc=doc,
- entry=dict(from_state=from_state,
- to_state=to_state,
- transition_date=doc.time,
- person=person,
- comment=comment)), cc=cc_receivers)
-
- return
-
- ctype = ContentType.objects.get_for_model(doc)
- from_state = get_state_for_draft(doc)
- to_state = set_state_for_draft(doc, to_state, estimated_date)
- if not to_state:
- return False
- entry = ObjectWorkflowHistoryEntry.objects.create(
- content_type=ctype,
- content_id=doc.pk,
- from_state=from_state and from_state.name or '',
- to_state=to_state and to_state.name or '',
- date=datetime.datetime.now(),
- comment=comment,
- person=person)
- notify_state_entry(entry, extra_notify)
-
-
-def update_stream(request, doc, comment, person, to_stream, extra_notify=[]):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- doc = Document.objects.get(pk=doc.pk)
- save_document_in_history(doc)
-
- doc.time = datetime.datetime.now()
- from_stream = doc.stream
- doc.stream = to_stream
- doc.save()
-
- e = DocEvent(type="changed_stream", time=doc.time, by=person, doc=doc)
- e.desc = u"Stream changed to %s" % to_stream.name
- if from_stream:
- e.desc += u"from %s" % from_stream.name
- e.save()
-
- receivers = get_notification_receivers(doc, extra_notify)
- send_mail(request, receivers, settings.DEFAULT_FROM_EMAIL,
- u"Stream changed for draft %s" % doc.name,
- 'ietfworkflows/stream_updated_mail.txt',
- dict(doc=doc,
- entry=dict(from_stream=from_stream,
- to_stream=to_stream,
- transition_date=doc.time,
- person=person,
- comment=comment)))
- return
-
- ctype = ContentType.objects.get_for_model(doc)
- from_stream = get_stream_from_draft(doc)
- to_stream = set_stream_for_draft(doc, to_stream)
- entry = ObjectStreamHistoryEntry.objects.create(
- content_type=ctype,
- content_id=doc.pk,
- from_stream=from_stream and from_stream.name or '',
- to_stream=to_stream and to_stream.name or '',
- date=datetime.datetime.now(),
- comment=comment,
- person=person)
- notify_stream_entry(entry, extra_notify)
-
-
-def get_full_info_for_draft(draft):
- return dict(
- streamed=get_streamed_draft(draft),
- stream=get_stream_from_draft(draft),
- workflow=get_workflow_for_draft(draft),
- tags=[i.annotation_tag for i in get_annotation_tags_for_draft(draft)],
- state=get_state_for_draft(draft),
- shepherd=draft.shepherd if draft.shepherd_id else None,
- )
diff --git a/ietf/ietfworkflows/views.py b/ietf/ietfworkflows/views.py
deleted file mode 100644
index 75ad58700..000000000
--- a/ietf/ietfworkflows/views.py
+++ /dev/null
@@ -1,150 +0,0 @@
-from django.http import HttpResponseRedirect, HttpResponseForbidden
-from django.shortcuts import get_object_or_404, render_to_response
-from django.template import RequestContext
-from django.conf import settings
-from django.core.urlresolvers import reverse as urlreverse
-
-from ietf.idtracker.models import InternetDraft
-from ietf.ietfworkflows.models import Stream, StreamDelegate
-from ietf.ietfworkflows.forms import (DraftTagsStateForm,
- NoWorkflowStateForm,
- StreamDelegatesForm)
-from ietf.ietfworkflows.streams import (get_stream_from_draft,
- get_streamed_draft)
-from ietf.ietfworkflows.utils import (get_workflow_history_for_draft,
- get_workflow_for_draft,
- get_annotation_tags_for_draft,
- get_state_for_draft)
-from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream,
- is_chair_of_stream, can_adopt)
-from ietf.doc.utils import get_tags_for_stream_id
-from ietf.name.models import DocTagName
-from ietf.group.utils import save_group_in_history
-from ietf.group.models import Group, Role
-
-
-REDUCED_HISTORY_LEN = 20
-
-
-def stream_history(request, name):
- draft = get_object_or_404(InternetDraft, filename=name)
- streamed = get_streamed_draft(draft)
- stream = get_stream_from_draft(draft)
- workflow = get_workflow_for_draft(draft)
- tags = []
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- used = list(draft.tags.all())
- tags = DocTagName.objects.filter(slug__in=get_tags_for_stream_id(draft.stream_id))
- for t in tags:
- t.setted = t.slug in used
- else:
- if workflow:
- tags_setted = [i.annotation_tag.pk for i in get_annotation_tags_for_draft(draft)]
- for tag in workflow.get_tags():
- tag.setted = tag.pk in tags_setted
- tags.append(tag)
- state = get_state_for_draft(draft)
- history = get_workflow_history_for_draft(draft)
- show_more = False
- if len(history) > REDUCED_HISTORY_LEN:
- show_more = True
-
- return render_to_response('ietfworkflows/stream_history.html',
- {'stream': stream,
- 'streamed': streamed,
- 'draft': draft,
- 'tags': tags,
- 'state': state,
- 'workflow': workflow,
- 'show_more': show_more,
- 'history': history[:REDUCED_HISTORY_LEN],
- },
- context_instance=RequestContext(request))
-
-
-def _edit_draft_stream(request, draft, form_class=DraftTagsStateForm):
- user = request.user
- workflow = get_workflow_for_draft(draft)
- if not workflow and form_class == DraftTagsStateForm:
- form_class = NoWorkflowStateForm
- if request.method == 'POST':
- form = form_class(user=user, draft=draft, data=request.POST)
- form.request = request
- if request.POST.get("cancel",""):
- return HttpResponseRedirect(draft.get_absolute_url())
- if form.is_valid():
- form.save()
-
- # This behavior surprises folks. Let's try running awhile without it.
- #if form_class == NoWorkflowStateForm and settings.USE_DB_REDESIGN_PROXY_CLASSES:
- # return HttpResponseRedirect(urlreverse('ietf.ietfworkflows.views.edit_state', kwargs={ 'name': draft.filename } ))
-
- return HttpResponseRedirect(draft.get_absolute_url())
- else:
- form = form_class(user=user, draft=draft)
- form.request = request
- state = get_state_for_draft(draft)
- stream = get_stream_from_draft(draft)
- history = get_workflow_history_for_draft(draft, 'objectworkflowhistoryentry')
- tags = get_annotation_tags_for_draft(draft)
- milestones = draft.groupmilestone_set.all()
- return render_to_response('ietfworkflows/state_edit.html',
- {'draft': draft,
- 'state': state,
- 'stream': stream,
- 'workflow': workflow,
- 'history': history,
- 'tags': tags,
- 'form': form,
- 'milestones': milestones,
- },
- context_instance=RequestContext(request))
-
-# these three views are reusing the same view really, which apart from
-# being somewhat obscure means that there are subtle bugs (like the
-# title being wrong) - would probably be better to switch to a model
-# where each part is edited on its own, we come from an overview page
-# anyway, so there's not a big win in putting in a common
-# overview/edit page here
-def edit_adopt(request, name):
- draft = get_object_or_404(InternetDraft, filename=name)
- if not can_adopt(request.user, draft):
- return HttpResponseForbidden("You don't have permission to access this view")
- return _edit_draft_stream(request, draft, NoWorkflowStateForm)
-
-def edit_state(request, name):
- draft = get_object_or_404(InternetDraft, filename=name, stream__isnull=False)
- if not can_edit_state(request.user, draft, ):
- return HttpResponseForbidden("You don't have permission to access this view")
- return _edit_draft_stream(request, draft, DraftTagsStateForm)
-
-
-
-def stream_delegates(request, stream_name):
- stream = get_object_or_404(Stream, name=stream_name)
- if not is_chair_of_stream(request.user, stream):
- return HttpResponseForbidden('You have no permission to access this view')
- chairs = stream.get_chairs()
- form = StreamDelegatesForm(stream=stream)
- if request.method == 'POST':
- if request.POST.get('delete', False):
- pk_list = request.POST.getlist('remove_delegate')
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- stream_group = Group.objects.get(acronym=stream.slug)
- save_group_in_history(stream_group)
- Role.objects.filter(person__in=pk_list, group=stream_group, name="delegate").delete()
- else:
- StreamDelegate.objects.filter(stream=stream, person__pk__in=pk_list).delete()
- else:
- form = StreamDelegatesForm(request.POST, stream=stream)
- if form.is_valid():
- form.save()
- form = StreamDelegatesForm(stream=stream)
- delegates = stream.get_delegates()
- return render_to_response('ietfworkflows/stream_delegates.html',
- {'stream': stream,
- 'chairs': chairs,
- 'delegates': delegates,
- 'form': form,
- },
- context_instance=RequestContext(request))
diff --git a/ietf/liaisons/tests.py b/ietf/liaisons/tests.py
index c5d3c9f91..bd7da0995 100644
--- a/ietf/liaisons/tests.py
+++ b/ietf/liaisons/tests.py
@@ -91,10 +91,7 @@ def make_liaison_models():
return l
-class LiaisonManagementTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
+class LiaisonManagementTests(TestCase):
def setUp(self):
self.liaison_dir = os.path.abspath("tmp-liaison-dir")
try:
diff --git a/ietf/meeting/tests/view.py b/ietf/meeting/tests/view.py
index 000c5efe8..07f2b491b 100644
--- a/ietf/meeting/tests/view.py
+++ b/ietf/meeting/tests/view.py
@@ -13,12 +13,12 @@ from ietf.meeting.views import edit_agenda
class ViewTestCase(TestCase):
# See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = [ 'names.xml', # ietf/names/fixtures/names.xml for MeetingTypeName, and TimeSlotTypeName
- 'meeting83.json',
- 'constraint83.json',
- 'workinggroups.json',
- 'groupgroup.json',
- 'person.json', 'users.json' ]
+ perma_fixtures = [ 'names', # ietf/names/fixtures/names.xml for MeetingTypeName, and TimeSlotTypeName
+ 'meeting83',
+ 'constraint83',
+ 'workinggroups',
+ 'groupgroup',
+ 'person', 'users' ]
def test_nameOfClueWg(self):
clue_session = Session.objects.get(pk=2194)
diff --git a/ietf/name/fixtures/names.json b/ietf/name/fixtures/names.json
new file mode 100644
index 000000000..fe8b99f58
--- /dev/null
+++ b/ietf/name/fixtures/names.json
@@ -0,0 +1,3756 @@
+[
+ {
+ "pk": "yes",
+ "model": "name.ballotpositionname",
+ "fields": {
+ "order": 1,
+ "used": true,
+ "name": "Yes",
+ "blocking": false,
+ "desc": ""
+ }
+ },
+ {
+ "pk": "noobj",
+ "model": "name.ballotpositionname",
+ "fields": {
+ "order": 2,
+ "used": true,
+ "name": "No Objection",
+ "blocking": false,
+ "desc": ""
+ }
+ },
+ {
+ "pk": "discuss",
+ "model": "name.ballotpositionname",
+ "fields": {
+ "order": 3,
+ "used": true,
+ "name": "Discuss",
+ "blocking": true,
+ "desc": ""
+ }
+ },
+ {
+ "pk": "block",
+ "model": "name.ballotpositionname",
+ "fields": {
+ "order": 3,
+ "used": true,
+ "name": "Block",
+ "blocking": true,
+ "desc": ""
+ }
+ },
+ {
+ "pk": "abstain",
+ "model": "name.ballotpositionname",
+ "fields": {
+ "order": 4,
+ "used": true,
+ "name": "Abstain",
+ "blocking": false,
+ "desc": ""
+ }
+ },
+ {
+ "pk": "recuse",
+ "model": "name.ballotpositionname",
+ "fields": {
+ "order": 5,
+ "used": true,
+ "name": "Recuse",
+ "blocking": false,
+ "desc": ""
+ }
+ },
+ {
+ "pk": "norecord",
+ "model": "name.ballotpositionname",
+ "fields": {
+ "order": 6,
+ "used": true,
+ "name": "No Record",
+ "blocking": false,
+ "desc": ""
+ }
+ },
+ {
+ "pk": "conflict",
+ "model": "name.constraintname",
+ "fields": {
+ "order": 0,
+ "penalty": 0,
+ "used": true,
+ "name": "Conflicts with",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "conflic2",
+ "model": "name.constraintname",
+ "fields": {
+ "order": 0,
+ "penalty": 0,
+ "used": true,
+ "name": "Conflicts with (secondary)",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "conflic3",
+ "model": "name.constraintname",
+ "fields": {
+ "order": 0,
+ "penalty": 0,
+ "used": true,
+ "name": "Conflicts with (tertiary)",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "rst",
+ "model": "name.dbtemplatetypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "reStructuredText",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "plain",
+ "model": "name.dbtemplatetypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Plain",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "django",
+ "model": "name.dbtemplatetypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Django",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "obs",
+ "model": "name.docrelationshipname",
+ "fields": {
+ "order": 0,
+ "revname": "Obsoleted by",
+ "used": true,
+ "name": "Obsoletes",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "updates",
+ "model": "name.docrelationshipname",
+ "fields": {
+ "order": 0,
+ "revname": "Updated by",
+ "used": true,
+ "name": "Updates",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "replaces",
+ "model": "name.docrelationshipname",
+ "fields": {
+ "order": 0,
+ "revname": "Replaced by",
+ "used": true,
+ "name": "Replaces",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "conflrev",
+ "model": "name.docrelationshipname",
+ "fields": {
+ "order": 0,
+ "revname": "Conflict reviewed by",
+ "used": true,
+ "name": "conflict reviews",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "refnorm",
+ "model": "name.docrelationshipname",
+ "fields": {
+ "order": 0,
+ "revname": "fixme",
+ "used": true,
+ "name": "Normative Reference",
+ "desc": "Normative Reference"
+ }
+ },
+ {
+ "pk": "refold",
+ "model": "name.docrelationshipname",
+ "fields": {
+ "order": 0,
+ "revname": "fixme",
+ "used": true,
+ "name": "Reference",
+ "desc": "A reference found in a document which does not have split normative/informative reference sections."
+ }
+ },
+ {
+ "pk": "refinfo",
+ "model": "name.docrelationshipname",
+ "fields": {
+ "order": 0,
+ "revname": "fixme",
+ "used": true,
+ "name": "Informative Reference",
+ "desc": "Informative Reference"
+ }
+ },
+ {
+ "pk": "tops",
+ "model": "name.docrelationshipname",
+ "fields": {
+ "order": 0,
+ "revname": "Moved to Proposed Standard by",
+ "used": true,
+ "name": "Moves to Proposed Standard",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "tois",
+ "model": "name.docrelationshipname",
+ "fields": {
+ "order": 0,
+ "revname": "Moved to Internet Standard by",
+ "used": true,
+ "name": "Moves to Internet Standard",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "tohist",
+ "model": "name.docrelationshipname",
+ "fields": {
+ "order": 0,
+ "revname": "Moved to Historic by",
+ "used": true,
+ "name": "Moves to Historic",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "toinf",
+ "model": "name.docrelationshipname",
+ "fields": {
+ "order": 0,
+ "revname": "Moved to Informational by",
+ "used": true,
+ "name": "Moves to Informational",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "tobcp",
+ "model": "name.docrelationshipname",
+ "fields": {
+ "order": 0,
+ "revname": "Moved to BCP by",
+ "used": true,
+ "name": "Moves to BCP",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "toexp",
+ "model": "name.docrelationshipname",
+ "fields": {
+ "order": 0,
+ "revname": "Moved to Experimental by",
+ "used": true,
+ "name": "Moves to Experimental",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "refunk",
+ "model": "name.docrelationshipname",
+ "fields": {
+ "order": 3,
+ "revname": "fixme",
+ "used": true,
+ "name": "Possible Reference",
+ "desc": "Reference of unknown type, likely found in the text of the document."
+ }
+ },
+ {
+ "pk": "stream-s",
+ "model": "name.docremindertypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Stream state should change",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "iana-crd",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "IANA coordination",
+ "desc": "RFC-Editor/IANA Registration Coordination"
+ }
+ },
+ {
+ "pk": "ref",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Holding for references",
+ "desc": "Holding for normative reference"
+ }
+ },
+ {
+ "pk": "missref",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Missing references",
+ "desc": "Awaiting missing normative reference"
+ }
+ },
+ {
+ "pk": "errata",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Has errata",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "rfc-rev",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Review by RFC Editor",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "via-rfc",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Via RFC Editor",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "app-min",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Approved in minutes",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "need-sh",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Shepherd Needed",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "w-dep",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Waiting for Dependency on Other Document",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "iesg-com",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "IESG Review Completed",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "iana",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "IANA",
+ "desc": "The document has IANA actions that are not yet completed."
+ }
+ },
+ {
+ "pk": "rev-wg",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Revised I-D Needed - Issue raised by WG",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "point",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 1,
+ "used": true,
+ "name": "Point Raised - writeup needed",
+ "desc": "IESG discussions on the document have raised some issues that need to be brought to the attention of the authors/WG, but those issues have not been written down yet. (It is common for discussions during a telechat to result in such situations. An AD may raise a possible issue during a telechat and only decide as a result of that discussion whether the issue is worth formally writing up and bringing to the attention of the authors/WG). A document stays in the \"Point Raised - Writeup Needed\" state until *ALL* IESG comments that have been raised have been documented."
+ }
+ },
+ {
+ "pk": "w-expert",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 1,
+ "used": true,
+ "name": "Awaiting Expert Review/Resolution of Issues Raised",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "need-ed",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 1,
+ "used": true,
+ "name": "Editor Needed",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "ad-f-up",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 2,
+ "used": true,
+ "name": "AD Followup",
+ "desc": "A generic substate indicating that the shepherding AD has the action item to determine appropriate next steps. In particular, the appropriate steps (and the corresponding next state or substate) depend entirely on the nature of the issues that were raised and can only be decided with active involvement of the shepherding AD. Examples include:\n\n- if another AD raises an issue, the shepherding AD may first iterate with the other AD to get a better understanding of the exact issue. Or, the shepherding AD may attempt to argue that the issue is not serious enough to bring to the attention of the authors/WG.\n\n- if a documented issue is forwarded to a WG, some further iteration may be needed before it can be determined whether a new revision is needed or whether the WG response to an issue clarifies the issue sufficiently.\n\n- when a new revision appears, the shepherding AD will first look at the changes to determine whether they believe all outstanding issues have been raised satisfactorily, prior to asking the ADs who raised the original issues to verify the changes."
+ }
+ },
+ {
+ "pk": "w-extern",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 2,
+ "used": true,
+ "name": "Awaiting External Review/Resolution of Issues Raised",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "w-part",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 2,
+ "used": true,
+ "name": "Waiting for Partner Feedback",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "extpty",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 3,
+ "used": true,
+ "name": "External Party",
+ "desc": "The document is awaiting review or input from an external party (i.e, someone other than the shepherding AD, the authors, or the WG). See the \"note\" field for more details on who has the action."
+ }
+ },
+ {
+ "pk": "w-merge",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 3,
+ "used": true,
+ "name": "Awaiting Merge with Other Document",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "w-review",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 3,
+ "used": true,
+ "name": "Awaiting Reviews",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "need-aut",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 4,
+ "used": true,
+ "name": "Author or Editor Needed",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "sh-f-up",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 4,
+ "used": true,
+ "name": "Document Shepherd Followup",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "need-rev",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 5,
+ "used": true,
+ "name": "Revised I-D Needed",
+ "desc": "An updated I-D is needed to address the issues that have been raised."
+ }
+ },
+ {
+ "pk": "w-refdoc",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 5,
+ "used": true,
+ "name": "Waiting for Referenced Document",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "w-refing",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 6,
+ "used": true,
+ "name": "Waiting for Referencing Document",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "rev-wglc",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 7,
+ "used": true,
+ "name": "Revised I-D Needed - Issue raised by WGLC",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "rev-ad",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 8,
+ "used": true,
+ "name": "Revised I-D Needed - Issue raised by AD",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "rev-iesg",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 9,
+ "used": true,
+ "name": "Revised I-D Needed - Issue raised by IESG",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "sheph-u",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 10,
+ "used": true,
+ "name": "Doc Shepherd Follow-up Underway",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "other",
+ "model": "name.doctagname",
+ "fields": {
+ "order": 11,
+ "used": true,
+ "name": "Other - see Comment Log",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "charter",
+ "model": "name.doctypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Charter",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "agenda",
+ "model": "name.doctypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Agenda",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "minutes",
+ "model": "name.doctypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Minutes",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "slides",
+ "model": "name.doctypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Slides",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "draft",
+ "model": "name.doctypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Draft",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "liai-att",
+ "model": "name.doctypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Liaison Attachment",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "conflrev",
+ "model": "name.doctypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Conflict Review",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "statchg",
+ "model": "name.doctypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Status Change",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "shepwrit",
+ "model": "name.doctypename",
+ "fields": {
+ "order": 0,
+ "used": false,
+ "name": "Shepherd's writeup",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "liaison",
+ "model": "name.doctypename",
+ "fields": {
+ "order": 0,
+ "used": false,
+ "name": "Liaison",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "uploaded",
+ "model": "name.draftsubmissionstatename",
+ "fields": {
+ "order": 1,
+ "next_states": [
+ "auth",
+ "aut-appr",
+ "grp-appr",
+ "manual",
+ "cancel"
+ ],
+ "used": true,
+ "name": "Uploaded",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "auth",
+ "model": "name.draftsubmissionstatename",
+ "fields": {
+ "order": 2,
+ "next_states": [
+ "cancel",
+ "posted"
+ ],
+ "used": true,
+ "name": "Awaiting Submitter Authentication",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "aut-appr",
+ "model": "name.draftsubmissionstatename",
+ "fields": {
+ "order": 3,
+ "next_states": [
+ "cancel",
+ "posted"
+ ],
+ "used": true,
+ "name": "Awaiting Approval from Previous Version Authors'",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "grp-appr",
+ "model": "name.draftsubmissionstatename",
+ "fields": {
+ "order": 4,
+ "next_states": [
+ "cancel",
+ "posted"
+ ],
+ "used": true,
+ "name": "Awaiting Initial Version Approval",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "manual",
+ "model": "name.draftsubmissionstatename",
+ "fields": {
+ "order": 5,
+ "next_states": [
+ "cancel",
+ "posted"
+ ],
+ "used": true,
+ "name": "Awaiting Manual Post",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "cancel",
+ "model": "name.draftsubmissionstatename",
+ "fields": {
+ "order": 6,
+ "next_states": [],
+ "used": true,
+ "name": "Cancelled",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "posted",
+ "model": "name.draftsubmissionstatename",
+ "fields": {
+ "order": 7,
+ "next_states": [],
+ "used": true,
+ "name": "Posted",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "comment",
+ "model": "name.feedbacktype",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Comment",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "questio",
+ "model": "name.feedbacktype",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Questionnaire response",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "nomina",
+ "model": "name.feedbacktype",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Nomination",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "junk",
+ "model": "name.feedbacktype",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Junk",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "active",
+ "model": "name.groupmilestonestatename",
+ "fields": {
+ "order": 1,
+ "used": true,
+ "name": "Active",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "deleted",
+ "model": "name.groupmilestonestatename",
+ "fields": {
+ "order": 2,
+ "used": true,
+ "name": "Deleted",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "review",
+ "model": "name.groupmilestonestatename",
+ "fields": {
+ "order": 3,
+ "used": true,
+ "name": "For review",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "charter",
+ "model": "name.groupmilestonestatename",
+ "fields": {
+ "order": 4,
+ "used": true,
+ "name": "Chartering/rechartering",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "bof",
+ "model": "name.groupstatename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "BOF",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "proposed",
+ "model": "name.groupstatename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Proposed",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "active",
+ "model": "name.groupstatename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Active",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "dormant",
+ "model": "name.groupstatename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Dormant",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "conclude",
+ "model": "name.groupstatename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Concluded",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "unknown",
+ "model": "name.groupstatename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Unknown",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "abandon",
+ "model": "name.groupstatename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Abandonded",
+ "desc": "Formation of the group (most likely a BoF or Proposed WG) was abandoned"
+ }
+ },
+ {
+ "pk": "bof-conc",
+ "model": "name.groupstatename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "BOF Concluded",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "ietf",
+ "model": "name.grouptypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "IETF",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "area",
+ "model": "name.grouptypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Area",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "ag",
+ "model": "name.grouptypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "AG",
+ "desc": "Area group"
+ }
+ },
+ {
+ "pk": "wg",
+ "model": "name.grouptypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "WG",
+ "desc": "Working group"
+ }
+ },
+ {
+ "pk": "rg",
+ "model": "name.grouptypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "RG",
+ "desc": "Research group"
+ }
+ },
+ {
+ "pk": "team",
+ "model": "name.grouptypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Team",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "individ",
+ "model": "name.grouptypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Individual",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "sdo",
+ "model": "name.grouptypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "SDO",
+ "desc": "Standards organization"
+ }
+ },
+ {
+ "pk": "irtf",
+ "model": "name.grouptypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "IRTF",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "rfcedtyp",
+ "model": "name.grouptypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "RFC Editor",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "nomcom",
+ "model": "name.grouptypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Nomcom",
+ "desc": "An IETF/IAB Nominating Committee. Use 'SDO' for external nominating committees."
+ }
+ },
+ {
+ "pk": "ps",
+ "model": "name.intendedstdlevelname",
+ "fields": {
+ "order": 1,
+ "used": true,
+ "name": "Proposed Standard",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "ds",
+ "model": "name.intendedstdlevelname",
+ "fields": {
+ "order": 2,
+ "used": false,
+ "name": "Draft Standard",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "std",
+ "model": "name.intendedstdlevelname",
+ "fields": {
+ "order": 3,
+ "used": true,
+ "name": "Internet Standard",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "bcp",
+ "model": "name.intendedstdlevelname",
+ "fields": {
+ "order": 4,
+ "used": true,
+ "name": "Best Current Practice",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "inf",
+ "model": "name.intendedstdlevelname",
+ "fields": {
+ "order": 5,
+ "used": true,
+ "name": "Informational",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "exp",
+ "model": "name.intendedstdlevelname",
+ "fields": {
+ "order": 6,
+ "used": true,
+ "name": "Experimental",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "hist",
+ "model": "name.intendedstdlevelname",
+ "fields": {
+ "order": 7,
+ "used": true,
+ "name": "Historic",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "action",
+ "model": "name.liaisonstatementpurposename",
+ "fields": {
+ "order": 1,
+ "used": true,
+ "name": "For action",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "comment",
+ "model": "name.liaisonstatementpurposename",
+ "fields": {
+ "order": 2,
+ "used": true,
+ "name": "For comment",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "info",
+ "model": "name.liaisonstatementpurposename",
+ "fields": {
+ "order": 3,
+ "used": true,
+ "name": "For information",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "response",
+ "model": "name.liaisonstatementpurposename",
+ "fields": {
+ "order": 4,
+ "used": true,
+ "name": "In response",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "ietf",
+ "model": "name.meetingtypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "IETF",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "interim",
+ "model": "name.meetingtypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Interim",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "pending",
+ "model": "name.nomineepositionstate",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Nominated, pending response",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "accepted",
+ "model": "name.nomineepositionstate",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Accepted",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "declined",
+ "model": "name.nomineepositionstate",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Declined",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "ad",
+ "model": "name.rolename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Area Director",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "pre-ad",
+ "model": "name.rolename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Incoming Area Director",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "chair",
+ "model": "name.rolename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Chair",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "editor",
+ "model": "name.rolename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Editor",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "secr",
+ "model": "name.rolename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Secretary",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "techadv",
+ "model": "name.rolename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Tech Advisor",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "execdir",
+ "model": "name.rolename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Executive Director",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "admdir",
+ "model": "name.rolename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Administrative Director",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "liaiman",
+ "model": "name.rolename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Liaison Manager",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "auth",
+ "model": "name.rolename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Authorized Individual",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "delegate",
+ "model": "name.rolename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Delegate",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "atlarge",
+ "model": "name.rolename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "At Large Member",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "member",
+ "model": "name.rolename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Member",
+ "desc": "Regular group member in a group that has explicit membership, such as the NomCom"
+ }
+ },
+ {
+ "pk": "liaison",
+ "model": "name.rolename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Liaison Member",
+ "desc": "Liaison group member in a group that has explicit membership, such as the NomCom"
+ }
+ },
+ {
+ "pk": "advisor",
+ "model": "name.rolename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Advisor",
+ "desc": "Advisor in a group that has explicit membership, such as the NomCom"
+ }
+ },
+ {
+ "pk": "schedw",
+ "model": "name.sessionstatusname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Waiting for Scheduling",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "apprw",
+ "model": "name.sessionstatusname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Waiting for Approval",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "appr",
+ "model": "name.sessionstatusname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Approved",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "sched",
+ "model": "name.sessionstatusname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Scheduled",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "canceled",
+ "model": "name.sessionstatusname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Canceled",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "disappr",
+ "model": "name.sessionstatusname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Disapproved",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "notmeet",
+ "model": "name.sessionstatusname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Not meeting",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "deleted",
+ "model": "name.sessionstatusname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Deleted",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "std",
+ "model": "name.stdlevelname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Internet Standard",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "ds",
+ "model": "name.stdlevelname",
+ "fields": {
+ "order": 0,
+ "used": false,
+ "name": "Draft Standard",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "ps",
+ "model": "name.stdlevelname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Proposed Standard",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "inf",
+ "model": "name.stdlevelname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Informational",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "exp",
+ "model": "name.stdlevelname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Experimental",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "bcp",
+ "model": "name.stdlevelname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Best Current Practice",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "hist",
+ "model": "name.stdlevelname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Historic",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "unkn",
+ "model": "name.stdlevelname",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Unknown",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "ietf",
+ "model": "name.streamname",
+ "fields": {
+ "order": 1,
+ "used": true,
+ "name": "IETF",
+ "desc": "IETF stream"
+ }
+ },
+ {
+ "pk": "ise",
+ "model": "name.streamname",
+ "fields": {
+ "order": 2,
+ "used": true,
+ "name": "ISE",
+ "desc": "Independent Submission Editor stream"
+ }
+ },
+ {
+ "pk": "irtf",
+ "model": "name.streamname",
+ "fields": {
+ "order": 3,
+ "used": true,
+ "name": "IRTF",
+ "desc": "Independent Submission Editor stream"
+ }
+ },
+ {
+ "pk": "iab",
+ "model": "name.streamname",
+ "fields": {
+ "order": 4,
+ "used": true,
+ "name": "IAB",
+ "desc": "IAB stream"
+ }
+ },
+ {
+ "pk": "legacy",
+ "model": "name.streamname",
+ "fields": {
+ "order": 5,
+ "used": true,
+ "name": "Legacy",
+ "desc": "Legacy stream"
+ }
+ },
+ {
+ "pk": "other",
+ "model": "name.timeslottypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Other",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "session",
+ "model": "name.timeslottypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Session",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "break",
+ "model": "name.timeslottypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Break",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "reg",
+ "model": "name.timeslottypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Registration",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "plenary",
+ "model": "name.timeslottypename",
+ "fields": {
+ "order": 0,
+ "used": true,
+ "name": "Plenary",
+ "desc": ""
+ }
+ },
+ {
+ "pk": "draft",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "State"
+ }
+ },
+ {
+ "pk": "draft-iesg",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "IESG state"
+ }
+ },
+ {
+ "pk": "draft-iana",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "IANA state"
+ }
+ },
+ {
+ "pk": "draft-rfceditor",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "RFC Editor state"
+ }
+ },
+ {
+ "pk": "draft-stream-ietf",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "IETF WG state"
+ }
+ },
+ {
+ "pk": "draft-stream-irtf",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "IRTF state"
+ }
+ },
+ {
+ "pk": "draft-stream-ise",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "ISE state"
+ }
+ },
+ {
+ "pk": "draft-stream-iab",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "IAB state"
+ }
+ },
+ {
+ "pk": "slides",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "State"
+ }
+ },
+ {
+ "pk": "minutes",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "State"
+ }
+ },
+ {
+ "pk": "agenda",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "State"
+ }
+ },
+ {
+ "pk": "liai-att",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "State"
+ }
+ },
+ {
+ "pk": "charter",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "State"
+ }
+ },
+ {
+ "pk": "conflrev",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "Conflict Review State"
+ }
+ },
+ {
+ "pk": "draft-iana-action",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "IANA Action state"
+ }
+ },
+ {
+ "pk": "draft-iana-review",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "IANA Review state"
+ }
+ },
+ {
+ "pk": "statchg",
+ "model": "doc.statetype",
+ "fields": {
+ "label": "RFC Status Change"
+ }
+ },
+ {
+ "pk": 81,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Active",
+ "next_states": [],
+ "slug": "active",
+ "type": "agenda",
+ "order": 1,
+ "desc": ""
+ }
+ },
+ {
+ "pk": 82,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Deleted",
+ "next_states": [],
+ "slug": "deleted",
+ "type": "agenda",
+ "order": 2,
+ "desc": ""
+ }
+ },
+ {
+ "pk": 83,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Not currently under review",
+ "next_states": [],
+ "slug": "notrev",
+ "type": "charter",
+ "order": 0,
+ "desc": "The proposed charter is not being considered at this time. A proposed charter will remain in this state until an AD moves it to Informal IESG review."
+ }
+ },
+ {
+ "pk": 84,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Informal IESG review",
+ "next_states": [],
+ "slug": "infrev",
+ "type": "charter",
+ "order": 0,
+ "desc": "This is the initial state when an AD proposes a new charter. The normal next state is Internal review if the idea is accepted, or Not currently under review if the idea is abandoned."
+ }
+ },
+ {
+ "pk": 85,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Internal review",
+ "next_states": [],
+ "slug": "intrev",
+ "type": "charter",
+ "order": 0,
+ "desc": "The IESG and IAB are reviewing the early draft of the charter; this is the initial IESG and IAB review. The usual next state is External review if the idea is adopted, or Informal IESG review if the IESG decides the idea needs more work, or Not currently under review is the idea is abandoned"
+ }
+ },
+ {
+ "pk": 86,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "External review",
+ "next_states": [],
+ "slug": "extrev",
+ "type": "charter",
+ "order": 0,
+ "desc": "The IETF community and possibly other standards development organizations (SDOs) are reviewing the proposed charter. The usual next state is IESG review, although it might move to Not currently under review is the idea is abandoned during the external review."
+ }
+ },
+ {
+ "pk": 87,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "IESG review",
+ "next_states": [],
+ "slug": "iesgrev",
+ "type": "charter",
+ "order": 0,
+ "desc": "The IESG is reviewing the discussion from the external review of the proposed charter. The usual next state is Approved, or Not currently under review if the idea is abandoned."
+ }
+ },
+ {
+ "pk": 88,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Approved",
+ "next_states": [],
+ "slug": "approved",
+ "type": "charter",
+ "order": 0,
+ "desc": "The charter is approved by the IESG."
+ }
+ },
+ {
+ "pk": 90,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Needs Shepherd",
+ "next_states": [
+ 91,
+ 98,
+ 99
+ ],
+ "slug": "needshep",
+ "type": "conflrev",
+ "order": 1,
+ "desc": "A conflict review has been requested, but a shepherding AD has not yet been assigned"
+ }
+ },
+ {
+ "pk": 91,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "AD Review",
+ "next_states": [
+ 92,
+ 98,
+ 99
+ ],
+ "slug": "adrev",
+ "type": "conflrev",
+ "order": 2,
+ "desc": "The sponsoring AD is reviewing the document and preparing a proposed response"
+ }
+ },
+ {
+ "pk": 92,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "IESG Evaluation",
+ "next_states": [
+ 93,
+ 94,
+ 95,
+ 98,
+ 99
+ ],
+ "slug": "iesgeval",
+ "type": "conflrev",
+ "order": 3,
+ "desc": "The IESG is considering the proposed conflict review response"
+ }
+ },
+ {
+ "pk": 93,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "IESG Evaluation - Defer",
+ "next_states": [
+ 92,
+ 94,
+ 95,
+ 98,
+ 99
+ ],
+ "slug": "defer",
+ "type": "conflrev",
+ "order": 4,
+ "desc": "The evaluation of the proposed conflict review response has been deferred to the next telechat"
+ }
+ },
+ {
+ "pk": 100,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Approved Request to Not Publish - point raised",
+ "next_states": [
+ 94
+ ],
+ "slug": "appr-reqnopub-pr",
+ "type": "conflrev",
+ "order": 5,
+ "desc": "The IESG has approved the conflict review response (a request to not publish), but a point has been raised that should be cleared before moving to announcement to be sent"
+ }
+ },
+ {
+ "pk": 101,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Approved No Problem - point raised",
+ "next_states": [
+ 95
+ ],
+ "slug": "appr-noprob-pr",
+ "type": "conflrev",
+ "order": 6,
+ "desc": "The IESG has approved the conflict review response, but a point has been raised that should be cleared before proceeding to announcement to be sent"
+ }
+ },
+ {
+ "pk": 94,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Approved Request to Not Publish - announcement to be sent",
+ "next_states": [
+ 96,
+ 98
+ ],
+ "slug": "appr-reqnopub-pend",
+ "type": "conflrev",
+ "order": 7,
+ "desc": "The IESG has approved the conflict review response (a request to not publish), but the secretariat has not yet sent the response"
+ }
+ },
+ {
+ "pk": 95,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Approved No Problem - announcement to be sent",
+ "next_states": [
+ 97,
+ 98
+ ],
+ "slug": "appr-noprob-pend",
+ "type": "conflrev",
+ "order": 8,
+ "desc": "The IESG has approved the conflict review response, but the secretariat has not yet sent the response"
+ }
+ },
+ {
+ "pk": 96,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Approved Request to Not Publish - announcement sent",
+ "next_states": [
+ 96
+ ],
+ "slug": "appr-reqnopub-sent",
+ "type": "conflrev",
+ "order": 9,
+ "desc": "The secretariat has delivered the IESG's approved conflict review response (a request to not publish) to the requester"
+ }
+ },
+ {
+ "pk": 97,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Approved No Problem - announcement sent",
+ "next_states": [
+ 97
+ ],
+ "slug": "appr-noprob-sent",
+ "type": "conflrev",
+ "order": 10,
+ "desc": "The secretariat has delivered the IESG's approved conflict review response to the requester"
+ }
+ },
+ {
+ "pk": 98,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Withdrawn",
+ "next_states": [
+ 90
+ ],
+ "slug": "withdraw",
+ "type": "conflrev",
+ "order": 11,
+ "desc": "The request for conflict review was withdrawn"
+ }
+ },
+ {
+ "pk": 99,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Dead",
+ "next_states": [
+ 90
+ ],
+ "slug": "dead",
+ "type": "conflrev",
+ "order": 12,
+ "desc": "The conflict review has been abandoned"
+ }
+ },
+ {
+ "pk": 1,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Active",
+ "next_states": [],
+ "slug": "active",
+ "type": "draft",
+ "order": 1,
+ "desc": ""
+ }
+ },
+ {
+ "pk": 2,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Expired",
+ "next_states": [],
+ "slug": "expired",
+ "type": "draft",
+ "order": 2,
+ "desc": ""
+ }
+ },
+ {
+ "pk": 3,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "RFC",
+ "next_states": [],
+ "slug": "rfc",
+ "type": "draft",
+ "order": 3,
+ "desc": ""
+ }
+ },
+ {
+ "pk": 4,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Replaced",
+ "next_states": [],
+ "slug": "repl",
+ "type": "draft",
+ "order": 4,
+ "desc": ""
+ }
+ },
+ {
+ "pk": 5,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Withdrawn by Submitter",
+ "next_states": [],
+ "slug": "auth-rm",
+ "type": "draft",
+ "order": 5,
+ "desc": ""
+ }
+ },
+ {
+ "pk": 6,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Withdrawn by IETF",
+ "next_states": [],
+ "slug": "ietf-rm",
+ "type": "draft",
+ "order": 6,
+ "desc": ""
+ }
+ },
+ {
+ "pk": 102,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "New Document",
+ "next_states": [],
+ "slug": "newdoc",
+ "type": "draft-iana-action",
+ "order": 1,
+ "desc": "A new document has been received by IANA, but no actions have been taken"
+ }
+ },
+ {
+ "pk": 103,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "In Progress",
+ "next_states": [],
+ "slug": "inprog",
+ "type": "draft-iana-action",
+ "order": 2,
+ "desc": "IANA is currently processing the actions for this document"
+ }
+ },
+ {
+ "pk": 104,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Waiting on Authors",
+ "next_states": [],
+ "slug": "waitauth",
+ "type": "draft-iana-action",
+ "order": 3,
+ "desc": "IANA is waiting on the document's authors to respond"
+ }
+ },
+ {
+ "pk": 105,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Waiting on ADs",
+ "next_states": [],
+ "slug": "waitad",
+ "type": "draft-iana-action",
+ "order": 4,
+ "desc": "IANA is waiting on the IETF Area Directors to respond"
+ }
+ },
+ {
+ "pk": 106,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Waiting on WGC",
+ "next_states": [],
+ "slug": "waitwgc",
+ "type": "draft-iana-action",
+ "order": 5,
+ "desc": "IANA is waiting on the IETF Working Group Chairs to respond"
+ }
+ },
+ {
+ "pk": 107,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Waiting on RFC Editor",
+ "next_states": [],
+ "slug": "waitrfc",
+ "type": "draft-iana-action",
+ "order": 6,
+ "desc": "IANA has notified the RFC Editor that the actions have been completed"
+ }
+ },
+ {
+ "pk": 108,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "RFC-Ed-Ack",
+ "next_states": [],
+ "slug": "rfcedack",
+ "type": "draft-iana-action",
+ "order": 7,
+ "desc": "Request completed. The RFC Editor has acknowledged receipt of IANA's message that the actions have been completed"
+ }
+ },
+ {
+ "pk": 109,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "On Hold",
+ "next_states": [],
+ "slug": "onhold",
+ "type": "draft-iana-action",
+ "order": 8,
+ "desc": "IANA has suspended work on the document"
+ }
+ },
+ {
+ "pk": 110,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "No IC",
+ "next_states": [],
+ "slug": "noic",
+ "type": "draft-iana-action",
+ "order": 9,
+ "desc": "Request completed. There were no IANA actions for this document"
+ }
+ },
+ {
+ "pk": 111,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "IANA - Review Needed",
+ "next_states": [],
+ "slug": "need-rev",
+ "type": "draft-iana-review",
+ "order": 1,
+ "desc": "Document has not yet been reviewed by IANA."
+ }
+ },
+ {
+ "pk": 112,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "IANA OK - Actions Needed",
+ "next_states": [],
+ "slug": "ok-act",
+ "type": "draft-iana-review",
+ "order": 2,
+ "desc": "Document requires IANA actions, and the IANA Considerations section indicates the details of the actions correctly."
+ }
+ },
+ {
+ "pk": 113,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "IANA OK - No Actions Needed",
+ "next_states": [],
+ "slug": "ok-noact",
+ "type": "draft-iana-review",
+ "order": 3,
+ "desc": "Document requires no IANA action, and the IANA Considerations section indicates this correctly."
+ }
+ },
+ {
+ "pk": 114,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "IANA - Not OK",
+ "next_states": [],
+ "slug": "not-ok",
+ "type": "draft-iana-review",
+ "order": 4,
+ "desc": "IANA has issues with the text of the IANA Considerations section of the document."
+ }
+ },
+ {
+ "pk": 115,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Version Changed - Review Needed",
+ "next_states": [],
+ "slug": "changed",
+ "type": "draft-iana-review",
+ "order": 5,
+ "desc": "Document revision has changed after review by IANA."
+ }
+ },
+ {
+ "pk": 16,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Publication Requested",
+ "next_states": [
+ 13,
+ 11,
+ 8
+ ],
+ "slug": "pub-req",
+ "type": "draft-iesg",
+ "order": 10,
+ "desc": "A formal request has been made to advance/publish the document, following the procedures in Section 7.5 of RFC 2418. The request could be from a WG chair, from an individual through the RFC Editor, etc. (The Secretariat (iesg-secretary@ietf.org) is copied on these requests to ensure that the request makes it into the ID tracker.) A document in this state has not (yet) been reviewed by an AD nor has any official action been taken on it yet (other than to note that its publication has been requested."
+ }
+ },
+ {
+ "pk": 13,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "AD Evaluation",
+ "next_states": [
+ 21,
+ 14,
+ 12,
+ 11
+ ],
+ "slug": "ad-eval",
+ "type": "draft-iesg",
+ "order": 11,
+ "desc": "A specific AD (e.g., the Area Advisor for the WG) has begun reviewing the document to verify that it is ready for advancement. The shepherding AD is responsible for doing any necessary review before starting an IETF Last Call or sending the document directly to the IESG as a whole."
+ }
+ },
+ {
+ "pk": 21,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Expert Review",
+ "next_states": [
+ 13
+ ],
+ "slug": "review-e",
+ "type": "draft-iesg",
+ "order": 12,
+ "desc": "An AD sometimes asks for an external review by an outside party as part of evaluating whether a document is ready for advancement. MIBs, for example, are reviewed by the \"MIB doctors\". Other types of reviews may also be requested (e.g., security, operations impact, etc.). Documents stay in this state until the review is complete and possibly until the issues raised in the review are addressed. See the \"note\" field for specific details on the nature of the review."
+ }
+ },
+ {
+ "pk": 14,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Last Call Requested",
+ "next_states": [
+ 15
+ ],
+ "slug": "lc-req",
+ "type": "draft-iesg",
+ "order": 15,
+ "desc": "The AD has requested that the Secretariat start an IETF Last Call, but the the actual Last Call message has not been sent yet."
+ }
+ },
+ {
+ "pk": 15,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "In Last Call",
+ "next_states": [
+ 19,
+ 20
+ ],
+ "slug": "lc",
+ "type": "draft-iesg",
+ "order": 16,
+ "desc": "The document is currently waiting for IETF Last Call to complete. Last Calls for WG documents typically last 2 weeks, those for individual submissions last 4 weeks."
+ }
+ },
+ {
+ "pk": 19,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Waiting for Writeup",
+ "next_states": [
+ 20
+ ],
+ "slug": "writeupw",
+ "type": "draft-iesg",
+ "order": 18,
+ "desc": "Before a standards-track or BCP document is formally considered by the entire IESG, the AD must write up a protocol action. The protocol action is included in the approval message that the Secretariat sends out when the document is approved for publication as an RFC."
+ }
+ },
+ {
+ "pk": 20,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Waiting for AD Go-Ahead",
+ "next_states": [
+ 12
+ ],
+ "slug": "goaheadw",
+ "type": "draft-iesg",
+ "order": 19,
+ "desc": "As a result of the IETF Last Call, comments may need to be responded to and a revision of the ID may be needed as well. The AD is responsible for verifying that all Last Call comments have been adequately addressed and that the (possibly revised) document is in the ID directory and ready for consideration by the IESG as a whole."
+ }
+ },
+ {
+ "pk": 12,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "IESG Evaluation",
+ "next_states": [
+ 18,
+ 9,
+ 22
+ ],
+ "slug": "iesg-eva",
+ "type": "draft-iesg",
+ "order": 20,
+ "desc": "The document is now (finally!) being formally reviewed by the entire IESG. Documents are discussed in email or during a bi-weekly IESG telechat. In this phase, each AD reviews the document and airs any issues they may have. Unresolvable issues are documented as \"discuss\" comments that can be forwarded to the authors/WG. See the description of substates for additional details about the current state of the IESG discussion."
+ }
+ },
+ {
+ "pk": 18,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "IESG Evaluation - Defer",
+ "next_states": [
+ 12
+ ],
+ "slug": "defer",
+ "type": "draft-iesg",
+ "order": 21,
+ "desc": "During a telechat, one or more ADs requested an additional 2 weeks to review the document. A defer is designed to be an exception mechanism, and can only be invoked once, the first time the document comes up for discussion during a telechat."
+ }
+ },
+ {
+ "pk": 9,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Approved-announcement to be sent",
+ "next_states": [
+ 10
+ ],
+ "slug": "approved",
+ "type": "draft-iesg",
+ "order": 27,
+ "desc": "The IESG has approved the document for publication, but the Secretariat has not yet sent out on official approval message."
+ }
+ },
+ {
+ "pk": 10,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Approved-announcement sent",
+ "next_states": [
+ 17
+ ],
+ "slug": "ann",
+ "type": "draft-iesg",
+ "order": 30,
+ "desc": "The IESG has approved the document for publication, and the Secretariat has sent out the official approval message to the RFC editor."
+ }
+ },
+ {
+ "pk": 17,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "RFC Ed Queue",
+ "next_states": [
+ 7
+ ],
+ "slug": "rfcqueue",
+ "type": "draft-iesg",
+ "order": 31,
+ "desc": "The document is in the RFC editor Queue (as confirmed by http://www.rfc-editor.org/queue.html)."
+ }
+ },
+ {
+ "pk": 7,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "RFC Published",
+ "next_states": [
+ 8
+ ],
+ "slug": "pub",
+ "type": "draft-iesg",
+ "order": 32,
+ "desc": "The ID has been published as an RFC."
+ }
+ },
+ {
+ "pk": 22,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "DNP-waiting for AD note",
+ "next_states": [
+ 23
+ ],
+ "slug": "nopubadw",
+ "type": "draft-iesg",
+ "order": 33,
+ "desc": "Do Not Publish: The IESG recommends against publishing the document, but the writeup explaining its reasoning has not yet been produced. DNPs apply primarily to individual submissions received through the RFC editor. See the \"note\" field for more details on who has the action item."
+ }
+ },
+ {
+ "pk": 23,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "DNP-announcement to be sent",
+ "next_states": [
+ 8
+ ],
+ "slug": "nopubanw",
+ "type": "draft-iesg",
+ "order": 34,
+ "desc": "The IESG recommends against publishing the document, the writeup explaining its reasoning has been produced, but the Secretariat has not yet sent out the official \"do not publish\" recommendation message."
+ }
+ },
+ {
+ "pk": 11,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "AD is watching",
+ "next_states": [
+ 16
+ ],
+ "slug": "watching",
+ "type": "draft-iesg",
+ "order": 42,
+ "desc": "An AD is aware of the document and has chosen to place the document in a separate state in order to keep a closer eye on it (for whatever reason). Documents in this state are still not being actively tracked in the sense that no formal request has been made to publish or advance the document. The sole difference between this state and \"I-D Exists\" is that an AD has chosen to put it in a separate state, to make it easier to keep track of (for the AD's own reasons)."
+ }
+ },
+ {
+ "pk": 8,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Dead",
+ "next_states": [
+ 16
+ ],
+ "slug": "dead",
+ "type": "draft-iesg",
+ "order": 99,
+ "desc": "Document is \"dead\" and is no longer being tracked. (E.g., it has been replaced by another document with a different name, it has been withdrawn, etc.)"
+ }
+ },
+ {
+ "pk": 24,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "AUTH",
+ "next_states": [],
+ "slug": "auth",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Awaiting author action"
+ }
+ },
+ {
+ "pk": 25,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "AUTH48",
+ "next_states": [],
+ "slug": "auth48",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Awaiting final author approval"
+ }
+ },
+ {
+ "pk": 26,
+ "model": "doc.state",
+ "fields": {
+ "used": false,
+ "name": "EDIT",
+ "next_states": [],
+ "slug": "edit",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Approved by the stream manager (e.g., IESG, IAB, IRSG, ISE), awaiting processing and publishing"
+ }
+ },
+ {
+ "pk": 27,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "IANA",
+ "next_states": [],
+ "slug": "iana",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Document has been edited, but is holding for completion of IANA actions"
+ }
+ },
+ {
+ "pk": 28,
+ "model": "doc.state",
+ "fields": {
+ "used": false,
+ "name": "IESG",
+ "next_states": [],
+ "slug": "iesg",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Awaiting IESG action"
+ }
+ },
+ {
+ "pk": 29,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "ISR",
+ "next_states": [],
+ "slug": "isr",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Independent Submission Review by the ISE "
+ }
+ },
+ {
+ "pk": 30,
+ "model": "doc.state",
+ "fields": {
+ "used": false,
+ "name": "ISR-AUTH",
+ "next_states": [],
+ "slug": "isr-auth",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Independent submission awaiting author action, or in discussion between author and ISE"
+ }
+ },
+ {
+ "pk": 31,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "REF",
+ "next_states": [],
+ "slug": "ref",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Holding for normative reference"
+ }
+ },
+ {
+ "pk": 32,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "RFC-EDITOR",
+ "next_states": [],
+ "slug": "rfc-edit",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Awaiting final RFC Editor review before AUTH48"
+ }
+ },
+ {
+ "pk": 33,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "TO",
+ "next_states": [],
+ "slug": "timeout",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Time-out period during which the IESG reviews document for conflict/concurrence with other IETF working group work"
+ }
+ },
+ {
+ "pk": 34,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "MISSREF",
+ "next_states": [],
+ "slug": "missref",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Awaiting missing normative reference"
+ }
+ },
+ {
+ "pk": 89,
+ "model": "doc.state",
+ "fields": {
+ "used": false,
+ "name": "AUTH48-DONE",
+ "next_states": [
+ 74
+ ],
+ "slug": "auth48done",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Final approvals are complete"
+ }
+ },
+ {
+ "pk": 116,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "AUTH48-DONE",
+ "next_states": [],
+ "slug": "auth48-done",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Final approvals are complete"
+ }
+ },
+ {
+ "pk": 117,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "EDIT",
+ "next_states": [],
+ "slug": "edit",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Approved by the stream manager (e.g., IESG, IAB, IRSG, ISE), awaiting processing and publishing"
+ }
+ },
+ {
+ "pk": 118,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "IANA",
+ "next_states": [],
+ "slug": "iana-crd",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "RFC-Editor/IANA Registration Coordination"
+ }
+ },
+ {
+ "pk": 119,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "IESG",
+ "next_states": [],
+ "slug": "iesg",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Holding for IESG action"
+ }
+ },
+ {
+ "pk": 120,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "ISR-AUTH",
+ "next_states": [],
+ "slug": "isr-auth",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": "Independent Submission awaiting author update, or in discussion between author and ISE"
+ }
+ },
+ {
+ "pk": 133,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Pending",
+ "next_states": [],
+ "slug": "pending",
+ "type": "draft-rfceditor",
+ "order": 0,
+ "desc": ""
+ }
+ },
+ {
+ "pk": 45,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Candidate IAB Document",
+ "next_states": [],
+ "slug": "candidat",
+ "type": "draft-stream-iab",
+ "order": 1,
+ "desc": "A document being considered for the IAB stream."
+ }
+ },
+ {
+ "pk": 46,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Active IAB Document",
+ "next_states": [],
+ "slug": "active",
+ "type": "draft-stream-iab",
+ "order": 2,
+ "desc": "This document has been adopted by the IAB and is being actively developed."
+ }
+ },
+ {
+ "pk": 47,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Parked IAB Document",
+ "next_states": [],
+ "slug": "parked",
+ "type": "draft-stream-iab",
+ "order": 3,
+ "desc": "This document has lost its author or editor, is waiting for another document to be written, or cannot currently be worked on by the IAB for some other reason. Annotations probably explain why this document is parked."
+ }
+ },
+ {
+ "pk": 48,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "IAB Review",
+ "next_states": [],
+ "slug": "review-i",
+ "type": "draft-stream-iab",
+ "order": 4,
+ "desc": "This document is awaiting the IAB itself to come to internal consensus."
+ }
+ },
+ {
+ "pk": 49,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Community Review",
+ "next_states": [],
+ "slug": "review-c",
+ "type": "draft-stream-iab",
+ "order": 5,
+ "desc": "This document has completed internal consensus within the IAB and is now under community review."
+ }
+ },
+ {
+ "pk": 50,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Approved by IAB, To Be Sent to RFC Editor",
+ "next_states": [],
+ "slug": "approved",
+ "type": "draft-stream-iab",
+ "order": 6,
+ "desc": "The consideration of this document is complete, but it has not yet been sent to the RFC Editor for publication (although that is going to happen soon)."
+ }
+ },
+ {
+ "pk": 51,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Sent to a Different Organization for Publication",
+ "next_states": [],
+ "slug": "diff-org",
+ "type": "draft-stream-iab",
+ "order": 7,
+ "desc": "The IAB does not expect to publish the document itself, but has passed it on to a different organization that might continue work on the document. The expectation is that the other organization will eventually publish the document."
+ }
+ },
+ {
+ "pk": 52,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Sent to the RFC Editor",
+ "next_states": [],
+ "slug": "rfc-edit",
+ "type": "draft-stream-iab",
+ "order": 8,
+ "desc": "The IAB processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the IAB."
+ }
+ },
+ {
+ "pk": 53,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Published RFC",
+ "next_states": [],
+ "slug": "pub",
+ "type": "draft-stream-iab",
+ "order": 9,
+ "desc": "The document has been published as an RFC."
+ }
+ },
+ {
+ "pk": 54,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Dead IAB Document",
+ "next_states": [],
+ "slug": "dead",
+ "type": "draft-stream-iab",
+ "order": 10,
+ "desc": "This document was an active IAB document, but for some reason it is no longer being pursued for the IAB stream. It is possible that the document might be revived later, possibly in another stream."
+ }
+ },
+ {
+ "pk": 134,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Candidate for WG Adoption",
+ "next_states": [
+ 35
+ ],
+ "slug": "wg-cand",
+ "type": "draft-stream-ietf",
+ "order": 0,
+ "desc": "The document has been marked as a candidate for WG adoption by the WG Chair. This state can be used before a call for adoption is issued (and the document is put in the \"Call For Adoption By WG Issued\" state), to indicate that the document is in the queue for a call for adoption, even if none has been issued yet."
+ }
+ },
+ {
+ "pk": 35,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Call For Adoption By WG Issued",
+ "next_states": [
+ 36,
+ 37
+ ],
+ "slug": "c-adopt",
+ "type": "draft-stream-ietf",
+ "order": 1,
+ "desc": "4.2.1. Call for Adoption by WG Issued\n\n The \"Call for Adoption by WG Issued\" state should be used to indicate when an I-D is being considered for adoption by an IETF WG. An I-D that is in this state is actively being considered for adoption and has not yet achieved consensus, preference, or selection in the WG.\n\n This state may be used to describe an I-D that someone has asked a WG to consider for adoption, if the WG Chair has agreed with the request. This state may also be used to identify an I-D that a WG Chair asked an author to write specifically for consideration as a candidate WG item [WGDTSPEC], and/or an I-D that is listed as a 'candidate draft' in the WG's charter.\n\n Under normal conditions, it should not be possible for an I-D to be in the \"Call for Adoption by WG Issued\" state in more than one working group at the same time. This said, it is not uncommon for authors to \"shop\" their I-Ds to more than one WG at a time, with the hope of getting their documents adopted somewhere.\n\n After this state is implemented in the Datatracker, an I-D that is in the \"Call for Adoption by WG Issued\" state will not be able to be \"shopped\" to any other WG without the consent of the WG Chairs and the responsible ADs impacted by the shopping.\n\n Note that Figure 1 includes an arc leading from this state to outside of the WG state machine. This illustrates that some I-Ds that are considered do not get adopted as WG drafts. An I-D that is not adopted as a WG draft will transition out of the WG state machine and revert back to having no stream-specific state; however, the status change history log of the I-D will record that the I-D was previously in the \"Call for Adoption by WG Issued\" state."
+ }
+ },
+ {
+ "pk": 36,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Adopted by a WG",
+ "next_states": [
+ 38
+ ],
+ "slug": "adopt-wg",
+ "type": "draft-stream-ietf",
+ "order": 2,
+ "desc": "4.2.2. Adopted by a WG\n\n The \"Adopted by a WG\" state describes an individual submission I-D that an IETF WG has agreed to adopt as one of its WG drafts.\n\n WG Chairs who use this state will be able to clearly indicate when their WGs adopt individual submission I-Ds. This will facilitate the Datatracker's ability to correctly capture \"Replaces\" information for WG drafts and correct \"Replaced by\" information for individual submission I-Ds that have been replaced by WG drafts.\n\n This state is needed because the Datatracker uses the filename of an I-D as a key to search its database for status information about the I-D, and because the filename of a WG I-D is supposed to be different from the filename of an individual submission I-D. The filename of an individual submission I-D will typically be formatted as 'draft-author-wgname-topic-nn'.\n\n The filename of a WG document is supposed to be formatted as 'draft- ietf-wgname-topic-nn'.\n\n An individual I-D that is adopted by a WG may take weeks or months to be resubmitted by the author as a new (version-00) WG draft. If the \"Adopted by a WG\" state is not used, the Datatracker has no way to determine that an I-D has been adopted until a new version of the I-D is submitted to the WG by the author and until the I-D is approved for posting by a WG Chair."
+ }
+ },
+ {
+ "pk": 37,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Adopted for WG Info Only",
+ "next_states": [],
+ "slug": "info",
+ "type": "draft-stream-ietf",
+ "order": 3,
+ "desc": "4.2.3. Adopted for WG Info Only\n\n The \"Adopted for WG Info Only\" state describes a document that contains useful information for the WG that adopted it, but the document is not intended to be published as an RFC. The WG will not actively develop the contents of the I-D or progress it for publication as an RFC. The only purpose of the I-D is to provide information for internal use by the WG."
+ }
+ },
+ {
+ "pk": 38,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "WG Document",
+ "next_states": [
+ 39,
+ 40,
+ 41,
+ 43
+ ],
+ "slug": "wg-doc",
+ "type": "draft-stream-ietf",
+ "order": 4,
+ "desc": "4.2.4. WG Document\n\n The \"WG Document\" state describes an I-D that has been adopted by an IETF WG and is being actively developed.\n\n A WG Chair may transition an I-D into the \"WG Document\" state at any time as long as the I-D is not being considered or developed in any other WG.\n\n Alternatively, WG Chairs may rely upon new functionality to be added to the Datatracker to automatically move version-00 drafts into the \"WG Document\" state as described in Section 4.1.\n\n Under normal conditions, it should not be possible for an I-D to be in the \"WG Document\" state in more than one WG at a time. This said, I-Ds may be transferred from one WG to another with the consent of the WG Chairs and the responsible ADs."
+ }
+ },
+ {
+ "pk": 39,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Parked WG Document",
+ "next_states": [
+ 38
+ ],
+ "slug": "parked",
+ "type": "draft-stream-ietf",
+ "order": 5,
+ "desc": "4.2.5. Parked WG Document\n\n A \"Parked WG Document\" is an I-D that has lost its author or editor, is waiting for another document to be written or for a review to be completed, or cannot be progressed by the working group for some other reason.\n\n Some of the annotation tags described in Section 4.3 may be used in conjunction with this state to indicate why an I-D has been parked, and/or what may need to happen for the I-D to be un-parked.\n\n Parking a WG draft will not prevent it from expiring; however, this state can be used to indicate why the I-D has stopped progressing in the WG.\n\n A \"Parked WG Document\" that is not expired may be transferred from one WG to another with the consent of the WG Chairs and the responsible ADs."
+ }
+ },
+ {
+ "pk": 40,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Dead WG Document",
+ "next_states": [
+ 38
+ ],
+ "slug": "dead",
+ "type": "draft-stream-ietf",
+ "order": 6,
+ "desc": "4.2.6. Dead WG Document\n\n A \"Dead WG Document\" is an I-D that has been abandoned. Note that 'Dead' is not always a final state for a WG I-D. If consensus is subsequently achieved, a \"Dead WG Document\" may be resurrected. A \"Dead WG Document\" that is not resurrected will eventually expire.\n\n Note that an I-D that is declared to be \"Dead\" in one WG and that is not expired may be transferred to a non-dead state in another WG with the consent of the WG Chairs and the responsible ADs."
+ }
+ },
+ {
+ "pk": 41,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "In WG Last Call",
+ "next_states": [
+ 38,
+ 42,
+ 43
+ ],
+ "slug": "wg-lc",
+ "type": "draft-stream-ietf",
+ "order": 7,
+ "desc": "4.2.7. In WG Last Call\n\n A document \"In WG Last Call\" is an I-D for which a WG Last Call (WGLC) has been issued and is in progress.\n\n Note that conducting a WGLC is an optional part of the IETF WG process, per Section 7.4 of RFC 2418 [RFC2418].\n\n If a WG Chair decides to conduct a WGLC on an I-D, the \"In WG Last Call\" state can be used to track the progress of the WGLC. The Chair may configure the Datatracker to send a WGLC message to one or more mailing lists when the Chair moves the I-D into this state. The WG Chair may also be able to select a different set of mailing lists for a different document undergoing a WGLC; some documents may deserve coordination with other WGs.\n\n A WG I-D in this state should remain \"In WG Last Call\" until the WG Chair moves it to another state. The WG Chair may configure the Datatracker to send an e-mail after a specified period of time to remind or 'nudge' the Chair to conclude the WGLC and to determine the next state for the document.\n\n It is possible for one WGLC to lead into another WGLC for the same document. For example, an I-D that completed a WGLC as an \"Informational\" document may need another WGLC if a decision is taken to convert the I-D into a Standards Track document."
+ }
+ },
+ {
+ "pk": 42,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Waiting for WG Chair Go-Ahead",
+ "next_states": [
+ 41,
+ 43
+ ],
+ "slug": "chair-w",
+ "type": "draft-stream-ietf",
+ "order": 8,
+ "desc": "4.2.8. Waiting for WG Chair Go-Ahead\n\n A WG Chair may wish to place an I-D that receives a lot of comments during a WGLC into the \"Waiting for WG Chair Go-Ahead\" state. This state describes an I-D that has undergone a WGLC; however, the Chair is not yet ready to call consensus on the document.\n\n If comments from the WGLC need to be responded to, or a revision to the I-D is needed, the Chair may place an I-D into this state until all of the WGLC comments are adequately addressed and the (possibly revised) document is in the I-D repository."
+ }
+ },
+ {
+ "pk": 43,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "WG Consensus: Waiting for Write-Up",
+ "next_states": [
+ 44
+ ],
+ "slug": "writeupw",
+ "type": "draft-stream-ietf",
+ "order": 9,
+ "desc": "4.2.9. WG Consensus: Waiting for Writeup\n\n A document in the \"WG Consensus: Waiting for Writeup\" state has essentially completed its development within the working group, and is nearly ready to be sent to the IESG for publication. The last thing to be done is the preparation of a protocol writeup by a Document Shepherd. The IESG requires that a document shepherd writeup be completed before publication of the I-D is requested. The IETF document shepherding process and the role of a WG Document Shepherd is described in RFC 4858 [RFC4858]\n\n A WG Chair may call consensus on an I-D without a formal WGLC and transition an I-D that was in the \"WG Document\" state directly into this state.\n\n The name of this state includes the words \"Waiting for Writeup\" because a good document shepherd writeup takes time to prepare."
+ }
+ },
+ {
+ "pk": 44,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Submitted to IESG for Publication",
+ "next_states": [
+ 38
+ ],
+ "slug": "sub-pub",
+ "type": "draft-stream-ietf",
+ "order": 10,
+ "desc": "4.2.10. Submitted to IESG for Publication\n\n This state describes a WG document that has been submitted to the IESG for publication and that has not been sent back to the working group for revision.\n\n An I-D in this state may be under review by the IESG, it may have been approved and be in the RFC Editor's queue, or it may have been published as an RFC. Other possibilities exist too. The document may be \"Dead\" (in the IESG state machine) or in a \"Do Not Publish\" state."
+ }
+ },
+ {
+ "pk": 55,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Candidate RG Document",
+ "next_states": [],
+ "slug": "candidat",
+ "type": "draft-stream-irtf",
+ "order": 1,
+ "desc": "This document is under consideration in an RG for becoming an IRTF document. A document in this state does not imply any RG consensus and does not imply any precedence or selection. It's simply a way to indicate that somebody has asked for a document to be considered for adoption by an RG."
+ }
+ },
+ {
+ "pk": 56,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Active RG Document",
+ "next_states": [],
+ "slug": "active",
+ "type": "draft-stream-irtf",
+ "order": 2,
+ "desc": "This document has been adopted by the RG and is being actively developed."
+ }
+ },
+ {
+ "pk": 57,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Parked RG Document",
+ "next_states": [],
+ "slug": "parked",
+ "type": "draft-stream-irtf",
+ "order": 3,
+ "desc": "This document has lost its author or editor, is waiting for another document to be written, or cannot currently be worked on by the RG for some other reason."
+ }
+ },
+ {
+ "pk": 58,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "In RG Last Call",
+ "next_states": [],
+ "slug": "rg-lc",
+ "type": "draft-stream-irtf",
+ "order": 4,
+ "desc": "The document is in its final review in the RG."
+ }
+ },
+ {
+ "pk": 59,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Waiting for Document Shepherd",
+ "next_states": [],
+ "slug": "sheph-w",
+ "type": "draft-stream-irtf",
+ "order": 5,
+ "desc": "IRTF documents have document shepherds who help RG documents through the process after the RG has finished with the document."
+ }
+ },
+ {
+ "pk": 60,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Waiting for IRTF Chair",
+ "next_states": [],
+ "slug": "chair-w",
+ "type": "draft-stream-irtf",
+ "order": 6,
+ "desc": "The IRTF Chair is meant to be performing some task such as sending a request for IESG Review."
+ }
+ },
+ {
+ "pk": 61,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Awaiting IRSG Reviews",
+ "next_states": [],
+ "slug": "irsg-w",
+ "type": "draft-stream-irtf",
+ "order": 7,
+ "desc": "The document shepherd has taken the document to the IRSG and solicited reviews from one or more IRSG members."
+ }
+ },
+ {
+ "pk": 62,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "In IRSG Poll",
+ "next_states": [],
+ "slug": "irsgpoll",
+ "type": "draft-stream-irtf",
+ "order": 8,
+ "desc": "The IRSG is taking a poll on whether or not the document is ready to be published."
+ }
+ },
+ {
+ "pk": 63,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "In IESG Review",
+ "next_states": [],
+ "slug": "iesg-rev",
+ "type": "draft-stream-irtf",
+ "order": 9,
+ "desc": "The IRSG has asked the IESG to do a review of the document, as described in RFC5742."
+ }
+ },
+ {
+ "pk": 64,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Sent to the RFC Editor",
+ "next_states": [],
+ "slug": "rfc-edit",
+ "type": "draft-stream-irtf",
+ "order": 10,
+ "desc": "The RG processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the RG."
+ }
+ },
+ {
+ "pk": 65,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Published RFC",
+ "next_states": [],
+ "slug": "pub",
+ "type": "draft-stream-irtf",
+ "order": 11,
+ "desc": "The document has been published as an RFC."
+ }
+ },
+ {
+ "pk": 66,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Document on Hold Based On IESG Request",
+ "next_states": [],
+ "slug": "iesghold",
+ "type": "draft-stream-irtf",
+ "order": 12,
+ "desc": "The IESG has requested that the document be held pending further review, as specified in RFC 5742, and the IRTF has agreed to such a hold."
+ }
+ },
+ {
+ "pk": 67,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Dead IRTF Document",
+ "next_states": [],
+ "slug": "dead",
+ "type": "draft-stream-irtf",
+ "order": 13,
+ "desc": "This document was an active IRTF document, but for some reason it is no longer being pursued for the IRTF stream. It is possible that the document might be revived later, possibly in another stream."
+ }
+ },
+ {
+ "pk": 68,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Submission Received",
+ "next_states": [],
+ "slug": "receive",
+ "type": "draft-stream-ise",
+ "order": 1,
+ "desc": "The draft has been sent to the ISE with a request for publication."
+ }
+ },
+ {
+ "pk": 69,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Finding Reviewers",
+ "next_states": [],
+ "slug": "find-rev",
+ "type": "draft-stream-ise",
+ "order": 2,
+ "desc": " The ISE is finding initial reviewers for the document."
+ }
+ },
+ {
+ "pk": 70,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "In ISE Review",
+ "next_states": [],
+ "slug": "ise-rev",
+ "type": "draft-stream-ise",
+ "order": 3,
+ "desc": "The ISE is actively working on the document."
+ }
+ },
+ {
+ "pk": 71,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Response to Review Needed",
+ "next_states": [],
+ "slug": "need-res",
+ "type": "draft-stream-ise",
+ "order": 4,
+ "desc": " One or more reviews have been sent to the author, and the ISE is awaiting response."
+ }
+ },
+ {
+ "pk": 72,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "In IESG Review",
+ "next_states": [],
+ "slug": "iesg-rev",
+ "type": "draft-stream-ise",
+ "order": 5,
+ "desc": "The ISE has asked the IESG to do a review of the document, as described in RFC5742."
+ }
+ },
+ {
+ "pk": 73,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Sent to the RFC Editor",
+ "next_states": [],
+ "slug": "rfc-edit",
+ "type": "draft-stream-ise",
+ "order": 6,
+ "desc": "The ISE processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the ISE."
+ }
+ },
+ {
+ "pk": 74,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Published RFC",
+ "next_states": [],
+ "slug": "pub",
+ "type": "draft-stream-ise",
+ "order": 7,
+ "desc": "The document has been published as an RFC."
+ }
+ },
+ {
+ "pk": 75,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "No Longer In Independent Submission Stream",
+ "next_states": [],
+ "slug": "dead",
+ "type": "draft-stream-ise",
+ "order": 8,
+ "desc": "This document was actively considered in the Independent Submission stream, but the ISE chose not to publish it. It is possible that the document might be revived later. A document in this state may have a comment explaining the reasoning of the ISE (such as if the document was going to move to a different stream)."
+ }
+ },
+ {
+ "pk": 76,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Document on Hold Based On IESG Request",
+ "next_states": [],
+ "slug": "iesghold",
+ "type": "draft-stream-ise",
+ "order": 9,
+ "desc": "The IESG has requested that the document be held pending further review, as specified in RFC 5742, and the ISE has agreed to such a hold."
+ }
+ },
+ {
+ "pk": 79,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Active",
+ "next_states": [],
+ "slug": "active",
+ "type": "minutes",
+ "order": 1,
+ "desc": ""
+ }
+ },
+ {
+ "pk": 80,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Deleted",
+ "next_states": [],
+ "slug": "deleted",
+ "type": "minutes",
+ "order": 2,
+ "desc": ""
+ }
+ },
+ {
+ "pk": 77,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Active",
+ "next_states": [],
+ "slug": "active",
+ "type": "slides",
+ "order": 1,
+ "desc": ""
+ }
+ },
+ {
+ "pk": 78,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Deleted",
+ "next_states": [],
+ "slug": "deleted",
+ "type": "slides",
+ "order": 2,
+ "desc": ""
+ }
+ },
+ {
+ "pk": 121,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Needs Shepherd",
+ "next_states": [
+ 122,
+ 129
+ ],
+ "slug": "needshep",
+ "type": "statchg",
+ "order": 1,
+ "desc": "An RFC status change has been requested, but a shepherding AD has not yet been assigned"
+ }
+ },
+ {
+ "pk": 122,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "AD Review",
+ "next_states": [
+ 130,
+ 123,
+ 129
+ ],
+ "slug": "adrev",
+ "type": "statchg",
+ "order": 2,
+ "desc": "The sponsoring AD is preparing an RFC status change document"
+ }
+ },
+ {
+ "pk": 130,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Last Call Requested",
+ "next_states": [
+ 131
+ ],
+ "slug": "lc-req",
+ "type": "statchg",
+ "order": 3,
+ "desc": "Last Call has been requested for this proposed status change"
+ }
+ },
+ {
+ "pk": 131,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "In Last Call",
+ "next_states": [
+ 132
+ ],
+ "slug": "in-lc",
+ "type": "statchg",
+ "order": 4,
+ "desc": "This proposed status change is in IETF Last Call"
+ }
+ },
+ {
+ "pk": 132,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Waiting for AD Go-Ahead",
+ "next_states": [
+ 123,
+ 129
+ ],
+ "slug": "goahead",
+ "type": "statchg",
+ "order": 5,
+ "desc": "The AD is following up on IETF LC comments"
+ }
+ },
+ {
+ "pk": 123,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "IESG Evaluation",
+ "next_states": [
+ 124,
+ 125,
+ 126,
+ 129
+ ],
+ "slug": "iesgeval",
+ "type": "statchg",
+ "order": 6,
+ "desc": "The IESG is considering the proposed RFC status changes"
+ }
+ },
+ {
+ "pk": 124,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "IESG Evaluation - Defer",
+ "next_states": [
+ 123,
+ 125,
+ 126,
+ 129
+ ],
+ "slug": "defer",
+ "type": "statchg",
+ "order": 7,
+ "desc": "The evaluation of the proposed RFC status changes have been deferred to the next telechat"
+ }
+ },
+ {
+ "pk": 125,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Approved - point raised",
+ "next_states": [
+ 126,
+ 127
+ ],
+ "slug": "appr-pr",
+ "type": "statchg",
+ "order": 8,
+ "desc": "The IESG has approved the RFC status changes, but a point has been raised that should be cleared before proceeding to announcement to be sent"
+ }
+ },
+ {
+ "pk": 126,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Approved - announcement to be sent",
+ "next_states": [
+ 127
+ ],
+ "slug": "appr-pend",
+ "type": "statchg",
+ "order": 9,
+ "desc": "The IESG has approved the RFC status changes, but the secretariat has not yet sent the announcement"
+ }
+ },
+ {
+ "pk": 127,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Approved - announcement sent",
+ "next_states": [],
+ "slug": "appr-sent",
+ "type": "statchg",
+ "order": 10,
+ "desc": "The secretariat has announced the IESG's approved RFC status changes"
+ }
+ },
+ {
+ "pk": 129,
+ "model": "doc.state",
+ "fields": {
+ "used": true,
+ "name": "Dead",
+ "next_states": [
+ 121
+ ],
+ "slug": "dead",
+ "type": "statchg",
+ "order": 11,
+ "desc": "The RFC status changes have been abandoned"
+ }
+ },
+ {
+ "pk": 5,
+ "model": "doc.ballottype",
+ "fields": {
+ "doc_type": "conflrev",
+ "used": true,
+ "name": "Approve",
+ "positions": [
+ "yes",
+ "noobj",
+ "discuss",
+ "abstain",
+ "recuse",
+ "norecord"
+ ],
+ "question": "Is this the correct conflict review response?",
+ "slug": "conflrev",
+ "order": 0
+ }
+ },
+ {
+ "pk": 6,
+ "model": "doc.ballottype",
+ "fields": {
+ "doc_type": "statchg",
+ "used": true,
+ "name": "Approve",
+ "positions": [
+ "yes",
+ "noobj",
+ "discuss",
+ "abstain",
+ "recuse",
+ "norecord"
+ ],
+ "question": "Do we approve these RFC status changes?",
+ "slug": "statchg",
+ "order": 0
+ }
+ },
+ {
+ "pk": 1,
+ "model": "doc.ballottype",
+ "fields": {
+ "doc_type": "charter",
+ "used": true,
+ "name": "Ready for external review",
+ "positions": [
+ "yes",
+ "noobj",
+ "block",
+ "abstain",
+ "norecord"
+ ],
+ "question": "Is this charter ready for external review?",
+ "slug": "r-extrev",
+ "order": 1
+ }
+ },
+ {
+ "pk": 4,
+ "model": "doc.ballottype",
+ "fields": {
+ "doc_type": "draft",
+ "used": true,
+ "name": "Approve",
+ "positions": [
+ "yes",
+ "noobj",
+ "discuss",
+ "abstain",
+ "recuse",
+ "norecord"
+ ],
+ "question": "",
+ "slug": "approve",
+ "order": 1
+ }
+ },
+ {
+ "pk": 2,
+ "model": "doc.ballottype",
+ "fields": {
+ "doc_type": "charter",
+ "used": true,
+ "name": "Ready w/o external review",
+ "positions": [
+ "yes",
+ "noobj",
+ "block",
+ "abstain",
+ "norecord"
+ ],
+ "question": "Is this charter ready for external review? Is this charter ready for approval without external review?",
+ "slug": "r-wo-ext",
+ "order": 2
+ }
+ },
+ {
+ "pk": 3,
+ "model": "doc.ballottype",
+ "fields": {
+ "doc_type": "charter",
+ "used": true,
+ "name": "Approve",
+ "positions": [
+ "yes",
+ "noobj",
+ "block",
+ "abstain",
+ "norecord"
+ ],
+ "question": "Do we approve of this charter?",
+ "slug": "approve",
+ "order": 3
+ }
+ }
+]
\ No newline at end of file
diff --git a/ietf/name/fixtures/names.xml b/ietf/name/fixtures/names.xml
deleted file mode 100644
index 93b326aa7..000000000
--- a/ietf/name/fixtures/names.xml
+++ /dev/null
@@ -1,2280 +0,0 @@
-
-
-
- Yes
-
- True
- 1
- False
-
-
- No Objection
-
- True
- 2
- False
-
-
- Discuss
-
- True
- 3
- True
-
-
- Block
-
- True
- 3
- True
-
-
- Abstain
-
- True
- 4
- False
-
-
- Recuse
-
- True
- 5
- False
-
-
- No Record
-
- True
- 6
- False
-
-
- Conflicts with
-
- True
- 0
-
-
- Conflicts with (secondary)
-
- True
- 0
-
-
- Conflicts with (tertiary)
-
- True
- 0
-
-
- Person must be present
-
- True
- 0
-
-
- Obsoletes
-
- True
- 0
- Obsoleted by
-
-
- Updates
-
- True
- 0
- Updated by
-
-
- Replaces
-
- True
- 0
- Replaced by
-
-
- conflict reviews
-
- True
- 0
- Conflict reviewed by
-
-
- Normative Reference
- Normative Reference
- True
- 0
- fixme
-
-
- Reference
- A reference found in a document which does not have split normative/informative reference sections.
- True
- 0
- fixme
-
-
- Informative Reference
- Informative Reference
- True
- 0
- fixme
-
-
- Moves to Proposed Standard
-
- True
- 0
- Moved to Proposed Standard by
-
-
- Moves to Internet Standard
-
- True
- 0
- Moved to Internet Standard by
-
-
- Moves to Historic
-
- True
- 0
- Moved to Historic by
-
-
- Moves to Informational
-
- True
- 0
- Moved to Informational by
-
-
- Moves to BCP
-
- True
- 0
- Moved to BCP by
-
-
- Moves to Experimental
-
- True
- 0
- Moved to Experimental by
-
-
- Possible Reference
- Reference of unknown type, likely found in the text of the document.
- True
- 3
- fixme
-
-
- Stream state should change
-
- True
- 0
-
-
- IANA coordination
- RFC-Editor/IANA Registration Coordination
- True
- 0
-
-
- Holding for references
- Holding for normative reference
- True
- 0
-
-
- Missing references
- Awaiting missing normative reference
- True
- 0
-
-
- Has errata
-
- True
- 0
-
-
- Review by RFC Editor
-
- True
- 0
-
-
- Via RFC Editor
-
- True
- 0
-
-
- Approved in minutes
-
- True
- 0
-
-
- Shepherd Needed
-
- True
- 0
-
-
- Waiting for Dependency on Other Document
-
- True
- 0
-
-
- IESG Review Completed
-
- True
- 0
-
-
- IANA
- The document has IANA actions that are not yet completed.
- True
- 0
-
-
- Revised I-D Needed - Issue raised by WG
-
- True
- 0
-
-
- Point Raised - writeup needed
- IESG discussions on the document have raised some issues that need to be brought to the attention of the authors/WG, but those issues have not been written down yet. (It is common for discussions during a telechat to result in such situations. An AD may raise a possible issue during a telechat and only decide as a result of that discussion whether the issue is worth formally writing up and bringing to the attention of the authors/WG). A document stays in the "Point Raised - Writeup Needed" state until *ALL* IESG comments that have been raised have been documented.
- True
- 1
-
-
- Awaiting Expert Review/Resolution of Issues Raised
-
- True
- 1
-
-
- Editor Needed
-
- True
- 1
-
-
- AD Followup
- A generic substate indicating that the shepherding AD has the action item to determine appropriate next steps. In particular, the appropriate steps (and the corresponding next state or substate) depend entirely on the nature of the issues that were raised and can only be decided with active involvement of the shepherding AD. Examples include:
-
-- if another AD raises an issue, the shepherding AD may first iterate with the other AD to get a better understanding of the exact issue. Or, the shepherding AD may attempt to argue that the issue is not serious enough to bring to the attention of the authors/WG.
-
-- if a documented issue is forwarded to a WG, some further iteration may be needed before it can be determined whether a new revision is needed or whether the WG response to an issue clarifies the issue sufficiently.
-
-- when a new revision appears, the shepherding AD will first look at the changes to determine whether they believe all outstanding issues have been raised satisfactorily, prior to asking the ADs who raised the original issues to verify the changes.
- True
- 2
-
-
- Awaiting External Review/Resolution of Issues Raised
-
- True
- 2
-
-
- Waiting for Partner Feedback
-
- True
- 2
-
-
- External Party
- The document is awaiting review or input from an external party (i.e, someone other than the shepherding AD, the authors, or the WG). See the "note" field for more details on who has the action.
- True
- 3
-
-
- Awaiting Merge with Other Document
-
- True
- 3
-
-
- Awaiting Reviews
-
- True
- 3
-
-
- Author or Editor Needed
-
- True
- 4
-
-
- Document Shepherd Followup
-
- True
- 4
-
-
- Revised I-D Needed
- An updated I-D is needed to address the issues that have been raised.
- True
- 5
-
-
- Waiting for Referenced Document
-
- True
- 5
-
-
- Waiting for Referencing Document
-
- True
- 6
-
-
- Revised I-D Needed - Issue raised by WGLC
-
- True
- 7
-
-
- Revised I-D Needed - Issue raised by AD
-
- True
- 8
-
-
- Revised I-D Needed - Issue raised by IESG
-
- True
- 9
-
-
- Doc Shepherd Follow-up Underway
-
- True
- 10
-
-
- Other - see Comment Log
-
- True
- 11
-
-
- Charter
-
- True
- 0
-
-
- Agenda
-
- True
- 0
-
-
- Minutes
-
- True
- 0
-
-
- Slides
-
- True
- 0
-
-
- Draft
-
- True
- 0
-
-
- Liaison Attachment
-
- True
- 0
-
-
- Conflict Review
-
- True
- 0
-
-
- Status Change
-
- True
- 0
-
-
- Active
-
- True
- 1
-
-
- Deleted
-
- True
- 2
-
-
- For review
-
- True
- 3
-
-
- Chartering/rechartering
-
- True
- 4
-
-
- BOF
-
- True
- 0
-
-
- Proposed
-
- True
- 0
-
-
- Active
-
- True
- 0
-
-
- Dormant
-
- True
- 0
-
-
- Concluded
-
- True
- 0
-
-
- Unknown
-
- True
- 0
-
-
- Abandonded
- Formation of the group (most likely a BoF or Proposed WG) was abandoned
- True
- 0
-
-
- BOF Concluded
-
- True
- 0
-
-
- IETF
-
- True
- 0
-
-
- Area
-
- True
- 0
-
-
- AG
- Area group
- True
- 0
-
-
- WG
- Working group
- True
- 0
-
-
- RG
- Research group
- True
- 0
-
-
- Team
-
- True
- 0
-
-
- Individual
-
- True
- 0
-
-
- SDO
- Standards organization
- True
- 0
-
-
- IRTF
-
- True
- 0
-
-
- RFC Editor
-
- True
- 0
-
-
- NomCom
-
- True
- 0
-
-
- Proposed Standard
-
- True
- 1
-
-
- Draft Standard
-
- False
- 2
-
-
- Internet Standard
-
- True
- 3
-
-
- Best Current Practice
-
- True
- 4
-
-
- Informational
-
- True
- 5
-
-
- Experimental
-
- True
- 6
-
-
- Historic
-
- True
- 7
-
-
- For action
-
- True
- 1
-
-
- For comment
-
- True
- 2
-
-
- For information
-
- True
- 3
-
-
- In response
-
- True
- 4
-
-
- Pending
-
- True
- 0
-
-
- Accepted
-
- True
- 0
-
-
- Declined
-
- True
- 0
-
-
- Comment
-
- True
- 0
-
-
- Questionnaire response
-
- True
- 0
-
-
- Nomination
-
- True
- 0
-
-
- Offtopic
-
- True
- 0
-
-
- reStructuredText
-
- True
- 0
-
-
- Plain
-
- True
- 0
-
-
- Django
-
- True
- 0
-
-
- IETF
-
- True
- 0
-
-
- Interim
-
- True
- 0
-
-
- Area Director
-
- True
- 0
-
-
- Incoming Area Director
-
- True
- 0
-
-
- Chair
-
- True
- 0
-
-
- Editor
-
- True
- 0
-
-
- Secretary
-
- True
- 0
-
-
- Tech Advisor
-
- True
- 0
-
-
- Executive Director
-
- True
- 0
-
-
- Administrative Director
-
- True
- 0
-
-
- Liaison Manager
-
- True
- 0
-
-
- Authorized Individual
-
- True
- 0
-
-
- Delegate
-
- True
- 0
-
-
- At Large Member
-
- True
- 0
-
-
- Member
-
- True
- 0
-
-
- Waiting for Scheduling
-
- True
- 0
-
-
- Waiting for Approval
-
- True
- 0
-
-
- Approved
-
- True
- 0
-
-
- Scheduled
-
- True
- 0
-
-
- Canceled
-
- True
- 0
-
-
- Disapproved
-
- True
- 0
-
-
- Not meeting
-
- True
- 0
-
-
- Deleted
-
- True
- 0
-
-
- Internet Standard
-
- True
- 0
-
-
- Draft Standard
-
- False
- 0
-
-
- Proposed Standard
-
- True
- 0
-
-
- Informational
-
- True
- 0
-
-
- Experimental
-
- True
- 0
-
-
- Best Current Practice
-
- True
- 0
-
-
- Historic
-
- True
- 0
-
-
- Unknown
-
- True
- 0
-
-
- IETF
- IETF stream
- True
- 1
-
-
- ISE
- Independent Submission Editor stream
- True
- 2
-
-
- IRTF
- Independent Submission Editor stream
- True
- 3
-
-
- IAB
- IAB stream
- True
- 4
-
-
- Legacy
- Legacy stream
- True
- 5
-
-
- Other
-
- True
- 0
-
-
- Session
-
- True
- 0
-
-
- Break
-
- True
- 0
-
-
- Registration
-
- True
- 0
-
-
- Plenary
-
- True
- 0
-
-
- State
-
-
- IESG state
-
-
- IANA state
-
-
- RFC Editor state
-
-
- IETF WG state
-
-
- IRTF state
-
-
- ISE state
-
-
- IAB state
-
-
- State
-
-
- State
-
-
- State
-
-
- State
-
-
- State
-
-
- Conflict Review State
-
-
- IANA Action state
-
-
- IANA Review state
-
-
- RFC Status Change
-
-
- agenda
- active
- Active
- True
-
- 1
-
-
-
- agenda
- deleted
- Deleted
- True
-
- 2
-
-
-
- charter
- notrev
- Not currently under review
- True
- The proposed charter is not being considered at this time. A proposed charter will remain in this state until an AD moves it to Informal IESG review.
- 0
-
-
-
- charter
- infrev
- Informal IESG review
- True
- This is the initial state when an AD proposes a new charter. The normal next state is Internal review if the idea is accepted, or Not currently under review if the idea is abandoned.
- 0
-
-
-
- charter
- intrev
- Internal review
- True
- The IESG and IAB are reviewing the early draft of the charter; this is the initial IESG and IAB review. The usual next state is External review if the idea is adopted, or Informal IESG review if the IESG decides the idea needs more work, or Not currently under review is the idea is abandoned
- 0
-
-
-
- charter
- extrev
- External review
- True
- The IETF community and possibly other standards development organizations (SDOs) are reviewing the proposed charter. The usual next state is IESG review, although it might move to Not currently under review is the idea is abandoned during the external review.
- 0
-
-
-
- charter
- iesgrev
- IESG review
- True
- The IESG is reviewing the discussion from the external review of the proposed charter. The usual next state is Approved, or Not currently under review if the idea is abandoned.
- 0
-
-
-
- charter
- approved
- Approved
- True
- The charter is approved by the IESG.
- 0
-
-
-
- conflrev
- needshep
- Needs Shepherd
- True
- A conflict review has been requested, but a shepherding AD has not yet been assigned
- 1
-
-
-
- conflrev
- adrev
- AD Review
- True
- The sponsoring AD is reviewing the document and preparing a proposed response
- 2
-
-
-
- conflrev
- iesgeval
- IESG Evaluation
- True
- The IESG is considering the proposed conflict review response
- 3
-
-
-
- conflrev
- defer
- IESG Evaluation - Defer
- True
- The evaluation of the proposed conflict review response has been deferred to the next telechat
- 4
-
-
-
- conflrev
- appr-reqnopub-pr
- Approved Request to Not Publish - point raised
- True
- The IESG has approved the conflict review response (a request to not publish), but a point has been raised that should be cleared before moving to announcement to be sent
- 5
-
-
-
- conflrev
- appr-noprob-pr
- Approved No Problem - point raised
- True
- The IESG has approved the conflict review response, but a point has been raised that should be cleared before proceeding to announcement to be sent
- 6
-
-
-
- conflrev
- appr-reqnopub-pend
- Approved Request to Not Publish - announcement to be sent
- True
- The IESG has approved the conflict review response (a request to not publish), but the secretariat has not yet sent the response
- 7
-
-
-
- conflrev
- appr-noprob-pend
- Approved No Problem - announcement to be sent
- True
- The IESG has approved the conflict review response, but the secretariat has not yet sent the response
- 8
-
-
-
- conflrev
- appr-reqnopub-sent
- Approved Request to Not Publish - announcement sent
- True
- The secretariat has delivered the IESG's approved conflict review response (a request to not publish) to the requester
- 9
-
-
-
- conflrev
- appr-noprob-sent
- Approved No Problem - announcement sent
- True
- The secretariat has delivered the IESG's approved conflict review response to the requester
- 10
-
-
-
- conflrev
- withdraw
- Withdrawn
- True
- The request for conflict review was withdrawn
- 11
-
-
-
- conflrev
- dead
- Dead
- True
- The conflict review has been abandoned
- 12
-
-
-
- draft
- active
- Active
- True
-
- 1
-
-
-
- draft
- expired
- Expired
- True
-
- 2
-
-
-
- draft
- rfc
- RFC
- True
-
- 3
-
-
-
- draft
- repl
- Replaced
- True
-
- 4
-
-
-
- draft
- auth-rm
- Withdrawn by Submitter
- True
-
- 5
-
-
-
- draft
- ietf-rm
- Withdrawn by IETF
- True
-
- 6
-
-
-
- draft-iana-action
- newdoc
- New Document
- True
- A new document has been received by IANA, but no actions have been taken
- 1
-
-
-
- draft-iana-action
- inprog
- In Progress
- True
- IANA is currently processing the actions for this document
- 2
-
-
-
- draft-iana-action
- waitauth
- Waiting on Authors
- True
- IANA is waiting on the document's authors to respond
- 3
-
-
-
- draft-iana-action
- waitad
- Waiting on ADs
- True
- IANA is waiting on the IETF Area Directors to respond
- 4
-
-
-
- draft-iana-action
- waitwgc
- Waiting on WGC
- True
- IANA is waiting on the IETF Working Group Chairs to respond
- 5
-
-
-
- draft-iana-action
- waitrfc
- Waiting on RFC Editor
- True
- IANA has notified the RFC Editor that the actions have been completed
- 6
-
-
-
- draft-iana-action
- rfcedack
- RFC-Ed-Ack
- True
- Request completed. The RFC Editor has acknowledged receipt of IANA's message that the actions have been completed
- 7
-
-
-
- draft-iana-action
- onhold
- On Hold
- True
- IANA has suspended work on the document
- 8
-
-
-
- draft-iana-action
- noic
- No IC
- True
- Request completed. There were no IANA actions for this document
- 9
-
-
-
- draft-iana-review
- need-rev
- IANA - Review Needed
- True
- Document has not yet been reviewed by IANA.
- 1
-
-
-
- draft-iana-review
- ok-act
- IANA OK - Actions Needed
- True
- Document requires IANA actions, and the IANA Considerations section indicates the details of the actions correctly.
- 2
-
-
-
- draft-iana-review
- ok-noact
- IANA OK - No Actions Needed
- True
- Document requires no IANA action, and the IANA Considerations section indicates this correctly.
- 3
-
-
-
- draft-iana-review
- not-ok
- IANA - Not OK
- True
- IANA has issues with the text of the IANA Considerations section of the document.
- 4
-
-
-
- draft-iana-review
- changed
- Version Changed - Review Needed
- True
- Document revision has changed after review by IANA.
- 5
-
-
-
- draft-iesg
- pub-req
- Publication Requested
- True
- A formal request has been made to advance/publish the document, following the procedures in Section 7.5 of RFC 2418. The request could be from a WG chair, from an individual through the RFC Editor, etc. (The Secretariat (iesg-secretary@ietf.org) is copied on these requests to ensure that the request makes it into the ID tracker.) A document in this state has not (yet) been reviewed by an AD nor has any official action been taken on it yet (other than to note that its publication has been requested.
- 10
-
-
-
- draft-iesg
- ad-eval
- AD Evaluation
- True
- A specific AD (e.g., the Area Advisor for the WG) has begun reviewing the document to verify that it is ready for advancement. The shepherding AD is responsible for doing any necessary review before starting an IETF Last Call or sending the document directly to the IESG as a whole.
- 11
-
-
-
- draft-iesg
- review-e
- Expert Review
- True
- An AD sometimes asks for an external review by an outside party as part of evaluating whether a document is ready for advancement. MIBs, for example, are reviewed by the "MIB doctors". Other types of reviews may also be requested (e.g., security, operations impact, etc.). Documents stay in this state until the review is complete and possibly until the issues raised in the review are addressed. See the "note" field for specific details on the nature of the review.
- 12
-
-
-
- draft-iesg
- lc-req
- Last Call Requested
- True
- The AD has requested that the Secretariat start an IETF Last Call, but the the actual Last Call message has not been sent yet.
- 15
-
-
-
- draft-iesg
- lc
- In Last Call
- True
- The document is currently waiting for IETF Last Call to complete. Last Calls for WG documents typically last 2 weeks, those for individual submissions last 4 weeks.
- 16
-
-
-
- draft-iesg
- writeupw
- Waiting for Writeup
- True
- Before a standards-track or BCP document is formally considered by the entire IESG, the AD must write up a protocol action. The protocol action is included in the approval message that the Secretariat sends out when the document is approved for publication as an RFC.
- 18
-
-
-
- draft-iesg
- goaheadw
- Waiting for AD Go-Ahead
- True
- As a result of the IETF Last Call, comments may need to be responded to and a revision of the ID may be needed as well. The AD is responsible for verifying that all Last Call comments have been adequately addressed and that the (possibly revised) document is in the ID directory and ready for consideration by the IESG as a whole.
- 19
-
-
-
- draft-iesg
- iesg-eva
- IESG Evaluation
- True
- The document is now (finally!) being formally reviewed by the entire IESG. Documents are discussed in email or during a bi-weekly IESG telechat. In this phase, each AD reviews the document and airs any issues they may have. Unresolvable issues are documented as "discuss" comments that can be forwarded to the authors/WG. See the description of substates for additional details about the current state of the IESG discussion.
- 20
-
-
-
- draft-iesg
- defer
- IESG Evaluation - Defer
- True
- During a telechat, one or more ADs requested an additional 2 weeks to review the document. A defer is designed to be an exception mechanism, and can only be invoked once, the first time the document comes up for discussion during a telechat.
- 21
-
-
-
- draft-iesg
- approved
- Approved-announcement to be sent
- True
- The IESG has approved the document for publication, but the Secretariat has not yet sent out on official approval message.
- 27
-
-
-
- draft-iesg
- ann
- Approved-announcement sent
- True
- The IESG has approved the document for publication, and the Secretariat has sent out the official approval message to the RFC editor.
- 30
-
-
-
- draft-iesg
- rfcqueue
- RFC Ed Queue
- True
- The document is in the RFC editor Queue (as confirmed by http://www.rfc-editor.org/queue.html).
- 31
-
-
-
- draft-iesg
- pub
- RFC Published
- True
- The ID has been published as an RFC.
- 32
-
-
-
- draft-iesg
- nopubadw
- DNP-waiting for AD note
- True
- Do Not Publish: The IESG recommends against publishing the document, but the writeup explaining its reasoning has not yet been produced. DNPs apply primarily to individual submissions received through the RFC editor. See the "note" field for more details on who has the action item.
- 33
-
-
-
- draft-iesg
- nopubanw
- DNP-announcement to be sent
- True
- The IESG recommends against publishing the document, the writeup explaining its reasoning has been produced, but the Secretariat has not yet sent out the official "do not publish" recommendation message.
- 34
-
-
-
- draft-iesg
- watching
- AD is watching
- True
- An AD is aware of the document and has chosen to place the document in a separate state in order to keep a closer eye on it (for whatever reason). Documents in this state are still not being actively tracked in the sense that no formal request has been made to publish or advance the document. The sole difference between this state and "I-D Exists" is that an AD has chosen to put it in a separate state, to make it easier to keep track of (for the AD's own reasons).
- 42
-
-
-
- draft-iesg
- dead
- Dead
- True
- Document is "dead" and is no longer being tracked. (E.g., it has been replaced by another document with a different name, it has been withdrawn, etc.)
- 99
-
-
-
- draft-rfceditor
- auth
- AUTH
- True
- Awaiting author action
- 0
-
-
-
- draft-rfceditor
- auth48
- AUTH48
- True
- Awaiting final author approval
- 0
-
-
-
- draft-rfceditor
- edit
- EDIT
- False
- Approved by the stream manager (e.g., IESG, IAB, IRSG, ISE), awaiting processing and publishing
- 0
-
-
-
- draft-rfceditor
- iana
- IANA
- True
- Document has been edited, but is holding for completion of IANA actions
- 0
-
-
-
- draft-rfceditor
- iesg
- IESG
- False
- Awaiting IESG action
- 0
-
-
-
- draft-rfceditor
- isr
- ISR
- True
- Independent Submission Review by the ISE
- 0
-
-
-
- draft-rfceditor
- isr-auth
- ISR-AUTH
- False
- Independent submission awaiting author action, or in discussion between author and ISE
- 0
-
-
-
- draft-rfceditor
- ref
- REF
- True
- Holding for normative reference
- 0
-
-
-
- draft-rfceditor
- rfc-edit
- RFC-EDITOR
- True
- Awaiting final RFC Editor review before AUTH48
- 0
-
-
-
- draft-rfceditor
- timeout
- TO
- True
- Time-out period during which the IESG reviews document for conflict/concurrence with other IETF working group work
- 0
-
-
-
- draft-rfceditor
- missref
- MISSREF
- True
- Awaiting missing normative reference
- 0
-
-
-
- draft-rfceditor
- auth48done
- AUTH48-DONE
- False
- Final approvals are complete
- 0
-
-
-
- draft-rfceditor
- auth48-done
- AUTH48-DONE
- True
- Final approvals are complete
- 0
-
-
-
- draft-rfceditor
- edit
- EDIT
- True
- Approved by the stream manager (e.g., IESG, IAB, IRSG, ISE), awaiting processing and publishing
- 0
-
-
-
- draft-rfceditor
- iana-crd
- IANA
- True
- RFC-Editor/IANA Registration Coordination
- 0
-
-
-
- draft-rfceditor
- iesg
- IESG
- True
- Holding for IESG action
- 0
-
-
-
- draft-rfceditor
- isr-auth
- ISR-AUTH
- True
- Independent Submission awaiting author update, or in discussion between author and ISE
- 0
-
-
-
- draft-stream-iab
- candidat
- Candidate IAB Document
- True
- A document being considered for the IAB stream.
- 1
-
-
-
- draft-stream-iab
- active
- Active IAB Document
- True
- This document has been adopted by the IAB and is being actively developed.
- 2
-
-
-
- draft-stream-iab
- parked
- Parked IAB Document
- True
- This document has lost its author or editor, is waiting for another document to be written, or cannot currently be worked on by the IAB for some other reason. Annotations probably explain why this document is parked.
- 3
-
-
-
- draft-stream-iab
- review-i
- IAB Review
- True
- This document is awaiting the IAB itself to come to internal consensus.
- 4
-
-
-
- draft-stream-iab
- review-c
- Community Review
- True
- This document has completed internal consensus within the IAB and is now under community review.
- 5
-
-
-
- draft-stream-iab
- approved
- Approved by IAB, To Be Sent to RFC Editor
- True
- The consideration of this document is complete, but it has not yet been sent to the RFC Editor for publication (although that is going to happen soon).
- 6
-
-
-
- draft-stream-iab
- diff-org
- Sent to a Different Organization for Publication
- True
- The IAB does not expect to publish the document itself, but has passed it on to a different organization that might continue work on the document. The expectation is that the other organization will eventually publish the document.
- 7
-
-
-
- draft-stream-iab
- rfc-edit
- Sent to the RFC Editor
- True
- The IAB processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the IAB.
- 8
-
-
-
- draft-stream-iab
- pub
- Published RFC
- True
- The document has been published as an RFC.
- 9
-
-
-
- draft-stream-iab
- dead
- Dead IAB Document
- True
- This document was an active IAB document, but for some reason it is no longer being pursued for the IAB stream. It is possible that the document might be revived later, possibly in another stream.
- 10
-
-
-
- draft-stream-ietf
- c-adopt
- Call For Adoption By WG Issued
- True
- <a href="http://tools.ietf.org/html/rfc6174#section-4.2.1" target="_blank">4.2.1. Call for Adoption by WG Issued</a>
-
- The "Call for Adoption by WG Issued" state should be used to indicate when an I-D is being considered for adoption by an IETF WG. An I-D that is in this state is actively being considered for adoption and has not yet achieved consensus, preference, or selection in the WG.
-
- This state may be used to describe an I-D that someone has asked a WG to consider for adoption, if the WG Chair has agreed with the request. This state may also be used to identify an I-D that a WG Chair asked an author to write specifically for consideration as a candidate WG item [WGDTSPEC], and/or an I-D that is listed as a 'candidate draft' in the WG's charter.
-
- Under normal conditions, it should not be possible for an I-D to be in the "Call for Adoption by WG Issued" state in more than one working group at the same time. This said, it is not uncommon for authors to "shop" their I-Ds to more than one WG at a time, with the hope of getting their documents adopted somewhere.
-
- After this state is implemented in the Datatracker, an I-D that is in the "Call for Adoption by WG Issued" state will not be able to be "shopped" to any other WG without the consent of the WG Chairs and the responsible ADs impacted by the shopping.
-
- Note that Figure 1 includes an arc leading from this state to outside of the WG state machine. This illustrates that some I-Ds that are considered do not get adopted as WG drafts. An I-D that is not adopted as a WG draft will transition out of the WG state machine and revert back to having no stream-specific state; however, the status change history log of the I-D will record that the I-D was previously in the "Call for Adoption by WG Issued" state.
- 1
-
-
-
- draft-stream-ietf
- adopt-wg
- Adopted by a WG
- True
- <a href="http://tools.ietf.org/html/rfc6174#section-4.2.2" target="_blank">4.2.2. Adopted by a WG</a>
-
- The "Adopted by a WG" state describes an individual submission I-D that an IETF WG has agreed to adopt as one of its WG drafts.
-
- WG Chairs who use this state will be able to clearly indicate when their WGs adopt individual submission I-Ds. This will facilitate the Datatracker's ability to correctly capture "Replaces" information for WG drafts and correct "Replaced by" information for individual submission I-Ds that have been replaced by WG drafts.
-
- This state is needed because the Datatracker uses the filename of an I-D as a key to search its database for status information about the I-D, and because the filename of a WG I-D is supposed to be different from the filename of an individual submission I-D. The filename of an individual submission I-D will typically be formatted as 'draft-author-wgname-topic-nn'.
-
- The filename of a WG document is supposed to be formatted as 'draft- ietf-wgname-topic-nn'.
-
- An individual I-D that is adopted by a WG may take weeks or months to be resubmitted by the author as a new (version-00) WG draft. If the "Adopted by a WG" state is not used, the Datatracker has no way to determine that an I-D has been adopted until a new version of the I-D is submitted to the WG by the author and until the I-D is approved for posting by a WG Chair.
- 2
-
-
-
- draft-stream-ietf
- info
- Adopted for WG Info Only
- True
- <a href="http://tools.ietf.org/html/rfc6174#section-4.2.3" target="_blank">4.2.3. Adopted for WG Info Only</a>
-
- The "Adopted for WG Info Only" state describes a document that contains useful information for the WG that adopted it, but the document is not intended to be published as an RFC. The WG will not actively develop the contents of the I-D or progress it for publication as an RFC. The only purpose of the I-D is to provide information for internal use by the WG.
- 3
-
-
-
- draft-stream-ietf
- wg-doc
- WG Document
- True
- <a href="http://tools.ietf.org/html/rfc6174#section-4.2.4" target="_blank">4.2.4. WG Document</a>
-
- The "WG Document" state describes an I-D that has been adopted by an IETF WG and is being actively developed.
-
- A WG Chair may transition an I-D into the "WG Document" state at any time as long as the I-D is not being considered or developed in any other WG.
-
- Alternatively, WG Chairs may rely upon new functionality to be added to the Datatracker to automatically move version-00 drafts into the "WG Document" state as described in Section 4.1.
-
- Under normal conditions, it should not be possible for an I-D to be in the "WG Document" state in more than one WG at a time. This said, I-Ds may be transferred from one WG to another with the consent of the WG Chairs and the responsible ADs.
- 4
-
-
-
- draft-stream-ietf
- parked
- Parked WG Document
- True
- <a href="http://tools.ietf.org/html/rfc6174#section-4.2.5" target="_blank">4.2.5. Parked WG Document</a>
-
- A "Parked WG Document" is an I-D that has lost its author or editor, is waiting for another document to be written or for a review to be completed, or cannot be progressed by the working group for some other reason.
-
- Some of the annotation tags described in Section 4.3 may be used in conjunction with this state to indicate why an I-D has been parked, and/or what may need to happen for the I-D to be un-parked.
-
- Parking a WG draft will not prevent it from expiring; however, this state can be used to indicate why the I-D has stopped progressing in the WG.
-
- A "Parked WG Document" that is not expired may be transferred from one WG to another with the consent of the WG Chairs and the responsible ADs.
- 5
-
-
-
- draft-stream-ietf
- dead
- Dead WG Document
- True
- <a href="http://tools.ietf.org/html/rfc6174#section-4.2.6" target="_blank">4.2.6. Dead WG Document</a>
-
- A "Dead WG Document" is an I-D that has been abandoned. Note that 'Dead' is not always a final state for a WG I-D. If consensus is subsequently achieved, a "Dead WG Document" may be resurrected. A "Dead WG Document" that is not resurrected will eventually expire.
-
- Note that an I-D that is declared to be "Dead" in one WG and that is not expired may be transferred to a non-dead state in another WG with the consent of the WG Chairs and the responsible ADs.
- 6
-
-
-
- draft-stream-ietf
- wg-lc
- In WG Last Call
- True
- <a href="http://tools.ietf.org/html/rfc6174#section-4.2.7" target="_blank">4.2.7. In WG Last Call</a>
-
- A document "In WG Last Call" is an I-D for which a WG Last Call (WGLC) has been issued and is in progress.
-
- Note that conducting a WGLC is an optional part of the IETF WG process, per Section 7.4 of RFC 2418 [RFC2418].
-
- If a WG Chair decides to conduct a WGLC on an I-D, the "In WG Last Call" state can be used to track the progress of the WGLC. The Chair may configure the Datatracker to send a WGLC message to one or more mailing lists when the Chair moves the I-D into this state. The WG Chair may also be able to select a different set of mailing lists for a different document undergoing a WGLC; some documents may deserve coordination with other WGs.
-
- A WG I-D in this state should remain "In WG Last Call" until the WG Chair moves it to another state. The WG Chair may configure the Datatracker to send an e-mail after a specified period of time to remind or 'nudge' the Chair to conclude the WGLC and to determine the next state for the document.
-
- It is possible for one WGLC to lead into another WGLC for the same document. For example, an I-D that completed a WGLC as an "Informational" document may need another WGLC if a decision is taken to convert the I-D into a Standards Track document.
- 7
-
-
-
- draft-stream-ietf
- chair-w
- Waiting for WG Chair Go-Ahead
- True
- <a href="http://tools.ietf.org/html/rfc6174#section-4.2.8" target="_blank">4.2.8. Waiting for WG Chair Go-Ahead</a>
-
- A WG Chair may wish to place an I-D that receives a lot of comments during a WGLC into the "Waiting for WG Chair Go-Ahead" state. This state describes an I-D that has undergone a WGLC; however, the Chair is not yet ready to call consensus on the document.
-
- If comments from the WGLC need to be responded to, or a revision to the I-D is needed, the Chair may place an I-D into this state until all of the WGLC comments are adequately addressed and the (possibly revised) document is in the I-D repository.
- 8
-
-
-
- draft-stream-ietf
- writeupw
- WG Consensus: Waiting for Write-Up
- True
- <a href="http://tools.ietf.org/html/rfc6174#section-4.2.9" target="_blank">4.2.9. WG Consensus: Waiting for Writeup</a>
-
- A document in the "WG Consensus: Waiting for Writeup" state has essentially completed its development within the working group, and is nearly ready to be sent to the IESG for publication. The last thing to be done is the preparation of a protocol writeup by a Document Shepherd. The IESG requires that a document shepherd writeup be completed before publication of the I-D is requested. The IETF document shepherding process and the role of a WG Document Shepherd is described in RFC 4858 [RFC4858]
-
- A WG Chair may call consensus on an I-D without a formal WGLC and transition an I-D that was in the "WG Document" state directly into this state.
-
- The name of this state includes the words "Waiting for Writeup" because a good document shepherd writeup takes time to prepare.
- 9
-
-
-
- draft-stream-ietf
- sub-pub
- Submitted to IESG for Publication
- True
- <a href="http://tools.ietf.org/html/rfc6174#section-4.2.10" target="_blank">4.2.10. Submitted to IESG for Publication</a>
-
- This state describes a WG document that has been submitted to the IESG for publication and that has not been sent back to the working group for revision.
-
- An I-D in this state may be under review by the IESG, it may have been approved and be in the RFC Editor's queue, or it may have been published as an RFC. Other possibilities exist too. The document may be "Dead" (in the IESG state machine) or in a "Do Not Publish" state.
- 10
-
-
-
- draft-stream-irtf
- candidat
- Candidate RG Document
- True
- This document is under consideration in an RG for becoming an IRTF document. A document in this state does not imply any RG consensus and does not imply any precedence or selection. It's simply a way to indicate that somebody has asked for a document to be considered for adoption by an RG.
- 1
-
-
-
- draft-stream-irtf
- active
- Active RG Document
- True
- This document has been adopted by the RG and is being actively developed.
- 2
-
-
-
- draft-stream-irtf
- parked
- Parked RG Document
- True
- This document has lost its author or editor, is waiting for another document to be written, or cannot currently be worked on by the RG for some other reason.
- 3
-
-
-
- draft-stream-irtf
- rg-lc
- In RG Last Call
- True
- The document is in its final review in the RG.
- 4
-
-
-
- draft-stream-irtf
- sheph-w
- Waiting for Document Shepherd
- True
- IRTF documents have document shepherds who help RG documents through the process after the RG has finished with the document.
- 5
-
-
-
- draft-stream-irtf
- chair-w
- Waiting for IRTF Chair
- True
- The IRTF Chair is meant to be performing some task such as sending a request for IESG Review.
- 6
-
-
-
- draft-stream-irtf
- irsg-w
- Awaiting IRSG Reviews
- True
- The document shepherd has taken the document to the IRSG and solicited reviews from one or more IRSG members.
- 7
-
-
-
- draft-stream-irtf
- irsgpoll
- In IRSG Poll
- True
- The IRSG is taking a poll on whether or not the document is ready to be published.
- 8
-
-
-
- draft-stream-irtf
- iesg-rev
- In IESG Review
- True
- The IRSG has asked the IESG to do a review of the document, as described in RFC5742.
- 9
-
-
-
- draft-stream-irtf
- rfc-edit
- Sent to the RFC Editor
- True
- The RG processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the RG.
- 10
-
-
-
- draft-stream-irtf
- pub
- Published RFC
- True
- The document has been published as an RFC.
- 11
-
-
-
- draft-stream-irtf
- iesghold
- Document on Hold Based On IESG Request
- True
- The IESG has requested that the document be held pending further review, as specified in RFC 5742, and the IRTF has agreed to such a hold.
- 12
-
-
-
- draft-stream-irtf
- dead
- Dead IRTF Document
- True
- This document was an active IRTF document, but for some reason it is no longer being pursued for the IRTF stream. It is possible that the document might be revived later, possibly in another stream.
- 13
-
-
-
- draft-stream-ise
- receive
- Submission Received
- True
- The draft has been sent to the ISE with a request for publication.
- 1
-
-
-
- draft-stream-ise
- find-rev
- Finding Reviewers
- True
- The ISE is finding initial reviewers for the document.
- 2
-
-
-
- draft-stream-ise
- ise-rev
- In ISE Review
- True
- The ISE is actively working on the document.
- 3
-
-
-
- draft-stream-ise
- need-res
- Response to Review Needed
- True
- One or more reviews have been sent to the author, and the ISE is awaiting response.
- 4
-
-
-
- draft-stream-ise
- iesg-rev
- In IESG Review
- True
- The ISE has asked the IESG to do a review of the document, as described in RFC5742.
- 5
-
-
-
- draft-stream-ise
- rfc-edit
- Sent to the RFC Editor
- True
- The ISE processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the ISE.
- 6
-
-
-
- draft-stream-ise
- pub
- Published RFC
- True
- The document has been published as an RFC.
- 7
-
-
-
- draft-stream-ise
- dead
- No Longer In Independent Submission Stream
- True
- This document was actively considered in the Independent Submission stream, but the ISE chose not to publish it. It is possible that the document might be revived later. A document in this state may have a comment explaining the reasoning of the ISE (such as if the document was going to move to a different stream).
- 8
-
-
-
- draft-stream-ise
- iesghold
- Document on Hold Based On IESG Request
- True
- The IESG has requested that the document be held pending further review, as specified in RFC 5742, and the ISE has agreed to such a hold.
- 9
-
-
-
- minutes
- active
- Active
- True
-
- 1
-
-
-
- minutes
- deleted
- Deleted
- True
-
- 2
-
-
-
- slides
- active
- Active
- True
-
- 1
-
-
-
- slides
- deleted
- Deleted
- True
-
- 2
-
-
-
- statchg
- needshep
- Needs Shepherd
- True
- An RFC status change has been requested, but a shepherding AD has not yet been assigned
- 1
-
-
-
- statchg
- adrev
- AD Review
- True
- The sponsoring AD is preparing an RFC status change document
- 2
-
-
-
- statchg
- lc-req
- Last Call Requested
- True
- Last Call has been requested for this proposed status change
- 3
-
-
-
- statchg
- in-lc
- In Last Call
- True
- This proposed status change is in IETF Last Call
- 4
-
-
-
- statchg
- goahead
- Waiting for AD Go-Ahead
- True
- The AD is following up on IETF LC comments
- 5
-
-
-
- statchg
- iesgeval
- IESG Evaluation
- True
- The IESG is considering the proposed RFC status changes
- 6
-
-
-
- statchg
- defer
- IESG Evaluation - Defer
- True
- The evaluation of the proposed RFC status changes have been deferred to the next telechat
- 7
-
-
-
- statchg
- appr-pr
- Approved - point raised
- True
- The IESG has approved the RFC status changes, but a point has been raised that should be cleared before proceeding to announcement to be sent
- 8
-
-
-
- statchg
- appr-pend
- Approved - announcement to be sent
- True
- The IESG has approved the RFC status changes, but the secretariat has not yet sent the announcement
- 9
-
-
-
- statchg
- appr-sent
- Approved - announcement sent
- True
- The secretariat has announced the IESG's approved RFC status changes
- 10
-
-
-
- statchg
- dead
- Dead
- True
- The RFC status changes have been abandoned
- 11
-
-
-
- conflrev
- conflrev
- Approve
- Is this the correct conflict review response?
- True
- 0
-
-
-
- statchg
- statchg
- Approve
- Do we approve these RFC status changes?
- True
- 0
-
-
-
- charter
- r-extrev
- Ready for external review
- Is this charter ready for external review?
- True
- 1
-
-
-
- draft
- approve
- Approve
-
- True
- 1
-
-
-
- charter
- r-wo-ext
- Ready w/o external review
- Is this charter ready for external review? Is this charter ready for approval without external review?
- True
- 2
-
-
-
- charter
- approve
- Approve
- Do we approve of this charter?
- True
- 3
-
-
-
diff --git a/ietf/name/generate_fixtures.py b/ietf/name/generate_fixtures.py
index c4b8bed35..144f17475 100644
--- a/ietf/name/generate_fixtures.py
+++ b/ietf/name/generate_fixtures.py
@@ -17,8 +17,8 @@ from django.db.models import Q
def output(name, qs):
try:
- f = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures/%s.xml" % name), 'w')
- f.write(serialize("xml", qs, indent=4))
+ f = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures/%s.json" % name), 'w')
+ f.write(serialize("json", qs, indent=1))
f.close()
except:
from django.db import connection
@@ -29,12 +29,13 @@ def output(name, qs):
# pick all name models directly out of the module
objects = []
+import inspect
import ietf.name.models
for n in dir(ietf.name.models):
- if n[:1].upper() == n[:1] and n.endswith("Name"):
- model = getattr(ietf.name.models, n)
- if not model._meta.abstract:
- objects.extend(model.objects.all())
+ symbol = getattr(ietf.name.models, n)
+ if inspect.isclass(symbol) and issubclass(symbol, ietf.name.models.NameModel):
+ if not symbol._meta.abstract:
+ objects.extend(symbol.objects.all())
import ietf.doc.models # also pick some other name-like types while we're at it
diff --git a/ietf/name/migrations/0018_auto__add_draftsubmissionstatename.py b/ietf/name/migrations/0018_auto__add_draftsubmissionstatename.py
new file mode 100644
index 000000000..c1891f69b
--- /dev/null
+++ b/ietf/name/migrations/0018_auto__add_draftsubmissionstatename.py
@@ -0,0 +1,214 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'DraftSubmissionStateName'
+ db.create_table('name_draftsubmissionstatename', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['DraftSubmissionStateName'])
+
+ # Adding M2M table for field next_states on 'DraftSubmissionStateName'
+ db.create_table('name_draftsubmissionstatename_next_states', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('from_draftsubmissionstatename', models.ForeignKey(orm['name.draftsubmissionstatename'], null=False)),
+ ('to_draftsubmissionstatename', models.ForeignKey(orm['name.draftsubmissionstatename'], null=False))
+ ))
+ db.create_unique('name_draftsubmissionstatename_next_states', ['from_draftsubmissionstatename_id', 'to_draftsubmissionstatename_id'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'DraftSubmissionStateName'
+ db.delete_table('name_draftsubmissionstatename')
+
+ # Removing M2M table for field next_states on 'DraftSubmissionStateName'
+ db.delete_table('name_draftsubmissionstatename_next_states')
+
+
+ models = {
+ 'name.ballotpositionname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
+ 'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.constraintname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'},
+ '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'}),
+ 'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.dbtemplatetypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DBTemplateTypeName'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.docrelationshipname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
+ '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'}),
+ 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.docremindertypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.draftsubmissionstatename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': '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': "orm['name.DraftSubmissionStateName']"}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.feedbacktype': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'FeedbackType'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.groupmilestonestatename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'GroupMilestoneStateName'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.meetingtypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.nomineepositionstate': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionState'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.rolename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'RoleName'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.sessionstatusname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.timeslottypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ }
+ }
+
+ complete_apps = ['name']
diff --git a/ietf/name/migrations/0019_populate_draftsubmissionstate.py b/ietf/name/migrations/0019_populate_draftsubmissionstate.py
new file mode 100644
index 000000000..0d91bfa02
--- /dev/null
+++ b/ietf/name/migrations/0019_populate_draftsubmissionstate.py
@@ -0,0 +1,204 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ uploaded, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="uploaded", order=1, name="Uploaded")
+ submitter, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="auth", order=2, name="Awaiting Submitter Authentication")
+ author_approval, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="aut-appr", order=3, name="Awaiting Approval from Previous Version Authors'")
+ group_approval, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="grp-appr", order=4, name="Awaiting Initial Version Approval")
+ manual_post, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="manual", order=5, name="Awaiting Manual Post")
+ cancel, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="cancel", order=6, name="Cancelled")
+ posted, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="posted", order=7, name="Posted")
+
+ uploaded.next_states = [submitter, author_approval, group_approval, manual_post, cancel]
+ submitter.next_states = [cancel, posted]
+ author_approval.next_states = [cancel, posted]
+ group_approval.next_states = [cancel, posted]
+ manual_post.next_states = [cancel, posted]
+
+
+ def backwards(self, orm):
+ "Write your backwards methods here."
+
+
+ models = {
+ 'name.ballotpositionname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
+ 'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.constraintname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'},
+ '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'}),
+ 'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.dbtemplatetypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DBTemplateTypeName'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.docrelationshipname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
+ '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'}),
+ 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.docremindertypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.draftsubmissionstatename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': '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': "orm['name.DraftSubmissionStateName']"}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.feedbacktype': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'FeedbackType'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.groupmilestonestatename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'GroupMilestoneStateName'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.meetingtypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.nomineepositionstate': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionState'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.rolename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'RoleName'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.sessionstatusname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.timeslottypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'},
+ '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': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ }
+ }
+
+ complete_apps = ['name']
diff --git a/ietf/name/models.py b/ietf/name/models.py
index 0f97aaa14..7403256d1 100644
--- a/ietf/name/models.py
+++ b/ietf/name/models.py
@@ -66,3 +66,8 @@ class FeedbackType(NameModel):
"""Type of feedback: questionnaires, nominations, comments"""
class DBTemplateTypeName(NameModel):
"""reStructuredText, Plain, Django"""
+class DraftSubmissionStateName(NameModel):
+ """Uploaded, Awaiting Submitter Authentication, Awaiting Approval from
+ Previous Version Authors, Awaiting Initial Version Approval, Awaiting
+ Manual Post, Cancelled, Posted"""
+ next_states = models.ManyToManyField('DraftSubmissionStateName', related_name="previous_states", blank=True)
diff --git a/ietf/nomcom/models.py b/ietf/nomcom/models.py
index 410a20cee..ff6d8c8f4 100644
--- a/ietf/nomcom/models.py
+++ b/ietf/nomcom/models.py
@@ -1,4 +1,4 @@
-# -*- coding: utf-8 -*-
+# -*- coding: utf-8 -*-
import os
from django.db import models
diff --git a/ietf/nomcom/templatetags/nomcom_tags.py b/ietf/nomcom/templatetags/nomcom_tags.py
index 29a640492..97aa4ee67 100644
--- a/ietf/nomcom/templatetags/nomcom_tags.py
+++ b/ietf/nomcom/templatetags/nomcom_tags.py
@@ -25,7 +25,7 @@ def is_chair(user, year):
nomcom = get_nomcom_by_year(year=year)
if has_role(user, "Secretariat"):
return True
- return nomcom.group.is_chair(user)
+ return nomcom.group.has_role(user, "chair")
@register.filter
diff --git a/ietf/nomcom/tests.py b/ietf/nomcom/tests.py
index 73061570e..864ba0ae8 100644
--- a/ietf/nomcom/tests.py
+++ b/ietf/nomcom/tests.py
@@ -30,7 +30,7 @@ from ietf.nomcom.management.commands.send_reminders import Command, is_time_to_s
class NomcomViewsTest(TestCase):
"""Tests to create a new nomcom"""
# See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names', 'nomcom_templates']
+ perma_fixtures = ['nomcom_templates']
def check_url_status(self, url, status):
response = self.client.get(url)
@@ -595,7 +595,7 @@ class NomcomViewsTest(TestCase):
class NomineePositionStateSaveTest(TestCase):
"""Tests for the NomineePosition save override method"""
# See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names', 'nomcom_templates']
+ perma_fixtures = ['nomcom_templates']
def setUp(self):
nomcom_test_data()
@@ -627,7 +627,7 @@ class NomineePositionStateSaveTest(TestCase):
class FeedbackTest(TestCase):
- perma_fixtures = ['names', 'nomcom_templates']
+ perma_fixtures = ['nomcom_templates']
def setUp(self):
nomcom_test_data()
@@ -659,7 +659,7 @@ class FeedbackTest(TestCase):
os.unlink(self.cert_file.name)
class ReminderTest(TestCase):
- perma_fixtures = ['names', 'nomcom_templates']
+ perma_fixtures = ['nomcom_templates']
def setUp(self):
nomcom_test_data()
diff --git a/ietf/nomcom/utils.py b/ietf/nomcom/utils.py
index 152da53c2..c2deffd9a 100644
--- a/ietf/nomcom/utils.py
+++ b/ietf/nomcom/utils.py
@@ -84,18 +84,6 @@ def get_user_email(user):
pass
return user._email_cache
-def is_nomcom_member(user, nomcom):
- is_group_member = nomcom.group.is_member(user)
- if not is_group_member:
- raise PermissionDenied("Must be nomcom member")
-
-
-def is_nomcom_chair(user, nomcom):
- is_group_chair = nomcom.group.is_chair(user)
- if not is_group_chair:
- raise PermissionDenied("Must be nomcom chair")
-
-
def get_hash_nominee_position(date, nominee_position_id):
return hashlib.md5('%s%s%s' % (settings.SECRET_KEY, date, nominee_position_id)).hexdigest()
diff --git a/ietf/nomcom/views.py b/ietf/nomcom/views.py
index 05381d07d..d6632c3ad 100644
--- a/ietf/nomcom/views.py
+++ b/ietf/nomcom/views.py
@@ -1,4 +1,4 @@
- # -*- coding: utf-8 -*-
+ # -*- coding: utf-8 -*-
import datetime
@@ -101,7 +101,7 @@ def private_key(request, year):
def private_index(request, year):
nomcom = get_nomcom_by_year(year)
all_nominee_positions = NomineePosition.objects.get_by_nomcom(nomcom).not_duplicated()
- is_chair = nomcom.group.is_chair(request.user)
+ is_chair = nomcom.group.has_role(request.user, "chair")
message = None
if is_chair and request.method == 'POST':
action = request.POST.get('action')
diff --git a/ietf/secr/announcement/forms.py b/ietf/secr/announcement/forms.py
index eca264aac..0dab4343a 100644
--- a/ietf/secr/announcement/forms.py
+++ b/ietf/secr/announcement/forms.py
@@ -7,7 +7,6 @@ from ietf.secr.utils.group import current_nomcom
from ietf.message.models import Message
from ietf.ietfauth.decorators import has_role
-from ietf.wgchairs.accounts import get_person_for_user
# ---------------------------------------------
# Globals
@@ -185,7 +184,7 @@ class AnnounceForm(forms.ModelForm):
def save(self, *args, **kwargs):
user = kwargs.pop('user')
message = super(AnnounceForm, self).save(commit=False)
- message.by = get_person_for_user(user)
+ message.by = user.get_profile()
if self.cleaned_data['to'] == 'Other...':
message.to = self.cleaned_data['to_custom']
if kwargs['commit']:
diff --git a/ietf/secr/announcement/tests.py b/ietf/secr/announcement/tests.py
index 1884843f3..89fcf6c00 100644
--- a/ietf/secr/announcement/tests.py
+++ b/ietf/secr/announcement/tests.py
@@ -22,9 +22,6 @@ AD_USER=''
class MainTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
# ------- Test View -------- #
def test_main(self):
"Main Test"
@@ -37,8 +34,6 @@ class DummyCase(TestCase):
name = connection.settings_dict['NAME']
class UnauthorizedCase(TestCase):
- perma_fixtures = ['names']
-
def test_unauthorized(self):
"Unauthorized Test"
draft = make_test_data()
@@ -48,8 +43,6 @@ class UnauthorizedCase(TestCase):
self.assertEquals(r.status_code, 403)
class SubmitCase(TestCase):
- perma_fixtures = ['names']
-
def test_invalid_submit(self):
"Invalid Submit"
draft = make_test_data()
diff --git a/ietf/secr/announcement/views.py b/ietf/secr/announcement/views.py
index a32ec44f5..71810c7fc 100644
--- a/ietf/secr/announcement/views.py
+++ b/ietf/secr/announcement/views.py
@@ -6,7 +6,6 @@ from django.template import RequestContext
from ietf.ietfauth.decorators import has_role
from ietf.utils.mail import send_mail_text
-from ietf.wgchairs.accounts import get_person_for_user
from ietf.group.models import Group
from ietf.secr.utils.group import current_nomcom
from ietf.secr.utils.decorators import check_for_cancel
@@ -111,4 +110,4 @@ def confirm(request):
'message': data,
'to': to},
RequestContext(request, {}),
- )
\ No newline at end of file
+ )
diff --git a/ietf/secr/areas/tests.py b/ietf/secr/areas/tests.py
index 9b70fc916..cec3b65a8 100644
--- a/ietf/secr/areas/tests.py
+++ b/ietf/secr/areas/tests.py
@@ -22,7 +22,7 @@ def augment_data():
class MainTestCase(TestCase):
# See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names', 'persons', 'groupgroup', 'groupevents']
+ perma_fixtures = ['persons', 'groupgroup', 'groupevents']
def test_main(self):
"Main Test"
diff --git a/ietf/secr/drafts/email.py b/ietf/secr/drafts/email.py
index d72af76e9..534f0578f 100644
--- a/ietf/secr/drafts/email.py
+++ b/ietf/secr/drafts/email.py
@@ -78,19 +78,6 @@ def get_abbr_authors(draft):
return result
-def get_authors_email(draft):
- """
- Takes a draft object and returns a string of authors suitable for an email to or cc field
- """
- authors = []
- for a in draft.authors.all():
- initial = ''
- if a.person.first_name:
- initial = a.person.first_name[0] + '. '
- entry = '%s%s <%s>' % (initial,a.person.last_name,a.person.email())
- authors.append(entry)
- return ', '.join(authors)
-
def get_last_revision(filename):
"""
This function takes a filename, in the same form it appears in the InternetDraft record,
diff --git a/ietf/secr/drafts/forms.py b/ietf/secr/drafts/forms.py
index 5d4d13846..e5ced7dc3 100644
--- a/ietf/secr/drafts/forms.py
+++ b/ietf/secr/drafts/forms.py
@@ -375,7 +375,7 @@ class UploadForm(forms.Form):
# ensure that the basename is unique
base = splitext(txt.name)[0]
if Document.objects.filter(name=base[:-3]):
- raise forms.ValidationError, "This doucment filename already exists: %s" % base[:-3]
+ raise forms.ValidationError, "This document filename already exists: %s" % base[:-3]
# ensure that rev is 00
if base[-2:] != '00':
diff --git a/ietf/secr/drafts/tests.py b/ietf/secr/drafts/tests.py
index 53c5e89ef..c905dc250 100644
--- a/ietf/secr/drafts/tests.py
+++ b/ietf/secr/drafts/tests.py
@@ -9,9 +9,6 @@ from pyquery import PyQuery
SECR_USER='secretary'
class MainTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
def test_main(self):
"Main Test"
draft = make_test_data()
@@ -25,4 +22,4 @@ class MainTestCase(TestCase):
drafts = Document.objects.filter(type='draft')
url = reverse('drafts_view', kwargs={'id':drafts[0].name})
response = self.client.get(url, REMOTE_USER=SECR_USER)
- self.assertEquals(response.status_code, 200)
\ No newline at end of file
+ self.assertEquals(response.status_code, 200)
diff --git a/ietf/secr/drafts/views.py b/ietf/secr/drafts/views.py
index b0ebc9126..1191aea14 100644
--- a/ietf/secr/drafts/views.py
+++ b/ietf/secr/drafts/views.py
@@ -17,7 +17,7 @@ from ietf.meeting.models import Meeting
from ietf.name.models import StreamName
from ietf.doc.models import Document, DocumentAuthor
from ietf.doc.utils import augment_with_start_time
-from ietf.submit.models import IdSubmissionDetail, Preapproval
+from ietf.submit.models import Submission, Preapproval, DraftSubmissionStateName, SubmissionEvent
from ietf.utils.draft import Draft
from ietf.secr.proceedings.proc_utils import get_progress_stats
from ietf.secr.sreq.views import get_meeting
@@ -107,7 +107,7 @@ def process_files(request,draft):
the basename, revision number and a list of file types. Basename and revision
are assumed to be the same for all because this is part of the validation process.
- It also creates the IdSubmissionDetail record WITHOUT saving, and places it in the
+ It also creates the Submission record WITHOUT saving, and places it in the
session for saving in the final action step.
'''
files = request.FILES
@@ -124,29 +124,37 @@ def process_files(request,draft):
wrapper = Draft(file.read(),file.name)
handle_uploaded_file(file)
- # create IdSubmissionDetail record, leaved unsaved
- idsub = IdSubmissionDetail(
- id_document_name=draft.title,
- filename=basename,
- revision=revision,
- txt_page_count=draft.pages,
- filesize=txt_size,
- creation_date=wrapper.get_creation_date(),
+ # create Submission record, leaved unsaved
+ idsub = Submission(
+ name=basename,
+ title=draft.title,
+ rev=revision,
+ pages=draft.pages,
+ file_size=txt_size,
+ document_date=wrapper.get_creation_date(),
submission_date=datetime.date.today(),
idnits_message='idnits bypassed by manual posting',
- temp_id_document_tag=None,
- group_acronym_id=draft.group.id,
+ group_id=draft.group.id,
remote_ip=request.META['REMOTE_ADDR'],
first_two_pages=''.join(wrapper.pages[:2]),
- status_id=-2,
+ state=DraftSubmissionStateName.objects.get(slug="posted"),
abstract=draft.abstract,
- file_type=','.join(file_type_list),
- man_posted_date=datetime.date.today(),
- man_posted_by=request.user.get_profile())
+ file_types=','.join(file_type_list),
+ )
request.session['idsub'] = idsub
return (filename,revision,file_type_list)
+def post_submission(request):
+ submission = request.session['idsub']
+ submission.save()
+
+ SubmissionEvent.objects.create(
+ submission=submission,
+ by=request.user.person,
+ desc="Submitted and posted manually")
+
+
def promote_files(draft, types):
'''
This function takes one argument, a draft object. It then moves the draft files from
@@ -307,8 +315,8 @@ def do_revision(draft, request):
promote_files(new_draft, request.session['file_type'])
# save the submission record
- request.session['idsub'].save()
-
+ post_submission(request)
+
# send announcement if we are in IESG process
if new_draft.get_state('draft-iesg'):
announcement_from_form(request.session['email'],by=request.user.get_profile())
@@ -355,8 +363,8 @@ def do_update(draft,request):
promote_files(new_draft, request.session['file_type'])
# save the submission record
- request.session['idsub'].save()
-
+ post_submission(request)
+
# send announcement
announcement_from_form(request.session['email'],by=request.user.get_profile())
@@ -580,7 +588,7 @@ def add(request):
promote_files(draft, file_type_list)
# save the submission record
- request.session['idsub'].save()
+ post_submission(request)
request.session['action'] = 'add'
diff --git a/ietf/secr/groups/tests.py b/ietf/secr/groups/tests.py
index b1f023594..1c9c6e539 100644
--- a/ietf/secr/groups/tests.py
+++ b/ietf/secr/groups/tests.py
@@ -9,7 +9,7 @@ SECR_USER='secretary'
class GroupsTest(TestCase):
# See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names','persons','groupgroup',]
+ perma_fixtures = ['persons','groupgroup',]
"""
perma_fixtures = [ 'acronym.json',
'area.json',
diff --git a/ietf/secr/ipradmin/tests.py b/ietf/secr/ipradmin/tests.py
index c64a50654..36e33c677 100644
--- a/ietf/secr/ipradmin/tests.py
+++ b/ietf/secr/ipradmin/tests.py
@@ -9,9 +9,6 @@ from pyquery import PyQuery
SECR_USER='secretary'
class MainTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
def test_main(self):
"Main Test"
draft = make_test_data()
@@ -26,4 +23,4 @@ class MainTestCase(TestCase):
url = reverse('drafts_view', kwargs={'id':drafts[0].name})
response = self.client.get(url, REMOTE_USER=SECR_USER)
self.assertEquals(response.status_code, 200)
-"""
\ No newline at end of file
+"""
diff --git a/ietf/secr/meetings/tests.py b/ietf/secr/meetings/tests.py
index e0995c318..af7dfd095 100644
--- a/ietf/secr/meetings/tests.py
+++ b/ietf/secr/meetings/tests.py
@@ -9,9 +9,6 @@ from pyquery import PyQuery
SECR_USER='secretary'
class MainTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
def test_main(self):
"Main Test"
url = reverse('meetings')
diff --git a/ietf/secr/proceedings/tests.py b/ietf/secr/proceedings/tests.py
index 2b50c9c64..f62f6daef 100644
--- a/ietf/secr/proceedings/tests.py
+++ b/ietf/secr/proceedings/tests.py
@@ -10,9 +10,6 @@ import debug
SECR_USER='secretary'
class MainTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
def test_main(self):
"Main Test"
make_test_data()
diff --git a/ietf/secr/roles/tests.py b/ietf/secr/roles/tests.py
index 8e49494a2..bb2272212 100644
--- a/ietf/secr/roles/tests.py
+++ b/ietf/secr/roles/tests.py
@@ -11,13 +11,13 @@ import debug
SECR_USER='secretary'
def augment_data():
- # need this for the RoleForm intialization
- Group.objects.create(acronym='dummy',name='Dummy Group',type_id='sdo')
+ # need this for the RoleForm intialization
+ Group.objects.create(acronym='dummy',name='Dummy Group',type_id='sdo')
class MainTestCase(TestCase):
# See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names', 'persons', 'groupgroup']
-
+ perma_fixtures = ['persons', 'groupgroup']
+
def test_main(self):
"Main Test"
augment_data()
diff --git a/ietf/secr/rolodex/tests.py b/ietf/secr/rolodex/tests.py
index fa6298bfc..61a632c82 100644
--- a/ietf/secr/rolodex/tests.py
+++ b/ietf/secr/rolodex/tests.py
@@ -9,9 +9,6 @@ from pyquery import PyQuery
SECR_USER='secretary'
class MainTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
def test_main(self):
"Main Test"
url = reverse('rolodex')
diff --git a/ietf/secr/sreq/tests.py b/ietf/secr/sreq/tests.py
index 0854ee55a..4aec3f2f6 100644
--- a/ietf/secr/sreq/tests.py
+++ b/ietf/secr/sreq/tests.py
@@ -20,9 +20,6 @@ class SreqUrlTestCase(SimpleUrlTestCase):
self.doTestUrls(__file__)
class MainTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
def test_main(self):
draft = make_test_data()
url = reverse('sessions')
@@ -34,8 +31,6 @@ class MainTestCase(TestCase):
self.failUnless(len(unsched) > 0)
class SubmitRequestCase(TestCase):
- perma_fixtures = ['names']
-
def test_submit_request(self):
draft = make_test_data()
acronym = Group.objects.all()[0].acronym
@@ -71,4 +66,4 @@ class RetrievePreviousCase(TestCase):
# test error if already scheduled
# test get previous exists/doesn't exist
# test that groups scheduled and unscheduled add up to total groups
- # test locking function, access by unauthorized
\ No newline at end of file
+ # test locking function, access by unauthorized
diff --git a/ietf/secr/telechat/tests.py b/ietf/secr/telechat/tests.py
index c2a0b03c4..887e4f16f 100644
--- a/ietf/secr/telechat/tests.py
+++ b/ietf/secr/telechat/tests.py
@@ -1,7 +1,7 @@
from django.core.urlresolvers import reverse
from ietf.utils import TestCase
-from ietf.iesg.models import TelechatDate, TelechatAgendaItem, WGAction
+from ietf.iesg.models import TelechatDate, TelechatAgendaItem
from ietf.person.models import Person
from ietf.utils.test_data import make_test_data
@@ -15,9 +15,6 @@ def augment_data():
TelechatDate.objects.create(date=datetime.datetime.today())
class MainTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
def test_main(self):
"Main Test"
augment_data()
diff --git a/ietf/secr/telechat/views.py b/ietf/secr/telechat/views.py
index 05d4e79bc..f55462a48 100644
--- a/ietf/secr/telechat/views.py
+++ b/ietf/secr/telechat/views.py
@@ -8,15 +8,14 @@ from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from ietf.doc.models import DocEvent, Document, BallotDocEvent, BallotPositionDocEvent, TelechatDocEvent, WriteupDocEvent, save_document_in_history
-from ietf.doc.proxy import InternetDraft
from ietf.doc.utils import get_document_content, log_state_changed
from ietf.group.models import Group
from ietf.name.models import BallotPositionName
from ietf.person.models import Person
from ietf.doc.lastcall import request_last_call
from ietf.doc.mails import email_ad, email_state_changed
-from ietf.iesg.models import TelechatDate, TelechatAgendaItem, WGAction
-from ietf.iesg.views import _agenda_data
+from ietf.iesg.models import TelechatDate, TelechatAgendaItem
+from ietf.iesg.agenda import agenda_data, get_doc_section
from forms import *
import os
@@ -25,8 +24,6 @@ import datetime
'''
EXPECTED CHANGES:
x group pages will be just another doc, charter doc
-x charter docs to discuss will be passed in the 'docs' section of agenda
-x expand get_section_header to include section 4
x consolidate views (get rid of get_group_header,group,group_navigate)
'''
@@ -38,9 +35,7 @@ active_ballot_positions: takes one argument, doc. returns a dictionary with a k
NOTE: this function has been deprecated as of Datatracker 4.34. Should now use methods on the Document.
For example: doc.active_ballot().active_ad_positions()
-_agenda_data: takes a request object and a date string in the format YYYY-MM-DD.
- - 2012-07-28 this function was changed to return Document objects instead
- of old InternetDraft wrappers
+agenda_data: takes a date string in the format YYYY-MM-DD.
'''
# -------------------------------------------------
@@ -52,10 +47,11 @@ def get_doc_list(agenda):
Document objects in the order they appear in the agenda sections 1-3.
'''
docs = []
- for key in sorted(agenda['docs']):
- docs.extend(agenda['docs'][key])
+ for num, section in sorted(agenda['sections'].iteritems()):
+ if "docs" in section:
+ docs.extend(section["docs"])
- return [x['obj'] for x in docs]
+ return docs
def get_doc_writeup(doc):
'''
@@ -87,44 +83,29 @@ def get_next_telechat_date():
'''
return TelechatDate.objects.filter(date__gte=datetime.date.today()).order_by('date')[0].date
-def get_section_header(file,agenda):
+def get_section_header(doc, agenda):
'''
This function takes a filename and an agenda dictionary and returns the
- agenda section header as a string for use in the doc template
+ agenda section header as a list for use in the doc template
'''
- h1 = {'2':'Protocol Actions','3':'Document Actions','4':'Working Group Actions'}
- h2a = {'1':'WG Submissions','2':'Individual Submissions','3':'Status Changes'}
- h2b = {'1':'WG Submissions','2':'Individual Submissions via AD','3':'Status Changes','4':'IRTF and Independent Submission Stream Documents'}
- h2c = {'1':'WG Creation','2':'WG Chartering'}
- h3a = {'1':'New Item','2':'Returning Item','3':'For Action'}
- h3b = {'1':'Proposed for IETF Review','2':'Proposed for Approval'}
- h3c = {'1':'Under Evaluation for IETF Review','2':'Proposed for Approval'}
+ num = get_doc_section(doc)
- # Robert updated _agenda_data to return Document objects instead of the ID wrapper
- #doc = InternetDraft.objects.get(filename=file)
- doc = Document.objects.get(name=file)
+ header = []
- test = {'obj':doc}
- for k,v in agenda['docs'].iteritems():
- if test in v:
- section = k
- count = '%s of %s' % (v.index(test) + 1, len(v))
- break
+ split = num.split(".")
- header = [ '%s %s' % (section[1], h1[section[1]]) ]
- if section[1] == '2':
- header.append('%s.%s %s' % (section[1], section[2], h2a[section[2]]))
- elif section[1] == '4':
- header.append('%s.%s %s' % (section[1], section[2], h2c[section[2]]))
- else:
- header.append('%s.%s %s' % (section[1], section[2], h2b[section[2]]))
- if section[1] == '4':
- if section[2] == '1':
- header.append('%s.%s.%s %s' % (section[1], section[2], section[3], h3b[section[3]]))
- elif section[2] == '2':
- header.append('%s.%s.%s %s' % (section[1], section[2], section[3], h3c[section[3]]))
- else:
- header.append('%s.%s.%s %s' % (section[1], section[2], section[3], h3a[section[3]]))
+ for i in xrange(num.count(".")):
+ parent_num = ".".join(split[:i + 1])
+ parent = agenda["sections"].get(parent_num)
+ if parent:
+ if "." not in parent_num:
+ parent_num += "."
+ header.append(u"%s %s" % (parent_num, parent["title"]))
+
+ section = agenda["sections"][num]
+ header.append(u"%s %s" % (num, section["title"]))
+
+ count = '%s of %s' % (section["docs"].index(doc) + 1, len(section["docs"]))
header.append(count)
return header
@@ -133,9 +114,9 @@ def get_first_doc(agenda):
'''
This function takes an agenda dictionary and returns the first document in the agenda
'''
- for k,v in sorted(agenda['docs'].iteritems()):
- if v:
- return v[0]['obj']
+ for num, section in sorted(agenda['sections'].iteritems()):
+ if "docs" in section and section["docs"]:
+ return section["docs"][0]
return None
@@ -144,7 +125,7 @@ def get_first_doc(agenda):
# -------------------------------------------------
def bash(request, date):
- agenda = _agenda_data(request, date=date)
+ agenda = agenda_data(date=date)
return render_to_response('telechat/bash.html', {
'agenda': agenda,
@@ -158,7 +139,7 @@ def doc(request, date):
displays the message "No Documents"
'''
- agenda = _agenda_data(request, date=date)
+ agenda = agenda_data(date=date)
doc = get_first_doc(agenda)
if doc:
url = reverse('telechat_doc_detail', kwargs={'date':date,'name':doc.name})
@@ -212,8 +193,8 @@ def doc_detail(request, date, name):
'substate':tag}
BallotFormset = formset_factory(BallotForm, extra=0)
- agenda = _agenda_data(request, date=date)
- header = get_section_header(name,agenda) if name else ''
+ agenda = agenda_data(date=date)
+ header = get_section_header(doc, agenda)
# nav button logic
doc_list = get_doc_list(agenda)
@@ -329,7 +310,7 @@ def doc_navigate(request, date, name, nav):
The view retrieves the appropriate document and redirects to the doc view.
'''
doc = get_object_or_404(Document, docalias__name=name)
- agenda = _agenda_data(request, date=date)
+ agenda = agenda_data(date=date)
target = name
docs = get_doc_list(agenda)
@@ -346,10 +327,6 @@ def doc_navigate(request, date, name, nav):
def main(request):
'''
The is the main view where the user selects an existing telechat or creates a new one.
-
- NOTES ON EXTERNAL HELPER FUNCTIONS:
- _agenda_data(): returns dictionary of agenda sections
- get_ballot(name): returns a BallotWrapper and RfcWrapper or IdWrapper
'''
if request.method == 'POST':
date=request.POST['date']
@@ -371,7 +348,7 @@ def management(request, date):
This view displays management issues and lets the user update the status
'''
- agenda = _agenda_data(request, date=date)
+ agenda = agenda_data(date=date)
issues = TelechatAgendaItem.objects.filter(type=3).order_by('id')
return render_to_response('telechat/management.html', {
@@ -386,17 +363,18 @@ def minutes(request, date):
This view shows a list of documents that were approved since the last telechat
'''
# get the telechat previous to selected one
- dates = [ t.date for t in TelechatDate.objects.all() ]
y,m,d = date.split('-')
current = datetime.date(int(y),int(m),int(d))
- index = dates.index(current)
- previous = dates[index + 1]
+
+ previous = TelechatDate.objects.filter(date__lt=current).order_by("-date")[0].date
events = DocEvent.objects.filter(type='iesg_approved',time__gte=previous,time__lt=current,doc__type='draft')
docs = [ e.doc for e in events ]
pa_docs = [ d for d in docs if d.intended_std_level.slug not in ('inf','exp','hist') ]
da_docs = [ d for d in docs if d.intended_std_level.slug in ('inf','exp','hist') ]
- agenda = _agenda_data(request, date=date)
+ agenda = agenda_data(date=date)
+
+ # FIXME: this doesn't show other documents
return render_to_response('telechat/minutes.html', {
'agenda': agenda,
@@ -422,8 +400,8 @@ def new(request):
def roll_call(request, date):
- agenda = _agenda_data(request, date=date)
- ads = Person.objects.filter(role__name='ad')
+ agenda = agenda_data(date=date)
+ ads = Person.objects.filter(role__name='ad', role__group__state="active")
sorted_ads = sorted(ads, key = lambda a: a.name_parts()[3])
return render_to_response('telechat/roll_call.html', {
diff --git a/ietf/secr/templates/telechat/agenda_outline_23.html b/ietf/secr/templates/telechat/agenda_outline_23.html
deleted file mode 100644
index a3fc9c8d0..000000000
--- a/ietf/secr/templates/telechat/agenda_outline_23.html
+++ /dev/null
@@ -1,142 +0,0 @@
-{% comment %}
-Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}
-{% with "2. Protocol Actions" as title1 %}{% with 1 as title1_first %}
-{% with "2.1 WG Submissions" as title2 %}
-{% with 1 as title2_first %}
-
-{% with "2.1.1 New Items" as title3 %}
-{% with docs.s211 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endwith %}{# title2_first #}
-
-{% with "2.1.2 Returning Items" as title3 %}
-{% with docs.s212 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-
-{% if docs.s213 %}
-{% with "2.1.3 For Action" as title3 %}
-{% with docs.s213 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endif %}
-
-{% endwith %}{# title2 #}
-{% endwith %}{# title1_first #}
-
-{% with "2.2 Individual Submissions" as title2 %}
-{% with 1 as title2_first %}
-
-{% with "2.2.1 New Items" as title3 %}
-{% with docs.s221 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endwith %}{# title2_first #}
-
-{% with "2.2.2 Returning Items" as title3 %}
-{% with docs.s222 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-
-{% if docs.s223 %}
-{% with "2.2.3 For Action" as title3 %}
-{% with docs.s223 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endif %}
-
-{% endwith %}{# title2 #}
-
-{% endwith %}{# title1 #}
-
-{% with "3. Document Actions" as title1 %}{% with 1 as title1_first %}
-
-{% with "3.1 WG Submissions" as title2 %}
-{% with 1 as title2_first %}
-
-{% with "3.1.1 New Items" as title3 %}
-{% with docs.s311 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endwith %}{# title2_first #}
-
-{% with "3.1.2 Returning Items" as title3 %}
-{% with docs.s312 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-
-{% if docs.s313 %}
-{% with "3.1.3 For Action" as title3 %}
-{% with docs.s313 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endif %}
-
-{% endwith %}{# title2 #}
-
-{% endwith %}{# title1_first #}
-
-{% with "3.2 Individual Submissions Via AD" as title2 %}
-{% with 1 as title2_first %}
-
-{% with "3.2.1 New Items" as title3 %}
-{% with docs.s321 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endwith %}{# title2_first #}
-
-{% with "3.2.2 Returning Items" as title3 %}
-{% with docs.s322 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-
-{% if docs.s323 %}
-{% with "3.2.3 For Action" as title3 %}
-{% with docs.s323 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endif %}
-
-{% endwith %}{# title2 #}
-
-{% with "3.3 IRTF and Independent Submission Stream Documents" as title2 %}
-{% with 1 as title2_first %}
-
-{% with "3.3.1 New Items" as title3 %}
-{% with docs.s331 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endwith %}{# title2_first #}
-
-{% with "3.3.2 Returning Items" as title3 %}
-{% with docs.s332 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-
-{% if docs.s333 %}
-{% with "3.3.3 For Action" as title3 %}
-{% with docs.s333 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endif %}
-
-{% endwith %}{# title2 #}
-
-{% endwith %}
diff --git a/ietf/secr/templates/telechat/base_telechat.html b/ietf/secr/templates/telechat/base_telechat.html
index cecad39b8..a972b3e5f 100644
--- a/ietf/secr/templates/telechat/base_telechat.html
+++ b/ietf/secr/templates/telechat/base_telechat.html
@@ -12,77 +12,30 @@
{% endblock %}
{% block content %}
-
+{% load ietf_filters %}
Agenda {{ date }}
{% if agenda %}
-
2 Protocol Actions
-
2.1 WG Submissions
-
2.1.1 New Item
- {% with agenda.docs.s211 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
2.1.2 Returning Item
- {% with agenda.docs.s212 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
2.1.3 For Action
- {% with agenda.docs.s213 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
-
2.2 Individual Submissions
-
2.2.1 New Item
- {% with agenda.docs.s221 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
2.2.2 Returning Item
- {% with agenda.docs.s222 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
2.2.3 For Action
- {% with agenda.docs.s223 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
-
2.3 Status Changes
-
2.3.1 New Item
- {% with agenda.docs.s231 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
2.3.2 Returning Item
- {% with agenda.docs.232 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
-
3 Document Actions
-
3.1 WG Submissions
-
3.1.1 New Item
- {% with agenda.docs.s311 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
3.1.2 Returning Item
- {% with agenda.docs.s312 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
3.1.3 For Action
- {% with agenda.docs.s313 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
-
3.2 Individual Submissions via AD
-
3.2.1 New Item
- {% with agenda.docs.s321 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
3.2.2 Returning Item
- {% with agenda.docs.s322 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
3.2.3 For Action
- {% with agenda.docs.s323 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
-
3.3 Status Changes
-
3.3.1 New Item
- {% with agenda.docs.s331 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
3.3.2 Returning Item
- {% with agenda.docs.s332 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
-
3.4 Independent Submissions via RFC Editor
-
3.4.1 New Item
- {% with agenda.docs.s341 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
3.4.2 Returning Item
- {% with agenda.docs.s342 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
3.4.3 For Action
- {% with agenda.docs.s343 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
-
4 Working Group Actions
-
4.1 WG Creation
-
4.1.1 Proposed for IETF Review
- {% with agenda.docs.s411 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
4.1.2 Proposed for Approval
- {% with agenda.docs.s412 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
-
4.2 WG Rechartering
-
4.2.1 Under evaluation for IETF Review
- {% with agenda.docs.s421 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
-
4.2.2 Proposed for Approval
- {% with agenda.docs.s422 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %}
+ {% for num, section in agenda.sections.iteritems %}
+ {% if num >= "2" and num < "5" %}
+
{{ num }} {{ section.title }}
+
+ {% if "docs" in section %}
+ {% if section.docs %}
+
{% else %}
Please select a telechat to display the agenda.
diff --git a/ietf/secr/templates/telechat/doc.html b/ietf/secr/templates/telechat/doc.html
index a26d39fd7..793bf308f 100644
--- a/ietf/secr/templates/telechat/doc.html
+++ b/ietf/secr/templates/telechat/doc.html
@@ -7,8 +7,8 @@
{% block subsection %}
{% if document %}
- {% if not nav_start %}<< Previous{% endif %}
- {% if not nav_end %}Next >>{% endif %}
+ {% if not nav_start %}« Previous{% endif %}
+ {% if not nav_end %}Next »{% endif %}
\n'
+def two_pages_decorated_with_errors(submission, errors):
+ pages = submission.first_two_pages or ''
+ if 'rev' not in errors.keys():
+ return mark_safe('
%s
' % escape(pages))
+ result = '
\n'
for line in pages.split('\n'):
- if line.find('%s-%s' % (value.filename, value.revision)) > -1:
- result += '
'
+ if line.find('%s-%s' % (submission.name, submission.rev)) > -1:
+ result += '
'
result += escape(line)
result += '\n'
- result += '
\n'
+ result += '
\n'
else:
result += escape(line)
result += '\n'
diff --git a/ietf/submit/test_submission.txt b/ietf/submit/test_submission.txt
index eaa2780b0..c5017a4d1 100644
--- a/ietf/submit/test_submission.txt
+++ b/ietf/submit/test_submission.txt
@@ -1,11 +1,11 @@
-Informational Test Name
+Informational Author Name
Internet-Draft Test Center Inc.
Intended status: Informational %(date)s
Expires: %(expire)s
Testing tests
- %(filename)s
+ %(name)s
Abstract
@@ -94,12 +94,13 @@ Internet-Draft Testing tests %(month_year)s
Authors' Addresses
- Test Name
+ Author Name
Test Center Inc.
- Some way 42
- Some Where, NY
+ 42 Some Road
+ Some Where 12345
+ US
- Email: testname@example.com
+ Email: author@example.com
diff --git a/ietf/submit/tests.py b/ietf/submit/tests.py
index 7e21c9c36..bacf7c898 100644
--- a/ietf/submit/tests.py
+++ b/ietf/submit/tests.py
@@ -14,15 +14,14 @@ from ietf.utils.test_data import make_test_data
from ietf.utils.mail import outbox
from ietf.utils import TestCase
+from ietf.submit.utils import expirable_submissions, expire_submission
+
from ietf.person.models import Person, Email
from ietf.group.models import Group, Role
from ietf.doc.models import *
-from ietf.submit.models import IdSubmissionDetail, Preapproval
-
-class SubmitTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names', 'idsubmissionstatus']
+from ietf.submit.models import Submission, Preapproval
+class SubmitTests(TestCase):
def setUp(self):
self.staging_dir = os.path.abspath("tmp-submit-staging-dir")
os.mkdir(self.staging_dir)
@@ -41,17 +40,7 @@ class SubmitTestCase(TestCase):
shutil.rmtree(self.repository_dir)
shutil.rmtree(self.archive_dir)
- def do_submission(self, name, rev):
- # break early in case of missing configuration
- self.assertTrue(os.path.exists(settings.IDSUBMIT_IDNITS_BINARY))
-
- # get
- url = urlreverse('submit_index')
- r = self.client.get(url)
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- self.assertEquals(len(q('input[type=file][name=txt]')), 1)
-
+ def submission_txt_file(self, name, rev):
# construct appropriate text draft
f = open(os.path.join(settings.BASE_DIR, "submit", "test_submission.txt"))
template = f.read()
@@ -62,62 +51,90 @@ class SubmitTestCase(TestCase):
expire=(datetime.date.today() + datetime.timedelta(days=100)).strftime("%Y-%m-%d"),
year=datetime.date.today().strftime("%Y"),
month_year=datetime.date.today().strftime("%B, %Y"),
- filename="%s-%s" % (name, rev),
+ name="%s-%s" % (name, rev),
)
- test_file = StringIO(str(submission_text))
- test_file.name = "somename.txt"
+ txt_file = StringIO(str(submission_text))
+ txt_file.name = "somename.txt"
+ return txt_file
+
+ def do_submission(self, name, rev):
+ # break early in case of missing configuration
+ self.assertTrue(os.path.exists(settings.IDSUBMIT_IDNITS_BINARY))
+
+ # get
+ url = urlreverse('submit_upload_submission')
+ r = self.client.get(url)
+ self.assertEqual(r.status_code, 200)
+ q = PyQuery(r.content)
+ self.assertEqual(len(q('input[type=file][name=txt]')), 1)
# submit
+ txt_file = self.submission_txt_file(name, rev)
+
r = self.client.post(url,
- dict(txt=test_file))
- self.assertEquals(r.status_code, 302)
- supply_submitter_url = r["Location"]
+ dict(txt=txt_file))
+ self.assertEqual(r.status_code, 302)
+ status_url = r["Location"]
self.assertTrue(os.path.exists(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev))))
- self.assertEquals(IdSubmissionDetail.objects.filter(filename=name).count(), 1)
- submission = IdSubmissionDetail.objects.get(filename=name)
- self.assertEquals(submission.tempidauthors_set.count(), 1)
+ self.assertEqual(Submission.objects.filter(name=name).count(), 1)
+ submission = Submission.objects.get(name=name)
self.assertTrue(re.search('\s+Summary:\s+0\s+errors|No nits found', submission.idnits_message))
- author = submission.tempidauthors_set.all()[0]
- self.assertEquals(author.first_name, "Test Name")
+ self.assertEqual(len(submission.authors_parsed()), 1)
+ author = submission.authors_parsed()[0]
+ self.assertEqual(author["name"], "Author Name")
+ self.assertEqual(author["email"], "author@example.com")
- return supply_submitter_url
+ return status_url
- def supply_submitter(self, name, supply_submitter_url):
+ def supply_submitter(self, name, status_url, submitter_name, submitter_email):
# check the page
- r = self.client.get(supply_submitter_url)
+ r = self.client.get(status_url)
q = PyQuery(r.content)
- self.assertEquals(len(q('input[type=submit][name=autopost]')), 1)
+ post_button = q('input[type=submit][value*="Post"]')
+ self.assertEqual(len(post_button), 1)
+ action = post_button.parents("form").find('input[type=hidden][name="action"]').val()
# post submitter info
- r = self.client.post(supply_submitter_url,
- dict(autopost="1",
- name="Test Name",
- email="testname@example.com",
- ))
- # submitter is saved as author order 0
- submission = IdSubmissionDetail.objects.get(filename=name)
- self.assertEquals(submission.tempidauthors_set.count(), 2)
- self.assertEquals(submission.tempidauthors_set.get(author_order=0).first_name, "Test Name")
+ r = self.client.post(status_url, {
+ "action": action,
+ "submitter-name": submitter_name,
+ "submitter-email": submitter_email,
+ })
+
+ submission = Submission.objects.get(name=name)
+ self.assertEqual(submission.submitter, u"%s <%s>" % (submitter_name, submitter_email))
return r
- def test_submit_new(self):
+ def extract_confirm_url(self, confirm_email):
+ # dig out confirm_email link
+ msg = confirm_email.get_payload(decode=True)
+ line_start = "http"
+ confirm_url = None
+ for line in msg.split("\n"):
+ if line.strip().startswith(line_start):
+ confirm_url = line.strip()
+ self.assertTrue(confirm_url)
+
+ return confirm_url
+
+ def test_submit_new_wg(self):
# submit new -> supply submitter info -> approve
draft = make_test_data()
name = "draft-ietf-mars-testing-tests"
rev = "00"
- supply_submitter_url = self.do_submission(name, rev)
+ status_url = self.do_submission(name, rev)
# supply submitter info, then draft should be in and ready for approval
mailbox_before = len(outbox)
- r = self.supply_submitter(name, supply_submitter_url)
+ r = self.supply_submitter(name, status_url, "Author Name", "author@example.com")
- self.assertEquals(r.status_code, 302)
+ self.assertEqual(r.status_code, 302)
status_url = r["Location"]
- self.assertEquals(len(outbox), mailbox_before + 1)
+ self.assertEqual(len(outbox), mailbox_before + 1)
self.assertTrue("New draft waiting for approval" in outbox[-1]["Subject"])
self.assertTrue(name in outbox[-1]["Subject"])
@@ -125,42 +142,44 @@ class SubmitTestCase(TestCase):
self.client.login(remote_user="marschairman")
r = self.client.get(status_url)
- self.assertEquals(r.status_code, 200)
+ self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
- approve_submit = q('input[type=submit][value*="Approve"]')
- self.assertEquals(len(approve_submit), 1)
+ approve_button = q('input[type=submit][value*="Approve"]')
+ self.assertEqual(len(approve_button), 1)
+
+ action = approve_button.parents("form").find('input[type=hidden][name="action"]').val()
# approve submission
mailbox_before = len(outbox)
- approve_url = approve_submit.parents("form").attr("action")
- r = self.client.post(approve_url, dict())
- self.assertEquals(r.status_code, 302)
+ r = self.client.post(status_url, dict(action=action))
+ self.assertEqual(r.status_code, 302)
draft = Document.objects.get(docalias__name=name)
- self.assertEquals(draft.rev, rev)
+ self.assertEqual(draft.rev, rev)
new_revision = draft.latest_event()
- self.assertEquals(draft.group.acronym, "mars")
- self.assertEquals(new_revision.type, "new_revision")
- self.assertEquals(new_revision.by.name, "Test Name")
+ self.assertEqual(draft.group.acronym, "mars")
+ self.assertEqual(new_revision.type, "new_revision")
+ self.assertEqual(new_revision.by.name, "Author Name")
self.assertTrue(not os.path.exists(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev))))
self.assertTrue(os.path.exists(os.path.join(self.repository_dir, u"%s-%s.txt" % (name, rev))))
- self.assertEquals(draft.type_id, "draft")
- self.assertEquals(draft.stream_id, "ietf")
+ self.assertEqual(draft.type_id, "draft")
+ self.assertEqual(draft.stream_id, "ietf")
self.assertTrue(draft.expires >= datetime.datetime.now() + datetime.timedelta(days=settings.INTERNET_DRAFT_DAYS_TO_EXPIRE - 1))
- self.assertEquals(draft.get_state("draft-stream-%s" % draft.stream_id).slug, "wg-doc")
- self.assertEquals(draft.authors.count(), 1)
- self.assertEquals(draft.authors.all()[0].get_name(), "Test Name")
- self.assertEquals(draft.authors.all()[0].address, "testname@example.com")
- self.assertEquals(len(outbox), mailbox_before + 2)
+ self.assertEqual(draft.get_state("draft-stream-%s" % draft.stream_id).slug, "wg-doc")
+ self.assertEqual(draft.authors.count(), 1)
+ self.assertEqual(draft.authors.all()[0].get_name(), "Author Name")
+ self.assertEqual(draft.authors.all()[0].address, "author@example.com")
+ self.assertEqual(len(outbox), mailbox_before + 2)
self.assertTrue((u"I-D Action: %s" % name) in outbox[-2]["Subject"])
- self.assertTrue("Test Name" in unicode(outbox[-2]))
+ self.assertTrue("Author Name" in unicode(outbox[-2]))
self.assertTrue("New Version Notification" in outbox[-1]["Subject"])
self.assertTrue(name in unicode(outbox[-1]))
self.assertTrue("mars" in unicode(outbox[-1]))
def test_submit_existing(self):
- # submit new revision of existing -> supply submitter info -> confirm
+ # submit new revision of existing -> supply submitter info -> prev authors confirm
draft = make_test_data()
+ prev_author = draft.documentauthor_set.all()[0]
# pretend IANA reviewed it
draft.set_state(State.objects.get(used=True, type="draft-iana-review", slug="not-ok"))
@@ -189,60 +208,58 @@ class SubmitTestCase(TestCase):
with open(os.path.join(self.repository_dir, "%s-%s.txt" % (name, old_rev)), 'w') as f:
f.write("a" * 2000)
- supply_submitter_url = self.do_submission(name, rev)
+ status_url = self.do_submission(name, rev)
- # supply submitter info, then we get a confirmation email
+ # supply submitter info, then previous authors get a confirmation email
mailbox_before = len(outbox)
- r = self.supply_submitter(name, supply_submitter_url)
+ r = self.supply_submitter(name, status_url, "Submitter Name", "submitter@example.com")
+ self.assertEqual(r.status_code, 302)
+ status_url = r["Location"]
+ r = self.client.get(status_url)
+ self.assertEqual(r.status_code, 200)
+ self.assertTrue("The submission is pending approval by the authors" in r.content)
- self.assertEquals(r.status_code, 200)
- self.assertTrue("Your submission is pending email authentication" in r.content)
+ self.assertEqual(len(outbox), mailbox_before + 1)
+ confirm_email = outbox[-1]
+ self.assertTrue("Confirm submission" in confirm_email["Subject"])
+ self.assertTrue(name in confirm_email["Subject"])
+ self.assertTrue(prev_author.author.address in confirm_email["To"])
+ # submitter and new author can't confirm
+ self.assertTrue("author@example.com" not in confirm_email["To"])
+ self.assertTrue("submitter@example.com" not in confirm_email["To"])
- self.assertEquals(len(outbox), mailbox_before + 1)
- confirmation = outbox[-1]
- self.assertTrue("Confirmation for" in confirmation["Subject"])
- self.assertTrue(name in confirmation["Subject"])
-
- # dig out confirmation link
- msg = confirmation.get_payload(decode=True)
- line_start = "Confirmation URL:"
- self.assertTrue(line_start in msg)
- confirm_url = None
- for line in msg.split("\n"):
- if line.startswith(line_start):
- confirm_url = line[len(line_start):].strip()
+ confirm_url = self.extract_confirm_url(confirm_email)
# go to confirm page
r = self.client.get(confirm_url)
q = PyQuery(r.content)
- self.assertEquals(len(q('input[type=submit][value=Auto-Post]')), 1)
+ self.assertEqual(len(q('input[type=submit][value*="Confirm"]')), 1)
# confirm
mailbox_before = len(outbox)
r = self.client.post(confirm_url)
- self.assertEquals(r.status_code, 200)
- self.assertTrue('Authorization key accepted' in r.content)
+ self.assertEqual(r.status_code, 302)
draft = Document.objects.get(docalias__name=name)
- self.assertEquals(draft.rev, rev)
- self.assertEquals(draft.group.acronym, name.split("-")[2])
- self.assertEquals(draft.docevent_set.all()[1].type, "new_revision")
- self.assertEquals(draft.docevent_set.all()[1].by.name, "Test Name")
+ self.assertEqual(draft.rev, rev)
+ self.assertEqual(draft.group.acronym, name.split("-")[2])
+ self.assertEqual(draft.docevent_set.all()[1].type, "new_revision")
+ self.assertEqual(draft.docevent_set.all()[1].by.name, "Submitter Name")
self.assertTrue(not os.path.exists(os.path.join(self.repository_dir, "%s-%s.txt" % (name, old_rev))))
self.assertTrue(os.path.exists(os.path.join(self.archive_dir, "%s-%s.txt" % (name, old_rev))))
self.assertTrue(not os.path.exists(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev))))
self.assertTrue(os.path.exists(os.path.join(self.repository_dir, u"%s-%s.txt" % (name, rev))))
- self.assertEquals(draft.type_id, "draft")
- self.assertEquals(draft.stream_id, "ietf")
- self.assertEquals(draft.get_state_slug("draft-stream-%s" % draft.stream_id), "wg-doc")
- self.assertEquals(draft.get_state_slug("draft-iana-review"), "changed")
- self.assertEquals(draft.authors.count(), 1)
- self.assertEquals(draft.authors.all()[0].get_name(), "Test Name")
- self.assertEquals(draft.authors.all()[0].address, "testname@example.com")
- self.assertEquals(len(outbox), mailbox_before + 3)
+ self.assertEqual(draft.type_id, "draft")
+ self.assertEqual(draft.stream_id, "ietf")
+ self.assertEqual(draft.get_state_slug("draft-stream-%s" % draft.stream_id), "wg-doc")
+ self.assertEqual(draft.get_state_slug("draft-iana-review"), "changed")
+ self.assertEqual(draft.authors.count(), 1)
+ self.assertEqual(draft.authors.all()[0].get_name(), "Author Name")
+ self.assertEqual(draft.authors.all()[0].address, "author@example.com")
+ self.assertEqual(len(outbox), mailbox_before + 3)
self.assertTrue((u"I-D Action: %s" % name) in outbox[-3]["Subject"])
self.assertTrue((u"I-D Action: %s" % name) in draft.message_set.order_by("-time")[0].subject)
- self.assertTrue("Test Name" in unicode(outbox[-3]))
+ self.assertTrue("Author Name" in unicode(outbox[-3]))
self.assertTrue("New Version Notification" in outbox[-2]["Subject"])
self.assertTrue(name in unicode(outbox[-2]))
self.assertTrue("mars" in unicode(outbox[-2]))
@@ -252,6 +269,51 @@ class SubmitTestCase(TestCase):
self.assertTrue(name in unicode(outbox[-1]))
self.assertTrue("mars" in unicode(outbox[-1]))
+ def test_submit_new_individual(self):
+ # submit new -> supply submitter info -> confirm
+ draft = make_test_data()
+
+ name = "draft-authorname-testing-tests"
+ rev = "00"
+
+ status_url = self.do_submission(name, rev)
+
+ # supply submitter info, then draft should be be ready for email auth
+ mailbox_before = len(outbox)
+ r = self.supply_submitter(name, status_url, "Submitter Name", "submitter@example.com")
+
+ self.assertEqual(r.status_code, 302)
+ status_url = r["Location"]
+ r = self.client.get(status_url)
+ self.assertEqual(r.status_code, 200)
+ self.assertTrue("The submission is pending email authentication" in r.content)
+
+ self.assertEqual(len(outbox), mailbox_before + 1)
+ confirm_email = outbox[-1]
+ self.assertTrue("Confirm submission" in confirm_email["Subject"])
+ self.assertTrue(name in confirm_email["Subject"])
+ # both submitter and author get email
+ self.assertTrue("author@example.com" in confirm_email["To"])
+ self.assertTrue("submitter@example.com" in confirm_email["To"])
+
+ confirm_url = self.extract_confirm_url(outbox[-1])
+
+ # go to confirm page
+ r = self.client.get(confirm_url)
+ q = PyQuery(r.content)
+ self.assertEqual(len(q('input[type=submit][value*="Confirm"]')), 1)
+
+ # confirm
+ mailbox_before = len(outbox)
+ r = self.client.post(confirm_url)
+ self.assertEqual(r.status_code, 302)
+
+ draft = Document.objects.get(docalias__name=name)
+ self.assertEqual(draft.rev, rev)
+ new_revision = draft.latest_event()
+ self.assertEqual(new_revision.type, "new_revision")
+ self.assertEqual(new_revision.by.name, "Submitter Name")
+
def test_submit_new_wg_with_dash(self):
draft = make_test_data()
@@ -261,7 +323,7 @@ class SubmitTestCase(TestCase):
self.do_submission(name, "00")
- self.assertEquals(IdSubmissionDetail.objects.get(filename=name).group_acronym.acronym, group.acronym)
+ self.assertEqual(Submission.objects.get(name=name).group.acronym, group.acronym)
def test_submit_new_irtf(self):
draft = make_test_data()
@@ -272,8 +334,8 @@ class SubmitTestCase(TestCase):
self.do_submission(name, "00")
- self.assertEquals(IdSubmissionDetail.objects.get(filename=name).group_acronym.acronym, group.acronym)
- self.assertEquals(IdSubmissionDetail.objects.get(filename=name).group_acronym.type_id, group.type_id)
+ self.assertEqual(Submission.objects.get(name=name).group.acronym, group.acronym)
+ self.assertEqual(Submission.objects.get(name=name).group.type_id, group.type_id)
def test_submit_new_iab(self):
draft = make_test_data()
@@ -282,7 +344,7 @@ class SubmitTestCase(TestCase):
self.do_submission(name, "00")
- self.assertEquals(IdSubmissionDetail.objects.get(filename=name).group_acronym.acronym, "iab")
+ self.assertEqual(Submission.objects.get(name=name).group.acronym, "iab")
def test_cancel_submission(self):
# submit -> cancel
@@ -291,87 +353,109 @@ class SubmitTestCase(TestCase):
name = "draft-ietf-mars-testing-tests"
rev = "00"
- supply_submitter_url = self.do_submission(name, rev)
+ status_url = self.do_submission(name, rev)
# check we got cancel button
- r = self.client.get(supply_submitter_url)
- self.assertEquals(r.status_code, 200)
+ r = self.client.get(status_url)
+ self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
- cancel_submission = q('input[type=submit][value*="Cancel"]')
- self.assertEquals(len(cancel_submission), 1)
+ cancel_button = q('input[type=submit][value*="Cancel"]')
+ self.assertEqual(len(cancel_button), 1)
- cancel_url = cancel_submission.parents("form").attr("action")
+ action = cancel_button.parents("form").find("input[type=hidden][name=\"action\"]").val()
# cancel
- r = self.client.post(cancel_url)
+ r = self.client.post(status_url, dict(action=action))
self.assertTrue(not os.path.exists(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev))))
- def test_edit_submission(self):
+ def test_edit_submission_and_force_post(self):
# submit -> edit
draft = make_test_data()
name = "draft-ietf-mars-testing-tests"
rev = "00"
- supply_submitter_url = self.do_submission(name, rev)
+ status_url = self.do_submission(name, rev)
# check we got edit button
- r = self.client.get(supply_submitter_url)
- self.assertEquals(r.status_code, 200)
+ r = self.client.get(status_url)
+ self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
- self.assertEquals(len(q('input[type=submit][value*="Adjust"]')), 1)
+ adjust_button = q('input[type=submit][value*="Adjust"]')
+ self.assertEqual(len(adjust_button), 1)
+
+ action = adjust_button.parents("form").find('input[type=hidden][name="action"]').val()
# go to edit, we do this by posting, slightly weird
- r = self.client.post(supply_submitter_url)
- self.assertEquals(r.status_code, 302)
+ r = self.client.post(status_url, dict(action=action))
+ self.assertEqual(r.status_code, 302)
edit_url = r['Location']
# check page
r = self.client.get(edit_url)
- self.assertEquals(r.status_code, 200)
+ self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
- self.assertEquals(len(q('input[name=title]')), 1)
+ self.assertEqual(len(q('input[name=edit-title]')), 1)
# edit
mailbox_before = len(outbox)
- creation_date = datetime.date.today() - datetime.timedelta(days=-3)
- r = self.client.post(edit_url,
- dict(title="some title",
- version="00",
- creation_date=creation_date.strftime("%Y-%m-%d"),
- abstract="some abstract",
- pages="123",
- name="Some Random Test Person",
- email="random@example.com",
- comments="no comments",
- name_0="Person 1",
- email_0="person1@example.com",
- name_1="Person 2",
- email_1="person2@example.com",
- ))
- self.assertEquals(r.status_code, 302)
+ document_date = datetime.date.today() - datetime.timedelta(days=-3)
+ r = self.client.post(edit_url, {
+ "edit-title": "some title",
+ "edit-rev": "00",
+ "edit-document_date": document_date.strftime("%Y-%m-%d"),
+ "edit-abstract": "some abstract",
+ "edit-pages": "123",
+ "submitter-name": "Some Random Test Person",
+ "submitter-email": "random@example.com",
+ "edit-note": "no comments",
+ "authors-0-name": "Person 1",
+ "authors-0-email": "person1@example.com",
+ "authors-1-name": "Person 2",
+ "authors-1-email": "person2@example.com",
+ "authors-prefix": ["authors-", "authors-0", "authors-1"],
+ })
+ self.assertEqual(r.status_code, 302)
- submission = IdSubmissionDetail.objects.get(filename=name)
- self.assertEquals(submission.id_document_name, "some title")
- self.assertEquals(submission.creation_date, creation_date)
- self.assertEquals(submission.abstract, "some abstract")
- self.assertEquals(submission.txt_page_count, 123)
- self.assertEquals(submission.comment_to_sec, "no comments")
+ submission = Submission.objects.get(name=name)
+ self.assertEqual(submission.title, "some title")
+ self.assertEqual(submission.document_date, document_date)
+ self.assertEqual(submission.abstract, "some abstract")
+ self.assertEqual(submission.pages, 123)
+ self.assertEqual(submission.note, "no comments")
+ self.assertEqual(submission.submitter, "Some Random Test Person ")
+ self.assertEqual(submission.state_id, "manual")
- authors = submission.tempidauthors_set
- self.assertEquals(authors.count(), 3)
- # first one is submitter
- self.assertEquals(authors.get(author_order=0).first_name, "Some Random Test Person")
- self.assertEquals(authors.get(author_order=0).email_address, "random@example.com")
- self.assertEquals(authors.get(author_order=1).first_name, "Person 1")
- self.assertEquals(authors.get(author_order=1).email_address, "person1@example.com")
- self.assertEquals(authors.get(author_order=2).first_name, "Person 2")
- self.assertEquals(authors.get(author_order=2).email_address, "person2@example.com")
+ authors = submission.authors_parsed()
+ self.assertEqual(len(authors), 2)
+ self.assertEqual(authors[0]["name"], "Person 1")
+ self.assertEqual(authors[0]["email"], "person1@example.com")
+ self.assertEqual(authors[1]["name"], "Person 2")
+ self.assertEqual(authors[1]["email"], "person2@example.com")
- self.assertEquals(len(outbox), mailbox_before + 1)
+ self.assertEqual(len(outbox), mailbox_before + 1)
self.assertTrue("Manual Post Requested" in outbox[-1]["Subject"])
self.assertTrue(name in outbox[-1]["Subject"])
+ # as Secretariat, we should see the force post button
+ self.client.login(remote_user="secretary")
+
+ r = self.client.get(status_url)
+ self.assertEqual(r.status_code, 200)
+ q = PyQuery(r.content)
+ post_button = q('input[type=submit][value*="Force"]')
+ self.assertEqual(len(post_button), 1)
+
+ action = post_button.parents("form").find('input[type=hidden][name="action"]').val()
+
+ # force post
+ mailbox_before = len(outbox)
+ r = self.client.post(status_url, dict(action=action))
+ self.assertEqual(r.status_code, 302)
+
+ draft = Document.objects.get(docalias__name=name)
+ self.assertEqual(draft.rev, rev)
+
def test_request_full_url(self):
# submit -> request full URL to be sent
draft = make_test_data()
@@ -381,62 +465,125 @@ class SubmitTestCase(TestCase):
self.do_submission(name, rev)
- submission = IdSubmissionDetail.objects.get(filename=name)
- url = urlreverse('draft_status', kwargs=dict(submission_id=submission.submission_id))
+ submission = Submission.objects.get(name=name)
+ url = urlreverse('submit_submission_status', kwargs=dict(submission_id=submission.pk))
# check we got request full URL button
r = self.client.get(url)
- self.assertEquals(r.status_code, 200)
+ self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
request_button = q('input[type=submit][value*="Request full access"]')
- self.assertEquals(len(request_button), 1)
-
- request_url = request_button.parents("form").attr("action")
+ self.assertEqual(len(request_button), 1)
# request URL to be sent
mailbox_before = len(outbox)
- r = self.client.post(request_url)
- self.assertEquals(r.status_code, 200)
- self.assertEquals(len(outbox), mailbox_before + 1)
+ action = request_button.parents("form").find("input[type=hidden][name=\"action\"]").val()
+ r = self.client.post(url, dict(action=action))
+ self.assertEqual(r.status_code, 200)
+
+ self.assertEqual(len(outbox), mailbox_before + 1)
self.assertTrue("Full URL for managing submission" in outbox[-1]["Subject"])
self.assertTrue(name in outbox[-1]["Subject"])
-class ApprovalsTestCase(TestCase):
- perma_fixtures = ['names', 'idsubmissionstatus']
+ def test_submit_all_file_types(self):
+ draft = make_test_data()
+ name = "draft-ietf-mars-testing-tests"
+ rev = "00"
+
+ txt_file = self.submission_txt_file(name, rev)
+
+ # the checks for other file types are currently embarrassingly
+ # dumb, so don't bother constructing proper XML/PS/PDF draft
+ # files
+ xml_file = StringIO('\nThis is XML')
+ xml_file.name = "somename.xml"
+
+ pdf_file = StringIO('%PDF-1.5\nThis is PDF')
+ pdf_file.name = "somename.pdf"
+
+ ps_file = StringIO('%!PS-Adobe-2.0\nThis is PostScript')
+ ps_file.name = "somename.ps"
+
+ r = self.client.post(urlreverse('submit_upload_submission'), dict(
+ txt=txt_file,
+ xml=xml_file,
+ pdf=pdf_file,
+ ps=ps_file,
+ ))
+ self.assertEqual(r.status_code, 302)
+
+ self.assertEqual(Submission.objects.filter(name=name).count(), 1)
+
+ self.assertTrue(os.path.exists(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev))))
+ self.assertTrue(name in open(os.path.join(self.staging_dir, u"%s-%s.txt" % (name, rev))).read())
+ self.assertTrue(os.path.exists(os.path.join(self.staging_dir, u"%s-%s.xml" % (name, rev))))
+ self.assertTrue('This is XML' in open(os.path.join(self.staging_dir, u"%s-%s.xml" % (name, rev))).read())
+ self.assertTrue(os.path.exists(os.path.join(self.staging_dir, u"%s-%s.pdf" % (name, rev))))
+ self.assertTrue('This is PDF' in open(os.path.join(self.staging_dir, u"%s-%s.pdf" % (name, rev))).read())
+ self.assertTrue(os.path.exists(os.path.join(self.staging_dir, u"%s-%s.ps" % (name, rev))))
+ self.assertTrue('This is PostScript' in open(os.path.join(self.staging_dir, u"%s-%s.ps" % (name, rev))).read())
+
+ def test_expire_submissions(self):
+ s = Submission.objects.create(name="draft-ietf-mars-foo",
+ group=None,
+ submission_date=datetime.date.today() - datetime.timedelta(days=10),
+ rev="00",
+ state_id="uploaded")
+
+ self.assertEqual(len(expirable_submissions(older_than_days=10)), 0)
+ self.assertEqual(len(expirable_submissions(older_than_days=9)), 1)
+
+ s.state_id = "cancel"
+ s.save()
+
+ self.assertEqual(len(expirable_submissions(older_than_days=9)), 0)
+
+ s.state_id = "posted"
+ s.save()
+
+ self.assertEqual(len(expirable_submissions(older_than_days=9)), 0)
+
+ s.state_id = "uploaded"
+ s.save()
+
+ expire_submission(s, by=None)
+
+ self.assertEqual(s.state_id, "cancel")
+
+
+class ApprovalsTestCase(TestCase):
def test_approvals(self):
make_test_data()
url = urlreverse('submit_approvals')
self.client.login(remote_user="marschairman")
- from ietf.submit.views import POSTED, INITIAL_VERSION_APPROVAL_REQUESTED
-
Preapproval.objects.create(name="draft-ietf-mars-foo", by=Person.objects.get(user__username="marschairman"))
Preapproval.objects.create(name="draft-ietf-mars-baz", by=Person.objects.get(user__username="marschairman"))
- IdSubmissionDetail.objects.create(filename="draft-ietf-mars-foo",
- group_acronym_id=Group.objects.get(acronym="mars").pk,
- submission_date=datetime.date.today(),
- revision="00",
- status_id=POSTED)
- IdSubmissionDetail.objects.create(filename="draft-ietf-mars-bar",
- group_acronym_id=Group.objects.get(acronym="mars").pk,
- submission_date=datetime.date.today(),
- revision="00",
- status_id=INITIAL_VERSION_APPROVAL_REQUESTED)
+ Submission.objects.create(name="draft-ietf-mars-foo",
+ group=Group.objects.get(acronym="mars"),
+ submission_date=datetime.date.today(),
+ rev="00",
+ state_id="posted")
+ Submission.objects.create(name="draft-ietf-mars-bar",
+ group=Group.objects.get(acronym="mars"),
+ submission_date=datetime.date.today(),
+ rev="00",
+ state_id="grp-appr")
# get
r = self.client.get(url)
- self.assertEquals(r.status_code, 200)
+ self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
- self.assertEquals(len(q('.approvals a:contains("draft-ietf-mars-foo")')), 0)
- self.assertEquals(len(q('.approvals a:contains("draft-ietf-mars-bar")')), 1)
- self.assertEquals(len(q('.preapprovals td:contains("draft-ietf-mars-foo")')), 0)
- self.assertEquals(len(q('.preapprovals td:contains("draft-ietf-mars-baz")')), 1)
- self.assertEquals(len(q('.recently-approved a:contains("draft-ietf-mars-foo")')), 1)
+ self.assertEqual(len(q('.approvals a:contains("draft-ietf-mars-foo")')), 0)
+ self.assertEqual(len(q('.approvals a:contains("draft-ietf-mars-bar")')), 1)
+ self.assertEqual(len(q('.preapprovals td:contains("draft-ietf-mars-foo")')), 0)
+ self.assertEqual(len(q('.preapprovals td:contains("draft-ietf-mars-baz")')), 1)
+ self.assertEqual(len(q('.recently-approved a:contains("draft-ietf-mars-foo")')), 1)
def test_add_preapproval(self):
make_test_data()
@@ -446,21 +593,21 @@ class ApprovalsTestCase(TestCase):
# get
r = self.client.get(url)
- self.assertEquals(r.status_code, 200)
+ self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
- self.assertEquals(len(q('input[type=submit]')), 1)
+ self.assertEqual(len(q('input[type=submit]')), 1)
# faulty post
r = self.client.post(url, dict(name="draft-test-nonexistingwg-something"))
- self.assertEquals(r.status_code, 200)
+ self.assertEqual(r.status_code, 200)
self.assertTrue("errorlist" in r.content)
# add
name = "draft-ietf-mars-foo"
r = self.client.post(url, dict(name=name))
- self.assertEquals(r.status_code, 302)
+ self.assertEqual(r.status_code, 302)
- self.assertEquals(len(Preapproval.objects.filter(name=name)), 1)
+ self.assertEqual(len(Preapproval.objects.filter(name=name)), 1)
def test_cancel_preapproval(self):
make_test_data()
@@ -472,12 +619,12 @@ class ApprovalsTestCase(TestCase):
# get
r = self.client.get(url)
- self.assertEquals(r.status_code, 200)
+ self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
- self.assertEquals(len(q('input[type=submit]')), 1)
+ self.assertEqual(len(q('input[type=submit]')), 1)
# cancel
r = self.client.post(url, dict(action="cancel"))
- self.assertEquals(r.status_code, 302)
+ self.assertEqual(r.status_code, 302)
- self.assertEquals(len(Preapproval.objects.filter(name=preapproval.name)), 0)
+ self.assertEqual(len(Preapproval.objects.filter(name=preapproval.name)), 0)
diff --git a/ietf/submit/urls.py b/ietf/submit/urls.py
index f04bc53ac..049cbb0f5 100644
--- a/ietf/submit/urls.py
+++ b/ietf/submit/urls.py
@@ -2,32 +2,17 @@ from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('ietf.submit.views',
- url(r'^$', 'submit_index', name='submit_index'),
- url(r'^status/$', 'submit_status', name='submit_status'),
- url(r'^status/(?P\d+)/$', 'draft_status', name='draft_status'),
- url(r'^status/(?P\d+)/edit/$', 'draft_edit', name='draft_edit'),
- url(r'^status/(?P\d+)/confirm/(?P[a-f\d]+)/$', 'draft_confirm', name='draft_confirm'),
- url(r'^status/(?P\d+)/cancel/$', 'draft_cancel', name='draft_cancel'),
- url(r'^status/(?P\d+)/approve/$', 'draft_approve', name='draft_approve'),
- url(r'^status/(?P\d+)/force/$', 'draft_force', name='draft_force'),
- url(r'^status/(?P\d+)/request/$', 'full_url_request', name='full_url_request'),
- url(r'^status/(?P\d+)/(?P[a-f\d]+)/$', 'draft_status', name='draft_status_by_hash'),
- url(r'^status/(?P\d+)/(?P[a-f\d]+)/cancel/$', 'draft_cancel', name='draft_cancel_by_hash'),
- url(r'^status/(?P\d+)/(?P[a-f\d]+)/edit/$', 'draft_edit', name='draft_edit_by_hash'),
+ url(r'^$', 'upload_submission', name='submit_upload_submission'),
+ url(r'^status/$', 'search_submission', name='submit_search_submission'),
+ url(r'^status/(?P\d+)/$', 'submission_status', name='submit_submission_status'),
+ url(r'^status/(?P\d+)/edit/$', 'edit_submission', name='submit_edit_submission'),
+ url(r'^status/(?P\d+)/confirm/(?P[a-f\d]+)/$', 'confirm_submission', name='submit_confirm_submission'),
+ url(r'^status/(?P\d+)/(?P[a-f\d]*)/$', 'submission_status', name='submit_submission_status_by_hash'),
+ url(r'^status/(?P\d+)/(?P[a-f\d]+)/edit/$', 'edit_submission', name='submit_edit_submission_by_hash'),
+ url(r'^note-well/$', 'note_well', name='submit_note_well'),
+ url(r'^tool-instructions/$', 'tool_instructions', name='submit_tool_instructions'),
+
url(r'^approvals/$', 'approvals', name='submit_approvals'),
url(r'^approvals/addpreapproval/$', 'add_preapproval', name='submit_add_preapproval'),
url(r'^approvals/cancelpreapproval/(?P[a-f\d]+)/$', 'cancel_preapproval', name='submit_cancel_preapproval'),
)
-
-urlpatterns += patterns('django.views.generic.simple',
- url(r'^note-well/$', 'direct_to_template',
- {'template': 'submit/note_well.html',
- 'extra_context': {'selected': 'notewell'}
- },
- name='submit_note_well'),
- url(r'^tool-instructions/$', 'direct_to_template',
- {'template': 'submit/tool_instructions.html',
- 'extra_context': {'selected': 'instructions'}
- },
- name='submit_tool_instructions'),
-)
diff --git a/ietf/submit/utils.py b/ietf/submit/utils.py
index 0638996b3..08d51644e 100644
--- a/ietf/submit/utils.py
+++ b/ietf/submit/utils.py
@@ -7,68 +7,134 @@ from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse as urlreverse
from django.template.loader import render_to_string
-from ietf.idtracker.models import (InternetDraft, PersonOrOrgInfo, IETFWG,
- IDAuthor, EmailAddress, IESGLogin, BallotInfo)
-from ietf.submit.models import TempIdAuthors, IdSubmissionDetail, Preapproval
from ietf.utils.mail import send_mail, send_mail_message
from ietf.utils.log import log
from ietf.utils import unaccent
-from ietf.ietfauth.decorators import has_role
+from ietf.ietfauth.utils import has_role
+from ietf.submit.models import Submission, SubmissionEvent, Preapproval, DraftSubmissionStateName
from ietf.doc.models import *
from ietf.person.models import Person, Alias, Email
from ietf.doc.utils import add_state_change_event, rebuild_reference_relations
from ietf.message.models import Message
+from ietf.utils.pipe import pipe
+from ietf.utils.log import log
+from ietf.submit.mail import announce_to_lists, announce_new_version, announce_to_authors
+
+def check_idnits(path):
+ #p = subprocess.Popen([self.idnits, '--submitcheck', '--nitcount', path], stdout=subprocess.PIPE)
+ cmd = "%s --submitcheck --nitcount %s" % (settings.IDSUBMIT_IDNITS_BINARY, path)
+ code, out, err = pipe(cmd)
+ if code != 0:
+ log("idnits error: %s:\n Error %s: %s" %( cmd, code, err))
+ return out
+
+def found_idnits(idnits_message):
+ if not idnits_message:
+ return False
+ success_re = re.compile('\s+Summary:\s+0\s+|No nits found')
+ if success_re.search(idnits_message):
+ return True
+ return False
+
+def validate_submission(submission):
+ errors = {}
+
+ if submission.state_id not in ("cancel", "posted"):
+ for ext in submission.file_types.split(','):
+ source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.name, submission.rev, ext))
+ if not os.path.exists(source):
+ errors['files'] = '"%s" was not found in the staging area. We recommend you that you cancel this submission and upload your files again.' % os.path.basename(source)
+ break
+
+ if not submission.title:
+ errors['title'] = 'Title is empty or was not found'
+
+ if submission.group and submission.group.state_id != "active":
+ errors['group'] = 'Group exists but is not an active group'
+
+ if not submission.abstract:
+ errors['abstract'] = 'Abstract is empty or was not found'
+
+ if not submission.authors_parsed():
+ errors['authors'] = 'No authors found'
+
+ # revision
+ if submission.state_id != "posted":
+ error = validate_submission_rev(submission.name, submission.rev)
+ if error:
+ errors['rev'] = error
+
+ # draft date
+ error = validate_submission_document_date(submission.submission_date, submission.document_date)
+ if error:
+ errors['document_date'] = error
+
+ return errors
+
+def validate_submission_rev(name, rev):
+ if not rev:
+ return 'Revision not found'
+
+ try:
+ rev = int(rev)
+ except ValueError:
+ return 'Revision must be a number'
+ else:
+ if not (0 <= rev <= 99):
+ return 'Revision must be between 00 and 99'
+
+ expected = 0
+ existing_revs = [int(i.rev) for i in Document.objects.filter(name=name)]
+ if existing_revs:
+ expected = max(existing_revs) + 1
+
+ if rev != expected:
+ return 'Invalid revision (revision %02d is expected)' % expected
+
+ return None
+
+def validate_submission_document_date(submission_date, document_date):
+ if not document_date:
+ return 'Document date is empty or not in a proper format'
+ elif abs(submission_date - document_date) > datetime.timedelta(days=3):
+ return 'Document date must be within 3 days of submission date'
+
+ return None
+
+def create_submission_event(request, submission, desc):
+ by = None
+ if request and request.user.is_authenticated():
+ try:
+ by = request.user.person
+ except Person.DoesNotExist:
+ pass
+
+ SubmissionEvent.objects.create(submission=submission, by=by, desc=desc)
-# Some useful states
-UPLOADED = 1
-AWAITING_AUTHENTICATION = 4
-MANUAL_POST_REQUESTED = 5
-POSTED = -1
-POSTED_BY_SECRETARIAT = -2
-CANCELLED = -4
-INITIAL_VERSION_APPROVAL_REQUESTED = 10
-
-
-# Not a real WG
-NONE_WG = 1027
-
-
-def request_full_url(request, submission):
- subject = 'Full URL for managing submission of draft %s' % submission.filename
- from_email = settings.IDSUBMIT_FROM_EMAIL
- to_email = submission.confirmation_email_list()
- url = settings.IDTRACKER_BASE_URL + urlreverse('draft_status_by_hash',
- kwargs=dict(submission_id=submission.submission_id,
- submission_hash=submission.get_hash()))
- send_mail(request, to_email, from_email, subject, 'submit/request_full_url.txt',
- {'submission': submission,
- 'url': url})
-
-
-def perform_post(request, submission):
+def post_submission(request, submission):
system = Person.objects.get(name="(System)")
- group_id = submission.group_acronym_id or NONE_WG
try:
- draft = Document.objects.get(name=submission.filename)
+ draft = Document.objects.get(name=submission.name)
save_document_in_history(draft)
except Document.DoesNotExist:
- draft = Document(name=submission.filename)
+ draft = Document(name=submission.name)
draft.intended_std_level = None
prev_rev = draft.rev
draft.type_id = "draft"
draft.time = datetime.datetime.now()
- draft.title = submission.id_document_name
- if not (group_id == NONE_WG and draft.group and draft.group.type_id == "area"):
+ draft.title = submission.title
+ group = submission.group or Group.objects.get(type="individ")
+ if not (group.type_id == "individ" and draft.group and draft.group.type_id == "area"):
# don't overwrite an assigned area if it's still an individual
# submission
- draft.group_id = group_id
- draft.rev = submission.revision
- draft.pages = submission.txt_page_count
+ draft.group_id = group.pk
+ draft.rev = submission.rev
+ draft.pages = submission.pages
draft.abstract = submission.abstract
was_rfc = draft.get_state_slug() == "rfc"
@@ -87,14 +153,14 @@ def perform_post(request, submission):
draft.expires = datetime.datetime.now() + datetime.timedelta(settings.INTERNET_DRAFT_DAYS_TO_EXPIRE)
draft.save()
- a = submission.tempidauthors_set.filter(author_order=0)
- if a:
- submitter = ensure_person_email_info_exists(a[0]).person
+ submitter_parsed = submission.submitter_parsed()
+ if submitter_parsed["name"] and submitter_parsed["email"]:
+ submitter = ensure_person_email_info_exists(submitter_parsed["name"], submitter_parsed["email"]).person
else:
submitter = system
draft.set_state(State.objects.get(used=True, type="draft", slug="active"))
- DocAlias.objects.get_or_create(name=submission.filename, document=draft)
+ DocAlias.objects.get_or_create(name=submission.name, document=draft)
update_authors(draft, submission)
@@ -136,8 +202,8 @@ def perform_post(request, submission):
state_change_msg = e.desc
- move_docs(submission)
- submission.status_id = POSTED
+ move_files_to_repository(submission)
+ submission.state = DraftSubmissionStateName.objects.get(slug="posted")
announce_to_lists(request, submission)
announce_new_version(request, submission, draft, state_change_msg)
@@ -145,209 +211,33 @@ def perform_post(request, submission):
submission.save()
-def send_announcements(submission, draft, state_change_msg):
- announce_to_lists(request, submission)
- if draft.idinternal and not draft.idinternal.rfc_flag:
- announce_new_version(request, submission, draft, state_change_msg)
- announce_to_authors(request, submission)
-
-
-def announce_to_lists(request, submission):
- authors = []
- for i in submission.tempidauthors_set.order_by('author_order'):
- if not i.author_order:
- continue
- authors.append(i.get_full_name())
-
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- m = Message()
- m.by = Person.objects.get(name="(System)")
- if request.user.is_authenticated():
- try:
- m.by = request.user.get_profile()
- except Person.DoesNotExist:
- pass
- m.subject = 'I-D Action: %s-%s.txt' % (submission.filename, submission.revision)
- m.frm = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL
- m.to = settings.IDSUBMIT_ANNOUNCE_LIST_EMAIL
- if submission.group_acronym:
- m.cc = submission.group_acronym.email_address
- m.body = render_to_string('submit/announce_to_lists.txt', dict(submission=submission,
- authors=authors,
- settings=settings,))
- m.save()
- m.related_docs.add(Document.objects.get(name=submission.filename))
-
- send_mail_message(request, m)
- else:
- subject = 'I-D Action: %s-%s.txt' % (submission.filename, submission.revision)
- from_email = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL
- to_email = [settings.IDSUBMIT_ANNOUNCE_LIST_EMAIL]
- if submission.group_acronym:
- cc = [submission.group_acronym.email_address]
- else:
- cc = None
-
- send_mail(request, to_email, from_email, subject, 'submit/announce_to_lists.txt',
- {'submission': submission,
- 'authors': authors}, cc=cc, save_message=True)
-
-
-def announce_new_version(request, submission, draft, state_change_msg):
- to_email = []
- if draft.idinternal.state_change_notice_to:
- to_email.append(draft.idinternal.state_change_notice_to)
- if draft.idinternal.job_owner:
- to_email.append(draft.idinternal.job_owner.person.email()[1])
- try:
- if draft.idinternal.ballot:
- for p in draft.idinternal.ballot.positions.all():
- if p.discuss == 1 and p.ad.user_level == IESGLogin.AD_LEVEL:
- to_email.append(p.ad.person.email()[1])
- except BallotInfo.DoesNotExist:
- pass
- subject = 'New Version Notification - %s-%s.txt' % (submission.filename, submission.revision)
- from_email = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL
- send_mail(request, to_email, from_email, subject, 'submit/announce_new_version.txt',
- {'submission': submission,
- 'msg': state_change_msg})
-
-
-def announce_new_versionREDESIGN(request, submission, draft, state_change_msg):
- to_email = []
- if draft.notify:
- to_email.append(draft.notify)
- if draft.ad:
- to_email.append(draft.ad.role_email("ad").address)
-
- if draft.stream_id == "iab":
- to_email.append("IAB Stream ")
- elif draft.stream_id == "ise":
- to_email.append("Independent Submission Editor ")
- elif draft.stream_id == "irtf":
- to_email.append("IRSG ")
-
- # if it has been sent to the RFC Editor, keep them in the loop
- if draft.get_state_slug("draft-iesg") in ("ann", "rfcqueue"):
- to_email.append("RFC Editor ")
-
- active_ballot = draft.active_ballot()
- if active_ballot:
- for ad, pos in active_ballot.active_ad_positions().iteritems():
- if pos and pos.pos_id == "discuss":
- to_email.append(ad.role_email("ad").address)
-
- if to_email:
- subject = 'New Version Notification - %s-%s.txt' % (submission.filename, submission.revision)
- from_email = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL
- send_mail(request, to_email, from_email, subject, 'submit/announce_new_version.txt',
- {'submission': submission,
- 'msg': state_change_msg})
-
-if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- announce_new_version = announce_new_versionREDESIGN
-
-def announce_to_authors(request, submission):
- authors = submission.tempidauthors_set.all()
- to_email = list(set(submission.confirmation_email_list() + [u'%s <%s>' % i.email() for i in authors]))
- from_email = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL
- subject = 'New Version Notification for %s-%s.txt' % (submission.filename, submission.revision)
- if submission.group_acronym:
- wg = submission.group_acronym.group_acronym.acronym
- elif submission.filename.startswith('draft-iesg'):
- wg = 'IESG'
- else:
- wg = 'Individual Submission'
- send_mail(request, to_email, from_email, subject, 'submit/announce_to_authors.txt',
- {'submission': submission,
- 'submitter': authors[0].get_full_name(),
- 'wg': wg})
-
-
-def find_person(first_name, last_name, middle_initial, name_suffix, email):
- person_list = None
- if email:
- person_list = PersonOrOrgInfo.objects.filter(emailaddress__address=email).distinct()
- if person_list and len(person_list) == 1:
- return person_list[0]
- if not person_list:
- person_list = PersonOrOrgInfo.objects.all()
- person_list = person_list.filter(first_name=first_name,
- last_name=last_name)
- if middle_initial:
- person_list = person_list.filter(middle_initial=middle_initial)
- if name_suffix:
- person_list = person_list.filter(name_suffix=name_suffix)
- if person_list:
- return person_list[0]
- return None
-
-
-def update_authors(draft, submission):
- # TempAuthor of order 0 is submitter
- new_authors = list(submission.tempidauthors_set.filter(author_order__gt=0))
- person_pks = []
- for author in new_authors:
- person = find_person(author.first_name, author.last_name,
- author.middle_initial, author.name_suffix,
- author.email_address)
- if not person:
- person = PersonOrOrgInfo(
- first_name=author.first_name,
- last_name=author.last_name,
- middle_initial=author.middle_initial or '',
- name_suffix=author.name_suffix or '',
- )
- person.save()
- if author.email_address:
- EmailAddress.objects.create(
- address=author.email_address,
- priority=1,
- type='INET',
- person_or_org=person,
- )
- person_pks.append(person.pk)
- try:
- idauthor = IDAuthor.objects.get(
- document=draft,
- person=person,
- )
- idauthor.author_order = author.author_order
- except IDAuthor.DoesNotExist:
- idauthor = IDAuthor(
- document=draft,
- person=person,
- author_order=author.author_order,
- )
- idauthor.save()
- draft.authors.exclude(person__pk__in=person_pks).delete()
-
-def get_person_from_author(author):
- persons = None
+def get_person_from_name_email(name, email):
# try email
- if author.email_address:
- persons = Person.objects.filter(email__address=author.email_address).distinct()
+ if email:
+ persons = Person.objects.filter(email__address=email).distinct()
if len(persons) == 1:
return persons[0]
+ else:
+ persons = Person.objects.none()
if not persons:
persons = Person.objects.all()
# try full name
- p = persons.filter(alias__name=author.get_full_name()).distinct()
+ p = persons.filter(alias__name=name).distinct()
if p:
return p[0]
return None
-def ensure_person_email_info_exists(author):
- person = get_person_from_author(author)
+def ensure_person_email_info_exists(name, email):
+ person = get_person_from_name_email(name, email)
- # make sure we got a person
+ # make sure we have a person
if not person:
person = Person()
- person.name = author.get_full_name()
+ person.name = name
person.ascii = unaccent.asciify(person.name)
person.save()
@@ -355,9 +245,9 @@ def ensure_person_email_info_exists(author):
if person.name != person.ascii:
Alias.objects.create(name=ascii, person=person)
- # make sure we got an email address
- if author.email_address:
- addr = author.email_address.lower()
+ # make sure we have an email address
+ if email:
+ addr = email.lower()
else:
# we're in trouble, use a fake one
addr = u"unknown-email-%s" % person.name.replace(" ", "-")
@@ -378,12 +268,10 @@ def ensure_person_email_info_exists(author):
return email
-
-def update_authorsREDESIGN(draft, submission):
- # order 0 is submitter
+def update_authors(draft, submission):
authors = []
- for author in submission.tempidauthors_set.exclude(author_order=0).order_by('author_order'):
- email = ensure_person_email_info_exists(author)
+ for order, author in enumerate(submission.authors_parsed()):
+ email = ensure_person_email_info_exists(author["name"], author["email"])
a = DocumentAuthor.objects.filter(document=draft, author=email)
if a:
@@ -391,37 +279,29 @@ def update_authorsREDESIGN(draft, submission):
else:
a = DocumentAuthor(document=draft, author=email)
- a.order = author.author_order
+ a.order = order
a.save()
authors.append(email)
draft.documentauthor_set.exclude(author__in=authors).delete()
+def cancel_submission(submission):
+ submission.state = DraftSubmissionStateName.objects.get(slug="cancel")
+ submission.save()
-if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- update_authors = update_authorsREDESIGN
+ remove_submission_files(submission)
+def rename_submission_files(submission, prev_rev, new_rev):
+ for ext in submission.file_types.split(','):
+ source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.name, prev_rev, ext))
+ dest = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.name, new_rev, ext))
+ os.rename(source, dest)
-def get_person_for_user(user):
- try:
- return user.get_profile().person()
- except:
- return None
-
-
-def is_secretariat(user):
- if not user or not user.is_authenticated():
- return False
- return bool(user.groups.filter(name='Secretariat'))
-
-if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- from ietf.liaisons.accounts import is_secretariat, get_person_for_user
-
-def move_docs(submission):
- for ext in submission.file_type.split(','):
- source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.filename, submission.revision, ext))
- dest = os.path.join(settings.IDSUBMIT_REPOSITORY_PATH, '%s-%s%s' % (submission.filename, submission.revision, ext))
+def move_files_to_repository(submission):
+ for ext in submission.file_types.split(','):
+ source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.name, submission.rev, ext))
+ dest = os.path.join(settings.IDSUBMIT_REPOSITORY_PATH, '%s-%s%s' % (submission.name, submission.rev, ext))
if os.path.exists(source):
os.rename(source, dest)
else:
@@ -430,28 +310,28 @@ def move_docs(submission):
else:
raise ValueError("Intended to move '%s' to '%s', but found source and destination missing.")
-def remove_docs(submission):
- for ext in submission.file_type.split(','):
- source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.filename, submission.revision, ext))
+def remove_submission_files(submission):
+ for ext in submission.file_types.split(','):
+ source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.name, submission.rev, ext))
if os.path.exists(source):
os.unlink(source)
-def get_approvable_submissions(user):
+def approvable_submissions_for_user(user):
if not user.is_authenticated():
return []
- res = IdSubmissionDetail.objects.filter(status=INITIAL_VERSION_APPROVAL_REQUESTED).order_by('-submission_date')
+ res = Submission.objects.filter(state="grp-appr").order_by('-submission_date')
if has_role(user, "Secretariat"):
return res
# those we can reach as chair
- return res.filter(group_acronym__role__name="chair", group_acronym__role__person__user=user)
+ return res.filter(group__role__name="chair", group__role__person__user=user)
-def get_preapprovals(user):
+def preapprovals_for_user(user):
if not user.is_authenticated():
return []
- posted = IdSubmissionDetail.objects.distinct().filter(status__in=[POSTED, POSTED_BY_SECRETARIAT]).values_list('filename', flat=True)
+ posted = Submission.objects.distinct().filter(state="posted").values_list('name', flat=True)
res = Preapproval.objects.exclude(name__in=posted).order_by("-time").select_related('by')
if has_role(user, "Secretariat"):
return res
@@ -462,125 +342,23 @@ def get_preapprovals(user):
return res
-def get_recently_approved(user, since):
+def recently_approved_by_user(user, since):
if not user.is_authenticated():
return []
- res = IdSubmissionDetail.objects.distinct().filter(status__in=[POSTED, POSTED_BY_SECRETARIAT], submission_date__gte=since, revision="00").order_by('-submission_date')
+ res = Submission.objects.distinct().filter(state="posted", submission_date__gte=since, rev="00").order_by('-submission_date')
if has_role(user, "Secretariat"):
return res
# those we can reach as chair
- return res.filter(group_acronym__role__name="chair", group_acronym__role__person__user=user)
+ return res.filter(group__role__name="chair", group__role__person__user=user)
-class DraftValidation(object):
+def expirable_submissions(older_than_days):
+ cutoff = datetime.date.today() - datetime.timedelta(days=older_than_days)
+ return Submission.objects.exclude(state__in=("cancel", "posted")).filter(submission_date__lt=cutoff)
- def __init__(self, draft):
- self.draft = draft
- self.warnings = {}
- self.passes_idnits = self.passes_idnits()
- self.wg = self.get_working_group()
- self.authors = self.get_authors()
- self.submitter = self.get_submitter()
+def expire_submission(submission, by):
+ submission.state_id = "cancel"
+ submission.save()
- def passes_idnits(self):
- passes_idnits = self.check_idnits_success(self.draft.idnits_message)
- return passes_idnits
-
- def get_working_group(self):
- if self.draft.group_acronym and self.draft.group_acronym.pk == NONE_WG:
- return None
- return self.draft.group_acronym
-
- def check_idnits_success(self, idnits_message):
- if not idnits_message:
- return False
- success_re = re.compile('\s+Summary:\s+0\s+|No nits found')
- if success_re.search(idnits_message):
- return True
- return False
-
- def is_valid_attr(self, key):
- if key in self.warnings.keys():
- return False
- return True
-
- def is_valid(self):
- self.validate_metadata()
- return not bool(self.warnings.keys()) and self.passes_idnits
-
- def validate_metadata(self):
- self.validate_revision()
- self.validate_title()
- self.validate_authors()
- self.validate_abstract()
- self.validate_creation_date()
- self.validate_wg()
- self.validate_files()
-
- def validate_files(self):
- if self.draft.status_id in [POSTED, POSTED_BY_SECRETARIAT]:
- return
- for ext in self.draft.file_type.split(','):
- source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (self.draft.filename, self.draft.revision, ext))
- if not os.path.exists(source):
- self.add_warning('document_files', '"%s" were not found in the staging area. We recommend you that you cancel this submission and upload your files again.' % os.path.basename(source))
- break
-
- def validate_title(self):
- if not self.draft.id_document_name:
- self.add_warning('title', 'Title is empty or was not found')
-
- def validate_wg(self):
- if self.wg and not self.wg.status_id == IETFWG.ACTIVE:
- self.add_warning('group', 'Group exists but is not an active group')
-
- def validate_abstract(self):
- if not self.draft.abstract:
- self.add_warning('abstract', 'Abstract is empty or was not found')
-
- def add_warning(self, key, value):
- self.warnings.update({key: value})
-
- def validate_revision(self):
- if self.draft.status_id in [POSTED, POSTED_BY_SECRETARIAT]:
- return
- revision = self.draft.revision
- existing_revisions = [int(i.revision_display()) for i in InternetDraft.objects.filter(filename=self.draft.filename)]
- expected = 0
- if existing_revisions:
- expected = max(existing_revisions) + 1
- try:
- if int(revision) != expected:
- self.add_warning('revision', 'Invalid Version Number (Version %02d is expected)' % expected)
- except ValueError:
- self.add_warning('revision', 'Revision not found')
-
- def validate_authors(self):
- if not self.authors:
- self.add_warning('authors', 'No authors found')
- return
-
- def validate_creation_date(self):
- date = self.draft.creation_date
- if not date:
- self.add_warning('creation_date', 'Creation Date field is empty or the creation date is not in a proper format')
- return
- submit_date = self.draft.submission_date
- if (date + datetime.timedelta(days=3) < submit_date or
- date - datetime.timedelta(days=3) > submit_date):
- self.add_warning('creation_date', 'Creation Date must be within 3 days of submission date')
-
- def get_authors(self):
- return self.draft.tempidauthors_set.exclude(author_order=0).order_by('author_order')
-
- def get_submitter(self):
- submitter = self.draft.tempidauthors_set.filter(author_order=0)
- if submitter:
- return submitter[0]
- elif self.draft.submitter_tag:
- try:
- return PersonOrOrgInfo.objects.get(pk=self.draft.submitter_tag)
- except PersonOrOrgInfo.DoesNotExist:
- return False
- return None
+ SubmissionEvent.objects.create(submission=submission, by=by, desc="Canceled expired submission")
diff --git a/ietf/submit/views.py b/ietf/submit/views.py
index 57063fa0c..60b91599b 100644
--- a/ietf/submit/views.py
+++ b/ietf/submit/views.py
@@ -1,35 +1,98 @@
# Copyright The IETF Trust 2007, All Rights Reserved
import datetime
+import os
from django.conf import settings
from django.core.urlresolvers import reverse as urlreverse
+from django.core.validators import validate_email, ValidationError
from django.contrib.sites.models import Site
from django.http import HttpResponseRedirect, Http404, HttpResponseForbidden, HttpResponseNotAllowed
-from django.shortcuts import get_object_or_404
+from django.shortcuts import get_object_or_404, redirect
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.utils.text import get_text_list
from django.utils.html import escape
-from ietf.group.models import Group
+from ietf.doc.models import Document
+from ietf.group.models import Group, Role
from ietf.utils.mail import send_mail
-from ietf.ietfauth.decorators import has_role, role_required
-from ietf.submit.models import IdSubmissionDetail, Preapproval
-from ietf.submit.forms import UploadForm, AutoPostForm, MetaDataForm, PreapprovalForm
-from ietf.submit.utils import UPLOADED, AWAITING_AUTHENTICATION, MANUAL_POST_REQUESTED, CANCELLED, POSTED, INITIAL_VERSION_APPROVAL_REQUESTED
-from ietf.submit.utils import is_secretariat, get_approvable_submissions, get_preapprovals, get_recently_approved, get_person_for_user, perform_post, remove_docs, request_full_url
-from ietf.submit.utils import DraftValidation
+from ietf.ietfauth.utils import has_role, role_required
+from ietf.submit.models import Submission, Preapproval, DraftSubmissionStateName
+from ietf.submit.forms import UploadForm, NameEmailForm, EditSubmissionForm, PreapprovalForm
+from ietf.submit.utils import approvable_submissions_for_user, preapprovals_for_user, recently_approved_by_user
+from ietf.submit.utils import check_idnits, found_idnits, validate_submission, create_submission_event
+from ietf.submit.utils import post_submission, cancel_submission, rename_submission_files
+from ietf.submit.mail import send_full_url, send_approval_request_to_group, send_submission_confirmation, submission_confirmation_email_list, send_manual_post_request
+from ietf.utils.accesstoken import generate_random_key, generate_access_token
-import debug
-
-def submit_index(request):
+def upload_submission(request):
if request.method == 'POST':
try:
- form = UploadForm(request=request, data=request.POST, files=request.FILES)
+ form = UploadForm(request, data=request.POST, files=request.FILES)
if form.is_valid():
- submit = form.save()
- return HttpResponseRedirect(urlreverse(draft_status, None, kwargs={'submission_id': submit.submission_id, 'submission_hash': submit.get_hash()}))
- except IOError, e:
+ # save files
+ file_types = []
+ for ext in ['txt', 'pdf', 'xml', 'ps']:
+ f = form.cleaned_data[ext]
+ if not f:
+ continue
+ file_types.append('.%s' % ext)
+
+ draft = form.parsed_draft
+
+ name = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s.%s' % (draft.filename, draft.revision, ext))
+ with open(name, 'wb+') as destination:
+ for chunk in f.chunks():
+ destination.write(chunk)
+
+ # check idnits
+ text_path = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s.txt' % (draft.filename, draft.revision))
+ idnits_message = check_idnits(text_path)
+
+ # extract author lines
+ authors = []
+ for author in draft.get_author_list():
+ full_name, first_name, middle_initial, last_name, name_suffix, email, company = author
+
+ line = full_name.replace("\n", "").replace("\r", "").replace("<", "").replace(">", "").strip()
+ email = (email or "").strip()
+
+ if email:
+ try:
+ validate_email(email)
+ except ValidationError:
+ email = ""
+
+ if email:
+ line += u" <%s>" % email
+
+ authors.append(line)
+
+ # save submission
+ submission = Submission.objects.create(
+ state=DraftSubmissionStateName.objects.get(slug="uploaded"),
+ remote_ip=form.remote_ip,
+ name=draft.filename,
+ group=form.group,
+ title=draft.get_title(),
+ abstract=draft.get_abstract(),
+ rev=draft.revision,
+ pages=draft.get_pagecount(),
+ authors="\n".join(authors),
+ note="",
+ first_two_pages=''.join(draft.pages[:2]),
+ file_size=form.cleaned_data['txt'].size,
+ file_types=','.join(file_types),
+ submission_date=datetime.date.today(),
+ document_date=draft.get_creation_date(),
+ replaces="",
+ idnits_message=idnits_message,
+ )
+
+ create_submission_event(request, submission, desc="Uploaded submission")
+
+ return redirect("submit_submission_status_by_hash", submission_id=submission.pk, access_token=submission.access_token())
+ except IOError as e:
if "read error" in str(e): # The server got an IOError when trying to read POST data
form = UploadForm(request=request)
form._errors = {}
@@ -38,269 +101,297 @@ def submit_index(request):
raise
else:
form = UploadForm(request=request)
- return render_to_response('submit/submit_index.html',
+
+ return render_to_response('submit/upload_submission.html',
{'selected': 'index',
'form': form},
context_instance=RequestContext(request))
+def note_well(request):
+ return render_to_response('submit/note_well.html', {'selected': 'notewell'},
+ context_instance=RequestContext(request))
-def submit_status(request):
+def tool_instructions(request):
+ return render_to_response('submit/tool_instructions.html', {'selected': 'instructions'},
+ context_instance=RequestContext(request))
+
+def search_submission(request):
error = None
- filename = None
+ name = None
if request.method == 'POST':
- filename = request.POST.get('filename', '')
- detail = IdSubmissionDetail.objects.filter(filename=filename).order_by('-pk')
- if detail:
- return HttpResponseRedirect(urlreverse(draft_status, None, kwargs={'submission_id': detail[0].submission_id}))
- error = 'No valid history found for %s' % filename
- return render_to_response('submit/submit_status.html',
+ name = request.POST.get('name', '')
+ submission = Submission.objects.filter(name=name).order_by('-pk')
+ if submission:
+ return HttpResponseRedirect(urlreverse(submission_status, None, kwargs={'submission_id': submission[0].pk}))
+ error = 'No valid submission found for %s' % name
+ return render_to_response('submit/search_submission.html',
{'selected': 'status',
'error': error,
- 'filename': filename},
+ 'name': name},
context_instance=RequestContext(request))
+def can_edit_submission(request, submission, access_token):
+ key_matched = access_token and submission.access_token() == access_token
+ if not key_matched: key_matched = submission.access_key == access_token # backwards-compat
+ return key_matched or has_role(request.user, "Secretariat")
-def _can_approve(user, detail):
- person = get_person_for_user(user)
- if detail.status_id != INITIAL_VERSION_APPROVAL_REQUESTED or not detail.group_acronym:
- return None
- if person in [i.person for i in detail.group_acronym.wgchair_set.all()] or is_secretariat(user):
- return True
- return False
+def submission_status(request, submission_id, access_token=None):
+ submission = get_object_or_404(Submission, pk=submission_id)
-
-def _can_force_post(user, detail):
- if detail.status_id not in [MANUAL_POST_REQUESTED,
- AWAITING_AUTHENTICATION, INITIAL_VERSION_APPROVAL_REQUESTED]:
- return None
- if is_secretariat(user):
- return True
- return False
-
-def _can_cancel(user, detail, submission_hash):
- if detail.status_id in [CANCELLED, POSTED]:
- return None
- if is_secretariat(user):
- return True
- if submission_hash and detail.get_hash() == submission_hash:
- return True
- return False
-
-def _can_edit(user, detail, submission_hash):
- if detail.status_id != UPLOADED:
- return None
- if is_secretariat(user):
- return True
- if submission_hash and detail.get_hash() == submission_hash:
- return True
- return False
-
-def draft_status(request, submission_id, submission_hash=None, message=None):
- detail = get_object_or_404(IdSubmissionDetail, submission_id=submission_id)
- if submission_hash and not detail.get_hash() == submission_hash:
+ key_matched = access_token and submission.access_token() == access_token
+ if not key_matched: key_matched = submission.access_key == access_token # backwards-compat
+ if access_token and not key_matched:
raise Http404
- validation = DraftValidation(detail)
- is_valid = validation.is_valid()
- status = None
- allow_edit = _can_edit(request.user, detail, submission_hash)
- can_force_post = _can_force_post(request.user, detail)
- can_approve = _can_approve(request.user, detail)
- can_cancel = _can_cancel(request.user, detail, submission_hash)
- if detail.status_id != UPLOADED:
- if detail.status_id == CANCELLED:
- message = ('error', 'This submission has been cancelled, modification is no longer possible')
- status = detail.status
- allow_edit = None
- if detail.group_acronym and detail.revision == '00':
- replaces = "Replaces draft"
- else:
- replaces = None
- def get_submitter(details):
- submitter = details.tempidauthors_set.filter(author_order=0)
- if submitter:
- return submitter[0]
- elif details.submitter_tag:
- try:
- return PersonOrOrgInfo.objects.get(pk=details.submitter_tag)
- except PersonOrOrgInfo.DoesNotExist:
- return False
- return None
+ errors = validate_submission(submission)
+ passes_idnits = found_idnits(submission.idnits_message)
+ is_secretariat = has_role(request.user, "Secretariat")
+ is_chair = submission.group and submission.group.has_role(request.user, "chair")
- if request.method == 'POST' and allow_edit:
- if request.POST.get('autopost', False):
- auto_post_form = AutoPostForm(draft=detail, validation=validation, replaces=replaces, data=request.POST)
- if auto_post_form.is_valid():
- try:
- preapproval = Preapproval.objects.get(name=detail.filename)
- except Preapproval.DoesNotExist:
- preapproval = None
+ can_edit = can_edit_submission(request, submission, access_token) and submission.state_id == "uploaded"
+ can_cancel = (key_matched or is_secretariat) and submission.state.next_states.filter(slug="cancel")
+ can_group_approve = (is_secretariat or is_chair) and submission.state_id == "grp-appr"
+ can_force_post = is_secretariat and submission.state.next_states.filter(slug="posted")
+ show_send_full_url = not key_matched and not is_secretariat and submission.state_id not in ("cancel", "posted")
- if detail.revision == '00' and detail.group_acronym and detail.group_acronym.type_id == "wg" and not preapproval:
- detail.status_id = INITIAL_VERSION_APPROVAL_REQUESTED
- detail.save()
+ confirmation_list = submission_confirmation_email_list(submission)
- submitter = auto_post_form.save_submitter_info()
- subject = 'New draft waiting for approval: %s' % detail.filename
- from_email = settings.IDSUBMIT_FROM_EMAIL
- to_email = list(set(i.person.email()[1] for i in detail.group_acronym.wgchair_set.all()))
- if to_email:
- authors = detail.tempidauthors_set.exclude(author_order=0).order_by('author_order')
- send_mail(request, to_email, from_email, subject, 'submit/submission_approval.txt',
- {'submitter': submitter, 'authors': authors,
- 'draft': detail, 'domain': Site.objects.get_current().domain})
- return HttpResponseRedirect(urlreverse(draft_status, None, kwargs={'submission_id': detail.submission_id}))
- else:
- auto_post_form.save(request)
- detail = get_object_or_404(IdSubmissionDetail, submission_id=submission_id)
- submitter = get_submitter(detail)
- validation = DraftValidation(detail)
- is_valid = validation.is_valid()
- status = detail.status
- can_force_post = _can_force_post(request.user, detail)
- can_approve = _can_approve(request.user, detail)
- can_cancel = _can_cancel(request.user, detail, submission_hash)
- allow_edit = None
- confirmation_email_addresses = get_text_list(detail.confirmation_email_list(), "and")
- submitter_email_address = "<%s>" % submitter.email_address
- if submitter_email_address in confirmation_email_addresses:
- message = ('success', 'Your submission is pending email authentication. An email has been sent to %s with instructions.' % escape(confirmation_email_addresses) )
- else:
- message = ('warning',
- """Your submission is pending email authentication. An email has been sent to %s with instructions.
-
- Please note that since the database does not have your email address in the list of authors of previous
- revisions of the document, you are not receiving a confirmation email yourself; one of the
- addressees above will have to send a confirmation in order to complete the submission. This is done
- to avoid document hijacking. If none of the known previous authors will be able to confirm the
- submission, please contact the secretariat for action.
- """ % escape(confirmation_email_addresses) )
-
- else:
- submission_hash = detail.get_hash()
- if submission_hash:
- return HttpResponseRedirect(urlreverse('draft_edit_by_hash', None, kwargs={'submission_id': detail.submission_id, 'submission_hash': submission_hash}))
- else:
- return HttpResponseRedirect(urlreverse(draft_edit, None, kwargs={'submission_id': detail.submission_id }))
- else:
- auto_post_form = AutoPostForm(draft=detail, validation=validation, replaces=replaces)
+ try:
+ preapproval = Preapproval.objects.get(name=submission.name)
+ except Preapproval.DoesNotExist:
+ preapproval = None
- show_notify_button = False
- if allow_edit == False or can_cancel == False:
- show_notify_button = True
- if submission_hash is None and is_secretariat(request.user):
- submission_hash = detail.get_hash() # we'll need this when rendering the cancel button in the form
- return render_to_response('submit/draft_status.html',
- {'selected': 'status',
- 'detail': detail,
- 'validation': validation,
- 'auto_post_form': auto_post_form,
- 'is_valid': is_valid,
- 'status': status,
- 'message': message,
- 'allow_edit': allow_edit,
- 'can_force_post': can_force_post,
- 'can_approve': can_approve,
- 'can_cancel': can_cancel,
- 'submission_hash': submission_hash,
- 'show_notify_button': show_notify_button,
- },
- context_instance=RequestContext(request))
+ requires_group_approval = submission.rev == '00' and submission.group and submission.group.type_id in ("wg", "rg") and not preapproval
+ requires_prev_authors_approval = Document.objects.filter(name=submission.name)
-def draft_cancel(request, submission_id, submission_hash=None):
- if request.method!='POST':
- return HttpResponseNotAllowed(['POST'])
- detail = get_object_or_404(IdSubmissionDetail, submission_id=submission_id)
- can_cancel = _can_cancel(request.user, detail, submission_hash)
- if not can_cancel:
- if can_cancel == None:
- raise Http404
- return HttpResponseForbidden('You have no permission to perform this action')
- detail.status_id = CANCELLED
- detail.save()
- remove_docs(detail)
- return HttpResponseRedirect(urlreverse(draft_status, None, kwargs={'submission_id': submission_id}))
-
-
-def draft_edit(request, submission_id, submission_hash=None):
- detail = get_object_or_404(IdSubmissionDetail, submission_id=submission_id)
- can_edit = _can_edit(request.user, detail, submission_hash)
- if not can_edit:
- if can_edit == None:
- raise Http404
- return HttpResponseForbidden('You have no permission to perform this action')
- validation = DraftValidation(detail)
- validation.validate_wg()
- if request.method == 'POST':
- form = MetaDataForm(draft=detail, validation=validation, data=request.POST)
- if form.is_valid():
- form.save(request)
- return HttpResponseRedirect(urlreverse(draft_status, None, kwargs={'submission_id': detail.submission_id}))
- else:
- form = MetaDataForm(draft=detail, validation=validation)
- return render_to_response('submit/draft_edit.html',
- {'selected': 'status',
- 'detail': detail,
- 'validation': validation,
- 'form': form,
- 'settings': settings
- },
- context_instance=RequestContext(request))
-
-
-def draft_confirm(request, submission_id, auth_key):
- detail = get_object_or_404(IdSubmissionDetail, submission_id=submission_id)
message = None
- if auth_key != detail.auth_key:
- message = ('error', 'Incorrect authorization key')
- elif detail.status_id != AWAITING_AUTHENTICATION:
- message = ('error', 'The submission can not be autoposted because it is in state: %s' % detail.status.status_value)
- else:
- if request.method=='POST':
- message = ('success', 'Authorization key accepted. Auto-Post complete')
- perform_post(request, detail)
+
+ if submission.state_id == "cancel":
+ message = ('error', 'This submission has been canceled, modification is no longer possible.')
+ elif submission.state_id == "auth":
+ message = ('success', u'The submission is pending email authentication. An email has been sent to: %s' % ", ".join(confirmation_list))
+ elif submission.state_id == "grp-appr":
+ message = ('success', 'The submission is pending approval by the group chairs.')
+ elif submission.state_id == "aut-appr":
+ message = ('success', 'The submission is pending approval by the authors of the previous version. An email has been sent to: %s' % ", ".join(confirmation_list))
+
+
+ submitter_form = NameEmailForm(initial=submission.submitter_parsed(), prefix="submitter")
+
+ if request.method == 'POST':
+ action = request.POST.get('action')
+ if action == "autopost" and submission.state_id == "uploaded":
+ if not can_edit:
+ return HttpResponseForbidden("You do not have permission to perfom this action")
+
+ submitter_form = NameEmailForm(request.POST, prefix="submitter")
+ if submitter_form.is_valid():
+ submission.submitter = submitter_form.cleaned_line()
+
+ if requires_group_approval:
+ submission.state = DraftSubmissionStateName.objects.get(slug="grp-appr")
+ submission.save()
+
+ sent_to = send_approval_request_to_group(request, submission)
+
+ desc = "sent approval email to group chairs: %s" % u", ".join(sent_to)
+
+ else:
+ submission.auth_key = generate_random_key()
+ if requires_prev_authors_approval:
+ submission.state = DraftSubmissionStateName.objects.get(slug="aut-appr")
+ else:
+ submission.state = DraftSubmissionStateName.objects.get(slug="auth")
+ submission.save()
+
+ sent_to = send_submission_confirmation(request, submission)
+
+ if submission.state_id == "aut-appr":
+ desc = u"sent confirmation email to previous authors: %s" % u", ".join(sent_to)
+ else:
+ desc = u"sent confirmation email to submitter and authors: %s" % u", ".join(sent_to)
+
+ create_submission_event(request, submission, u"Set submitter to \"%s\" and %s" % (submission.submitter, desc))
+
+ return redirect("submit_submission_status_by_hash", submission_id=submission.pk, access_token=access_token)
+
+ elif action == "edit" and submission.state_id == "uploaded":
+ if access_token:
+ return redirect("submit_edit_submission_by_hash", submission_id=submission.pk, access_token=access_token)
+ else:
+ return redirect("submit_edit_submission", submission_id=submission.pk)
+
+ elif action == "sendfullurl" and submission.state_id not in ("cancel", "posted"):
+ sent_to = send_full_url(request, submission)
+
+ message = ('success', u'An email has been sent with the full access URL to: %s' % u",".join(confirmation_list))
+
+ create_submission_event(request, submission, u"Sent full access URL to: %s" % u", ".join(sent_to))
+
+ elif action == "cancel" and submission.state.next_states.filter(slug="cancel"):
+ if not can_cancel:
+ return HttpResponseForbidden('You do not have permission to perform this action')
+
+ cancel_submission(submission)
+
+ create_submission_event(request, submission, "Canceled submission")
+
+ return redirect("submit_submission_status", submission_id=submission_id)
+
+
+ elif action == "approve" and submission.state_id == "grp-appr":
+ if not can_group_approve:
+ return HttpResponseForbidden('You do not have permission to perform this action')
+
+ post_submission(request, submission)
+
+ create_submission_event(request, submission, "Approved and posted submission")
+
+ return redirect("doc_view", name=submission.name)
+
+
+ elif action == "forcepost" and submission.state.next_states.filter(slug="posted"):
+ if not can_force_post:
+ return HttpResponseForbidden('You do not have permission to perform this action')
+
+ post_submission(request, submission)
+
+ if submission.state_id == "manual":
+ desc = "Posted submission manually"
+ else:
+ desc = "Forced post of submission"
+
+ create_submission_event(request, submission, desc)
+
+ return redirect("doc_view", name=submission.name)
+
+
else:
- return render_to_response('submit/last_confirmation_step.html',
- {'detail': detail, },
- context_instance=RequestContext(request))
- return draft_status(request, submission_id, message=message)
+ # something went wrong, turn this into a GET and let the user deal with it
+ return HttpResponseRedirect("")
+
+ return render_to_response('submit/submission_status.html',
+ {'selected': 'status',
+ 'submission': submission,
+ 'errors': errors,
+ 'passes_idnits': passes_idnits,
+ 'submitter_form': submitter_form,
+ 'message': message,
+ 'can_edit': can_edit,
+ 'can_force_post': can_force_post,
+ 'can_group_approve': can_group_approve,
+ 'can_cancel': can_cancel,
+ 'show_send_full_url': show_send_full_url,
+ 'requires_group_approval': requires_group_approval,
+ 'requires_prev_authors_approval': requires_prev_authors_approval,
+ 'confirmation_list': confirmation_list,
+ },
+ context_instance=RequestContext(request))
-def draft_approve(request, submission_id, check_function=_can_approve):
- if request.method!='POST':
- return HttpResponseNotAllowed(['POST'])
- detail = get_object_or_404(IdSubmissionDetail, submission_id=submission_id)
- can_perform = check_function(request.user, detail)
- if not can_perform:
- if can_perform == None:
- raise Http404
- return HttpResponseForbidden('You have no permission to perform this action')
- perform_post(request, detail)
- return HttpResponseRedirect(urlreverse(draft_status, None, kwargs={'submission_id': submission_id}))
+def edit_submission(request, submission_id, access_token=None):
+ submission = get_object_or_404(Submission, pk=submission_id, state="uploaded")
+
+ if not can_edit_submission(request.user, submission, access_token):
+ return HttpResponseForbidden('You do not have permission to access this page')
+
+ errors = validate_submission(submission)
+ form_errors = False
+
+ # we split the form handling into multiple forms, one for the
+ # submission itself, one for the submitter, and a list of forms
+ # for the authors
+
+ empty_author_form = NameEmailForm(email_required=False)
+
+ if request.method == 'POST':
+ # get a backup submission now, the model form may change some
+ # fields during validation
+ prev_submission = Submission.objects.get(pk=submission.pk)
+
+ edit_form = EditSubmissionForm(request.POST, instance=submission, prefix="edit")
+ submitter_form = NameEmailForm(request.POST, prefix="submitter")
+ author_forms = [ NameEmailForm(request.POST, email_required=False, prefix=prefix)
+ for prefix in request.POST.getlist("authors-prefix")
+ if prefix != "authors-" ]
+
+ # trigger validation of all forms
+ validations = [edit_form.is_valid(), submitter_form.is_valid()] + [ f.is_valid() for f in author_forms ]
+ if all(validations):
+ submission.submitter = submitter_form.cleaned_line()
+ submission.authors = "\n".join(f.cleaned_line() for f in author_forms)
+ edit_form.save(commit=False) # transfer changes
+
+ if submission.rev != prev_submission.rev:
+ rename_submission_files(submission, prev_submission.rev, submission.rev)
+
+ submission.state = DraftSubmissionStateName.objects.get(slug="manual")
+ submission.save()
+
+ send_manual_post_request(request, submission, errors)
+
+ changed_fields = [
+ submission._meta.get_field(f).verbose_name
+ for f in list(edit_form.fields.keys()) + ["submitter", "authors"]
+ if getattr(submission, f) != getattr(prev_submission, f)
+ ]
+
+ if changed_fields:
+ desc = u"Edited %s and sent request for manual post" % u", ".join(changed_fields)
+ else:
+ desc = "Sent request for manual post"
+
+ create_submission_event(request, submission, desc)
+
+ return redirect("submit_submission_status", submission_id=submission.pk)
+ else:
+ form_errors = True
+ else:
+ edit_form = EditSubmissionForm(instance=submission, prefix="edit")
+ submitter_form = NameEmailForm(initial=submission.submitter_parsed(), prefix="submitter")
+ author_forms = [ NameEmailForm(initial=author, email_required=False, prefix="authors-%s" % i)
+ for i, author in enumerate(submission.authors_parsed()) ]
+
+ return render_to_response('submit/edit_submission.html',
+ {'selected': 'status',
+ 'submission': submission,
+ 'edit_form': edit_form,
+ 'submitter_form': submitter_form,
+ 'author_forms': author_forms,
+ 'empty_author_form': empty_author_form,
+ 'errors': errors,
+ 'form_errors': form_errors,
+ },
+ context_instance=RequestContext(request))
-def draft_force(request, submission_id):
- if request.method!='POST':
- return HttpResponseNotAllowed(['POST'])
- return draft_approve(request, submission_id, check_function=_can_force_post)
+def confirm_submission(request, submission_id, auth_token):
+ submission = get_object_or_404(Submission, pk=submission_id)
+ key_matched = submission.auth_key and auth_token == generate_access_token(submission.auth_key)
+ if not key_matched: key_matched = auth_token == submission.auth_key # backwards-compat
+
+ if request.method == 'POST' and submission.state_id in ("auth", "aut-appr") and key_matched:
+ post_submission(request, submission)
+
+ create_submission_event(request, submission, "Confirmed and posted submission")
+
+ return redirect("doc_view", name=submission.name)
+
+ return render_to_response('submit/confirm_submission.html', {
+ 'submission': submission,
+ 'key_matched': key_matched,
+ }, context_instance=RequestContext(request))
-def full_url_request(request, submission_id):
- if request.method!='POST':
- return HttpResponseNotAllowed(['POST'])
- detail = get_object_or_404(IdSubmissionDetail, submission_id=submission_id)
- request_full_url(request, detail)
- message = ('success', 'An email has been sent to draft authors to inform them of the full access url')
- return draft_status(request, submission_id, message=message)
def approvals(request):
- approvals = get_approvable_submissions(request.user)
- preapprovals = get_preapprovals(request.user)
+ approvals = approvable_submissions_for_user(request.user)
+ preapprovals = preapprovals_for_user(request.user)
days = 30
- recently_approved = get_recently_approved(request.user, datetime.date.today() - datetime.timedelta(days=days))
+ recently_approved = recently_approved_by_user(request.user, datetime.date.today() - datetime.timedelta(days=days))
return render_to_response('submit/approvals.html',
{'selected': 'approvals',
@@ -316,7 +407,7 @@ def add_preapproval(request):
groups = Group.objects.filter(type="wg").exclude(state="conclude").order_by("acronym").distinct()
if not has_role(request.user, "Secretariat"):
- groups = groups.filter(role__person=request.user.get_profile())
+ groups = groups.filter(role__person__user=request.user)
if request.method == "POST":
form = PreapprovalForm(request.POST)
@@ -341,7 +432,7 @@ def add_preapproval(request):
def cancel_preapproval(request, preapproval_id):
preapproval = get_object_or_404(Preapproval, pk=preapproval_id)
- if not preapproval in get_preapprovals(request.user):
+ if preapproval not in preapprovals_for_user(request.user):
raise HttpResponseForbidden("You do not have permission to cancel this preapproval.")
if request.method == "POST" and request.POST.get("action", "") == "cancel":
diff --git a/ietf/sync/tests.py b/ietf/sync/tests.py
index c89ebe49e..e180da3bd 100644
--- a/ietf/sync/tests.py
+++ b/ietf/sync/tests.py
@@ -15,10 +15,7 @@ from ietf.sync import iana, rfceditor
from pyquery import PyQuery
-class IANASyncTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
+class IANASyncTests(TestCase):
def test_protocol_page_sync(self):
draft = make_test_data()
DocAlias.objects.create(name="rfc1234", document=draft)
@@ -177,10 +174,7 @@ ICANN
self.assertEqual(DocEvent.objects.filter(doc=draft, type="iana_review").count(), 1)
-class RFCSyncTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
+class RFCSyncTests(TestCase):
def test_rfc_index(self):
doc = make_test_data()
doc.set_state(State.objects.get(used=True, type="draft-iesg", slug="rfcqueue"))
@@ -369,10 +363,7 @@ class RFCSyncTestCase(TestCase):
self.assertEquals(len(changed), 0)
self.assertEquals(len(warnings), 0)
-class DiscrepanciesTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
+class DiscrepanciesTests(TestCase):
def test_discrepancies(self):
make_test_data()
@@ -411,10 +402,7 @@ class DiscrepanciesTestCase(TestCase):
r = self.client.get(urlreverse("ietf.sync.views.discrepancies"))
self.assertTrue(doc.name in r.content)
-class RFCEditorUndoTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
+class RFCEditorUndoTests(TestCase):
def test_rfceditor_undo(self):
draft = make_test_data()
diff --git a/ietf/templates/base.html b/ietf/templates/base.html
index a6e057215..3ef9f82bd 100644
--- a/ietf/templates/base.html
+++ b/ietf/templates/base.html
@@ -70,7 +70,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% include "base_leftmenu.html" %}
+{% include "base/left_menu.html" %}
{% if version_num %}
diff --git a/ietf/templates/base_leftmenu.html b/ietf/templates/base/left_menu.html
similarity index 79%
rename from ietf/templates/base_leftmenu.html
rename to ietf/templates/base/left_menu.html
index 645a95a9b..ed5f4bce8 100644
--- a/ietf/templates/base_leftmenu.html
+++ b/ietf/templates/base/left_menu.html
@@ -32,49 +32,37 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
{% endcomment %}
-{% load wg_menu %}
-{% load ietf_filters ietf_streams community_tags %}
+{% load wg_menu %}
+{% load streams_menu %}
{% load ietf_filters community_tags %}
{% endif %}
{% get_user_managed_lists user as community_lists %}
@@ -113,7 +101,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% else %}
For a WG, the draft enters the IETF stream and the
+stream state becomes "Call for Adoption by WG Issued". For an RG, the
+draft enters the IRTF stream and the stream state becomes "Active RG
+Document".
+ Recommended next state{{ next_states|pluralize }}:
+ {% for state in next_states %}{{ state.name }} {% if not forloop.last %} or {% endif %}{% endfor %}
+
+{% endif %}
+
+
+{% endblock %}
+
+{% block js %}
+
+{% endblock %}
diff --git a/ietf/templates/doc/draft/edit_info.html b/ietf/templates/doc/draft/edit_info.html
index 6ff175f9b..68851832a 100644
--- a/ietf/templates/doc/draft/edit_info.html
+++ b/ietf/templates/doc/draft/edit_info.html
@@ -44,7 +44,7 @@ form.edit-info .actions {
{{ form.returning_item }} {{ form.returning_item.label_tag }} {{ form.returning_item.errors }}
{% endifequal %}
{% ifequal field.name "ad" %}
- {% if user|in_group:"Area_Director" %}
+ {% if user|has_role:"Area Director" %}
{% endif %}
{% endifequal %}
diff --git a/ietf/templates/doc/mail/draft_adopted_email.txt b/ietf/templates/doc/mail/draft_adopted_email.txt
new file mode 100644
index 000000000..1e01fb13d
--- /dev/null
+++ b/ietf/templates/doc/mail/draft_adopted_email.txt
@@ -0,0 +1,8 @@
+{% autoescape off %}{% filter wordwrap:73 %}
+The document {{ doc }} has been adopted in the {{ doc.group.acronym }} {{ doc.group.type.name }} by {{ by }}:
+
+{{ url }}
+{% if comment %}
+
+Comment:
+{{ comment }}{% endif %}{% endfilter %}{% endautoescape %}
diff --git a/ietf/templates/doc/mail/stream_state_changed_email.txt b/ietf/templates/doc/mail/stream_state_changed_email.txt
new file mode 100644
index 000000000..09f9ba922
--- /dev/null
+++ b/ietf/templates/doc/mail/stream_state_changed_email.txt
@@ -0,0 +1,8 @@
+{% autoescape off %}{% filter wordwrap:73 %}
+The {{ state_type.label }} of {{ doc }} has been changed to "{{ new_state.name }}"{% if prev_state %} from "{{ prev_state.name }}"{% endif %} by {{ by }}:
+
+{{ url }}
+{% if comment %}
+
+Comment:
+{{ comment }}{% endif %}{% endfilter %}{% endautoescape %}
diff --git a/ietf/templates/doc/mail/stream_tags_changed_email.txt b/ietf/templates/doc/mail/stream_tags_changed_email.txt
new file mode 100644
index 000000000..99b34b134
--- /dev/null
+++ b/ietf/templates/doc/mail/stream_tags_changed_email.txt
@@ -0,0 +1,10 @@
+{% autoescape off %}{% filter wordwrap:73 %}
+The tags on {{ doc }} have been changed by {{ by }}:
+{{ url }}
+
+{% if added %}Tag{{ added|pluralize }} {% for t in added %}"{{ t }}"{% if not forloop.last %}, {% endif %}{% endfor %} added.{% endif %}
+{% if removed %}Tag{{ removed|pluralize }} {% for t in removed %}"{{ t }}"{% if not forloop.last %}, {% endif %}{% endfor %} cleared.{% endif %}
+{% if comment %}
+
+Comment:
+{{ comment }}{% endif %}{% endfilter %}{% endautoescape %}
diff --git a/ietf/templates/doc/search/status_columns.html b/ietf/templates/doc/search/status_columns.html
index 80691fbe5..71135cebf 100644
--- a/ietf/templates/doc/search/status_columns.html
+++ b/ietf/templates/doc/search/status_columns.html
@@ -2,12 +2,9 @@
{{ doc.friendly_state|safe }} {% if not doc.get_state_slug == "rfc" %}{{ doc|state_age_colored }}{% endif %}
- {% if not hide_telechat_date and doc.telechat_date %}
- IESG Telechat: {{ doc.telechat_date }}
- {% endif %}
-
- {% block extra_status %}{% endblock %}
-
+ {% block extra_status %}
+ {% if doc.telechat_date %} IESG Telechat: {{ doc.telechat_date }}{% endif %}
+ {% endblock %}
{% if doc.get_state_slug != "rfc" %}{# I-D #}
diff --git a/ietf/templates/doc/state_help.html b/ietf/templates/doc/state_help.html
index e0c1e3131..a328d6c87 100644
--- a/ietf/templates/doc/state_help.html
+++ b/ietf/templates/doc/state_help.html
@@ -24,7 +24,7 @@
{% for state in states %}
{{ state.name }}
-
{{ state.desc|linebreaksbr }}
+
{{ state.desc|safe|linebreaksbr }}
{% if has_next_states %}
{% for s in state.next_states.all %}
diff --git a/ietf/templates/doc/status_change/last_call.html b/ietf/templates/doc/status_change/last_call.html
index 6da9ec714..58e156c73 100644
--- a/ietf/templates/doc/status_change/last_call.html
+++ b/ietf/templates/doc/status_change/last_call.html
@@ -33,7 +33,7 @@ form #id_last_call_text {
{% load ietf_filters %}
-{% if user|in_group:"Secretariat" %}
+{% if user|has_role:"Secretariat" %}
+ Chair{{ chairs|pluralize }}:
+ {% for chair in chairs %}
+ {{ chair.person.plain_name }} <{{ chair.address }}>{% if not forloop.last %}, {% endif %}
+ {% endfor %}
+
+
+
Delegates can be assigned with permission to do the tasks of the
+chair{{ chairs|pluralize }}. Note that in order to actually do so, the delegates need a
+Datatracker account. New accounts can be
+created here.
+
+
+{% endblock %}
+
+{% block content_end %}
+
+
+
+{% endblock %}
diff --git a/ietf/templates/idrfc/status_columns.html b/ietf/templates/idrfc/status_columns.html
deleted file mode 100644
index 00491018e..000000000
--- a/ietf/templates/idrfc/status_columns.html
+++ /dev/null
@@ -1,51 +0,0 @@
-{% comment %}
-Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}
-{% load ietf_filters ietf_streams %}{% load ballot_icon %}
-
-{{ doc.friendly_state|safe }} {% if not doc.rfc %}{{ doc.id|state_age_colored|safe }}{% endif %}
-{% if not hide_telechat_date %}{% if doc.telechat_date %} IESG Telechat: {{ doc.telechat_date }}{% endif %}{% endif %}
-
-{% block extra_status %}{% endblock %}
-{% if doc.rfc %}
-{% if doc.rfc.obsoleted_by %} Obsoleted by {{ doc.rfc.obsoleted_by|urlize_ietf_docs }}{%endif %}
-{% if doc.rfc.updated_by %} Updated by {{ doc.rfc.updated_by|urlize_ietf_docs }}{%endif %}
-{% if doc.rfc.has_errata %} Errata{% endif %}
-{% else %}{# not rfc #}
-{% if doc.id.rfc_editor_state %} RFC Editor State: {{ doc.id.rfc_editor_state|escape }}{% endif %}
-{% stream_state doc %}
-{% endif %}
-
-
-{% if doc.rfc and doc.rfc.in_ietf_process and doc.rfc.ietf_process.has_active_iesg_ballot %}{% ballot_icon doc.rfc %}{% else %}{% if doc.id %}{% ballot_icon doc.id %}{%endif%}{%endif%}
-
diff --git a/ietf/templates/iesg/agenda.html b/ietf/templates/iesg/agenda.html
index c83339a50..04de8a160 100644
--- a/ietf/templates/iesg/agenda.html
+++ b/ietf/templates/iesg/agenda.html
@@ -1,113 +1,120 @@
{% extends "base.html" %}
-{% comment %}
-Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #}
-{% endcomment %}
{% load ietf_filters %}
-{% block title %}IESG Agenda: {{date}}{% endblock %}
-{% block morecss %}
-.agenda hr { margin-top: 2em; }
-.agenda #section1 h3, #section6 h3 { margin-top:0; margin-bottom:0; }
-.agenda noh3 { margin-left: 30px; }
-.agenda h4 { margin-top: 0; margin-bottom: 0; }
-.agenda h5 { font-size: inherit; margin: 1em 0;}
-.agenda #section23 p, #section4 p { margin-left:30px; nomargin-top: 0; nomargin-bottom:0; nofont-style:italic;}
-.agenda #section1 pre { margin-left: 30px; }
-.agenda blockquote { margin-left: 30px; width: 70ex; font-style:italic;}
-table.agenda-doc { margin-left: 30px; margin-top:0.5em; margin-bottom: 0.5em; width: 95%; }
-table.agenda-doc > tbody > tr { vertical-align:top; }
-div.agenda-wg { margin-left: 30px; margin-top:0.5em; margin-bottom: 0.5em; width: 95%; }
-.agenda .stream { padding-left: 0.5em; }
-{% endblock morecss %}
+{% block title %}IESG Agenda: {{ date }}{% endblock %}
{% block pagehead %}
{% endblock %}
-{% block content %}
+{% block morecss %}
+.agenda hr { margin-top: 2em; }
+.agenda .section.s1 h3 { margin: 0; }
+.agenda .section p, .agenda .section pre, .agenda blockquote, .agenda .agenda-doc { margin-left: 2.2em; }
+.agenda h4 { margin-top: 0; margin-bottom: 0; }
+.agenda h5 { font-size: inherit; margin: 1em 0; }
+.agenda blockquote { width: 37.6em; font-style:italic; }
+.agenda .agenda-doc { margin-top: 0.5em; margin-bottom: 0.8em; width: 95%; clear: both; }
+.agenda .agenda-doc .ballot-icon-column { float: right; padding: 0.5em 1em; }
+.agenda .stream { padding-left: 0.5em; }
+{% endblock morecss %}
+{% block content %}
1.1 {% if private or user|in_group:"Area Director,IAB Chair,Secretariat" %}Roll Call{%else%}Roll Call{%endif%}
+{% if num|sectionlevel == 1 %}
{{ num }}. {{ section.title|safe }}
{% endif %}
+{% if num|sectionlevel == 2 %}
{{ num }} {{ section.title|safe }}
{% endif %}
+{% if num|sectionlevel == 3 %}
{{ num }} {{ section.title|safe }}
{% endif %}
-
1.2 Bash the Agenda
-
-
1.3 Approval of the {% if private or user|in_group:"Area Director,IAB Chair,Secretariat" %}Minutes{%else%}Minutes{%endif%} of Past Telechats
-
1.4 List of Remaining Action Items from Last Telechat
+{% if num == "1.4" %}
-{{ action_items }}
+{{ section.text }}
-
+{% endif %}
-
-{% with "iesg/agenda_doc.html" as doc_template %}
-{% with "iesg/agenda_conflict_doc.html" as doc_conflict_template %}
-{% include "iesg/agenda_outline_23.html" %}
-{% endwith %}
-{% endwith %}
-
+{% if num >= "2" and num < "5" %}
+ {% if num == "2" %}
+
+ Reviews should focus on these questions: "Is this document a
+ reasonable basis on which to build the salient part of the Internet
+ infrastructure? If not, what changes would make it so?"
+
+ {% endif %}
-
-{% with "iesg/agenda_wg.html" as wg_template %}
-{% include "iesg/agenda_outline_4.html" %}
-{% endwith %}
-
+ {% if num == "3.1" or num == "3.2" %}
+
+ Reviews should focus on these questions: "Is this document a
+ reasonable contribution to the area of Internet engineering
+ which it covers? If not, what changes would make it so?"
+
+ {% endif %}
+
+ {% if num == "3.3" %}
+
+ Reviews should focus on these questions: "Are the proposed
+ changes to document status appropriate? Have all requirements
+ for such a change been met? If not, what changes to the proposal
+ would make it appropriate?"
+
+ {% endif %}
-
5. IAB News We Can Use
+ {% if num == "3.4" %}
+
+ The IESG will use RFC 5742 responses: 1) The IESG has concluded
+ that there is no conflict between this document and IETF work; 2)
+ The IESG has concluded that this work is related to IETF work done
+ in WG <X>, but this relationship does not prevent
+ publishing; 3) The IESG has concluded that publication could
+ potentially disrupt the IETF work done in WG <X> and
+ recommends not publishing the document at this time; 4) The IESG
+ has concluded that this document violates IETF procedures for
+ <Y> and should therefore not be published without IETF
+ review and IESG approval; or 5) The IESG has concluded that this
+ document extends an IETF protocol in a way that requires IETF
+ review and should therefore not be published without IETF review
+ and IESG approval.
+
+ The document shepherd must propose one of these responses in the
+ conflict-review document, and the document shepherd may supply text
+ for an IESG Note in that document. The Area Director ballot positions
+ indicate consensus with the response proposed by the document shepherd
+ and agreement that the IESG should request inclusion of the IESG Note.
+
+ Other matters may be recorded in comments, and the comments will
+ be passed on to the RFC Editor as community review of the document.
+
+ {% endif %}
-
6. Management Issues
-
-{% for m in mgmt %}
-
6.{{forloop.counter}} {{m.title|escape}}
-{% if user|in_group:"Area Director,IAB Chair,Secretariat" %}
+
+ {% if "docs" in section %}
+ {% for doc in section.docs %}
+ {% if doc.type_id == "draft" or doc.type_id == "statchg" %}{% include "iesg/agenda_doc.html" %}{% endif %}
+ {% if doc.type_id == "conflrev" %}{% include "iesg/agenda_conflict_doc.html" %}{% endif %}
+ {% if doc.type_id == "charter" %}{% include "iesg/agenda_charter.html" %}{% endif %}
+ {% empty %}
+
NONE
+ {% endfor %}
+ {% endif %}
+{% endif %}
+
+{% if num|startswith:"6." and user|has_role:"Area Director,IAB Chair,Secretariat" %}
{% endblock content %}
diff --git a/ietf/templates/iesg/agenda.txt b/ietf/templates/iesg/agenda.txt
index 72d302660..e6aa31a6e 100644
--- a/ietf/templates/iesg/agenda.txt
+++ b/ietf/templates/iesg/agenda.txt
@@ -1,68 +1,24 @@
-{% comment %}
-Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #}
-{% endcomment %}{% autoescape off %}{% load ietf_filters %}{% filter compress_empty_lines %}{% filter linebreaks_lf %}
+{% autoescape off %}{% load ietf_filters %}{% filter compress_empty_lines %}{% filter linebreaks_lf %}
INTERNET ENGINEERING STEERING GROUP (IESG)
-Summarized Agenda for the {{date}} IESG Teleconference
+Summarized Agenda for the {{ date }} IESG Teleconference
This agenda was generated at {% now "Y-m-d H:i:s T" %}
Up-to-date web version of this agenda can be found at:
http://datatracker.ietf.org/iesg/agenda/
-
-1. Administrivia
-
-1.1 Roll Call
-1.2 Bash the Agenda
-1.3 Approval of the Minutes of Past Telechats
-1.4 List of Remaining Action Items from Last Telechat
- {{ action_items|indent|indent }}
+{% for num, section in sections %}
+{% if num|sectionlevel == 1 %}
+{{ num }}.{% else %}{{ num }}{% endif %} {{ section.title }}{% if num == "1.4" %}
-{% with "iesg/agenda_doc.txt" as doc_template %}
-{% with "iesg/agenda_conflict_doc.txt" as doc_conflict_template %}
-{% include "iesg/agenda_outline_23.html" %}
-{% endwith %}
-{% endwith %}
+ {{ section.text|indent:4 }}
+{% endif %}{% if num >= "2" and num < "5" and "docs" in section %}{% for doc in section.docs %}
+ {% if doc.type_id == "draft" or doc.type_id == "statchg" %}{% include "iesg/agenda_doc.txt" %}{% endif %}
+ {% if doc.type_id == "conflrev" %}{% include "iesg/agenda_conflict_doc.txt" %}{% endif %}
+ {% if doc.type_id == "charter" %}{% include "iesg/agenda_charter.txt" %}{% endif %}
+{% empty %}
-{% with "iesg/agenda_wg.txt" as wg_template %}
-{% include "iesg/agenda_outline_4.html" %}
-{% endwith %}
-
-5. IAB News We Can Use
-
-6. Management Issues
-{% for m in mgmt %}
-6.{{forloop.counter}} {{m.title}}
+ NONE
{% endfor %}
-
-7. Working Group News
+{% endif %}{% if num|startswith:"6"%}
+{% endif %}{% endfor %}
{% endfilter %}{% endfilter %}{% endautoescape %}
diff --git a/ietf/templates/iesg/agenda_charter.html b/ietf/templates/iesg/agenda_charter.html
new file mode 100644
index 000000000..a717c2e49
--- /dev/null
+++ b/ietf/templates/iesg/agenda_charter.html
@@ -0,0 +1,17 @@
+{% load ballot_icon %}
+
Area: {{ doc.group.parent.acronym|upper }} ({{ doc.ad|default:"Sponsoring AD not assigned" }})
+
+
diff --git a/ietf/templates/iesg/agenda_charter.txt b/ietf/templates/iesg/agenda_charter.txt
new file mode 100644
index 000000000..ec24fb30e
--- /dev/null
+++ b/ietf/templates/iesg/agenda_charter.txt
@@ -0,0 +1,2 @@
+{% load ietf_filters %}
+ o {{ doc.group.name }} ({{ doc.group.acronym }})
diff --git a/ietf/templates/iesg/agenda_conflict_doc.html b/ietf/templates/iesg/agenda_conflict_doc.html
index 5e5692404..0953039b5 100644
--- a/ietf/templates/iesg/agenda_conflict_doc.html
+++ b/ietf/templates/iesg/agenda_conflict_doc.html
@@ -1,116 +1,46 @@
-{% comment %}
-Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
+{% load ietf_filters ballot_icon %}
+
+
+ {% ballot_icon doc %}
+
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
+
+ {{doc.name}}-{{doc.rev}}
+ [txt]
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ {{ doc.title|escape }}
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
+ {% with doc.conflictdoc as conflictdoc %}
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
+
+ {{ conflictdoc.name }}-{{ conflictdoc.rev }}
+ [txt]
+ {{ conflictdoc.title|escape }} ({{ conflictdoc.stream }}: {{ conflictdoc.intended_std_level }})
+ {% if conflictdoc.note %}
+ Note: {{ conflictdoc.note|linebreaksbr }}
+ {% endif %}
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}
-{% comment %}
-Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
-{% endcomment %}
-{% load ietf_filters %}{% load ballot_icon %}
-
-{% if title2_first %}{% if title1_first %}
{{ title1 }}
-{% endif %}
-
{{ title2 }}
-{% if title2|startswith:"3.4" %}
-
- The IESG will use RFC 5742 responses: 1) The IESG has concluded
- that there is no conflict between this document and IETF work; 2)
- The IESG has concluded that this work is related to IETF work done
- in WG <X>, but this relationship does not prevent
- publishing; 3) The IESG has concluded that publication could
- potentially disrupt the IETF work done in WG <X> and
- recommends not publishing the document at this time; 4) The IESG
- has concluded that this document violates IETF procedures for
- <Y> and should therefore not be published without IETF
- review and IESG approval; or 5) The IESG has concluded that this
- document extends an IETF protocol in a way that requires IETF
- review and should therefore not be published without IETF review
- and IESG approval.
+ {% if conflictdoc.ipr %}
- The document shepherd must propose one of these responses in the
- conflict-review document, and the document shepherd may supply text
- for an IESG Note in that document. The Area Director ballot positions
- indicate consensus with the response proposed by the document shepherd
- and agreement that the IESG should request inclusion of the IESG Note.
-
- Other matters may be recorded in comments, and the comments will
- be passed on to the RFC Editor as community review of the document.
-
-{% endif %}
-{% endif %}
{{ title3 }}
+
IPR:
+
+ {% for ipr in conflictdoc.ipr %}
+ {% if ipr.ipr.status == 1 %}
+
-
-{% endif %}
+ Token: {{ doc.ad }}
+ {% with doc.active_defer_event as defer %}
+ {% if defer %}
+ Was deferred by {{ defer.by }} on {{ defer.time|date:"Y-m-d" }}
+ {% endif %}
+ {% endwith %}
+
-
-Token: {{ doc.obj.ad }}
-{% with doc.obj.active_defer_event as defer %}
-{% if defer %}
- Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}}
-{% endif %}
-{% endwith %}
-
-{% endfor %}
diff --git a/ietf/templates/iesg/agenda_conflict_doc.txt b/ietf/templates/iesg/agenda_conflict_doc.txt
index 3c7d920c2..4ff2e3b00 100644
--- a/ietf/templates/iesg/agenda_conflict_doc.txt
+++ b/ietf/templates/iesg/agenda_conflict_doc.txt
@@ -1,51 +1,8 @@
-{% comment %}
-Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}
-{% comment %}
-Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
-{% endcomment %}
-{% load ietf_filters %}
-{% if title2_first %}{% if title1_first %}{{ title1 }}{% endif %}
-{{ title2 }}
-{% endif %}{{ title3 }}
-{% for doc in section_docs %}
- o {{doc.obj.canonical_name}}-{{doc.obj.rev}}
- {% filter wordwrap:"68"|indent|indent %}{{ doc.obj.title }}{% endfilter %}
- {{doc.obj.conflictdoc.canonical_name}}-{{doc.obj.conflictdoc.rev}}
- {% filter wordwrap:"66"|indent:"4" %}{{ doc.obj.conflictdoc.title }} ({{doc.obj.conflictdoc.stream}}: {{ doc.obj.conflictdoc.intended_std_level }}){% endfilter %}
-{% if doc.obj.conflictdoc.note %}{# note: note is not escaped #} {% filter wordwrap:"64"|indent:"6" %}Note: {{ doc.obj.conflictdoc.note|striptags }}{% endfilter %}
-{% endif %} Token: {{ doc.obj.ad }}
-{% with doc.obj.active_defer_event as defer %}{% if defer %} Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}}{% endif %}{% endwith %}
-{% empty %}
- NONE
-{% endfor %}
+{% load ietf_filters %}{% with doc.conflictdoc as conflictdoc %}
+ o {{ doc.canonical_name }}-{{ doc.rev }}
+ {% filter wordwrap:"68"|indent|indent %}{{ doc.title }}{% endfilter %}
+ {{ conflictdoc.canonical_name }}-{{ conflictdoc.rev }}
+ {% filter wordwrap:"66"|indent:"4" %}{{ conflictdoc.title }} ({{ conflictdoc.stream }}: {{ conflictdoc.intended_std_level }}){% endfilter %}
+{% if conflictdoc.note %}{# note: note is not escaped #} {% filter wordwrap:"64"|indent:"6" %}Note: {{ conflictdoc.note|striptags }}{% endfilter %}
+{% endif %} Token: {{ doc.ad }}
+{% with doc.active_defer_event as defer %}{% if defer %} Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}}{% endif %}{% endwith %}{% endwith %}
diff --git a/ietf/templates/iesg/agenda_doc.html b/ietf/templates/iesg/agenda_doc.html
index 37544a7f2..9bc4f3143 100644
--- a/ietf/templates/iesg/agenda_doc.html
+++ b/ietf/templates/iesg/agenda_doc.html
@@ -1,130 +1,60 @@
-{% comment %}
-Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
+{% load ietf_filters ballot_icon %}
+
+
+ {% ballot_icon doc %}
+
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
+
+ {{ doc.canonical_name }}
+ {% with doc.rfc_number as rfc_number %}
+ {% if rfc_number %}
+ [txt]
+ {% else %}
+ [txt]
+ {% endif %}
+ {% endwith %}
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ {% if doc.stream %} - {{ doc.stream }} stream{% endif %}
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}
-{% comment %}
-Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
-{% endcomment %}
-{% load ietf_filters %}{% load ballot_icon %}
-
-{% if title2_first %}{% if title1_first %}
{{ title1 }}
-{% if title1|startswith:"2." %}
-
- Reviews should focus on these questions: "Is this document a
- reasonable basis on which to build the salient part of the Internet
- infrastructure? If not, what changes would make it so?"
-
-{% endif %}
-{% endif %}
-
{{ title2 }}
-{% if title2|startswith:"3.1" or title2|startswith:"3.2" %}
-
- Reviews should focus on these questions: "Is this document a
- reasonable contribution to the area of Internet engineering
- which it covers? If not, what changes would make it so?"
-
-{% endif %}
-{% if title2|startswith:"3.3" %}
-
- Reviews should focus on these questions: "Are the proposed
- changes to document status appropriate? Have all requirements
- for such a change been met? If not, what changes to the proposal
- would make it appropriate?"
-
-{% endif %}
-{% endif %}
{{ title3 }}
-
-{% for doc in section_docs %}
-{% if forloop.first %}
-{% endif %}
-
-
diff --git a/ietf/templates/iesg/agenda_doc.txt b/ietf/templates/iesg/agenda_doc.txt
index abb6706db..2bff27169 100644
--- a/ietf/templates/iesg/agenda_doc.txt
+++ b/ietf/templates/iesg/agenda_doc.txt
@@ -1,52 +1,9 @@
-{% comment %}
-Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}
-{% comment %}
-Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
-{% endcomment %}
-{% load ietf_filters %}
-{% if title2_first %}{% if title1_first %}{{ title1 }}{% endif %}
-{{ title2 }}
-{% endif %}{{ title3 }}
-{% for doc in section_docs %}{% with doc.obj.rfc_number as rfc_number %}
- o {{doc.obj.canonical_name}}{% if not rfc_number %}-{{doc.obj.rev}}{% endif %}{% endwith %}{% if doc.obj.stream %} - {{ doc.obj.stream }} stream{% endif %}
- {% filter wordwrap:"68"|indent|indent %}{{ doc.obj.title }} ({{ doc.obj.intended_std_level }}){% endfilter %}
-{% if doc.obj.note %}{# note: note is not escaped #} {% filter wordwrap:"68"|indent|indent %}Note: {{ doc.obj.note|striptags }}{% endfilter %}
-{% endif %} Token: {{ doc.obj.ad }}{% if doc.obj.iana_review_state %}
- IANA Review: {{ doc.obj.iana_review_state }}{% endif %}{% if doc.obj.consensus %}
- Consensus: {{ doc.obj.consensus }}{% endif %}{% if doc.obj.lastcall_expires %}
- Last call expires: {{ doc.obj.lastcall_expires|date:"Y-m-d" }}{% endif %}
-{% with doc.obj.active_defer_event as defer %}{% if defer %} Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}}{% endif %}{% endwith %}
-{% empty %}
- NONE
-{% endfor %}
+{% load ietf_filters %}{% with doc.rfc_number as rfc_number %}
+ o {{doc.canonical_name}}{% if not rfc_number %}-{{doc.rev}}{% endif %}{% endwith %}{% if doc.stream %} - {{ doc.stream }} stream{% endif %}
+ {% filter wordwrap:"68"|indent|indent %}{{ doc.title }} ({{ doc.intended_std_level }}){% endfilter %}
+{% if doc.note %}{# note: note is not escaped #} {% filter wordwrap:"68"|indent|indent %}Note: {{ doc.note|striptags }}{% endfilter %}
+{% endif %} Token: {{ doc.ad }}{% if doc.iana_review_state %}
+ IANA Review: {{ doc.iana_review_state }}{% endif %}{% if doc.consensus %}
+ Consensus: {{ doc.consensus }}{% endif %}{% if doc.lastcall_expires %}
+ Last call expires: {{ doc.lastcall_expires|date:"Y-m-d" }}{% endif %}
+{% with doc.active_defer_event as defer %}{% if defer %} Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}}{% endif %}{% endwith %}
diff --git a/ietf/templates/iesg/agenda_documents.html b/ietf/templates/iesg/agenda_documents.html
new file mode 100644
index 000000000..ae040850e
--- /dev/null
+++ b/ietf/templates/iesg/agenda_documents.html
@@ -0,0 +1,102 @@
+{% extends "base.html" %}
+{% comment %}
+Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+All rights reserved. Contact: Pasi Eronen
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of the Nokia Corporation and/or its
+ subsidiary(-ies) nor the names of its contributors may be used
+ to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+{% endcomment %}
+{% load ballot_icon %}
+{% load ietf_filters %}
+
+{% block title %}Documents on Future IESG Telechat Agendas{% endblock %}
+
+{% block morecss %}
+.agenda_docs tr.oddrow {background-color: #EDF5FF; }
+.agenda_docs tr.header.telechat-date { margin-top:10px; background:#2647A0; color: white;}
+.agenda_docs tr.header.telechat-date td { font-size: 125%; }
+.agenda_docs tr.header + tr.header { border-top: 2px solid white;}
+.agenda_docs tr td { vertical-align: top; }
+.agenda_docs tr .reschedule,
+.agenda_docs tr .clear-returning-item { font-size: 11px; }
+.agenda_docs tr .doc_pages { font-size: 80%; font-style: italic; }
+.secretariat-actions { margin-bottom: 1em; }
+{% endblock %}
+
+{% block pagehead %}
+
+{% endblock %}
+
+{% block content %}
+
Documents on Future IESG Telechat Agendas
+
+
+
+{% endblock content %}
+
+{% block content_end %}
+
+
+
+{% endblock %}
diff --git a/ietf/templates/iesg/agenda_documents.txt b/ietf/templates/iesg/agenda_documents.txt
deleted file mode 100644
index 1079cd51d..000000000
--- a/ietf/templates/iesg/agenda_documents.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-{% comment %}
-Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}# Fields: telechat date, filename (draft-foo-bar or rfc1234), intended status, rfc editor submission flag (0=no, 1=yes), area acronym, AD name, version{% for doc in docs %}
-{{ doc.telechat_date }} {{ doc.name }} {{ doc.intended_std_level }} {% if doc.stream.slug in special_stream_list %}1{% else %}0{% endif %} {{doc.area_acronym|lower}} {% with doc.ad as ad %}{% if ad %}{{ ad.plain_name }}{% else %}None Assigned{% endif %}{% endwith %} {{doc.rev}}{% endfor %}
diff --git a/ietf/templates/iesg/agenda_documents_redesign.html b/ietf/templates/iesg/agenda_documents_redesign.html
deleted file mode 100644
index e8a2affdc..000000000
--- a/ietf/templates/iesg/agenda_documents_redesign.html
+++ /dev/null
@@ -1,176 +0,0 @@
-{% extends "base.html" %}
-{% comment %}
-Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}
-{% load ballot_icon %}
-{% load ietf_filters %}
-
-{% block title %}Documents on Future IESG Telechat Agendas{% endblock %}
-
-{% block morecss %}
-.agenda_docs tr.oddrow {background-color: #EDF5FF; }
-.agenda_docs tr.header.telechat_date { margin-top:10px; background:#2647A0; color: white;}
-.agenda_docs tr.header.telechat_date td { font-size: 125%; }
-.agenda_docs tr.header + tr.header { border-top: 2px solid white;}
-.agenda_docs tr td {
- vertical-align: top;
-}
-.agenda_docs tr .reschedule,
-.agenda_docs tr .clear-returning-item {
- font-size: 11px;
-}
-.agenda_docs tr .doc_pages {
-font-size:80%; font-style:italic;
-}
-.secretariat-actions {
- margin-bottom: 10px;
-}
-{% endblock %}
-
-{% block pagehead %}
-
-{% endblock %}
-
-{% block content %}
-
Documents on Future IESG Telechat Agendas
-
-
-
-{% endblock content %}
-
-{% block content_end %}
-
-
-
-{% endblock %}
diff --git a/ietf/templates/iesg/agenda_documents_row_redesign.html b/ietf/templates/iesg/agenda_documents_row.html
similarity index 58%
rename from ietf/templates/iesg/agenda_documents_row_redesign.html
rename to ietf/templates/iesg/agenda_documents_row.html
index 98f7878f1..fbbec4897 100644
--- a/ietf/templates/iesg/agenda_documents_row_redesign.html
+++ b/ietf/templates/iesg/agenda_documents_row.html
@@ -36,31 +36,32 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% load ballot_icon %}
{% load ietf_filters %}
-
+{% endwith %}
+{% endspaceless %} >
-
{{ doc.displayname_with_link|safe }}
-{% with doc.active_defer_event as defer %}{% if defer %}
-
{{ doc.title }}
-{% with doc.pages as pagecount %}{% if pagecount %}({{doc.pages}} pp){% endif %}{% endwith %}
+ {% if doc.pages %}({{doc.pages}} pp){% endif %}
-{% include "iesg/agenda_documents_row_status_redesign.html" %}
+{% include "iesg/agenda_documents_row_status.html" %}
{% include "doc/search/ipr_column_with_label.html" %}
-
{{ doc.ad.plain_name|default:"" }}
+
{{ doc.ad|default:"" }}
diff --git a/ietf/templates/iesg/agenda_documents_row_status.html b/ietf/templates/iesg/agenda_documents_row_status.html
index b70099152..191d11647 100644
--- a/ietf/templates/iesg/agenda_documents_row_status.html
+++ b/ietf/templates/iesg/agenda_documents_row_status.html
@@ -1,4 +1,4 @@
-{% extends "idrfc/status_columns.html" %}
+{% extends "doc/search/status_columns.html" %}
{% block extra_status %}
- Intended status: {{doc.ietf_process.intended_maturity_level}}
+{% if doc.type_id == 'draft' %} Intended status: {{ doc.intended_std_level }}{% endif %}
{% endblock %}
diff --git a/ietf/templates/iesg/agenda_documents_row_status_redesign.html b/ietf/templates/iesg/agenda_documents_row_status_redesign.html
deleted file mode 100644
index e73fa4d53..000000000
--- a/ietf/templates/iesg/agenda_documents_row_status_redesign.html
+++ /dev/null
@@ -1,6 +0,0 @@
-{% extends "doc/search/status_columns.html" %}
-{% block extra_status %}
-{% if doc.type.slug == 'draft' %}
- Intended status: {{ doc.intended_std_level }}
-{% endif %}
-{% endblock %}
diff --git a/ietf/templates/iesg/agenda_outline_23.html b/ietf/templates/iesg/agenda_outline_23.html
deleted file mode 100644
index 9311c9e25..000000000
--- a/ietf/templates/iesg/agenda_outline_23.html
+++ /dev/null
@@ -1,183 +0,0 @@
-{% comment %}
-Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}
-{% with "2. Protocol Actions" as title1 %}{% with 1 as title1_first %}
-{% with "2.1 WG Submissions" as title2 %}
-{% with 1 as title2_first %}
-
-{% with "2.1.1 New Items" as title3 %}
-{% with docs.s211 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endwith %}{# title2_first #}
-
-{% with "2.1.2 Returning Items" as title3 %}
-{% with docs.s212 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-
-{% if docs.s213 %}
-{% with "2.1.3 For Action" as title3 %}
-{% with docs.s213 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endif %}
-
-{% endwith %}{# title2 #}
-{% endwith %}{# title1_first #}
-
-{% with "2.2 Individual Submissions" as title2 %}
-{% with 1 as title2_first %}
-
-{% with "2.2.1 New Items" as title3 %}
-{% with docs.s221 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endwith %}{# title2_first #}
-
-{% with "2.2.2 Returning Items" as title3 %}
-{% with docs.s222 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-
-{% if docs.s223 %}
-{% with "2.2.3 For Action" as title3 %}
-{% with docs.s223 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endif %}
-
-{% endwith %}{# title2 #}
-
-{% with "2.3 Status Changes" as title2 %}
-{% with 1 as title2_first %}
-
-{% with "2.3.1 New Items" as title3 %}
-{% with docs.s231 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endwith %}{# title2_first #}
-
-{% with "2.3.2 Returning Items" as title3 %}
-{% with docs.s232 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-
-{% if docs.s233 %}
-{% with "2.3.3 For Action" as title3 %}
-{% with docs.s233 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endif %}
-
-{% endwith %}{# title2 #}
-
-{% endwith %}{# title1 #}
-
-
-{% with "3. Document Actions" as title1 %}{% with 1 as title1_first %}
-
-{% with "3.1 WG Submissions" as title2 %}
-{% with 1 as title2_first %}
-
-{% with "3.1.1 New Items" as title3 %}
-{% with docs.s311 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endwith %}{# title2_first #}
-
-{% with "3.1.2 Returning Items" as title3 %}
-{% with docs.s312 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-
-{% if docs.s313 %}
-{% with "3.1.3 For Action" as title3 %}
-{% with docs.s313 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endif %}
-
-{% endwith %}{# title2 #}
-
-{% endwith %}{# title1_first #}
-
-{% with "3.2 Individual Submissions Via AD" as title2 %}
-{% with 1 as title2_first %}
-
-{% with "3.2.1 New Items" as title3 %}
-{% with docs.s321 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endwith %}{# title2_first #}
-
-{% with "3.2.2 Returning Items" as title3 %}
-{% with docs.s322 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-
-{% if docs.s323 %}
-{% with "3.2.3 For Action" as title3 %}
-{% with docs.s323 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endif %}
-
-{% endwith %}{# title2 #}
-
-{% with "3.3 Status Changes" as title2 %}
-{% with 1 as title2_first %}
-
-{% with "3.3.1 New Items" as title3 %}
-{% with docs.s331 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endwith %}{# title2_first #}
-
-{% with "3.3.2 Returning Items" as title3 %}
-{% with docs.s332 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-
-{% if docs.s333 %}
-{% with "3.3.3 For Action" as title3 %}
-{% with docs.s333 as section_docs %}{% include doc_template %}{% endwith %}
-{% endwith %}
-{% endif %}
-
-{% endwith %} {# title2 #}
-
-{% with "3.4 IRTF and Independent Submission Stream Documents" as title2 %}
-{% with 1 as title2_first %}
-
-{% with "3.4.1 New Items" as title3 %}
-{% with docs.s341 as section_docs %}{% include doc_conflict_template %}{% endwith %}
-{% endwith %}
-{% endwith %}{# title2_first #}
-
-{% with "3.4.2 Returning Items" as title3 %}
-{% with docs.s342 as section_docs %}{% include doc_conflict_template %}{% endwith %}
-{% endwith %}
-
-{% if docs.s343 %}
-{% with "3.4.3 For Action" as title3 %}
-{% with docs.s343 as section_docs %}{% include doc_conflict_template %}{% endwith %}
-{% endwith %}
-{% endif %}
-
-{% endwith %}{# title2 #}
-
-{% endwith %}
diff --git a/ietf/templates/iesg/agenda_outline_4.html b/ietf/templates/iesg/agenda_outline_4.html
deleted file mode 100644
index 196fa0c86..000000000
--- a/ietf/templates/iesg/agenda_outline_4.html
+++ /dev/null
@@ -1,63 +0,0 @@
-{% comment %}
-Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}
-{% with "4. Working Group Actions" as title1 %}{% with 1 as title1_first %}
-{% with "4.1 WG Creation" as title2 %}
-{% with 1 as title2_first %}
-
-{% with "4.1.1 Proposed for IETF Review" as title3 %}
-{% with wgs.s411 as section_wgs %}{% include wg_template %}{% endwith %}
-{% endwith %}
-{% endwith %}{# title2_first #}
-
-{% with "4.1.2 Proposed for Approval" as title3 %}
-{% with wgs.s412 as section_wgs %}{% include wg_template %}{% endwith %}
-{% endwith %}
-
-{% endwith %}{# title2 #}
-{% endwith %}{# title1_first #}
-
-{% with "4.2 WG Rechartering" as title2 %}
-{% with 1 as title2_first %}
-
-{% with "4.2.1 Under Evaluation for IETF Review" as title3 %}
-{% with wgs.s421 as section_wgs %}{% include wg_template %}{% endwith %}
-{% endwith %}
-{% endwith %}{# title2_first #}
-
-{% with "4.2.2 Proposed for Approval" as title3 %}
-{% with wgs.s422 as section_wgs %}{% include wg_template %}{% endwith %}
-{% endwith %}
-
-{% endwith %}{# title2 #}
-{% endwith %}{# title1 #}
diff --git a/ietf/templates/iesg/agenda_package.txt b/ietf/templates/iesg/agenda_package.txt
index b06dee3c9..bee2be00f 100644
--- a/ietf/templates/iesg/agenda_package.txt
+++ b/ietf/templates/iesg/agenda_package.txt
@@ -5,7 +5,7 @@ Contents:
1. Roll Call and Dial-In Instructions
https://www.ietf.org/iesg/internal/rollcall.txt
2. Agenda
- http://datatracker.ietf.org/iesg/agenda/?private
+ https://datatracker.ietf.org/iesg/agenda/
3. Management Item Details
https://datatracker.ietf.org/cgi-bin/display_news.cgi?template_type=3
4. Previous minutes
@@ -24,10 +24,10 @@ Contents:
------------------------------------------------------------------------
3. MANAGEMENT ITEM DETAILS
------------------------------------------------------------------------
-{% for m in mgmt %}
-6.{{forloop.counter}} {{m.title}}
+{% for num, section in management_items %}
+{{ num }} {{ section.title}}
-{{m.text|wordwrap:"76"}}
+{{ section.text|wordwrap:"76" }}
{% endfor %}
------------------------------------------------------------------------
diff --git a/ietf/templates/iesg/agenda_wg.html b/ietf/templates/iesg/agenda_wg.html
deleted file mode 100644
index a5a61dd2c..000000000
--- a/ietf/templates/iesg/agenda_wg.html
+++ /dev/null
@@ -1,55 +0,0 @@
-{% comment %}
-Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}
-{% load ballot_icon %}
-{% if title2_first %}{% if title1_first %}
Area: {{ wg.doc.group.parent.acronym|upper }} ({{ wg.doc.ad|default:"Sponsoring AD not assigned" }})
-
-
-
-{% empty %}
-
NONE
-{% endfor %}
diff --git a/ietf/templates/iesg/agenda_wg.txt b/ietf/templates/iesg/agenda_wg.txt
deleted file mode 100644
index 3d444ec2d..000000000
--- a/ietf/templates/iesg/agenda_wg.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-{% comment %}
-Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}{% autoescape off %}{% load ietf_filters %}
-{% if title2_first %}{% if title1_first %}{{ title1 }}{% endif %}
-{{ title2 }}
-{% endif %}{{ title3 }}
-{% for wg in section_wgs %}
- o {{ wg.obj.name }} ({{wg.obj.acronym}})
-{% empty %}
- NONE
-{% endfor %}
-{% endautoescape %}
diff --git a/ietf/templates/iesg/ballotinfo_detail.html b/ietf/templates/iesg/ballotinfo_detail.html
deleted file mode 100644
index f9d30e634..000000000
--- a/ietf/templates/iesg/ballotinfo_detail.html
+++ /dev/null
@@ -1,33 +0,0 @@
-{# Copyright The IETF Trust 2007, All Rights Reserved #}
-{% extends "base.html" %}
-{% block title %}IESG Announcement{% endblock %}
-
-{% block content %}
-
IESG Announcement
-
-
- This page contains an IESG Protocol, Document, or Working Group Action
- announcement. Other announcements can be found using the links below.
-
-
-{% endblock %}
-
diff --git a/ietf/templates/iesg/discusses.html b/ietf/templates/iesg/discusses.html
index 99e7578fe..ef134858a 100644
--- a/ietf/templates/iesg/discusses.html
+++ b/ietf/templates/iesg/discusses.html
@@ -39,81 +39,49 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% block title %}IESG Discuss Positions{% endblock %}
{% block morecss %}
-.discuss_hidden {display:none;}
+.discusses-chooser label { display: inline-block; margin-right: 0.5em; }
+.discusses-chooser input { vertical-align: middle; margin: 0; padding: 0; margin-right: 0.3em; }
{% endblock %}
{% block content %}
IESG Discuss Positions
-{% if user|in_group:"Area_Director" %}
-
-Show: All
-By me
-For me
-
+{% if user|has_role:"Area Director" %}
+
+ Show:
+
+
+
+
{% endif %}
-
Document
Status
Area Director
Discusses
-{% for doc in docs %}
-
-
{{ doc.displayname_with_link|safe }}
-{% include "idrfc/status_columns.html" %}
-
{{ doc.ad_name|default:"" }}
-
-{% for po in doc.ietf_process.iesg_ballot.get_discuss|dictsort:"is_old_ad" %}
-{%if po.is_old_ad %}[{%endif%}{{po.ad_name}}{%if po.is_old_ad %}]{%endif%} ({% if po.discuss_date %}{{po.discuss_date|timesince_days}}{%endif%} days ago{% if doc.is_id_wrapper %}{% ifnotequal po.discuss_revision doc.latest_revision %} for -{{po.discuss_revision}}{% endifnotequal %}{% endif %})
-{% endfor %}
-
-
-{% endfor %}
+
+
Document
+
Status
+
Area Director
+
Discusses
+
+
+ {% for doc in docs %}
+
+
{{ doc.displayname_with_link }}
+
+ {% include "doc/search/status_columns.html" %}
+
+
{{ doc.ad|default:"" }}
+
+ {% for p in doc.blocking_positions %}
+ {% if p.old_ad %}[{% endif %}{{ p.ad }}{% if p.old_ad %}]{% endif %} ({% if p.discuss_time %}{{ p.discuss_time|timesince_days }}{% endif %} days ago{% if doc.get_state_url != "rfc" and p.rev != doc.rev %} for -{{ p.rev }}{% endif %})
+ {% endfor %}
+
-This page contains links to all IESG Protocol, Document, and Working Group Action announcements that were sent prior to six months ago. Announcements that have been sent within the past six months can be found in Recent Announcements.
-
-{% regroup object_list by b_approve_date|date:"F j, Y" as dates %}
-{% for date in dates %}
-{% if date.list.0.idinternal.ballot_id %}Date Sent: {{ date.grouper }} {% endif %}
-
-{% regroup date.list by idinternal.ballot_id as ballots %}
-{% for each_ballot in ballots %}
-{% if each_ballot.grouper %}
-
-{% regroup object_list_doc by b_approve_date|date:"F j, Y" as dates %}
-{% for date in dates %}
-{% if date.list.0.idinternal.ballot_id %}Date Sent: {{ date.grouper }} {% endif %}
-
-{% regroup date.list by idinternal.ballot_id as ballots %}
-{% for each_ballot in ballots %}
-{% if each_ballot.grouper %}
-
-Coming Soon ...
-
-{% endblock %}
diff --git a/ietf/templates/iesg/independent_doc.html b/ietf/templates/iesg/independent_doc.html
deleted file mode 100644
index fd63d45ca..000000000
--- a/ietf/templates/iesg/independent_doc.html
+++ /dev/null
@@ -1,44 +0,0 @@
-{# Copyright The IETF Trust 2007, All Rights Reserved #}
-{% extends "base.html" %}
-{% block title %}IESG Statements on Independent Submissions{% endblock %}
-{% block content %}
-
IESG Statements on Independent Submissions
-The RFC Editor receives requests to publish non-IETF Working Group documents as independent Informational or Experimental RFCs. Following the process defined in RFC 3932, the RFC Editor requests that the IESG review these documents and provide input. This page contains copies of those messages that were sent by the IESG to the RFC Editor following such reviews.
-
-
-
Positive IESG Responses
-
-{% regroup object_list by b_approve_date|date:"F j, Y" as dates %}
-{% for date in dates %}
-Date Sent: {{ date.grouper }}
-
-{% regroup date.list by idinternal.ballot_id as ballots %}
-{% for each_ballot in ballots %}
-{% if each_ballot.grouper %}
-
-{% endblock %}
diff --git a/ietf/templates/iesg/moderator_wg.html b/ietf/templates/iesg/moderator_charter.html
similarity index 89%
rename from ietf/templates/iesg/moderator_wg.html
rename to ietf/templates/iesg/moderator_charter.html
index 36c02c0ea..37b86189f 100644
--- a/ietf/templates/iesg/moderator_wg.html
+++ b/ietf/templates/iesg/moderator_charter.html
@@ -31,14 +31,9 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% endcomment %}{% load ietf_filters %}
-{% for wg in section_wgs %}
-
{{ wg.obj.name }} ({{wg.obj.acronym}})
-
-{% if title3|startswith:"4.1.1" %}
+{% if num|startswith:"4.1.1" %}
Does anyone have an objection to the charter being sent for
EXTERNAL REVIEW?
@@ -52,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* OR Back on the Agenda next time in the same category.
{% endif %}
-{% if title3|startswith:"4.1.2" %}
+{% if num|startswith:"4.1.2" %}
Does anyone have an objection to the creation of this working
group?
@@ -64,7 +59,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* OR Wait for instructions from the shepherding AD.
{% endif %}
-{% if title3|startswith:"4.2.1" %}
+{% if num|startswith:"4.2.1" %}
Does anyone have an objection with just making the changes to the
charter?
@@ -82,7 +77,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
acronym].
{% endif %}
-{% if title3|startswith:"4.2.2" %}
+{% if num|startswith:"4.2.2" %}
Does anyone have an objection to the rechartering of this working
group?
@@ -94,11 +89,3 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* Place the charter back on the agenda for the next telechat.
* OR Wait for instructions from the shepherding AD.
{% endif %}
-
-{% empty %}
-
{{ title1 }}
-{{ title2 }}
-{{ title3 }}
-
-
NONE
-{% endfor %}
diff --git a/ietf/templates/iesg/moderator_conflict_doc.html b/ietf/templates/iesg/moderator_conflict_doc.html
index 676de9a9a..0b32bdd80 100644
--- a/ietf/templates/iesg/moderator_conflict_doc.html
+++ b/ietf/templates/iesg/moderator_conflict_doc.html
@@ -35,28 +35,33 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
{% endcomment %}
{% load ietf_filters %}
-{% for doc in section_docs %}
-
{% endif %}
+{% endwith %}
-{% if title3|startswith:"3.4.1" or title3|startswith:"3.4.2" %}
-
Does anyone have an objection to the this conflict review response being sent to the {{doc.obj.conflictdoc.stream}}?
+{% if num|startswith:"3.4.1" or num|startswith:"3.4.2" %}
+
Does anyone have an objection to the this conflict review response being sent to the {{doc.conflictdoc.stream}}?
{% endif %}
-{% if title3|startswith:"3.4.3" %}
+{% if num|startswith:"3.4.3" %}
Who will do the review of this document?
{% endif %}
-
Current State: {{ doc.obj.friendly_state }}
+
Current State: {{ doc.friendly_state }}
Next State:
Sub State:
-{% if title3|startswith:"3.4.1" or title3|startswith:"3.4.2" %}
+{% if num|startswith:"3.4.1" or num|startswith:"3.4.2" %}
If APPROVED - The Secretariat will send a standard no problem
message to the RFC Editor. [Name of AD] will you supply the text for
the IESG Note?
@@ -94,12 +100,3 @@ Sub State:
the Do Not Publish message to the RFC Editor that includes the note
drafted by[Name the AD].
{% endif %}
-
-
-{% empty %}
-
{{ title1 }}
-{{ title2 }}
-{{ title3 }}
-
-
NONE
-{% endfor %}
diff --git a/ietf/templates/iesg/moderator_doc.html b/ietf/templates/iesg/moderator_doc.html
index ae5a4ea74..1b2156aa9 100644
--- a/ietf/templates/iesg/moderator_doc.html
+++ b/ietf/templates/iesg/moderator_doc.html
@@ -35,28 +35,29 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
{% endcomment %}
{% load ietf_filters %}
-{% for doc in section_docs %}
-
{% endif %}
+{% endwith %}
-{% if title2|startswith:"3.1" or title2|startswith:"3.2" %}
-
Does anyone have an[y further] objection to this document being published as an {{ doc.obj.intended_std_level }} RFC?
+{% if num|startswith:"3.1" or num|startswith:"3.2" %}
+
Does anyone have an[y further] objection to this document being published as an {{ doc.intended_std_level }} RFC?
{% endif %}
-{% if title3|startswith:"3.3.1" or title3|startswith:"3.3.2" %}
-
Does anyone have an objection to this conflict review response being sent to the {{doc.obj.conflictdoc.stream}}?
+{% if num|startswith:"3.3.1" or num|startswith:"3.3.2" %}
+
Does anyone have an objection to this conflict review response being sent to the {{ doc.conflictdoc.stream }}?
{% endif %}
-{% if title3|startswith:"3.3.3" %}
+{% if num|startswith:"3.3.3" %}
Who will do the review of this document?
{% endif %}
-
Current State: {{ doc.obj.friendly_state }}
+
Current State: {{ doc.friendly_state }}
Next State:
Sub State:
-{% if title3|startswith:"2.1.1" or title3|startswith:"2.1.2" %}
+{% if num|startswith:"2.1.1" or num|startswith:"2.1.2" %}
If APPROVED - The Secretariat will send a working group
submission, Protocol Action Announcement.
@@ -92,7 +94,7 @@ Sub State:
AD].
{% endif %}
-{% if title3|startswith:"2.2.1" or title3|startswith:"2.2.2" %}
+{% if num|startswith:"2.2.1" or num|startswith:"2.2.2" %}
If APPROVED - The Secretariat will send an individual submission,
Protocol Action Announcement.
@@ -102,7 +104,7 @@ Sub State:
AD].
{% endif %}
-{% if title3|startswith:"2.3.1" or title3|startswith:"2.3.2" %}
+{% if num|startswith:"2.3.1" or num|startswith:"2.3.2" %}
If APPROVED - The Secretariat will send the associated status change
Protocol Action Announcements.
@@ -112,7 +114,7 @@ Sub State:
AD].
{% endif %}
-{% if title3|startswith:"3.1.1" or title3|startswith:"3.1.2" %}
+{% if num|startswith:"3.1.1" or num|startswith:"3.1.2" %}
If APPROVED - The Secretariat will send a working group submission
Document Action Announcement.
@@ -121,7 +123,7 @@ Sub State:
Ed. Note, IESG, note, etc.] from [Name that AD].
{% endif %}
-{% if title3|startswith:"3.2.1" or title3|startswith:"3.2.2" %}
+{% if num|startswith:"3.2.1" or num|startswith:"3.2.2" %}
If APPROVED - The Secretariat will send an individual submission
Document Action Announcement.
@@ -130,7 +132,7 @@ Sub State:
[RFC Ed. Note, IESG, note, etc.] from [Name that AD].
{% endif %}
-{% if title3|startswith:"3.3.1" or title3|startswith:"3.3.2" %}
+{% if num|startswith:"3.3.1" or num|startswith:"3.3.2" %}
If APPROVED - The Secretariat will send the associated status change
Document Action Announcements.
@@ -139,7 +141,7 @@ Sub State:
Ed. Note, IESG, note, etc.] from [Name that AD].
{% endif %}
-{% if title3|startswith:"3.4.1" or title3|startswith:"3.4.2" %}
+{% if num|startswith:"3.4.1" or num|startswith:"3.4.2" %}
If APPROVED - The Secretariat will send a standard no problem
message to the RFC Editor. [Name of AD] will you supply the text for
the IESG Note?
@@ -155,12 +157,3 @@ Sub State:
the Do Not Publish message to the RFC Editor that includes the note
drafted by[Name the AD].
{% endif %}
-
-
-{% empty %}
-
{{ title1 }}
-{{ title2 }}
-{{ title3 }}
-
-
NONE
-{% endfor %}
diff --git a/ietf/templates/iesg/moderator_package.html b/ietf/templates/iesg/moderator_package.html
index 8c8eb11d5..2a68cf4ea 100644
--- a/ietf/templates/iesg/moderator_package.html
+++ b/ietf/templates/iesg/moderator_package.html
@@ -33,46 +33,47 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% endcomment %}{% comment %}
Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
{% endcomment %}
-{% load ietf_filters %}
-
-Moderator Package for {{date}} IESG Telechat
-
-
+
+
+
+ Moderator Package for {{ date }} IESG Telechat
+
-
Moderator Package for {{date}} IESG Telechat
+{% load ietf_filters %}
+
+
Moderator Package for {{ date }} IESG Telechat
Generated at {% now "Y-m-d H:i:s T" %}.
-
-
-
-
-
1. Administrivia 1.1 Roll Call
+{% for num, section in sections %}
+
{% for parent_num, parent_section in section.parents %}{{ parent_num }}{% if parent_num|sectionlevel == 1 %}.{% endif %} {{ parent_section.title }} {% endfor %}{{ num }}{% if num|sectionlevel == 1 %}.{% endif %} {{ section.title }}
+{% if num == "1.1" %}
{% filter linebreaks_crlf %}
-{{ roll_call }}
-
{% endfilter %}
-
-
1. Administrivia 1.2 Bash the Agenda
+{{ section.text }}
+
+{% endfilter %}
+{% endif %}
+{% if num == "1.2" %}
Does anyone want to add anything NEW to the agenda?
Does anyone have any other changes to the agenda as it stands?
+{% endif %}
-
1. Administrivia 1.3 Approval of the Minutes of Past Telechats
-
+{% if num == "1.3" %}
Does anyone have an objection to the minutes of the __________ IESG
Teleconference being approved?
@@ -82,70 +83,52 @@ teleconference. The Secretariat will post them in the public archive.
Are there narrative minutes to approve for today?
{% filter linebreaks_crlf %}
-{{ minutes }}
-
{% endfilter %}
-
-
1. Administrivia 1.4 List of Remaining Action Items from Last Telechat
+{{ section.text }}
+
+{% endfilter %}
+{% endif %}
+{% if num == "1.4" %}
{% filter linebreaks_crlf %}
-{{ action_items }}
-
{% endfilter %}
+{{ section.text }}
+
+{% endfilter %}
+{% endif %}
-
-
-
+{% if num >= "2" and num < "5" %}
+ {% if "doc" in section %}
+ {% with section.doc as doc %}
+ {% if doc.type_id == "draft" or doc.type_id == "statchg" %}{% include "iesg/moderator_doc.html" %}{% endif %}
+ {% if doc.type_id == "conflrev" %}{% include "iesg/moderator_conflict_doc.html" %}{% endif %}
+ {% if doc.type_id == "charter" %}{% include "iesg/moderator_charter.html" %}{% endif %}
+ {% endwith %}
+ {% else %}
+
NONE
+ {% endif %}
+{% endif %}
-{% with "iesg/moderator_doc.html" as doc_template %}
-{% with "iesg/moderator_conflict_doc.html" as doc_conflict_template %}
-{% include "iesg/agenda_outline_23.html" %}
-{% endwith %}
-{% endwith %}
+{% if num >= "6" and num < "7" %}
+ {% if num == "6" %}
+
NONE
+ {% else %}
+
Is there anything that you would like the Secretariat to record in the minutes for this management issue?
-
-
-
+
Decision:
-{% with "iesg/moderator_wg.html" as wg_template %}
-{% include "iesg/agenda_outline_4.html" %}
-{% endwith %}
+
+
+{% endblock %}
diff --git a/ietf/templates/iesg/scribe_conflict_doc.html b/ietf/templates/iesg/scribe_conflict_doc.html
index 8c618f9aa..8508438d4 100644
--- a/ietf/templates/iesg/scribe_conflict_doc.html
+++ b/ietf/templates/iesg/scribe_conflict_doc.html
@@ -1,98 +1,23 @@
-{% comment %}
-Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
+
+ {{ doc.title }}
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
+ {{doc.canonical_name}}
+ [txt]
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+
+ Token: {{ doc.ad }}
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}{% comment %}
-Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
-{% endcomment %}
-{% load ietf_filters %}
-{% if title2_first %}
- {% if title1_first %}
-
-{% endif%}
-{% endautoescape %}
\ No newline at end of file
diff --git a/ietf/templates/iesg/scribe_doc_ballot.html b/ietf/templates/iesg/scribe_doc_ballot.html
new file mode 100644
index 000000000..f00f67f93
--- /dev/null
+++ b/ietf/templates/iesg/scribe_doc_ballot.html
@@ -0,0 +1,33 @@
+ {% with doc.active_ballot as ballot %}
+ {% if ballot %}
+ Discusses/comments[ballot]:
+
+ {% for p in ballot.active_ad_positions.values %}
+ {% if p.pos %}
+ {% if p.discuss %}
+
\ No newline at end of file
diff --git a/ietf/templates/iesg/scribe_template.html b/ietf/templates/iesg/scribe_template.html
index 78c97dd1b..980089927 100644
--- a/ietf/templates/iesg/scribe_template.html
+++ b/ietf/templates/iesg/scribe_template.html
@@ -41,40 +41,50 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
{% load ietf_filters %}{% filter compress_empty_lines %}
-
Scribe template for IESG Narrative Minutes, {{date}}
+
Scribe template for IESG Narrative Minutes, {{ date }}
-{% with "iesg/scribe_doc.html" as doc_template %}
-{% with "iesg/scribe_conflict_doc.html" as doc_conflict_template %}
-{% include "iesg/agenda_outline_23.html" %}
-{% endwith %}
-{% endwith %}
+{% for num, section in sections %}
+
{{ num }}{% if num|sectionlevel == 1 %}.{% endif %} {{ section.title|safe }}
+
+ {% if "docs" in section %}
+
{% for doc in section.docs %}
+{% if doc.type_id == "draft" or doc.type_id == "statchg" %}{% include "iesg/scribe_doc.html" %}{% endif %}
+{% if doc.type_id == "conflrev" %}{% include "iesg/scribe_conflict_doc.html" %}{% endif %}
+ {% empty %}
+
(none)
{% endfor %}
+
+ {% endif %}
+{% endfor %}
Appendix: Snapshot of discusses/comments
(at {% now "Y-m-d H:i:s T" %})
-{% for doc in docs.s211 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s212 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s213 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s221 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s222 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s223 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s231 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s232 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s233 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s311 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s312 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s313 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s321 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s322 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s323 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s331 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s332 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s333 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s341 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s342 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
-{% for doc in docs.s343 %}{% include "iesg/scribe_doc2.html" %}{%endfor%}
+{% for doc in appendix_docs %}
+
+
{{ doc.name }}
+
+ {% with doc.active_ballot as ballot %}
+ {% if ballot %}
+
{% for p in ballot.active_ad_positions.values %}
+ {% if p.pos and p.discuss %}
+
These are the submitted WG descriptions and milestones for all
-Proposed Working Groups and recharter submissions, along with
-whatever status information is available.
-
-
NOTE: Explicit direction by the AD is required to add the group to
-an IESG Telechat agenda.
-
-{% if state and state.slug == "wg-doc" and not milestones %}
-
This document is not part of any milestone. You may wish to add it to one.
-{% endif %}
-
-{% if state and state.slug == "sub-pub" and milestones %}
-
This document is part of {% if milestones|length > 1 %}{{ milestones|length }}
-milestones{% else %}a milestone{% endif %}. Now that the draft is
-submitted to IESG for publication, you may wish to
-update the
-milestone{{ milestones|pluralize }}.
-{% endif %}
-
-{{ form }}
-
-{% endblock %}
diff --git a/ietf/templates/ietfworkflows/state_form.html b/ietf/templates/ietfworkflows/state_form.html
deleted file mode 100644
index a96b20f3e..000000000
--- a/ietf/templates/ietfworkflows/state_form.html
+++ /dev/null
@@ -1,32 +0,0 @@
-
diff --git a/ietf/templates/ietfworkflows/state_updated_mail.txt b/ietf/templates/ietfworkflows/state_updated_mail.txt
deleted file mode 100644
index 7eb828edd..000000000
--- a/ietf/templates/ietfworkflows/state_updated_mail.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-{% autoescape off %}
-The state of document {{ doc }} has been updated. See more information below.
-
-Previous state: {{ entry.from_state }}
-Current state: {{ entry.to_state }}
-Transition date: {{ entry.transition_date }}
-Author of the change: {{ entry.person }}
-{% if entry.set_tags %}Annotation tags set: {{ entry.set_tags }}{% endif %}
-{% if entry.reset_tags %}Annotation tags reset: {{ entry.reset_tags }}{% endif %}
-Comment:
-{{ entry.comment }}
-{% endautoescape %}
diff --git a/ietf/templates/ietfworkflows/stream_delegates.html b/ietf/templates/ietfworkflows/stream_delegates.html
deleted file mode 100644
index ca77c9575..000000000
--- a/ietf/templates/ietfworkflows/stream_delegates.html
+++ /dev/null
@@ -1,43 +0,0 @@
-{% extends "base.html" %}
-{% load ietf_streams ietf_filters %}
-
-{% block title %}Manage delegates for {{ stream.name }} stream{% endblock %}
-
-{% block content %}
-
-{% if stream %}
-{% if state %}{{ state.name }}{% else %}{{ stream }}{% endif %}
-{% else %}
- No stream assigned
-{% endif %}
-
-{% endif %}
diff --git a/ietf/templates/ietfworkflows/stream_updated_mail.txt b/ietf/templates/ietfworkflows/stream_updated_mail.txt
deleted file mode 100644
index b98640b88..000000000
--- a/ietf/templates/ietfworkflows/stream_updated_mail.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-{% autoescape off %}
-The stream of document {{ doc }} has been updated. See more information below.
-
-Previous stream: {{ entry.from_stream }}
-Current stream: {{ entry.to_stream }}
-Transition date: {{ entry.transition_date }}
-Author of the change: {{ entry.person }}
-
-Comment:
-{{ entry.comment }}
-{% endautoescape %}
diff --git a/ietf/templates/ietfworkflows/tags_form.html b/ietf/templates/ietfworkflows/tags_form.html
deleted file mode 100644
index e3388a868..000000000
--- a/ietf/templates/ietfworkflows/tags_form.html
+++ /dev/null
@@ -1,22 +0,0 @@
-
diff --git a/ietf/templates/ietfworkflows/workflow_history_entry.html b/ietf/templates/ietfworkflows/workflow_history_entry.html
deleted file mode 100644
index 3f30673f2..000000000
--- a/ietf/templates/ietfworkflows/workflow_history_entry.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
- {{ entry.date }}
- {{ entry.person }}
-
-
- {{ entry.describe_change|safe|linebreaks }}
-
-
- {{ entry.comment }}
-
-
diff --git a/ietf/templates/submit/announce_new_version.txt b/ietf/templates/submit/announce_new_version.txt
index 4ff47877b..79821349d 100644
--- a/ietf/templates/submit/announce_new_version.txt
+++ b/ietf/templates/submit/announce_new_version.txt
@@ -1,15 +1,15 @@
{% autoescape off %}
-A new version (-{{ submission.revision }}) has been submitted for {{ submission.filename }}:
-http://www.ietf.org/internet-drafts/{{ submission.filename }}-{{ submission.revision }}.txt
+A new version (-{{ submission.rev }}) has been submitted for {{ submission.name }}:
+http://www.ietf.org/internet-drafts/{{ submission.name }}-{{ submission.rev }}.txt
{% if msg %}
{{ msg|striptags }}
{% endif %}
The IETF datatracker page for this Internet-Draft is:
-https://datatracker.ietf.org/doc/{{ submission.filename }}/
+https://datatracker.ietf.org/doc/{{ submission.name }}/
Diff from previous version:
-http:{{rfcdiff_prefix}}?url2={{ submission.filename }}-{{ submission.revision }}
+http:{{rfcdiff_prefix}}?url2={{ submission.name }}-{{ submission.rev }}
Please note that it may take a couple of minutes from the time of submission
until the htmlized version and diff are available at tools.ietf.org.
diff --git a/ietf/templates/submit/announce_to_authors.txt b/ietf/templates/submit/announce_to_authors.txt
index 5f1e8c61d..1d61b9c86 100644
--- a/ietf/templates/submit/announce_to_authors.txt
+++ b/ietf/templates/submit/announce_to_authors.txt
@@ -1,23 +1,23 @@
{% autoescape off %}
-A new version of I-D, {{ submission.filename }}-{{ submission.revision }}.txt
-has been successfully submitted by {{ submitter }} and posted to the
+A new version of I-D, {{ submission.name }}-{{ submission.rev }}.txt
+has been successfully submitted by {{ submission.submitter_parsed.name }} and posted to the
IETF repository.
-Filename: {{ submission.filename }}
-Revision: {{ submission.revision }}
-Title: {{ submission.id_document_name }}
-Creation date: {{ submission.creation_date|date:"Y-m-d" }}
-Group: {{ wg }}
-Number of pages: {{ submission.txt_page_count }}
-URL: http://www.ietf.org/internet-drafts/{{ submission.filename }}-{{ submission.revision }}.txt
-Status: http://datatracker.ietf.org/doc/{{ submission.filename }}
-Htmlized: http://tools.ietf.org/html/{{ submission.filename }}-{{ submission.revision }}
-{% ifnotequal submission.revision "00" %}Diff: http:{{rfcdiff_prefix}}?url2={{ submission.filename }}-{{ submission.revision }}{% endifnotequal %}
+Name: {{ submission.name }}
+Revision: {{ submission.rev }}
+Title: {{ submission.title }}
+Document date: {{ submission.document_date|date:"Y-m-d" }}
+Group: {{ group }}
+Pages: {{ submission.pages }}
+URL: http://www.ietf.org/internet-drafts/{{ submission.name }}-{{ submission.rev }}.txt
+Status: https://datatracker.ietf.org/doc/{{ submission.name }}/
+Htmlized: http://tools.ietf.org/html/{{ submission.name }}-{{ submission.rev }}
+{% if submission.rev != "00" %}Diff: http:{{rfcdiff_prefix}}?url2={{ submission.name }}-{{ submission.rev }}{% endif %}
Abstract:
{{ submission.abstract }}
-{{ submission.comment_to_sec|default:"" }}
+{{ submission.note|default:"" }}
Please note that it may take a couple of minutes from the time of submission
until the htmlized version and diff are available at tools.ietf.org.
diff --git a/ietf/templates/submit/announce_to_lists.txt b/ietf/templates/submit/announce_to_lists.txt
index 76146152d..eb4324bda 100644
--- a/ietf/templates/submit/announce_to_lists.txt
+++ b/ietf/templates/submit/announce_to_lists.txt
@@ -1,25 +1,25 @@
{% autoescape off %}
A New Internet-Draft is available from the on-line Internet-Drafts directories.
-{% if submission.group_acronym %} This draft is a work item of the {{ submission.group_acronym.group_acronym.name }} Working Group of the IETF.{% endif %}
+{% if submission.group %} This draft is a work item of the {{ submission.group.name }} Working Group of the IETF.{% endif %}
- Title : {{ submission.id_document_name }}
- Author(s) : {% for author in authors %}{{ author }}{% if not forloop.last %}
+ Title : {{ submission.title }}
+ Author{{ submission.authors_parsed|pluralize:" ,s" }} : {% for author in submission.authors_parsed %}{{ author.name }}{% if not forloop.last %}
{% endif %}{% endfor %}
- Filename : {{ submission.filename }}-{{ submission.revision }}.txt
- Pages : {{ submission.txt_page_count }}
+ Filename : {{ submission.name }}-{{ submission.rev }}.txt
+ Pages : {{ submission.pages }}
Date : {{ submission.submission_date|date:"Y-m-d" }}
Abstract:
{{ submission.abstract }}
The IETF datatracker status page for this draft is:
-https://datatracker.ietf.org/doc/{{ submission.filename }}
+https://datatracker.ietf.org/doc/{{ submission.name }}/
There's also a htmlized version available at:
-http://tools.ietf.org/html/{{ submission.filename }}-{{ submission.revision }}
-{% if submission.revision != "00" %}
+http://tools.ietf.org/html/{{ submission.name }}-{{ submission.rev }}
+{% if submission.rev != "00" %}
A diff from the previous version is available at:
-http:{{settings.RFCDIFF_PREFIX}}?url2={{ submission.filename }}-{{ submission.revision }}
+http:{{settings.RFCDIFF_PREFIX}}?url2={{ submission.name }}-{{ submission.rev }}
{% endif %}
Please note that it may take a couple of minutes from the time of submission
diff --git a/ietf/templates/submit/approval_request.txt b/ietf/templates/submit/approval_request.txt
new file mode 100644
index 000000000..3eaa989fe
--- /dev/null
+++ b/ietf/templates/submit/approval_request.txt
@@ -0,0 +1,33 @@
+{% autoescape off %}
+Hi,
+
+Chair approval is needed for posting of {{ submission.name }}-{{ submission.rev }}.
+
+To approve the draft, go to this URL (note: you need to login to be able to approve):
+ https://{{ domain }}{% url submit_submission_status_by_hash submission_id=submission.pk access_token=submission.access_token %}
+
+ File name : {{ submission.name }}
+ Revision : {{ submission.rev }}
+ Submission date : {{ submission.submission_date }}
+ Group : {{ submission.group|default:"Individual Submission" }}
+
+ Title : {{ submission.title }}
+ Document date : {{ submission.document_date }}
+ Pages : {{ submission.pages }}
+ File size : {{ submission.file_size|filesizeformat }}
+
+ Submitter : {{ submission.submitter }}
+
+ Abstract : {{ submission.abstract }}
+
+
+ Authors:
+{% for author in submission.authors_parsed %} {{ author.name }}{% if author.email %} <{{ author.email }}>{% endif%}
+{% endfor %}
+{% endautoescape %}
+
+
+Best regards,
+
+ The IETF Secretariat
+ through the draft submission service
diff --git a/ietf/templates/submit/approvals.html b/ietf/templates/submit/approvals.html
index b4907d139..960eaa61b 100644
--- a/ietf/templates/submit/approvals.html
+++ b/ietf/templates/submit/approvals.html
@@ -22,16 +22,16 @@ table.preapprovals tr:hover td a.cancel { visibility: visible; }
Status of the submission: {{ status.status_value }}
-{% endif %}
-
-{% if message %}
-
{{ message.1|safe }}
-{% endif %}
-
-{% if auto_post_form.errors %}
-
Please fix errors in the form below
-{% endif %}
-
-
Check Page
-
-{% if validation.passes_idnits %}
-Your draft has been verified to meet IDNITS requirements.
-{% else %}
-Your draft has NOT been verified to meet IDNITS requirements.
-{% endif %}
-(View IDNITS Results)
-
-Please make sure that your Internet-Draft includes all of the required meta-data in the proper format.
-
-
-If your Internet-Draft *does* include all of the required meta-data in the proper format, and if
-the error(s) identified above are due to the failure of the tool to extract the meta-data correctly,
-then please use the 'Adjust Meta-Data' button below, which will take you to the 'Adjust Screen' where
-you can correct the improperly extracted meta-data. You will then be able to submit your Internet-Draft
-to the Secretariat for manual posting.
-
-
-If your Internet-Draft *does not* include all of the required meta-data in the proper format, then
-please cancel this submission, update your Internet-Draft, and resubmit it.
-
-
-NOTE: The Secretariat will NOT add any meta-data to your Internet-Draft or edit the meta-data. An
-Internet-Draft that does not include all of the required meta-data in the proper format WILL be
-returned to the submitter.
-
Please edit the following meta-data before proceeding to Auto-Post
-
- If you are one of the authors of this document, then please click the button with your name on it to automatically fill in the submitter information as requested below. Otherwise, please manually enter your information.
-
- This submission will be canceled, and its uploaded document(s) permanently deleted.
-
-{% endif %}
-
-{% if can_approve %}
-
-
-
-{% endif %}
-
-{% if can_force_post %}
-
-
-
-{% endif %}
-
-{% if show_notify_button %}
-
-
-You are not allowed to modify or cancel this submission. You only can modify or cancel this submission from the same URL you were redirected to after the submission.
-
-
-If you are the submitter check your browser history to find this url. You can share it with any person you need.
-
-
-If you are one of the authors you can request the URL from wich you can modify or cancel this submission by clicking the next button. An email will be sent to the draft authors and to the submitter (if the submitter's email is available).
-
{{ submission.group|default:"Individual Submission" }}
+ {% if errors.group %}
{{ errors.group }} (note: the Secretariat will be notified of this)
{% endif %}
+
+
+
+
File size
{{ submission.file_size|filesizeformat }}
+
+
+
Adjust meta-data
+
+{% if form_errors %}
+
+ Please fix the following errors.
+
+{% endif %}
+
+
+
+{% include "submit/problem-reports-footer.html" %}
+
+{% endblock %}
+
+{% block js %}
+{{ block.super }}
+
+
+{% endblock %}
diff --git a/ietf/templates/submit/request_full_url.txt b/ietf/templates/submit/full_url.txt
similarity index 75%
rename from ietf/templates/submit/request_full_url.txt
rename to ietf/templates/submit/full_url.txt
index ecb975303..a7df4d9f2 100644
--- a/ietf/templates/submit/request_full_url.txt
+++ b/ietf/templates/submit/full_url.txt
@@ -2,7 +2,7 @@
Hi,
The datatracker has received a request to send out the link to the URL where you
-can confirm the submission of your draft {{ submission.filename }}-{{ submission.revision }}.
+can confirm the submission of your draft {{ submission.name }}-{{ submission.rev }}.
Please follow this link to get full access to the submission page:
{{ url|safe }}
diff --git a/ietf/templates/submit/last_confirmation_step.html b/ietf/templates/submit/last_confirmation_step.html
deleted file mode 100644
index 1b6ca01a4..000000000
--- a/ietf/templates/submit/last_confirmation_step.html
+++ /dev/null
@@ -1,26 +0,0 @@
-{% extends "submit/submit_base.html" %}
-
-{% block title %}Confirm Auto-Post{% endblock %}
-
-
-{% block submit_content %}
-
-
Confirm auto-post
-
-Authorization key accepted. Please press the button below to finish Auto-Post of {{ detail.filename }}-{{ detail.revision }}
-
-
-{% endblock %}
-
-{% block scripts %}
-jQuery(function () {
- jQuery("form").submit(function() {
- if (this.submittedAlready)
- return false;
- else
- this.submittedAlready = true;
- });
-});
-{% endblock %}
diff --git a/ietf/templates/submit/manual_post_mail.txt b/ietf/templates/submit/manual_post_mail.txt
deleted file mode 100644
index 7fd5ffd9c..000000000
--- a/ietf/templates/submit/manual_post_mail.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-{% autoescape off %}
-Hi,
-
-Manual posting has been requested for the following Internet-Draft:
-
-I-D Submission Tool URL:
- {{ url }}
-
- File name : {{ draft.filename }}
- Version : {{ draft.revision }}
- Submission date : {{ draft.submission_date }}
- Group : {{ draft.group_acronym|default:"Individual Submission" }} {% if form.validation.warnings.group %}*Please note that this group is not an active one*{% endif %}
-
- Title : {{ draft.id_document_name }}
- Document date : {{ draft.creation_date }}
- Pages : {{ draft.txt_page_count }}
- File size : {{ draft.filesize|filesizeformat }}
-
- Submitter : {{ submitter.get_full_name }} <{{ submitter.email.1 }}>
-
- Abstract : {{ draft.abstract }}
-
-
- Authors:
-{% for author in form.get_authors %} {{ author.get_full_name }} <{{ author.email.1 }}>
-{% endfor %}
-
- Comments to the secretariat:
-
-{{ draft.comment_to_sec }}
-{% endautoescape %}
diff --git a/ietf/templates/submit/manual_post_request.txt b/ietf/templates/submit/manual_post_request.txt
new file mode 100644
index 000000000..1620a476f
--- /dev/null
+++ b/ietf/templates/submit/manual_post_request.txt
@@ -0,0 +1,31 @@
+{% autoescape off %}
+Hi,
+
+Manual posting has been requested for the following Internet-Draft:
+
+I-D Submission Tool URL:
+ {{ url }}
+
+ File name : {{ submission.name }}
+ Revision : {{ submission.rev }}
+ Submission date : {{ submission.submission_date }}
+ Group : {{ submission.group|default:"Individual Submission" }} {% if errors.group %}*Please note: {{ errors.group }}*{% endif %}
+
+ Title : {{ submission.title }}
+ Document date : {{ submission.document_date }}
+ Pages : {{ submission.pages }}
+ File size : {{ submission.file_size|filesizeformat }}
+
+ Submitter : {{ submission.submitter }}
+
+ Abstract : {{ submission.abstract }}
+
+
+ Authors:
+{% for author in submission.authors_parsed %} {{ author.name }}{% if author.email %} <{{ author.email }}>{% endif%}
+{% endfor %}
+
+ Comment to the secretariat:
+
+{{ submission.note }}
+{% endautoescape %}
diff --git a/ietf/templates/submit/problem-reports-footer.html b/ietf/templates/submit/problem-reports-footer.html
new file mode 100644
index 000000000..8216223a5
--- /dev/null
+++ b/ietf/templates/submit/problem-reports-footer.html
@@ -0,0 +1,4 @@
+
Please enter the name of the Internet-Draft you wish to view
+ submission status for:
+
+
+
+{% include "submit/problem-reports-footer.html" %}
+
+{% endblock %}
diff --git a/ietf/templates/submit/submission_approval.txt b/ietf/templates/submit/submission_approval.txt
deleted file mode 100644
index ddfa7fd94..000000000
--- a/ietf/templates/submit/submission_approval.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-{% autoescape off %}
-Hi,
-
-WG chair approval is needed for posting of {{ draft.filename }}-{{ draft.revision }}.
-
-To approve the draft, go to this URL (note: you need to login to be able to approve):
- https://{{ domain }}/submit/status/{{ draft.submission_id }}/{{ draft.submission_hash }}/
-
- File name : {{ draft.filename }}
- Version : {{ draft.revision }}
- Submission date : {{ draft.submission_date }}
- Group : {{ draft.group_acronym|default:"Individual Submission" }}
-
- Title : {{ draft.id_document_name }}
- Document date : {{ draft.creation_date }}
- Pages : {{ draft.txt_page_count }}
- File size : {{ draft.filesize|filesizeformat }}
-
- Submitter : {{ submitter.get_full_name }} <{{ submitter.email_address }}>
-
- Abstract : {{ draft.abstract }}
-
-
- Authors:
-{% for author in authors %} {{ author.get_full_name }} <{{ author.email.1 }}>
-{% endfor %}
-{% endautoescape %}
-
-
-Best regards,
-
- The IETF Secretariat
- through the draft submission service
diff --git a/ietf/templates/submit/submission_status.html b/ietf/templates/submit/submission_status.html
new file mode 100644
index 000000000..778ea24ef
--- /dev/null
+++ b/ietf/templates/submit/submission_status.html
@@ -0,0 +1,285 @@
+{% extends "submit/submit_base.html" %}
+
+{% load ietf_filters submit_tags %}
+
+{% block title %}Status of submission of {{ submission.name }}-{{ submission.rev }}{% endblock %}
+
+{% block submit_content %}
+
+{% if submission.state_id != "uploaded" %}
+
Status of the submission: {{ submission.state.name }}
+{% endif %}
+
+{% if message %}
+
{{ message.1 }}
+{% endif %}
+
+{% if submission.state_id == "aut-appr" and submission.submitter not in confirmation_list|join:", " %}
+
+ Please note that since the database does not have your email address in the list of authors of previous
+ revisions of the document, you are not receiving a confirmation email yourself; one of the
+ addressees above will have to send a confirmation in order to complete the submission. This is done
+ to avoid document hijacking. If none of the known previous authors will be able to confirm the
+ submission, please contact the Secretariat for action.
+
+{% endif %}
+
+{% if submitter_form.errors %}
+
Please fix errors in the form below
+{% endif %}
+
+
IDNITS
+
+ {% if passes_idnits %}
+ Your draft has been verified to meet IDNITS requirements.
+ {% else %}
+ Your draft has NOT been verified to meet IDNITS requirements.
+ {% endif %}
+ (View IDNITS Results)
+
Please make sure that your Internet-Draft includes all of the required meta-data in the proper format.
+
+
If your Internet-Draft *does* include all of the required meta-data in the proper format, and if
+ the error(s) identified above are due to the failure of the tool to extract the meta-data correctly,
+ then please use the 'Adjust Meta-Data' button below, which will take you to the 'Adjust Screen' where
+ you can correct the improperly extracted meta-data. You will then be able to submit your Internet-Draft
+ to the Secretariat for manual posting.
+
+
If your Internet-Draft *does not* include all of the required meta-data in the proper format, then
+ please cancel this submission, update your Internet-Draft, and resubmit it.
+
+
NOTE: The Secretariat will NOT add any
+ meta-data to your Internet-Draft or edit the meta-data. An
+ Internet-Draft that does not include all of the required meta-data in
+ the proper format WILL be returned to the submitter.
+ Cancel submission and delete the uploaded file{{ submission.file_types|split:","|pluralize }} permanently:
+
+
+
+{% endif %}
+
+{% if can_group_approve %}
+
Approve submission
+
+
+
+{% endif %}
+
+{% if can_force_post %}
+
+
+
+{% endif %}
+
+{% if show_send_full_url %}
+
+
You are not allowed to modify or cancel this submission. You can
+ only modify or cancel this submission from the same URL you were
+ redirected to after the submission.
+
+
If you are the submitter check your browser history to find this
+ URL. You can share it with any person you need.
+
+
If you are one of the authors you can request the URL from wich
+ you can modify or cancel this submission by clicking the next
+ button. An email will then be sent to the authors and submitter
+ (if submitter email was entered): {{ confirmation_list|join:", " }}.
+
+
+
+{% endif %}
+
+
History
+
+
+
Date
By
Text
+
+ {% for e in submission.submissionevent_set.all %}
+
This page is used to submit IETF Internet-Drafts to the Internet-Draft repository. The list of current Internet-Drafts can be accessed at http://www.ietf.org/ietf/1id-abstracts.txt
-
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF), its areas, and its working groups. Note that other groups may also distribute working documents as Internet-Drafts.
-
Internet-Drafts are draft documents, and are valid for a maximum of six months. They may be updated, replaced, or obsoleted by other documents at any time.
-{% if not form.shutdown %}
-
If you run into problems when submitting an Internet-Draft using this and the following pages, you may alternatively submit your draft by email to internet-drafts@ietf.org. However, be advised that manual processing always takes additional time.
-Please enter the filename of the Internet-Draft you wish to view submission status for:
-
-
-
-
-
-
-Note that the status page only displays the status of an Internet-Draft with a posting still in progress or an Internet-Draft that has been successfully posted.
-
+ If you are one of the authors, please click the button below
+ with your name on it to automatically fill in the
+ submitter information. Otherwise,
+ please manually enter your name and email address.
+
+ {% for author in submission.authors_parsed %}
+
+ {% endfor %}
+
This page will explain the purpose and content of each screen in the I-D Submission Tool, and the actions that result by clicking the form buttons on each screen.
The specification for this tool can be found in RFC 4228.
@@ -98,7 +98,7 @@ This is the screen where a user can adjust any meta-data that could have been in
Status Screen
-The Status screen is the screen where a user can view the current status of a document that has just been submitted by the user, or a document that was submitted previously via the tool. If a link 'Status' is clicked from the tool's first page, then a form field will be provided for a user to look up a document by filename.
+The Status screen is the screen where a user can view the current status of a document that has just been submitted by the user, or a document that was submitted previously via the tool. If a link 'Status' is clicked from the tool's first page, then a form field will be provided for a user to look up a document by name.
This page is used to submit IETF Internet-Drafts to the
+Internet-Draft repository. The list of current Internet-Drafts can be
+accessed athttp://www.ietf.org/ietf/1id-abstracts.txt
+
+
Internet-Drafts are working documents of the Internet Engineering
+Task Force (IETF), its areas, and its working groups. Note that other
+groups may also distribute working documents as Internet-Drafts.
+
+
Internet-Drafts are draft documents, and are valid for a maximum of
+six months. They may be updated, replaced, or obsoleted by other
+documents at any time.
+
+{% if not form.shutdown %}
+
If you run into problems when submitting an Internet-Draft
+using this and the following pages, you may alternatively submit
+your draft by email to
+internet-drafts@ietf.org.
+However, be advised that manual processing always takes additional time.
-Sometimes, a WG has one (or more) WG Secretaries, in addition to the WG Chairs.
-This page lets the WG Chairs delegate the authority to do updates to the WG state of WG documents in the datatracker.
-
-
-
-You may at most delegate the datatracker update rights to {{ max_delegates }} persons at any given time.
-
-The shepherd you are trying to designate does not have a personal user-id and password to log-on to the Datatracker.
-
-
-An email will be sent to the following addresses to inform that
-the person you have designated to be one of your document shepherds
-currently does not have login credentials for the Datatracker
-and should contact the Secretariat to obtain their own user-id and
-password for the Datatracker.
-
-{% else %}
-
-The delegate you are trying to designate does not have a personal user-id and password to log-on to the Datatracker.
-
-
-An email will be sent to the following addresses to inform that
-the person you have designated to be one of your delegates
-currently does not have login credentials for the Datatracker
-and should contact the Secretariat to obtain their own user-id and
-password for the Datatracker.
-
-{% endif %}
-
-{% for email in email_list %}
-
{{ email }}
-{% endfor %}
-
diff --git a/ietf/templates/wgchairs/notexistsdelegate_delegate_email.txt b/ietf/templates/wgchairs/notexistsdelegate_delegate_email.txt
deleted file mode 100644
index de2318a93..000000000
--- a/ietf/templates/wgchairs/notexistsdelegate_delegate_email.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-{% autoescape off %}{{ chair }} as a WG Chair of {{ wg }} wants to add you as a {{ wg }} {% if shepherd %}shepherd of document {{ shepherd }}{% else %}WG Delegate{% endif %}.
-
-You don't have an user/password to log into the datatracker so you must contact
-the Secretariat at iesg-secretary@ietf.org in order to get your credentials.
-
-When you get your credentials, please inform {{ chair }} at
-{{ chair.email.1 }} so he/she can finish the designate process.
-
-Thank you.
-{% endautoescape %}
diff --git a/ietf/templates/wgchairs/notexistsdelegate_secretariat_email.txt b/ietf/templates/wgchairs/notexistsdelegate_secretariat_email.txt
deleted file mode 100644
index 27edacc11..000000000
--- a/ietf/templates/wgchairs/notexistsdelegate_secretariat_email.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-{% autoescape off %}{{ chair }} as a WG Chair of {{ wg }} wants to add a person with email
-{{ delegate_email }} as a {% if shepherd %}shepherd of document {{ shepherd }}{% else %}WG Delegate{% endif %}.
-
-This person don't have an user/password to log into the datatracker so
-an email has been seent to {{ delegate_email }} in order to he/she contacs the
-Secretariat to request his/her credentials.
-
-{% if delegate_persons %}
-Please, note that the following persons with {{ delegate_email }} email address
-already exists in the system but they can not log in.
-{% for person in delegate_persons %}
-{{ person.pk }} - {{ person }}
-{% endfor %}
-{% endif %}
-{% endautoescape %}
\ No newline at end of file
diff --git a/ietf/templates/wgchairs/notexistsdelegate_wgchairs_email.txt b/ietf/templates/wgchairs/notexistsdelegate_wgchairs_email.txt
deleted file mode 100644
index 477111dcf..000000000
--- a/ietf/templates/wgchairs/notexistsdelegate_wgchairs_email.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-{% autoescape off %}{{ chair }} as a WG Chair of {{ wg }} wants to add a person with email
-{{ delegate_email }} as a {% if shepherd %}shepherd of document {{ shepherd }}{% else %}WG Delegate{% endif %}.
-
-This person don't have an user/password to log into the datatracker so
-an email has been seent to {{ delegate_email }} in order to he/she contacs the
-Secretariat to request his/her credentials.
-
-When he/she gets her credentials then he/she will send an email to
-{{ chair }} at {{ chair.email.1 }}.
-
-{{ chair }} could then assign this person as {% if shepherd %}shepherd of document {{ shepherd }}{% else %}WG Delegate{% endif %}.
-{% endautoescape%}
\ No newline at end of file
diff --git a/ietf/templates/wgchairs/shepherd_document_row.html b/ietf/templates/wgchairs/shepherd_document_row.html
deleted file mode 100644
index a58e1eac5..000000000
--- a/ietf/templates/wgchairs/shepherd_document_row.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% load wgchairs_tags %}
-
-
diff --git a/ietf/templates/wgchairs/wg_shepherd_documents.html b/ietf/templates/wgchairs/wg_shepherd_documents.html
deleted file mode 100644
index 2afcd9bac..000000000
--- a/ietf/templates/wgchairs/wg_shepherd_documents.html
+++ /dev/null
@@ -1,122 +0,0 @@
-{% extends "wginfo/wg_base.html" %}
-{% comment %}
-Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}
-{% block wg_titledetail %}Documents{% endblock %}
-
-{% block pagehead %}
-{{ block.super }}
-
-
-
-{% endblock pagehead %}
-
-{% block wg_content %}
-
-
-
- {% for doc in no_shepherd %}
- {% include "wgchairs/shepherd_document_row.html" %}
- {% endfor %}
-
-
-
-
-
-
-
-
Document
-
Status
-
Shepherd write-up
-
Shepherd write-up last update
-
-
- {% for doc in my_documents %}
- {% include "wgchairs/shepherd_document_row.html" %}
- {% endfor %}
-
-
-
-
-
-{% regroup other_shepherds by shepherd as regrouped %}
-{% for documents in regrouped %}
-
{{ documents.grouper }}
-
-
-
Document
-
Status
-
Shepherd write-up
-
Shepherd write-up last update
-
- {% for doc in documents.list %}
- {% include "wgchairs/shepherd_document_row.html" %}
- {% endfor %}
-
-{% endfor %}
-
-
-
-{% endblock wg_content %}
-
diff --git a/ietf/templates/wgchairs/wgchairs_admin_options.html b/ietf/templates/wgchairs/wgchairs_admin_options.html
deleted file mode 100644
index b8c45a00f..000000000
--- a/ietf/templates/wgchairs/wgchairs_admin_options.html
+++ /dev/null
@@ -1,23 +0,0 @@
-{% if can_manage_workflow %}
- {% ifequal selected "manage_workflow" %}
- Manage workflow
- {% else %}
- Manage workflow
- {% endifequal %} |
-{% endif %}
-
-{% if can_manage_delegates %}
- {% ifequal selected "manage_delegates" %}
- Manage delegations
- {% else %}
- Manage delegations
- {% endifequal %} |
-{% endif %}
-
-{% if can_manage_shepherds %}
- {% ifequal selected "manage_shepherds" %}
- Manage shepherds
- {% else %}
- Manage shepherds
- {% endifequal %} |
-{% endif %}
diff --git a/ietf/templates/wginfo/1wg-charters-by-acronym.txt b/ietf/templates/wginfo/1wg-charters-by-acronym.txt
index 247e1466c..bad8939cd 100644
--- a/ietf/templates/wginfo/1wg-charters-by-acronym.txt
+++ b/ietf/templates/wginfo/1wg-charters-by-acronym.txt
@@ -1,4 +1,4 @@
-{% load ietf_filters %}{% for wg in wg_list|dictsort:"group_acronym.acronym" %}{% if wg.area.area.status_id == 1 and wg.area.area.acronym != "iesg" %}{% if wg.start_date %}{{ wg }}
-{% endif %}{% endif %}{% endfor %}
-{% for wg in wg_list|dictsort:"group_acronym.acronym" %}{% if wg.area.area.status_id == 1 and wg.area.area.acronym != "iesg" %}{% if wg.start_date %}{% include "wginfo/wg-charter.txt" %}
-{% endif %}{% endif %}{% endfor %}
+{% autoescape off %}{% load ietf_filters %}{% for group in groups %}{{ group.acronym }}
+{% endfor %}
+
+{% for group in groups %}{% include "wginfo/group_entry_with_charter.txt" %}{% endfor %}{% endautoescape %}
diff --git a/ietf/templates/wginfo/1wg-charters.txt b/ietf/templates/wginfo/1wg-charters.txt
index 4e5e70941..767dd1bb0 100644
--- a/ietf/templates/wginfo/1wg-charters.txt
+++ b/ietf/templates/wginfo/1wg-charters.txt
@@ -1,7 +1,6 @@
-{% load ietf_filters %}{% regroup wg_list|dictsort:"area_acronym.acronym" by area.area as wga_list %}{% for area in wga_list %}{% for wg in area.list|dictsort:"group_acronym.name" %}{% if wg.area.area.status_id == 1 and wg.area.area.acronym != "iesg" %}{% if wg.start_date %}{{ wg }}
-{% endif %}{% endif %}{% endfor %}{% endfor %}
+{% autoescape off %}{% load ietf_filters %}{% for area in areas %}{% for group in area.groups %}{{ group.acronym }}
+{% endfor %}{% endfor %}
+
+{% for area in areas %}{% for group in area.groups %}{% include "wginfo/group_entry_with_charter.txt" %}{% endfor %}{% endfor %}{% endautoescape %}
-{% regroup wg_list|dictsort:"area_acronym.acronym" by area.area as wga_list %}{% for area in wga_list %}{% for wg in area.list|dictsort:"group_acronym.name" %}{% if wg.area.area.status_id == 1 and wg.area.area.acronym != "iesg" %}{% if wg.start_date %}
-{% include "wginfo/wg-charter.txt" %}
-{% endif %}{% endif %}{% endfor %}{% endfor %}
diff --git a/ietf/templates/wginfo/1wg-summary-by-acronym.txt b/ietf/templates/wginfo/1wg-summary-by-acronym.txt
index d1a0f46be..028d5fd0e 100644
--- a/ietf/templates/wginfo/1wg-summary-by-acronym.txt
+++ b/ietf/templates/wginfo/1wg-summary-by-acronym.txt
@@ -1,10 +1,10 @@
-{% load ietf_filters %}
+{% autoescape off %}{% load ietf_filters %}
IETF Working Group Summary (By Acronym)
The following Area Abbreviations are used in this document
-{% for area in area_list %}
-{{ area|upper }} - {{ area.area_acronym.name }}{% endfor %}
-{% for wg in wg_list|dictsort:"group_acronym.acronym" %}{% if wg.start_date %}
-{{ wg.group_acronym.name|safe }} ({{ wg }}) -- {{ wg.area.area|upper }}
-{% include "wginfo/wg_summary.txt" %}{% endif %}{% endfor %}
+{% for area in areas %}
+{{ area.acronym|upper }} - {{ area.name }}{% endfor %}
+{% for group in groups %}
+{{ group.name }} ({{ group.acronym }}) -- {{ group.parent.acronym|upper }}
+{% include "wginfo/group_entry.txt" %}{% endfor %}{% endautoescape %}
diff --git a/ietf/templates/wginfo/1wg-summary.txt b/ietf/templates/wginfo/1wg-summary.txt
index 55d76d1e2..f6ae51202 100644
--- a/ietf/templates/wginfo/1wg-summary.txt
+++ b/ietf/templates/wginfo/1wg-summary.txt
@@ -1,8 +1,11 @@
-{% load ietf_filters %} IETF Working Group Summary (By Area)
-{% regroup wg_list|dictsort:"area.area.area_acronym.acronym" by area.area as wga_list %}{% for area in wga_list %}{% for wg in area.list|dictsort:"group_acronym.acronym" %}{% ifequal wg.area.area.status_id 1 %}{% if forloop.first %}
-{{ wg.area_acronym.name }} ({{ wg.area_acronym }})
-{{ wg.area_acronym.name|dashify }}------{% for ad in wg.area_directors %}
- {{ ad.person }} <{{ ad.person.email.1 }}>{% endfor %}
-{% endif %}{% if wg.start_date %}
-{{ wg.group_acronym.name|safe }} ({{ wg }})
-{% include "wginfo/wg_summary.txt" %}{% endif %}{% endifequal %}{% endfor %}{% endfor %}
+{% autoescape off %}{% load ietf_filters %}
+ IETF Working Group Summary (By Area)
+
+
+{% for area in areas %}{{ area.name }} ({{ area.acronym }})
+{{ area.name|dashify }}------{% for ad in area.ads %}
+ {{ ad.person.plain_name }} <{{ ad.email.address }}>{% endfor %}
+
+{% for group in area.groups %}{{ group.name }} ({{ group.acronym }})
+{% include "wginfo/group_entry.txt" %}
+{% endfor %}{% endfor %}{% endautoescape %}
diff --git a/ietf/templates/wginfo/wg-dirREDESIGN.html b/ietf/templates/wginfo/active_wgs.html
similarity index 69%
rename from ietf/templates/wginfo/wg-dirREDESIGN.html
rename to ietf/templates/wginfo/active_wgs.html
index a74f48ba9..4d88435fa 100644
--- a/ietf/templates/wginfo/wg-dirREDESIGN.html
+++ b/ietf/templates/wginfo/active_wgs.html
@@ -53,47 +53,44 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% for area in areas %}
{{ area.name }}
- {% for ad in area.ads %}
- {% if forloop.first %}
-
{{ g.name }}
diff --git a/ietf/templates/wginfo/chartering_wgs.html b/ietf/templates/wginfo/chartering_wgs.html
index 291daa6a5..67255ca0b 100644
--- a/ietf/templates/wginfo/chartering_wgs.html
+++ b/ietf/templates/wginfo/chartering_wgs.html
@@ -28,7 +28,7 @@
{% for g in groups %}
Request closing of {{ wg.acronym }} {{ wg.type.name }}
Please provide instructions regarding the disposition of each
active Internet-Draft (such as to withdraw the draft, move it to
- another WG, convert it to an individual submission, and so on),
- wording for the closure announcement, and the status of the WG
+ another group, convert it to an individual submission, and so on),
+ wording for the closure announcement, and the status of the group
mailing list (will it remain open or should it be closed).
+
-
- {{ state.name }} {% if not state.used %} (not used in {{ wg.acronym }}){% endif %}
- +
-
-
{{ state.desc|safe|linebreaks }}
+ {{ state.name }} {% if not state.used %} (not used in {{ group.acronym }}){% endif %} {{ state|statehelp }}
{% if state.used_next_states %}
- {% for n in state.used_next_states %}{{ n.name }}{% if not forloop.last %} {% endif %}{% endfor %}
+ {% for n in state.used_next_states %}
-{% ifequal action "edit" %}
+{% if action == "edit" %}
Edit WG {{ wg.acronym }}
{% else %}
- {% ifequal action "charter" %}
+ {% if action == "charter" %}
Start chartering new WG
{% else %}
Create new WG or BoF
- {% endifequal %}
-{% endifequal %}
+ {% endif %}
+{% endif %}
+
Note that persons with authorization to manage information, e.g.
+chairs and delegates, need a Datatracker account to actually do
+so. New accounts can be created here.
-{% ifequal selected "documents" %}Documents{% else %}Documents{% endifequal %} |
-{% ifequal selected "charter" %}Charter{% else %}Charter{% endifequal %} |
-{% wgchairs_admin_options wg %}
-History |
-{% if wg.clean_email_archive|startswith:"http:" or wg.clean_email_archive|startswith:"https:" or wg.clean_email_archive|startswith:"ftp:" %}
-List Archive » |
-{% endif %}
-Tools WG Page »
+
+ Documents |
+ Charter |
+ History |
+ {% if group.list_archive|startswith:"http:" or group.list_archive|startswith:"https:" or group.list_archive|startswith:"ftp:" %}
+ List Archive » |
+ {% endif %}
+ Tools WG Page »
+
+
+ {% if menu_actions %}
+
+ {% for name, url in menu_actions %}
+ {{ name }}
+ {% endfor %}
+
+
+{% with group.groupurl_set.all as urls %}
+{% if urls %}
+
In addition to the charter maintained by the IETF Secretariat, there is additional information about this working group on the Web at:
+ {% for url in urls %}
+ {{ url.name }}{% if not forloop.last %}, {% endif %}
+ {% endfor %}
+
+{% endif %}
+{% endwith %}
+
+
Charter for {% if group.state_id == "proposed" %}Proposed{% endif %} Working Group
+
+{% include "doc/search/search_results.html" %}
+
+{% with docs_related as docs %}{% with meta_related as meta %}{% include "doc/search/search_results.html" %}{% endwith %}{% endwith %}
+
+
+{% endblock group_content %}
+
+{% block js %}
+
+
+{% endblock %}
diff --git a/ietf/templates/wginfo/group_entry.txt b/ietf/templates/wginfo/group_entry.txt
new file mode 100644
index 000000000..cd8d4b60c
--- /dev/null
+++ b/ietf/templates/wginfo/group_entry.txt
@@ -0,0 +1,4 @@
+{% for chair in group.chairs %}{% if forloop.first %} Chair{{ forloop.revcounter|pluralize:": ,s:" }} {% else %} {% endif %}{{ chair.person.plain_name }} <{{ chair.email.address }}>
+{% endfor %} WG Mail: {{ group.list_email }}
+ To Join: {{ group.list_subscribe }}
+ Archive: {{ group.list_archive }}
diff --git a/ietf/templates/wginfo/group_entry_with_charter.txt b/ietf/templates/wginfo/group_entry_with_charter.txt
new file mode 100644
index 000000000..abde2531a
--- /dev/null
+++ b/ietf/templates/wginfo/group_entry_with_charter.txt
@@ -0,0 +1,48 @@
+{% autoescape off %}{% load ietf_filters %}{{ group.name }} ({{group.acronym}})
+{{ group.name|dashify }}{{ group.acronym|dashify }}---
+
+ Charter
+ Last Modified: {{ group.time.date|date }}
+
+ Current Status: {{ group.state.name }}
+
+ Chair{{ group.chairs|pluralize }}:
+{% for chair in group.chairs %} {{ chair.person.name }} <{{chair.email.address}}>
+{% endfor %}
+ {{ group.area.name}} Directors:
+{% for ad in group.area.ads %} {{ ad.person.plain_name }} <{{ ad.email.address }}>
+{% endfor %}
+{% if group.areadirector %} {{ group.area.name }} Advisor:
+ {{ group.areadirector.person.plain_name }} <{{ group.areadirector.address }}>
+{% endif %}{% if group.techadvisors %}
+ Tech Advisor{{ group.techadvisors|pluralize }}:
+{% for techadvisor in group.techadvisors %} {{ techadvisor.person.plain_name }} <{{ techadvisor.email.address }}>
+{% endfor %}{% endif %}{% if group.editors %}
+ Editor{{ group.editors|pluralize }}:
+{% for editor in group.editors %} {{ editor.person.plain_name }} <{{ editor.email.address }}>
+{% endfor %}{% endif %}{% if group.secretaries %}
+ Secretar{{ group.secretaries|pluralize:"y,ies" }}:
+{% for secretary in group.secretaries %} {{ secretary.person.plain_name }} <{{ secretary.email.address }}>
+{% endfor %}{% endif %}
+ Mailing Lists:
+ General Discussion: {{ group.list_email }}
+ To Subscribe: {{ group.list_subscribe }}
+ Archive: {{ group.list_archive }}
+
+Description of Working Group:
+
+ {{ group.charter_text|indent }}
+
+Goals and Milestones:
+{% for milestone in group.milestones %} {% if milestone.resolved %}{{ milestone.resolved }} {% else %}{{ milestone.due|date:"M Y" }}{% endif %} - {{ milestone.desc }}
+{% endfor %}
+Internet-Drafts:
+{% for alias in group.drafts %} - {{ alias.document.title }} [{{ alias.name }}-{{ alias.document.rev }}] ({{ alias.document.pages }} pages)
+{% endfor %}
+{% if group.rfcs %}Requests for Comments:
+{% for alias in group.rfcs %} {{ alias.name.upper }}: {{ alias.document.title}} ({{ alias.document.pages }} pages){% for r in alias.rel %}
+ * {{ r.action }} {{ r.target.name|upper }}{% endfor %}{% for r in alias.invrel %}
+ * {% if r.relationsship == "obs" %}{{ r.inverse_action|upper }}{% else %}{{ r.action }}{% endif %} {{ r.source.canonical_name|upper }}{% endfor %}
+{% endfor %}
+{% else %}No Requests for Comments{% endif %}
+{% endautoescape %}
diff --git a/ietf/templates/wginfo/history.html b/ietf/templates/wginfo/history.html
index ed86d4324..bc1926609 100644
--- a/ietf/templates/wginfo/history.html
+++ b/ietf/templates/wginfo/history.html
@@ -1,15 +1,16 @@
-{% extends "wginfo/wg_base.html" %}
+{% extends "wginfo/group_base.html" %}
{% load ietf_filters %}
-{% block wg_titledetail %}History{% endblock %}
+{% block group_subtitle %}History{% endblock %}
-{% block wg_content %}
+{% block group_content %}
{% load ietf_filters %}
-
WG History
+
Group History
Date
By
Text
+
{% for e in events %}
{{ e.time|date:"Y-m-d"}}
diff --git a/ietf/templates/wginfo/wg-charter.txt b/ietf/templates/wginfo/wg-charter.txt
deleted file mode 100644
index c0d8af0d4..000000000
--- a/ietf/templates/wginfo/wg-charter.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-{% if USE_DB_REDESIGN_PROXY_CLASSES %}{% include "wginfo/wg-charterREDESIGN.txt" %}{% else %}{% load ietf_filters %}{{wg.group_acronym.name|safe}} ({{wg}})
-{{ wg.group_acronym.name|dashify }}{{ wg.group_acronym.acronym|dashify }}---
-
- Charter
- Last Modified: {{ wg.last_modified_date }}
-
- Current Status: {{ wg.status }}
-
- Chair{{ wg.chairs.count|pluralize:",s" }}:
-{% for chair in wg.chairs %} {{ chair.person|safe }} <{{chair.person.email.1}}>
-{% endfor %}
- {{wg.area.area.area_acronym.name}} Directors:
-{% for ad in wg.area_directors %} {{ ad.person|safe }} <{{ad.person.email.1}}>
-{% endfor %}
- {{wg.area.area.area_acronym.name}} Advisor:
- {{ wg.area_director.person|safe }} <{{wg.area_director.person.email.1}}>
-{% if wg.wgtechadvisor_set.count %}
- Tech Advisor{{ wg.wgtechadvisor_set.count|pluralize:",s" }}:
-{% for techadvisor in wg.wgtechadvisor_set.all %} {{ techadvisor.person|safe }} <{{techadvisor.person.email.1}}>
-{% endfor %}{% endif %}{% if wg.wgeditor_set.count %}
- Editor{{ wg.wgeditor_set.count|pluralize:",s" }}:
-{% for editor in wg.wgeditor_set.all %} {{ editor.person|safe }} <{{editor.person.email.1}}>
-{% endfor %}{% endif %}{% if wg.secretaries %}
- Secretar{{ wg.secretaries.count|pluralize:"y,ies" }}:
-{% for secretary in wg.secretaries %} {{ secretary.person|safe }} <{{secretary.person.email.1}}>
-{% endfor %}{% endif %}
- Mailing Lists:
- General Discussion: {{ wg.email_address }}
- To Subscribe: {{ wg.email_subscribe }}
- Archive: {{ wg.email_archive }}
-
-Description of Working Group:
-
- {{ wg.charter_text|indent|safe }}
-
-Goals and Milestones:
-{% for milestone in wg.milestones %} {% ifequal milestone.done 'Done' %}Done {% else %}{%ifequal milestone.expected_due_date.month 1 %}Jan{% endifequal %}{%ifequal milestone.expected_due_date.month 2 %}Feb{% endifequal %}{%ifequal milestone.expected_due_date.month 3 %}Mar{% endifequal %}{%ifequal milestone.expected_due_date.month 4 %}Apr{% endifequal %}{%ifequal milestone.expected_due_date.month 5 %}May{% endifequal %}{%ifequal milestone.expected_due_date.month 6 %}Jun{% endifequal %}{%ifequal milestone.expected_due_date.month 7 %}Jul{% endifequal %}{%ifequal milestone.expected_due_date.month 8 %}Aug{% endifequal %}{%ifequal milestone.expected_due_date.month 9 %}Sep{% endifequal %}{%ifequal milestone.expected_due_date.month 10 %}Oct{% endifequal %}{%ifequal milestone.expected_due_date.month 11 %}Nov{% endifequal %}{%ifequal milestone.expected_due_date.month 12 %}Dec{% endifequal %} {{ milestone.expected_due_date.year }}{% endifequal %} - {{ milestone.description|safe }}
-{% endfor %}
-Internet-Drafts:
-{% for draft in wg.drafts %} - {{draft.title|safe}} [{{draft.filename}}-{{draft.revision}}] ({{ draft.txt_page_count }} pages)
-{% endfor %}
-{% if wg.rfcs %}Requests for Comments:
-{% for rfc in wg.rfcs %} {{rfc}}: {{rfc.title|safe}} ({{ rfc.txt_page_count }} pages){% for obs in rfc.obsoletes%}
- * {{obs.action}} RFC{{obs.rfc_acted_on_id}}{% endfor %}{% for obs in rfc.obsoleted_by%}
- * {%ifequal obs.action 'Obsoletes'%}OBSOLETED BY{%else%}Updated by{%endifequal%} RFC{{obs.rfc_id}}{% endfor %}
-{%endfor%}
-{%else%}No Requests for Comments{% endif %}{% endif %}
diff --git a/ietf/templates/wginfo/wg-charterREDESIGN.txt b/ietf/templates/wginfo/wg-charterREDESIGN.txt
deleted file mode 100644
index 30e84e6ed..000000000
--- a/ietf/templates/wginfo/wg-charterREDESIGN.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-{% load ietf_filters %}{{wg.name|safe}} ({{wg.acronym}})
-{{ wg.name|dashify }}{{ wg.acronym|dashify }}---
-
- Charter
- Last Modified: {{ wg.time.date }}
-
- Current Status: {{ wg.state.name }}
-
- Chair{{ wg.chairs|pluralize }}:
-{% for chair in wg.chairs %} {{ chair.person.name|safe }} <{{chair.address}}>
-{% endfor %}
- {{wg.area.area.area_acronym.name}} Directors:
-{% for ad in wg.area_directors %} {{ ad.person|safe }} <{{ad.address}}>
-{% endfor %}
- {{wg.area.area.area_acronym.name}} Advisor:
- {{ wg.areadirector.person.name|safe }} <{{wg.areadirector.address}}>
-{% if wg.techadvisors %}
- Tech Advisor{{ wg.techadvisors|pluralize }}:
-{% for techadvisor in wg.techadvisors %} {{ techadvisor.person.plain_name|safe }} <{{techadvisor.address}}>
-{% endfor %}{% endif %}{% if wg.editors %}
- Editor{{ wg.editors|pluralize }}:
-{% for editor in wg.editors %} {{ editor.person.plain_name|safe }} <{{editor.address}}>
-{% endfor %}{% endif %}{% if wg.secretaries %}
- Secretar{{ wg.secretaries|pluralize:"y,ies" }}:
-{% for secretary in wg.secretaries %} {{ secretary.person.plain_name|safe }} <{{secretary.address}}>
-{% endfor %}{% endif %}
- Mailing Lists:
- General Discussion: {{ wg.email_address }}
- To Subscribe: {{ wg.email_subscribe }}
- Archive: {{ wg.email_archive }}
-
-Description of Working Group:
-
- {{ wg.charter_text|indent|safe }}
-
-Goals and Milestones:
-{% for milestone in wg.milestones %} {% if milestone.resolved %}{{ milestone.resolved }} {% else %}{{ milestone.due|date:"M Y" }}{% endif %} - {{ milestone.desc|safe }}
-{% endfor %}
-Internet-Drafts:
-{% for alias in wg.drafts %} - {{alias.document.title|safe}} [{{alias.name}}-{{alias.document.rev}}] ({{ alias.document.pages }} pages)
-{% endfor %}
-{% if wg.rfcs %}Requests for Comments:
-{% for alias in wg.rfcs %} {{ alias.name.upper }}: {{ alias.document.title|safe}} ({{ alias.document.pages }} pages){% for r in alias.rel %}
- * {{ r.action }} {{ r.target.name|upper }}{% endfor %}{% for r in alias.invrel %}
- * {% ifequal r.relationsship "obs" %}{{ r.inverse_action|upper }}{% else %}{{ r.action }}{% endifequal %} {{ r.source.canonical_name|upper }}{% endfor %}
-{%endfor%}
-{%else%}No Requests for Comments{% endif %}
diff --git a/ietf/templates/wginfo/wg-dir.html b/ietf/templates/wginfo/wg-dir.html
deleted file mode 100644
index b9cfb208b..000000000
--- a/ietf/templates/wginfo/wg-dir.html
+++ /dev/null
@@ -1,99 +0,0 @@
-{% extends "base.html" %}
-{# Copyright The IETF Trust 2009, All Rights Reserved #}
-{% comment %}
-Portion Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}
-
-{% block title %}Active IETF Working Groups{% endblock %}
-
-{% block morecss %}
-.ietf-wg-table { width: 100%; max-width:50em; }
-.ietf-wg-table tr { vertical-align:top; }
-{% endblock morecss %}
-
-{% block content %}
-
{% for ad in area.areadirector_set.all|dictsort:"person.last_name" %}{% ifequal ad wg.area_director %}{% endifequal %}{% endfor %}
-
{{ wg.group_acronym.name }}
-
{% for chair in wg.chairs %}{{chair.person}}{% if not forloop.last %}, {% endif %}{% endfor %}
-
- {% if forloop.last %}
-
-
- {% endif %}
- {% empty %}
-
No Active Working Groups
- {% endfor %}{# wg #}
-
- {% endfor %}{# area #}
-{% endblock %}
diff --git a/ietf/templates/wginfo/wg_charter.html b/ietf/templates/wginfo/wg_charter.html
deleted file mode 100644
index e4797950b..000000000
--- a/ietf/templates/wginfo/wg_charter.html
+++ /dev/null
@@ -1,182 +0,0 @@
-{% extends "wginfo/wg_base.html" %}
-{% comment %}
-Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}
-{% load ietf_filters %}
-{% block wg_titledetail %}Charter{% endblock %}
-
-{% block morecss %}
-{{ block.super }}
-h2 a.button { margin-left: 0.5em; font-size: 13px; }
-{% endblock %}
-
-{% block wg_content %}
-
-{% if concluded %}
-Note: The data for concluded WGs
-is occasionally incorrect.
-{% endif %}
-
-
Group
-
-
-
Name:
-
{{ wg.name }}
-
-
-
Acronym:
{{ wg.acronym }}
-
- {% if wg.parent %}
-
Area:
{{ wg.parent.name }} ({{ wg.parent.acronym }})
- {% endif %}
-
-
-
State:
-
{{ wg.state.name }}
- {% if requested_close %}
- (but in the process of being closed)
- {% endif %}
-
-
-{% if user|has_role:"Area Director,Secretariat" %}
-
- {% for name, url in actions %}
- {{ name }}
- {% if not forloop.last %}|{% endif %}
- {% endfor %}
-
-{% endif %}
-
-
-{% if wg.additional_urls %}
-
In addition to the charter maintained by the IETF Secretariat, there is additional information about this working group on the Web at:
-{% for url in wg.additional_urls %}
-{{ url.name }}{% if not forloop.last %}, {% endif %}
-{% endfor %}
-
-{% endif %}
-
-
Charter for {% if wg.state_id == "proposed" %}Proposed{% endif %} Working Group
-
{{ wg.charter_text|escape|format_charter|safe }}
-
-
{% if wg.state_id == "proposed" %}Proposed{% endif %} Milestones
-{% if wg.state_id != "proposed" %}
-{% if user|has_role:"Area Director,Secretariat" or is_chair %}
-Add or edit milestones
-{% endif %}
-{% endif %}
-
-
-{% with wg.milestones as milestones %}{% include "wginfo/milestones.html" %}{% endwith %}
-
-{% if milestones_in_review %}
-
+ {{ milestones_in_review|length }} new milestone{{ milestones_in_review|pluralize }}
-currently in Area Director review.
-{% endif %}
-{% endblock wg_content %}
diff --git a/ietf/templates/wginfo/wg_documents.html b/ietf/templates/wginfo/wg_documents.html
deleted file mode 100644
index f0fb4529b..000000000
--- a/ietf/templates/wginfo/wg_documents.html
+++ /dev/null
@@ -1,50 +0,0 @@
-{% extends "wginfo/wg_base.html" %}
-{% comment %}
-Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-All rights reserved. Contact: Pasi Eronen
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the Nokia Corporation and/or its
- subsidiary(-ies) nor the names of its contributors may be used
- to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-{% endcomment %}
-{% block wg_titledetail %}Documents{% endblock %}
-
-{% block wg_content %}
-
-
-{% include "doc/search/search_results.html" %}
-
-{% with docs_related as docs %}{% include "doc/search/search_results.html" %}{% endwith %}
-
-
-{% endblock wg_content %}
-
-{% block js %}
-
-
-{% endblock %}
diff --git a/ietf/templates/wginfo/wg_documents.txt b/ietf/templates/wginfo/wg_documents.txt
deleted file mode 100644
index 69302b533..000000000
--- a/ietf/templates/wginfo/wg_documents.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-{% load ietf_filters %}{% regroup docs by get_state as grouped_docs %}{% for doc_group in grouped_docs %}{% for doc in doc_group.list %}{% include "wginfo/wg_documents_entry.txt" %}{% endfor %}{% endfor %}{% regroup docs_related by get_state as grouped_docs_related %}{% for doc_group in grouped_docs_related %}{% for doc in doc_group.list %}Related {% include "wginfo/wg_documents_entry.txt" %}{% endfor %}{% endfor %}
-
diff --git a/ietf/templates/wginfo/wg_documents_entry.txt b/ietf/templates/wginfo/wg_documents_entry.txt
deleted file mode 100644
index ad034af62..000000000
--- a/ietf/templates/wginfo/wg_documents_entry.txt
+++ /dev/null
@@ -1 +0,0 @@
-{% load ietf_filters %}{{doc_group.grouper}} {% if doc.get_state_slug == "rfc" %}{{doc.rfc_number}} {{doc.title|clean_whitespace}}{% else %}{{doc.name}}-{{doc.rev}} {{doc.title|clean_whitespace}}{% endif %}
diff --git a/ietf/templates/wginfo/wg_summary.txt b/ietf/templates/wginfo/wg_summary.txt
deleted file mode 100644
index 603d9e2f8..000000000
--- a/ietf/templates/wginfo/wg_summary.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-{% for chair in wg.wgchair_set.all %}{% if forloop.first %} Chair{{ forloop.revcounter|pluralize:": ,s:" }} {% else %} {% endif %}{{ chair.person|safe }} <{{ chair.person.email.1 }}>
-{% endfor %} WG Mail: {{ wg.email_address }}
- To Join: {{ wg.email_subscribe }}{%if wg.email_keyword %}
- In Body: {{ wg.email_keyword|safe }}{% endif %}
- Archive: {{ wg.email_archive }}
diff --git a/ietf/urls.py b/ietf/urls.py
index 861f5fc55..fc14c83d7 100644
--- a/ietf/urls.py
+++ b/ietf/urls.py
@@ -71,7 +71,6 @@ urlpatterns = patterns('',
(r'^secr/', include('ietf.secr.urls')),
(r'^sitemap-(?P.+).xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.index', { 'sitemaps': sitemaps}),
- (r'^streams/', include('ietf.ietfworkflows.urls')),
(r'^submit/', include('ietf.submit.urls')),
(r'^sync/', include('ietf.sync.urls')),
(r'^wg/', include('ietf.wginfo.urls')),
diff --git a/ietf/utils/accesstoken.py b/ietf/utils/accesstoken.py
new file mode 100644
index 000000000..da07c9a3a
--- /dev/null
+++ b/ietf/utils/accesstoken.py
@@ -0,0 +1,15 @@
+import time, random, hashlib
+
+from django.conf import settings
+
+def generate_random_key(max_length=32):
+ """Generate a random access token."""
+ return hashlib.sha256(settings.SECRET_KEY + ("%.16f" % time.time()) + ("%.16f" % random.random())).hexdigest()[:max_length]
+
+def generate_access_token(key, max_length=32):
+ """Make an access token out of key."""
+ assert key, "key must not be empty"
+ # we hash it with the private key to make sure only we can
+ # generate and use the final token - so storing the key in the
+ # database is safe
+ return hashlib.sha256(settings.SECRET_KEY + key).hexdigest()[:max_length]
diff --git a/ietf/utils/mail.py b/ietf/utils/mail.py
index a99962b36..669fda92b 100644
--- a/ietf/utils/mail.py
+++ b/ietf/utils/mail.py
@@ -176,8 +176,13 @@ def send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=F
if extra:
for k, v in extra.items():
if v:
- msg[k] = v
- if test_mode or settings.SERVER_MODE == 'production':
+ msg[k] = v
+ # start debug server with python -m smtpd -n -c DebuggingServer localhost:2025
+ # then put USING_DEBUG_EMAIL_SERVER=True and EMAIL_HOST='localhost'
+ # and EMAIL_PORT=2025 in settings_local.py
+ debugging = getattr(settings, "USING_DEBUG_EMAIL_SERVER", False) and settings.EMAIL_HOST == 'localhost' and settings.EMAIL_PORT == 2025
+
+ if test_mode or debugging or settings.SERVER_MODE == 'production':
send_smtp(msg, bcc)
elif settings.SERVER_MODE == 'test':
if toUser:
@@ -188,7 +193,7 @@ def send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=F
copy_to = settings.EMAIL_COPY_TO
except AttributeError:
copy_to = "ietf.tracker.archive+%s@gmail.com" % settings.SERVER_MODE
- if copy_to and not test_mode: # if we're running automated tests, this copy is just annoying
+ if copy_to and not test_mode and not debugging: # if we're running automated tests, this copy is just annoying
if bcc:
msg['X-Tracker-Bcc']=bcc
copy_email(msg, copy_to,originalBcc=bcc)
diff --git a/ietf/utils/test_data.py b/ietf/utils/test_data.py
index 8d697b278..67df82345 100644
--- a/ietf/utils/test_data.py
+++ b/ietf/utils/test_data.py
@@ -1,7 +1,7 @@
from django.conf import settings
from django.contrib.auth.models import User
-from ietf.iesg.models import TelechatDate, WGAction
+from ietf.iesg.models import TelechatDate
from ietf.ipr.models import IprDetail, IprDocAlias
from ietf.meeting.models import Meeting
from ietf.doc.models import *
@@ -112,15 +112,6 @@ def make_test_data():
)
group.charter = charter
group.save()
- WGAction.objects.create(
- pk=group.pk,
- note="",
- status_date=datetime.date.today(),
- agenda=1,
- token_name="Aread",
- category=13,
- telechat_date=date2
- )
# persons
diff --git a/ietf/wgchairs/.gitignore b/ietf/wgchairs/.gitignore
deleted file mode 100644
index a74b07aee..000000000
--- a/ietf/wgchairs/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/*.pyc
diff --git a/ietf/wgchairs/__init__.py b/ietf/wgchairs/__init__.py
deleted file mode 100644
index e8d53c9a3..000000000
--- a/ietf/wgchairs/__init__.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# coding: latin-1
-
-from types import ModuleType
-import urls, models, views, forms, accounts
-
-# These people will be sent a stack trace if there's an uncaught exception in
-# code any of the modules imported above:
-DEBUG_EMAILS = [
- ('Emilio A. Sánchez', 'esanchez@yaco.es'),
-]
-
-for k in locals().keys():
- m = locals()[k]
- if isinstance(m, ModuleType):
- if hasattr(m, "DEBUG_EMAILS"):
- DEBUG_EMAILS += list(getattr(m, "DEBUG_EMAILS"))
- setattr(m, "DEBUG_EMAILS", DEBUG_EMAILS)
-
diff --git a/ietf/wgchairs/accounts.py b/ietf/wgchairs/accounts.py
deleted file mode 100644
index c6b5b7645..000000000
--- a/ietf/wgchairs/accounts.py
+++ /dev/null
@@ -1,108 +0,0 @@
-from django.conf import settings
-from ietf.group.models import Role
-
-def is_secretariat(user):
- if not user or not user.is_authenticated():
- return False
- return bool(user.groups.filter(name='Secretariat'))
-
-
-def is_area_director_for_group(person, group):
- return bool(group.area.area.areadirector_set.filter(person=person).count())
-
-def is_area_director_for_groupREDESIGN(person, group):
- return bool(Role.objects.filter(group=group.parent, person=person, name__in=("ad", "pre-ad")))
-
-
-def is_group_chair(person, group):
- if group.chairs().filter(person=person):
- return True
- return False
-
-def is_group_chairREDESIGN(person, group):
- return bool(Role.objects.filter(group=group, person=person, name="chair"))
-
-
-def is_group_delegate(person, group):
- return bool(group.wgdelegate_set.filter(person=person).count())
-
-def is_group_delegateREDESIGN(person, group):
- return bool(Role.objects.filter(group=group, person=person, name="delegate"))
-
-
-def get_person_for_user(user):
- try:
- return user.get_profile().person()
- except:
- return None
-
-
-if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- from ietf.liaisons.accounts import is_secretariat, get_person_for_user
- is_area_director_for_group = is_area_director_for_groupREDESIGN
- is_group_chair = is_group_chairREDESIGN
- is_group_delegate = is_group_delegateREDESIGN
-
-
-def can_do_wg_workflow_in_group(user, group):
- person = get_person_for_user(user)
- if not person:
- return False
- return (is_secretariat(user) or is_group_chair(person, group))
-
-
-def can_do_wg_workflow_in_document(user, document):
- person = get_person_for_user(user)
- if not person or not document.group:
- return False
- return (is_secretariat(user) or can_do_wg_workflow_in_group(document.group.ietfwg))
-
-
-def can_manage_workflow_in_group(user, group):
- person = get_person_for_user(user)
- if not person:
- return False
- return (is_secretariat(user) or is_group_chair(person, group))
-
-
-def can_manage_delegates_in_group(user, group):
- person = get_person_for_user(user)
- if not person:
- return False
- return (is_secretariat(user) or is_group_chair(person, group))
-
-
-def can_manage_shepherds_in_group(user, group):
- person = get_person_for_user(user)
- if not person:
- return False
- return (is_secretariat(user) or is_group_chair(person, group))
-
-
-def can_manage_shepherd_of_a_document(user, document):
- person = get_person_for_user(user)
- if not person or not document.group:
- return False
- return can_manage_shepherds_in_group(user, document.group.ietfwg)
-
-
-def can_manage_writeup_of_a_document_no_state(user, document):
- person = get_person_for_user(user)
- if not person or not document.group:
- return False
- group = document.group.ietfwg
- return (is_secretariat(user) or
- is_group_chair(person, group) or
- is_area_director_for_group(person, group) or
- is_group_delegate(person, group))
-
-
-def can_manage_writeup_of_a_document(user, document):
- person = get_person_for_user(user)
- if not person or not document.group:
- return False
- return (can_manage_writeup_of_a_document_no_state(user, document) or
- person == document.shepherd)
-
-
-
diff --git a/ietf/wgchairs/forms.py b/ietf/wgchairs/forms.py
deleted file mode 100644
index 001acec7c..000000000
--- a/ietf/wgchairs/forms.py
+++ /dev/null
@@ -1,478 +0,0 @@
-import datetime
-
-from django import forms
-from django.conf import settings
-from django.db.models import Q
-from django.forms.models import BaseModelFormSet
-from django.template.loader import render_to_string
-from django.utils.safestring import mark_safe
-
-from ietf.wgchairs.models import WGDelegate, ProtoWriteUp
-from ietf.wgchairs.accounts import get_person_for_user
-from ietf.ietfworkflows.constants import REQUIRED_STATES
-from ietf.ietfworkflows.utils import (get_default_workflow_for_wg, get_workflow_for_wg,
- update_tags, FOLLOWUP_TAG, get_state_by_name)
-from ietf.ietfworkflows.models import AnnotationTag, State
-from ietf.idtracker.models import PersonOrOrgInfo
-from ietf.utils.mail import send_mail_text
-
-from workflows.models import Transition
-
-from ietf.doc.models import WriteupDocEvent
-from ietf.person.models import Person, Email
-from ietf.group.models import Group, Role, RoleName
-from ietf.group.utils import save_group_in_history
-from ietf.name.models import DocTagName
-
-
-class RelatedWGForm(forms.Form):
-
- can_cancel = False
-
- def __init__(self, *args, **kwargs):
- self.wg = kwargs.pop('wg', None)
- self.user = kwargs.pop('user', None)
- self.message = {}
- super(RelatedWGForm, self).__init__(*args, **kwargs)
-
- def get_message(self):
- return self.message
-
- def set_message(self, msg_type, msg_value):
- self.message = {'type': msg_type,
- 'value': msg_value,
- }
-
-
-class TagForm(RelatedWGForm):
-
- tags = forms.ModelMultipleChoiceField(AnnotationTag.objects.filter(workflow__name='Default WG Workflow'),
- widget=forms.CheckboxSelectMultiple, required=False)
-
- def save(self):
- workflow = get_workflow_for_wg(self.wg)
- workflow.selected_tags.clear()
- for tag in self.cleaned_data['tags']:
- workflow.selected_tags.add(tag)
- return workflow
-
-
-class StateForm(RelatedWGForm):
-
- states = forms.ModelMultipleChoiceField(State.objects.filter(workflow__name='Default WG Workflow'),
- widget=forms.CheckboxSelectMultiple, required=False)
-
- def update_transitions(self, workflow):
- for transition in workflow.transitions.all():
- if not workflow.selected_states.filter(pk=transition.destination.pk).count():
- transition.delete()
- continue
- for state in transition.states.all():
- if not workflow.selected_states.filter(pk=state.pk).count():
- transition.states.remove(state)
- if not transition.states.count():
- transition.delete()
- continue
-
- def save(self):
- workflow = get_workflow_for_wg(self.wg)
- workflow.selected_states.clear()
- for state in self.cleaned_data['states']:
- workflow.selected_states.add(state)
- for name in REQUIRED_STATES:
- rstate = get_state_by_name(name)
- if rstate:
- workflow.selected_states.add(rstate)
- self.update_transitions(workflow)
- return workflow
-
-
-class DeleteTransitionForm(RelatedWGForm):
-
- transitions = forms.ModelMultipleChoiceField(Transition.objects.all(),
- widget=forms.CheckboxSelectMultiple)
-
- def __init__(self, *args, **kwargs):
- super(DeleteTransitionForm, self).__init__(*args, **kwargs)
- workflow = get_workflow_for_wg(self.wg)
- self.fields['transitions'].queryset = self.fields['transitions'].queryset.filter(workflow=workflow)
-
- def save(self):
- for transition in self.cleaned_data['transitions']:
- transition.delete()
-
-
-class TransitionForm(forms.ModelForm):
-
- states = forms.ModelMultipleChoiceField(State.objects.filter(workflow__name='Default WG Workflow'))
-
- class Meta:
- model = Transition
- fields = ('name', 'states', 'destination', )
-
- def __init__(self, *args, **kwargs):
- self.wg = kwargs.pop('wg', None)
- self.user = kwargs.pop('user', None)
- super(TransitionForm, self).__init__(*args, **kwargs)
- workflow = get_workflow_for_wg(self.wg)
- self.fields['states'].queryset = workflow.selected_states.all()
- self.fields['destination'].queryset = workflow.selected_states.all()
- self.fields['destination'].required = True
- if self.instance.pk:
- self.fields['states'].initial = [i.pk for i in self.instance.states.all()]
- self.instance.workflow = workflow
-
- def as_row(self):
- return self._html_output(u'
%(errors)s%(field)s%(help_text)s
', u'
%s
', '', u' %s', False)
-
- def save(self, *args, **kwargs):
- instance = super(TransitionForm, self).save(*args, **kwargs)
- for state in self.cleaned_data['states']:
- state.transitions.add(instance)
-
-
-class TransitionFormSet(BaseModelFormSet):
-
- form = TransitionForm
- can_delete = True
- extra = 2
- max_num = 0
- can_order = False
- model = Transition
-
- def __init__(self, *args, **kwargs):
- self.wg = kwargs.pop('wg', None)
- self.user = kwargs.pop('user', None)
- super(TransitionFormSet, self).__init__(*args, **kwargs)
-
- def _construct_form(self, i, **kwargs):
- kwargs = kwargs or {}
- kwargs.update({'wg': self.wg, 'user': self.user})
- return super(TransitionFormSet, self)._construct_form(i, **kwargs)
-
- def as_table(self):
- html = u''
- csscl = 'oddrow'
- for form in self.forms:
- html += u'
' % csscl
- html += form.as_row()
- html += u'
'
- if csscl == 'oddrow':
- csscl = 'evenrow'
- else:
- csscl = 'oddrow'
- return mark_safe(u'\n'.join([unicode(self.management_form), html]))
-
-
-def workflow_form_factory(request, wg, user):
-
- if request.POST.get('update_transitions', None):
- return TransitionFormSet(wg=wg, user=user, data=request.POST)
- elif request.POST.get('update_states', None):
- return StateForm(wg=wg, user=user, data=request.POST)
- return TagForm(wg=wg, user=user, data=request.POST)
-
-
-class RemoveDelegateForm(RelatedWGForm):
-
- delete = forms.MultipleChoiceField()
-
- def __init__(self, *args, **kwargs):
- super(RemoveDelegateForm, self).__init__(*args, **kwargs)
- self.fields['delete'].choices = [(i.pk, i.pk) for i in self.wg.wgdelegate_set.all()]
-
- def save(self):
- delegates = self.cleaned_data.get('delete')
- save_group_in_history(Group.objects.get(pk=self.wg.pk))
- WGDelegate.objects.filter(pk__in=delegates).delete()
- self.set_message('success', 'Delegates removed')
-
-def assign_shepherd(user, internetdraft, shepherd):
- if internetdraft.shepherd == shepherd:
- return
-
- from ietf.doc.models import save_document_in_history, DocEvent, Document
-
- # saving the proxy object is a bit of a mess, so convert it to a
- # proper document
- doc = Document.objects.get(name=internetdraft.name)
-
- save_document_in_history(doc)
-
- doc.time = datetime.datetime.now()
- doc.shepherd = shepherd
- doc.save()
-
- e = DocEvent(type="changed_document")
- e.time = doc.time
- e.doc = doc
- e.by = user.get_profile()
- if not shepherd:
- e.desc = u"Unassigned shepherd"
- else:
- e.desc = u"Changed shepherd to %s" % shepherd.plain_name()
- e.save()
-
- # update proxy too
- internetdraft.shepherd = shepherd
-
-class AddDelegateForm(RelatedWGForm):
-
- email = forms.EmailField()
- form_type = forms.CharField(widget=forms.HiddenInput, initial='single')
-
- def __init__(self, *args, **kwargs):
- self.shepherd = kwargs.pop('shepherd', False)
- super(AddDelegateForm, self).__init__(*args, **kwargs)
- self.next_form = self
-
- def get_next_form(self):
- return self.next_form
-
- def get_person(self, email):
- persons = PersonOrOrgInfo.objects.filter(emailaddress__address=email).filter(
- Q(iesglogin__isnull=False)|
- Q(legacywgpassword__isnull=False)|
- Q(legacyliaisonuser__isnull=False)).distinct()
- if not persons:
- raise PersonOrOrgInfo.DoesNotExist
- if len(persons) > 1:
- raise PersonOrOrgInfo.MultipleObjectsReturned
- return persons[0]
-
- def save(self):
- email = self.cleaned_data.get('email')
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- try:
- person = Person.objects.filter(email__address=email).exclude(user=None).distinct().get()
- except Person.DoesNotExist:
- self.next_form = NotExistDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd)
- self.next_form.set_message('doesnotexist', 'There is no user with this email allowed to login to the system')
- return
- except Person.MultipleObjectsReturned:
- self.next_form = MultipleDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd)
- self.next_form.set_message('multiple', 'There are multiple users with this email in the system')
- return
- else:
- try:
- person = self.get_person(email)
- except PersonOrOrgInfo.DoesNotExist:
- self.next_form = NotExistDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd)
- self.next_form.set_message('doesnotexist', 'There is no user with this email allowed to login to the system')
- return
- except PersonOrOrgInfo.MultipleObjectsReturned:
- self.next_form = MultipleDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd)
- self.next_form.set_message('multiple', 'There are multiple users with this email in the system')
- return
- if self.shepherd:
- self.assign_shepherd(person)
- else:
- self.create_delegate(person)
-
- def assign_shepherd(self, person):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- assign_shepherd(self.user, self.shepherd, person)
- else:
- self.shepherd.shepherd = person
- self.shepherd.save()
- self.next_form = AddDelegateForm(wg=self.wg, user=self.user, shepherd=self.shepherd)
- self.next_form.set_message('success', 'Shepherd assigned successfully')
-
- def create_delegate(self, person):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- created = False
- e = Email.objects.get(address=self.cleaned_data.get('email'))
- if not Role.objects.filter(name="delegate", group=self.wg, person=person, email=e):
- created = True
- save_group_in_history(Group.objects.get(pk=self.wg.pk))
- delegate, _ = Role.objects.get_or_create(
- name=RoleName.objects.get(slug="delegate"), group=self.wg, person=e.person, email=e)
- else:
- (delegate, created) = WGDelegate.objects.get_or_create(wg=self.wg,
- person=person)
- if not created:
- self.set_message('error', 'The email belongs to a person who is already a delegate')
- else:
- self.next_form = AddDelegateForm(wg=self.wg, user=self.user)
- self.next_form.set_message('success', 'A new delegate has been added')
-
-
-class MultipleDelegateForm(AddDelegateForm):
-
- email = forms.EmailField(widget=forms.HiddenInput)
- form_type = forms.CharField(widget=forms.HiddenInput, initial='multiple')
- persons = forms.ChoiceField(widget=forms.RadioSelect, help_text='Please select one person from the list')
- submit_msg = 'Designate as delegate'
-
- def __init__(self, *args, **kwargs):
- self.email = kwargs.pop('email', None)
- super(MultipleDelegateForm, self).__init__(*args, **kwargs)
- if not self.email:
- self.email = self.data.get('email', None)
- self.fields['email'].initial = self.email
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- self.fields['persons'].choices = [(i.pk, unicode(i)) for i in Person.objects.filter(email__address=self.email).exclude(user=None).distinct().order_by('name')]
- else:
- self.fields['persons'].choices = [(i.pk, unicode(i)) for i in PersonOrOrgInfo.objects.filter(emailaddress__address=self.email).filter(
- Q(iesglogin__isnull=False)|
- Q(legacywgpassword__isnull=False)|
- Q(legacyliaisonuser__isnull=False)).distinct().order_by('first_name')]
-
- def save(self):
- person_id = self.cleaned_data.get('persons')
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- person = Person.objects.get(pk=person_id)
- else:
- person = PersonOrOrgInfo.objects.get(pk=person_id)
- if self.shepherd:
- self.assign_shepherd(person)
- else:
- self.create_delegate(person)
-
-
-class NotExistDelegateForm(MultipleDelegateForm):
-
- email = forms.EmailField(widget=forms.HiddenInput)
- form_type = forms.CharField(widget=forms.HiddenInput, initial='notexist')
- can_cancel = True
- submit_msg = 'Send email to these addresses'
-
- def __init__(self, *args, **kwargs):
- super(NotExistDelegateForm, self).__init__(*args, **kwargs)
- self.email_list = []
- del(self.fields['persons'])
-
- def get_email_list(self):
- if self.email_list:
- return self.email_list
- email_list = [self.email]
- email_list.append('IETF Secretariat ')
- email_list += ['%s <%s>' % i.person.email() for i in self.wg.wgchair_set.all() if i.person.email()]
- self.email_list = email_list
- return email_list
-
- def as_p(self):
- email_list = self.get_email_list()
- info = render_to_string('wgchairs/notexistdelegate.html', {'email_list': email_list, 'shepherd': self.shepherd})
- return info + super(NotExistDelegateForm, self).as_p()
-
- def send_email(self, to_email, template):
- if self.shepherd:
- subject = 'WG shepherd needs system credentials'
- else:
- subject = 'WG Delegate needs system credentials'
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- persons = Person.objects.filter(email__address=self.email).distinct()
- else:
- persons = PersonOrOrgInfo.objects.filter(emailaddress__address=self.email).distinct()
- body = render_to_string(template,
- {'chair': get_person_for_user(self.user),
- 'delegate_email': self.email,
- 'shepherd': self.shepherd,
- 'delegate_persons': persons,
- 'wg': self.wg,
- })
-
- send_mail_text(self.request, to_email, settings.DEFAULT_FROM_EMAIL, subject, body)
-
- def save(self):
- self.next_form = AddDelegateForm(wg=self.wg, user=self.user)
- if settings.DEBUG:
- self.next_form.set_message('warning', 'Email was not sent cause tool is in DEBUG mode')
- else:
- # this is ugly...
- email_list = self.get_email_list()
- delegate = email_list[0]
- secretariat = email_list[1]
- wgchairs = email_list[2:]
- self.send_email(delegate, 'wgchairs/notexistsdelegate_delegate_email.txt')
- self.send_email(secretariat, 'wgchairs/notexistsdelegate_secretariat_email.txt')
- self.send_email(wgchairs, 'wgchairs/notexistsdelegate_wgchairs_email.txt')
- self.next_form.set_message('success', 'Email sent successfully')
-
-
-def add_form_factory(request, wg, user, shepherd=False):
- if request.method != 'POST' or request.POST.get('update_shepehrd'):
- return AddDelegateForm(wg=wg, user=user, shepherd=shepherd)
-
- if request.POST.get('form_type', None) == 'multiple':
- f = MultipleDelegateForm(wg=wg, user=user, data=request.POST.copy(), shepherd=shepherd)
- elif request.POST.get('form_type', None) == 'notexist':
- f = NotExistDelegateForm(wg=wg, user=user, data=request.POST.copy(), shepherd=shepherd)
- elif request.POST.get('form_type', None) == 'single':
- f = AddDelegateForm(wg=wg, user=user, data=request.POST.copy(), shepherd=shepherd)
- else:
- f = AddDelegateForm(wg=wg, user=user, shepherd=shepherd)
-
- f.request = request
- return f
-
-class WriteUpEditForm(RelatedWGForm):
-
- writeup = forms.CharField(widget=forms.Textarea, required=False)
- followup = forms.BooleanField(required=False)
- comment = forms.CharField(widget=forms.Textarea, required=False)
-
- def __init__(self, *args, **kwargs):
- self.doc = kwargs.pop('doc', None)
- self.doc_writeup = self.doc.protowriteup_set.all()
- if self.doc_writeup.count():
- self.doc_writeup = self.doc_writeup[0]
- else:
- self.doc_writeup = None
- super(WriteUpEditForm, self).__init__(*args, **kwargs)
- self.person = get_person_for_user(self.user)
-
- def get_writeup(self):
- return self.data.get('writeup', self.doc_writeup and self.doc_writeup.writeup or '')
-
- def save(self):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- e = WriteupDocEvent(type="changed_protocol_writeup")
- e.doc = self.doc
- e.by = self.person
- e.desc = e.get_type_display()
- e.text = self.cleaned_data['writeup']
- e.save()
- from ietf.wgchairs.models import ProtoWriteUpProxy
- self.doc_writeup = ProtoWriteUpProxy.objects.get(pk=e.pk)
- else:
- if not self.doc_writeup:
- self.doc_writeup = ProtoWriteUp.objects.create(
- person=self.person,
- draft=self.doc,
- writeup=self.cleaned_data['writeup'])
- else:
- self.doc_writeup.writeup = self.cleaned_data['writeup']
- self.doc_writeup.save()
-
- if self.data.get('modify_tag', False):
- followup = self.cleaned_data.get('followup', False)
- comment = self.cleaned_data.get('comment', False)
- try:
- shepherd = self.doc.shepherd
- except PersonOrOrgInfo.DoesNotExist:
- shepherd = None
- if shepherd:
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- extra_notify = [shepherd.formatted_email()]
- else:
- extra_notify = ['%s <%s>' % shepherd.email()]
- else:
- extra_notify = []
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- tags = DocTagName.objects.filter(slug="sheph-u")
- else:
- tags = [FOLLOWUP_TAG]
- if followup:
- update_tags(self.request, self.doc, comment, self.person, set_tags=tags, extra_notify=extra_notify)
- else:
- update_tags(self.request, self.doc, comment, self.person, reset_tags=tags, extra_notify=extra_notify)
- return self.doc_writeup
-
- def is_valid(self):
- if self.data.get('confirm', False) and self.data.get('modify_tag', False):
- self.fields['comment'].required = True
- else:
- self.fields['comment'].required = False
- return super(WriteUpEditForm, self).is_valid()
diff --git a/ietf/wgchairs/migrations/.gitignore b/ietf/wgchairs/migrations/.gitignore
deleted file mode 100644
index a74b07aee..000000000
--- a/ietf/wgchairs/migrations/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/*.pyc
diff --git a/ietf/wgchairs/migrations/0001_initial.py b/ietf/wgchairs/migrations/0001_initial.py
deleted file mode 100644
index 6aa9fb58f..000000000
--- a/ietf/wgchairs/migrations/0001_initial.py
+++ /dev/null
@@ -1,110 +0,0 @@
-
-from south.db import db
-from django.db import models
-from ietf.wgchairs.models import *
-
-class Migration:
-
- def forwards(self, orm):
-
- # Adding model 'WGDelegate'
- db.create_table('wgchairs_wgdelegate', (
- ('id', orm['wgchairs.WGDelegate:id']),
- ('person', orm['wgchairs.WGDelegate:person']),
- ('wg', orm['wgchairs.WGDelegate:wg']),
- ))
- db.send_create_signal('wgchairs', ['WGDelegate'])
-
-
-
- def backwards(self, orm):
-
- # Deleting model 'WGDelegate'
- db.delete_table('wgchairs_wgdelegate')
-
-
-
- models = {
- 'idtracker.acronym': {
- 'Meta': {'db_table': "'acronym'"},
- 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
- 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'})
- },
- 'idtracker.area': {
- 'Meta': {'db_table': "'areas'"},
- 'area_acronym': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.Acronym']", 'unique': 'True', 'primary_key': 'True'}),
- 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
- 'concluded_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'extra_email_addresses': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
- 'last_modified_date': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'blank': 'True'}),
- 'start_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.AreaStatus']"})
- },
- 'idtracker.areadirector': {
- 'Meta': {'db_table': "'area_directors'"},
- 'area': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Area']", 'null': 'True', 'db_column': "'area_acronym_id'"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'db_column': "'person_or_org_tag'"})
- },
- 'idtracker.areastatus': {
- 'Meta': {'db_table': "'area_status'"},
- 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}),
- 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
- },
- 'idtracker.ietfwg': {
- 'Meta': {'db_table': "'groups_ietf'"},
- 'area_director': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.AreaDirector']", 'null': 'True'}),
- 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
- 'concluded_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'dormant_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'email_address': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}),
- 'email_archive': ('django.db.models.fields.CharField', [], {'max_length': '95', 'blank': 'True'}),
- 'email_keyword': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
- 'email_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'}),
- 'group_acronym': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.Acronym']", 'unique': 'True', 'primary_key': 'True'}),
- 'group_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.WGType']"}),
- 'last_modified_date': ('django.db.models.fields.DateField', [], {}),
- 'meeting_scheduled': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}),
- 'meeting_scheduled_old': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}),
- 'proposed_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.WGStatus']"})
- },
- 'idtracker.personororginfo': {
- 'Meta': {'db_table': "'person_or_org_info'"},
- 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}),
- 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}),
- 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
- 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
- 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}),
- 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}),
- 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
- 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
- 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}),
- 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}),
- 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}),
- 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
- 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
- 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'})
- },
- 'idtracker.wgstatus': {
- 'Meta': {'db_table': "'g_status'"},
- 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}),
- 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
- },
- 'idtracker.wgtype': {
- 'Meta': {'db_table': "'g_type'"},
- 'group_type_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'type': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'group_type'"})
- },
- 'wgchairs.wgdelegate': {
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}),
- 'wg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IETFWG']"})
- }
- }
-
- complete_apps = ['wgchairs']
diff --git a/ietf/wgchairs/migrations/0002_add_writeup.py b/ietf/wgchairs/migrations/0002_add_writeup.py
deleted file mode 100644
index e1754ed22..000000000
--- a/ietf/wgchairs/migrations/0002_add_writeup.py
+++ /dev/null
@@ -1,163 +0,0 @@
-
-from south.db import db
-from django.db import models
-from ietf.wgchairs.models import *
-
-class Migration:
-
- def forwards(self, orm):
-
- # Adding model 'ProtoWriteUp'
- db.create_table('wgchairs_protowriteup', (
- ('id', orm['wgchairs.protowriteup:id']),
- ('person', orm['wgchairs.protowriteup:person']),
- ('draft', orm['wgchairs.protowriteup:draft']),
- ('date', orm['wgchairs.protowriteup:date']),
- ('writeup', orm['wgchairs.protowriteup:writeup']),
- ))
- db.send_create_signal('wgchairs', ['ProtoWriteUp'])
-
-
-
- def backwards(self, orm):
-
- # Deleting model 'ProtoWriteUp'
- db.delete_table('wgchairs_protowriteup')
-
-
-
- models = {
- 'idtracker.acronym': {
- 'Meta': {'db_table': "'acronym'"},
- 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
- 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'})
- },
- 'idtracker.area': {
- 'Meta': {'db_table': "'areas'"},
- 'area_acronym': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.Acronym']", 'unique': 'True', 'primary_key': 'True'}),
- 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
- 'concluded_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'extra_email_addresses': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
- 'last_modified_date': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'blank': 'True'}),
- 'start_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.AreaStatus']"})
- },
- 'idtracker.areadirector': {
- 'Meta': {'db_table': "'area_directors'"},
- 'area': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Area']", 'null': 'True', 'db_column': "'area_acronym_id'"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'db_column': "'person_or_org_tag'"})
- },
- 'idtracker.areastatus': {
- 'Meta': {'db_table': "'area_status'"},
- 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}),
- 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
- },
- 'idtracker.idintendedstatus': {
- 'Meta': {'db_table': "'id_intended_status'"},
- 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}),
- 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
- },
- 'idtracker.idstatus': {
- 'Meta': {'db_table': "'id_status'"},
- 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}),
- 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
- },
- 'idtracker.ietfwg': {
- 'Meta': {'db_table': "'groups_ietf'"},
- 'area_director': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.AreaDirector']", 'null': 'True'}),
- 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
- 'concluded_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'dormant_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'email_address': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}),
- 'email_archive': ('django.db.models.fields.CharField', [], {'max_length': '95', 'blank': 'True'}),
- 'email_keyword': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
- 'email_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'}),
- 'group_acronym': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.Acronym']", 'unique': 'True', 'primary_key': 'True'}),
- 'group_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.WGType']"}),
- 'last_modified_date': ('django.db.models.fields.DateField', [], {}),
- 'meeting_scheduled': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}),
- 'meeting_scheduled_old': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}),
- 'proposed_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.WGStatus']"})
- },
- 'idtracker.internetdraft': {
- 'Meta': {'db_table': "'internet_drafts'"},
- 'abstract': ('django.db.models.fields.TextField', [], {}),
- 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
- 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}),
- 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
- 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
- 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
- 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}),
- 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}),
- 'last_modified_date': ('django.db.models.fields.DateField', [], {}),
- 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}),
- 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
- 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
- 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}),
- 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
- 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}),
- 'revision_date': ('django.db.models.fields.DateField', [], {}),
- 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
- 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'blank': 'True'}),
- 'start_date': ('django.db.models.fields.DateField', [], {}),
- 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}),
- 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}),
- 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}),
- 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'})
- },
- 'idtracker.personororginfo': {
- 'Meta': {'db_table': "'person_or_org_info'"},
- 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}),
- 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}),
- 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
- 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
- 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}),
- 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}),
- 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
- 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
- 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}),
- 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}),
- 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}),
- 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
- 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
- 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'})
- },
- 'idtracker.wgstatus': {
- 'Meta': {'db_table': "'g_status'"},
- 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}),
- 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
- },
- 'idtracker.wgtype': {
- 'Meta': {'db_table': "'g_type'"},
- 'group_type_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'type': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'group_type'"})
- },
- 'wgchairs.protowriteup': {
- 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now()'}),
- 'draft': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.InternetDraft']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}),
- 'writeup': ('django.db.models.fields.TextField', [], {})
- },
- 'wgchairs.wgdelegate': {
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}),
- 'wg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IETFWG']"})
- }
- }
-
- complete_apps = ['wgchairs']
diff --git a/ietf/wgchairs/migrations/__init__.py b/ietf/wgchairs/migrations/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/ietf/wgchairs/models.py b/ietf/wgchairs/models.py
deleted file mode 100644
index ea0a74c97..000000000
--- a/ietf/wgchairs/models.py
+++ /dev/null
@@ -1,87 +0,0 @@
-import datetime
-
-from django.db import models
-from django.conf import settings
-
-from ietf.idtracker.models import (IETFWG, PersonOrOrgInfo,
- InternetDraft)
-
-
-class WGDelegate(models.Model):
- person = models.ForeignKey(
- PersonOrOrgInfo,
- )
-
- wg = models.ForeignKey(IETFWG, related_name="old_wgdelegate_set" if settings.USE_DB_REDESIGN_PROXY_CLASSES else None)
-
- def __unicode__(self):
- return "%s" % self.person
-
- class Meta:
- verbose_name = "WG Delegate"
-
-class ProtoWriteUp(models.Model):
- person = models.ForeignKey(
- PersonOrOrgInfo,
- blank=False,
- null=False,
- )
-
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- from ietf.idtracker.models import InternetDraftOld as InternetDraft
-
- draft = models.ForeignKey(
- InternetDraft,
- blank=False,
- null=False,
- )
-
- date = models.DateTimeField(
- default=datetime.datetime.now(),
- blank=False,
- null=False,
- )
-
- writeup = models.TextField(
- blank=False,
- null=False,
- )
-
-if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- from ietf.group.models import Role
- class WGDelegateProxy(Role):
- #person = models.ForeignKey(PersonOrOrgInfo) # same name
- #wg = models.ForeignKey(IETFWG)
- @property
- def wg(self):
- return self.group
-
- def __unicode__(self):
- return u"%s" % self.person
-
- class Meta:
- proxy = True
-
- from ietf.doc.models import WriteupDocEvent
- class ProtoWriteUpProxy(WriteupDocEvent):
- #person = models.ForeignKey(PersonOrOrgInfo, blank=False, null=False)
- @property
- def person(self):
- return self.by
- #draft = models.ForeignKey(InternetDraft, blank=False, null=False)
- @property
- def draft(self):
- return self.doc
- #date = models.DateTimeField(default=datetime.datetime.now(), blank=False, null=False)
- @property
- def date(self):
- return self.time
- #writeup = models.TextField(blank=False, null=False)
- @property
- def writeup(self):
- return self.text
- class Meta:
- proxy = True
-
- #WGDelegateOld = WGDelegate
- WGDelegate = WGDelegateProxy
diff --git a/ietf/wgchairs/templatetags/.gitignore b/ietf/wgchairs/templatetags/.gitignore
deleted file mode 100644
index a74b07aee..000000000
--- a/ietf/wgchairs/templatetags/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/*.pyc
diff --git a/ietf/wgchairs/templatetags/__init__.py b/ietf/wgchairs/templatetags/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/ietf/wgchairs/templatetags/wgchairs_tags.py b/ietf/wgchairs/templatetags/wgchairs_tags.py
deleted file mode 100644
index df51dafae..000000000
--- a/ietf/wgchairs/templatetags/wgchairs_tags.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from django.conf import settings
-from django import template
-
-from ietf.ietfworkflows.utils import get_state_for_draft
-from ietf.wgchairs.accounts import (can_manage_workflow_in_group,
- can_manage_delegates_in_group,
- can_manage_shepherds_in_group)
-
-
-register = template.Library()
-
-
-@register.inclusion_tag('wgchairs/wgchairs_admin_options.html', takes_context=True)
-def wgchairs_admin_options(context, wg):
- request = context.get('request', None)
- user = request and request.user
- return {'user': user,
- 'can_manage_delegates': can_manage_delegates_in_group(user, wg),
- 'can_manage_workflow': can_manage_workflow_in_group(user, wg),
- 'can_manage_shepherds': can_manage_shepherds_in_group(user, wg),
- 'wg': wg,
- 'selected': context.get('selected', None),
- }
-
-@register.simple_tag
-def writeup(doc):
- writeup = doc.protowriteup_set.all()
- if not writeup:
- return ''
- else:
- return writeup[0].writeup
-
-
-@register.simple_tag
-def writeupdate(doc):
- writeup = doc.protowriteup_set.all()
- if not writeup:
- return ''
- else:
- return writeup[0].date
-
-
-@register.inclusion_tag('wgchairs/draft_state.html', takes_context=True)
-def show_state(context, doc):
- return {'doc': doc,
- 'state': get_state_for_draft(doc),
- }
diff --git a/ietf/wgchairs/tests.py b/ietf/wgchairs/tests.py
deleted file mode 100644
index 1eab843bc..000000000
--- a/ietf/wgchairs/tests.py
+++ /dev/null
@@ -1,217 +0,0 @@
-import datetime, os, shutil
-
-from django.conf import settings
-from django.contrib.auth.models import User
-from django.core.urlresolvers import reverse as urlreverse
-from StringIO import StringIO
-from pyquery import PyQuery
-
-from ietf.utils.test_utils import login_testing_unauthorized
-from ietf.utils.test_data import make_test_data
-from ietf.utils.mail import outbox
-from ietf.utils import TestCase
-
-if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- from ietf.person.models import Person, Email
- from ietf.group.models import Group, GroupHistory, Role, GroupStateTransitions
- from ietf.doc.models import Document, State, WriteupDocEvent
- from ietf.name.models import DocTagName
-
-class ManageDelegatesTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
- def test_delete_delegate(self):
- make_test_data()
-
- url = urlreverse('manage_delegates', kwargs=dict(acronym="mars"))
- login_testing_unauthorized(self, "secretary", url)
-
- delegates = Role.objects.filter(name="delegate", group__acronym="mars")
- self.assertTrue(len(delegates) > 0)
-
- # get
- r = self.client.get(url)
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- self.assertEquals(len(q('form input[name=delete]')), len(delegates))
-
- # delete
- r = self.client.post(url,
- dict(remove="1",
- delete=[d.pk for d in delegates]))
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- self.assertEquals(len(q('form input[name=delete]')), 0)
- self.assertEquals(Role.objects.filter(name="delegate", group__acronym="mars").count(), 0)
-
- def test_add_not_existing_delegate(self):
- make_test_data()
-
- url = urlreverse('manage_delegates', kwargs=dict(acronym="mars"))
- login_testing_unauthorized(self, "secretary", url)
-
- # get
- r = self.client.get(url)
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- self.assertEquals(len(q('form input[name=email]')), 1)
-
- # add non-existing
- r = self.client.post(url,
- dict(email="unknown@example.com",
- form_type="single"))
- self.assertEquals(r.status_code, 200)
- self.assertTrue("unknown@example.com" in r.content)
- q = PyQuery(r.content)
- self.assertEquals(len(q('form input[type=submit][value*="Send email"]')), 1)
-
- # we get back a warning and offer to send email, do that
- mailbox_before = len(outbox)
- r = self.client.post(url,
- dict(email="unknown@example.com",
- form_type="notexist"))
- self.assertEquals(r.status_code, 200)
- self.assertTrue("Email sent" in r.content)
- self.assertEquals(len(outbox), mailbox_before + 3)
-
- def test_add_delegate(self):
- make_test_data()
-
- url = urlreverse('manage_delegates', kwargs=dict(acronym="mars"))
- login_testing_unauthorized(self, "secretary", url)
-
- # get
- r = self.client.get(url)
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- self.assertEquals(len(q('form input[name=email]')), 1)
-
- # add existing person
- history_before = GroupHistory.objects.filter(acronym="mars").count()
- r = self.client.post(url,
- dict(email="plain@example.com",
- form_type="single"))
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- self.assertTrue("new delegate" in r.content)
- self.assertTrue(Email.objects.get(address="plain@example.com").person.plain_name() in r.content)
- self.assertEquals(Role.objects.filter(name="delegate", group__acronym="mars", email__address="plain@example.com").count(), 1)
- self.assertEquals(history_before + 1, GroupHistory.objects.filter(acronym="mars").count())
-
-
-class ManageShepherdsTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
- def test_manage_shepherds(self):
- make_test_data()
-
- url = urlreverse('manage_shepherds', kwargs=dict(acronym="mars"))
- login_testing_unauthorized(self, "secretary", url)
-
- # setup test documents
- group = Group.objects.get(acronym="mars")
-
- from ietf.doc.models import Document
- common = dict(group=group,
- ad=Person.objects.get(user__username="ad"),
- type_id="draft")
- Document.objects.create(name="test-shepherd-no",
- title="No shepherd",
- shepherd=None,
- **common)
- Document.objects.create(name="test-shepherd-me",
- title="Shepherd me",
- shepherd=Person.objects.get(user__username="secretary"),
- **common)
- Document.objects.create(name="test-shepherd-other", title="Shepherd other",
- shepherd=Person.objects.get(user__username="plain"),
- **common)
- for d in Document.objects.filter(name__startswith="test-shepherd"):
- d.set_state(State.objects.get(used=True, type="draft", slug="active"))
-
- # get and make sure they are divided correctly
- r = self.client.get(url)
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- self.assertEquals(len(q('div#noshepherd a:contains("No shepherd")')), 1)
- self.assertEquals(len(q('div#mydocs a:contains("Shepherd me")')), 1)
- self.assertEquals(len(q('div#othershepherds a:contains("Shepherd other")')), 1)
-
-
-class ManageWorkflowTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
- def test_manage_workflows(self):
- make_test_data()
-
- group = Group.objects.get(acronym="mars")
-
- url = urlreverse('manage_workflow', kwargs=dict(acronym=group.acronym))
- login_testing_unauthorized(self, "secretary", url)
-
- state = State.objects.get(used=True, type="draft-stream-ietf", slug="wg-lc")
- self.assertTrue(state not in group.unused_states.all())
-
- # get
- r = self.client.get(url)
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- self.assertEquals(len(q("form.set-state").find("input[name=state][value='%s']" % state.pk).parents("form").find("input[name=active][value='0']")), 1)
-
- # deactivate state
- r = self.client.post(url,
- dict(action="setstateactive",
- state=state.pk,
- active="0"))
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- self.assertEquals(len(q("form.set-state").find("input[name=state][value='%s']" % state.pk).parents("form").find("input[name=active][value='1']")), 1)
- group = Group.objects.get(acronym=group.acronym)
- self.assertTrue(state in group.unused_states.all())
-
- # change next states
- state = State.objects.get(used=True, type="draft-stream-ietf", slug="wg-doc")
- next_states = State.objects.filter(used=True, type=b"draft-stream-ietf", slug__in=["parked", "dead", "wait-wgw", 'sub-pub']).values_list('pk', flat=True)
- r = self.client.post(url,
- dict(action="setnextstates",
- state=state.pk,
- next_states=next_states))
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- self.assertEquals(len(q("form.set-next-states").find("input[name=state][value='%s']" % state.pk).parents('form').find("input[name=next_states][checked=checked]")), len(next_states))
- transitions = GroupStateTransitions.objects.filter(group=group, state=state)
- self.assertEquals(len(transitions), 1)
- self.assertEquals(set(transitions[0].next_states.values_list("pk", flat=True)), set(next_states))
-
- # change them back to default
- next_states = state.next_states.values_list("pk", flat=True)
- r = self.client.post(url,
- dict(action="setnextstates",
- state=state.pk,
- next_states=next_states))
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- transitions = GroupStateTransitions.objects.filter(group=group, state=state)
- self.assertEquals(len(transitions), 0)
-
- # deactivate tag
- tag = DocTagName.objects.get(slug="w-expert")
- r = self.client.post(url,
- dict(action="settagactive",
- tag=tag.pk,
- active="0"))
- self.assertEquals(r.status_code, 200)
- q = PyQuery(r.content)
- self.assertEquals(len(q('form').find('input[name=tag][value="%s"]' % tag.pk).parents("form").find("input[name=active]")), 1)
- group = Group.objects.get(acronym=group.acronym)
- self.assertTrue(tag in group.unused_tags.all())
-
-if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
- # the above tests only work with the new schema
- del ManageDelegatesTestCase
- del ManageShepherdsTestCase
- del ManageWorkflowTestCase
- del ManageWriteupCase
diff --git a/ietf/wgchairs/urls.py b/ietf/wgchairs/urls.py
deleted file mode 100644
index d8cadb8ee..000000000
--- a/ietf/wgchairs/urls.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright The IETF Trust 2008, All Rights Reserved
-
-from django.conf.urls.defaults import patterns, url
-
-urlpatterns = patterns('ietf.wgchairs.views',
- url(r'^workflows/$', 'manage_workflow', name='manage_workflow'),
- url(r'^delegates/$', 'manage_delegates', name='manage_delegates'),
- url(r'^shepherds/$', 'wg_shepherd_documents', name='manage_shepherds'),
-)
diff --git a/ietf/wgchairs/views.py b/ietf/wgchairs/views.py
deleted file mode 100644
index 3bcd6e026..000000000
--- a/ietf/wgchairs/views.py
+++ /dev/null
@@ -1,200 +0,0 @@
-from django.conf import settings
-from ietf.idtracker.models import IETFWG, InternetDraft
-from django.shortcuts import get_object_or_404, render_to_response
-from django.template import RequestContext
-from django.http import HttpResponseForbidden, Http404
-
-from ietf.wgchairs.forms import (RemoveDelegateForm, add_form_factory,
- workflow_form_factory, TransitionFormSet,
- WriteUpEditForm, assign_shepherd)
-from ietf.wgchairs.accounts import (can_manage_delegates_in_group, get_person_for_user,
- can_manage_shepherds_in_group,
- can_manage_workflow_in_group,
- can_manage_shepherd_of_a_document,
- can_manage_writeup_of_a_document,
- can_manage_writeup_of_a_document_no_state,
- )
-from ietf.ietfworkflows.constants import REQUIRED_STATES
-from ietf.ietfworkflows.utils import (get_workflow_for_wg,
- get_default_workflow_for_wg,
- get_state_by_name,
- get_annotation_tags_for_draft,
- get_state_for_draft, WAITING_WRITEUP,
- FOLLOWUP_TAG)
-from ietf.name.models import DocTagName
-from ietf.doc.models import State
-from ietf.doc.utils import get_tags_for_stream_id
-
-def manage_delegates(request, acronym):
- wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1)
- user = request.user
- if not can_manage_delegates_in_group(user, wg):
- return HttpResponseForbidden('You have no permission to access this view')
- delegates = wg.wgdelegate_set.all()
- add_form = add_form_factory(request, wg, user)
- if request.method == 'POST':
- if request.POST.get('remove', None):
- form = RemoveDelegateForm(wg=wg, data=request.POST.copy())
- if form.is_valid():
- form.save()
- elif add_form.is_valid():
- add_form.save()
- add_form = add_form.get_next_form()
- max_delegates = getattr(settings, 'MAX_WG_DELEGATES', 3)
- return render_to_response('wgchairs/manage_delegates.html',
- {'wg': wg,
- 'delegates': delegates,
- 'selected': 'manage_delegates',
- 'can_add': delegates.count() < max_delegates,
- 'max_delegates': max_delegates,
- 'add_form': add_form,
- }, RequestContext(request))
-
-
-def manage_workflow(request, acronym):
- wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1)
- user = request.user
- if not can_manage_workflow_in_group(user, wg):
- return HttpResponseForbidden("You don't have permission to access this view")
- workflow = get_workflow_for_wg(wg)
- default_workflow = get_default_workflow_for_wg()
- formset = None
- if request.method == 'POST':
- form = workflow_form_factory(request, wg=wg, user=user)
- if form.is_valid():
- form.save()
- elif isinstance(form, TransitionFormSet):
- formset = form
- tags = workflow.selected_tags.all()
- default_tags = default_workflow.annotation_tags.all()
- states = workflow.selected_states.all().order_by('statedescription__order')
- default_states = default_workflow.states.all().order_by('statedescription__order')
- for i in default_states:
- if states.filter(name=i.name).count() == 1:
- i.used = True
- if i.name in REQUIRED_STATES:
- i.freeze = True
- for i in default_tags:
- if tags.filter(name=i.name).count() == 1:
- i.used = True
- if not formset:
- formset = TransitionFormSet(queryset=workflow.transitions.all(), user=user, wg=wg)
-
- return render_to_response('wgchairs/manage_workflow.html',
- {'wg': wg,
- 'workflow': workflow,
- 'default_workflow': default_workflow,
- 'states': states,
- 'tags': tags,
- 'default_states': default_states,
- 'default_tags': default_tags,
- 'formset': formset,
- 'selected': 'manage_workflow',
- }, RequestContext(request))
-
-def manage_workflowREDESIGN(request, acronym):
- from ietf.doc.models import State
- from ietf.group.models import GroupStateTransitions
-
- MANDATORY_STATES = ('c-adopt', 'wg-doc', 'sub-pub')
-
- wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1)
- user = request.user
- if not can_manage_workflow_in_group(user, wg):
- return HttpResponseForbidden("You don't have permission to access this view")
-
- if request.method == 'POST':
- action = request.POST.get("action")
- if action == "setstateactive":
- active = request.POST.get("active") == "1"
- try:
- state = State.objects.exclude(slug__in=MANDATORY_STATES).get(pk=request.POST.get("state"))
- except State.DoesNotExist:
- return HttpResponse("Invalid state %s" % request.POST.get("state"))
-
- if active:
- wg.unused_states.remove(state)
- else:
- wg.unused_states.add(state)
-
- if action == "setnextstates":
- try:
- state = State.objects.get(pk=request.POST.get("state"))
- except State.DoesNotExist:
- return HttpResponse("Invalid state %s" % request.POST.get("state"))
-
- next_states = State.objects.filter(used=True, type='draft-stream-ietf', pk__in=request.POST.getlist("next_states"))
- unused = wg.unused_states.all()
- if set(next_states.exclude(pk__in=unused)) == set(state.next_states.exclude(pk__in=unused)):
- # just use the default
- wg.groupstatetransitions_set.filter(state=state).delete()
- else:
- transitions, _ = GroupStateTransitions.objects.get_or_create(group=wg, state=state)
- transitions.next_states = next_states
-
- if action == "settagactive":
- active = request.POST.get("active") == "1"
- try:
- tag = DocTagName.objects.get(pk=request.POST.get("tag"))
- except DocTagName.DoesNotExist:
- return HttpResponse("Invalid tag %s" % request.POST.get("tag"))
-
- if active:
- wg.unused_tags.remove(tag)
- else:
- wg.unused_tags.add(tag)
-
-
- # put some info for the template on tags and states
- unused_tags = wg.unused_tags.all().values_list('slug', flat=True)
- tags = DocTagName.objects.filter(slug__in=get_tags_for_stream_id("ietf"))
- for t in tags:
- t.used = t.slug not in unused_tags
-
- unused_states = wg.unused_states.all().values_list('slug', flat=True)
- states = State.objects.filter(used=True, type="draft-stream-ietf")
- transitions = dict((o.state, o) for o in wg.groupstatetransitions_set.all())
- for s in states:
- s.used = s.slug not in unused_states
- s.mandatory = s.slug in MANDATORY_STATES
-
- default_n = s.next_states.all()
- if s in transitions:
- n = transitions[s].next_states.all()
- else:
- n = default_n
-
- s.next_states_checkboxes = [(x in n, x in default_n, x) for x in states]
- s.used_next_states = [x for x in n if x.slug not in unused_states]
-
- return render_to_response('wgchairs/manage_workflowREDESIGN.html',
- {'wg': wg,
- 'states': states,
- 'tags': tags,
- 'selected': 'manage_workflow',
- }, RequestContext(request))
-
-
-if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- manage_workflow = manage_workflowREDESIGN
-
-def wg_shepherd_documents(request, acronym):
- wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1)
- user = request.user
- if not can_manage_shepherds_in_group(user, wg):
- return HttpResponseForbidden('You have no permission to access this view')
- current_person = get_person_for_user(user)
-
- base_qs = InternetDraft.objects.filter(group=wg, states__type="draft", states__slug="active").select_related("status").order_by('title')
- documents_no_shepherd = base_qs.filter(shepherd=None)
- documents_my = base_qs.filter(shepherd=current_person)
- documents_other = base_qs.exclude(shepherd=None).exclude(shepherd__pk__in=[current_person.pk, 0])
- context = {
- 'no_shepherd': documents_no_shepherd,
- 'my_documents': documents_my,
- 'other_shepherds': documents_other,
- 'selected': 'manage_shepherds',
- 'wg': wg,
- }
- return render_to_response('wgchairs/wg_shepherd_documents.html', context, RequestContext(request))
-
diff --git a/ietf/wgcharter/feeds.py b/ietf/wgcharter/feeds.py
index 5abf7708c..1dd8bbfff 100644
--- a/ietf/wgcharter/feeds.py
+++ b/ietf/wgcharter/feeds.py
@@ -26,7 +26,7 @@ class GroupChanges(Feed):
def link(self, obj):
if not obj:
raise FeedDoesNotExist
- return urlreverse('wg_charter', kwargs={'acronym': obj.acronym})
+ return urlreverse('group_charter', kwargs={'acronym': obj.acronym})
def description(self, obj):
return self.title(obj)
@@ -44,7 +44,7 @@ class GroupChanges(Feed):
if isinstance(obj, DocEvent):
return urlreverse("doc_view", kwargs={'name': obj.doc_id })
elif isinstance(obj, GroupEvent):
- return urlreverse('wg_charter', kwargs={'acronym': obj.group.acronym })
+ return urlreverse('group_charter', kwargs={'acronym': obj.group.acronym })
def item_pubdate(self, obj):
return obj.time
diff --git a/ietf/wgcharter/mails.py b/ietf/wgcharter/mails.py
index 29c603b4e..0f983a017 100644
--- a/ietf/wgcharter/mails.py
+++ b/ietf/wgcharter/mails.py
@@ -33,7 +33,7 @@ def email_secretariat(request, group, type, text):
"wgcharter/email_secretariat.txt",
dict(text=text,
group=group,
- group_url=settings.IDTRACKER_BASE_URL + urlreverse('wg_charter', kwargs=dict(acronym=group.acronym)),
+ group_url=settings.IDTRACKER_BASE_URL + urlreverse('group_charter', kwargs=dict(acronym=group.acronym)),
charter_url=settings.IDTRACKER_BASE_URL + urlreverse('doc_view', kwargs=dict(name=group.charter.name)),
)
)
diff --git a/ietf/wgcharter/tests.py b/ietf/wgcharter/tests.py
index 56f639afb..7b55d9f91 100644
--- a/ietf/wgcharter/tests.py
+++ b/ietf/wgcharter/tests.py
@@ -21,10 +21,7 @@ from ietf.person.models import *
from ietf.iesg.models import TelechatDate
from ietf.wgcharter.utils import *
-class EditCharterTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
+class EditCharterTests(TestCase):
def setUp(self):
self.charter_dir = os.path.abspath("tmp-charter-dir")
os.mkdir(self.charter_dir)
@@ -198,10 +195,7 @@ class EditCharterTestCase(TestCase):
self.assertEquals(f.read(),
"Windows line\nMac line\nUnix line\n" + utf_8_snippet)
-class ApproveCharterTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ['names']
-
+class ApproveCharterTests(TestCase):
def setUp(self):
self.charter_dir = os.path.abspath("tmp-charter-dir")
os.mkdir(self.charter_dir)
diff --git a/ietf/wginfo/edit.py b/ietf/wginfo/edit.py
index 89be89523..69441242c 100644
--- a/ietf/wginfo/edit.py
+++ b/ietf/wginfo/edit.py
@@ -1,8 +1,9 @@
# edit/create view for WGs
-import re, os, string, datetime, shutil
+import re, os, datetime, shutil
from django.shortcuts import render_to_response, get_object_or_404, redirect
+from django.http import HttpResponseForbidden
from django.core.urlresolvers import reverse
from django.template import RequestContext
from django import forms
@@ -20,15 +21,18 @@ from ietf.group.models import *
from ietf.group.utils import save_group_in_history
from ietf.wgcharter.mails import email_secretariat
from ietf.person.forms import EmailsField
+from ietf.doc.utils import get_tags_for_stream_id
+MAX_GROUP_DELEGATES = 3
class WGForm(forms.Form):
- name = forms.CharField(max_length=255, label="WG Name", required=True)
- acronym = forms.CharField(max_length=10, label="WG Acronym", required=True)
- state = forms.ModelChoiceField(GroupStateName.objects.all(), label="WG State", required=True)
- chairs = EmailsField(label="WG Chairs", required=False)
- secretaries = EmailsField(label="WG Secretaries", required=False)
- techadv = EmailsField(label="WG Technical Advisors", required=False)
+ name = forms.CharField(max_length=255, label="Name", required=True)
+ acronym = forms.CharField(max_length=10, label="Acronym", required=True)
+ state = forms.ModelChoiceField(GroupStateName.objects.all(), label="State", required=True)
+ chairs = EmailsField(label="Chairs", required=False)
+ secretaries = EmailsField(label="Secretaries", required=False)
+ techadv = EmailsField(label="Technical Advisors", required=False)
+ delegates = EmailsField(label="Delegates", required=False, help_text=mark_safe("Type in name to search for person Chairs can delegate the authority to update the state of group documents - max %s persons at a given time" % MAX_GROUP_DELEGATES))
ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active").order_by('name'), label="Shepherding AD", empty_label="(None)", required=False)
parent = forms.ModelChoiceField(Group.objects.filter(type="area", state="active").order_by('name'), label="IETF Area", empty_label="(None)", required=False)
list_email = forms.CharField(max_length=64, required=False)
@@ -100,6 +104,13 @@ class WGForm(forms.Form):
def clean_urls(self):
return [x.strip() for x in self.cleaned_data["urls"].splitlines() if x.strip()]
+ def clean_delegates(self):
+ if len(self.cleaned_data["delegates"]) > MAX_GROUP_DELEGATES:
+ raise forms.ValidationError("At most %s delegates can be appointed at the same time, please remove %s delegates." % (
+ MAX_GROUP_DELEGATES, len(self.cleaned_data["delegates"]) - MAX_GROUP_DELEGATES))
+ return self.cleaned_data["delegates"]
+
+
def format_urls(urls, fs="\n"):
res = []
for u in urls:
@@ -224,7 +235,7 @@ def edit(request, acronym=None, action="edit"):
shutil.copy(old, new)
# update roles
- for attr, slug, title in [('chairs', 'chair', "Chairs"), ('secretaries', 'secr', "Secretaries"), ('techadv', 'techadv', "Tech Advisors")]:
+ for attr, slug, title in [('chairs', 'chair', "Chairs"), ('secretaries', 'secr', "Secretaries"), ('techadv', 'techadv', "Tech Advisors"), ('delegates', 'delegate', "Delegates")]:
new = clean[attr]
old = Email.objects.filter(role__group=wg, role__name=slug).select_related("person")
if set(new) != set(old):
@@ -262,7 +273,7 @@ def edit(request, acronym=None, action="edit"):
if action=="charter":
return redirect('charter_submit', name=wg.charter.name, option="initcharter")
- return redirect('wg_charter', acronym=wg.acronym)
+ return redirect('group_charter', acronym=wg.acronym)
else: # form.is_valid()
if not new_wg:
from ietf.person.forms import json_emails
@@ -272,6 +283,7 @@ def edit(request, acronym=None, action="edit"):
chairs=Email.objects.filter(role__group=wg, role__name="chair"),
secretaries=Email.objects.filter(role__group=wg, role__name="secr"),
techadv=Email.objects.filter(role__group=wg, role__name="techadv"),
+ delegates=Email.objects.filter(role__group=wg, role__name="delegate"),
ad=wg.ad_id if wg.ad else None,
parent=wg.parent.id if wg.parent else None,
list_email=wg.list_email if wg.list_email else None,
@@ -316,7 +328,7 @@ def conclude(request, acronym):
e.desc = "Requested closing group"
e.save()
- return redirect('wg_charter', acronym=wg.acronym)
+ return redirect('group_charter', acronym=wg.acronym)
else:
form = ConcludeForm()
@@ -324,3 +336,89 @@ def conclude(request, acronym):
dict(form=form,
wg=wg),
context_instance=RequestContext(request))
+
+
+def customize_workflow(request, acronym):
+ MANDATORY_STATES = ('c-adopt', 'wg-doc', 'sub-pub')
+
+ group = get_object_or_404(Group, acronym=acronym, type="wg")
+ if not request.user.is_authenticated() or not (has_role(request.user, "Secretariat") or group.role_set.filter(name="chair", person__user=request.user)):
+ return HttpResponseForbidden("You don't have permission to access this view")
+
+ if request.method == 'POST':
+ action = request.POST.get("action")
+ if action == "setstateactive":
+ active = request.POST.get("active") == "1"
+ try:
+ state = State.objects.exclude(slug__in=MANDATORY_STATES).get(pk=request.POST.get("state"))
+ except State.DoesNotExist:
+ return HttpResponse("Invalid state %s" % request.POST.get("state"))
+
+ if active:
+ group.unused_states.remove(state)
+ else:
+ group.unused_states.add(state)
+
+ # redirect so the back button works correctly, otherwise
+ # repeated POSTs fills up the history
+ return redirect("ietf.wginfo.edit.customize_workflow", acronym=group.acronym)
+
+ if action == "setnextstates":
+ try:
+ state = State.objects.get(pk=request.POST.get("state"))
+ except State.DoesNotExist:
+ return HttpResponse("Invalid state %s" % request.POST.get("state"))
+
+ next_states = State.objects.filter(used=True, type='draft-stream-ietf', pk__in=request.POST.getlist("next_states"))
+ unused = group.unused_states.all()
+ if set(next_states.exclude(pk__in=unused)) == set(state.next_states.exclude(pk__in=unused)):
+ # just use the default
+ group.groupstatetransitions_set.filter(state=state).delete()
+ else:
+ transitions, _ = GroupStateTransitions.objects.get_or_create(group=group, state=state)
+ transitions.next_states = next_states
+
+ return redirect("ietf.wginfo.edit.customize_workflow", acronym=group.acronym)
+
+ if action == "settagactive":
+ active = request.POST.get("active") == "1"
+ try:
+ tag = DocTagName.objects.get(pk=request.POST.get("tag"))
+ except DocTagName.DoesNotExist:
+ return HttpResponse("Invalid tag %s" % request.POST.get("tag"))
+
+ if active:
+ group.unused_tags.remove(tag)
+ else:
+ group.unused_tags.add(tag)
+
+ return redirect("ietf.wginfo.edit.customize_workflow", acronym=group.acronym)
+
+
+ # put some info for the template on tags and states
+ unused_tags = group.unused_tags.all().values_list('slug', flat=True)
+ tags = DocTagName.objects.filter(slug__in=get_tags_for_stream_id("ietf"))
+ for t in tags:
+ t.used = t.slug not in unused_tags
+
+ unused_states = group.unused_states.all().values_list('slug', flat=True)
+ states = State.objects.filter(used=True, type="draft-stream-ietf")
+ transitions = dict((o.state, o) for o in group.groupstatetransitions_set.all())
+ for s in states:
+ s.used = s.slug not in unused_states
+ s.mandatory = s.slug in MANDATORY_STATES
+
+ default_n = s.next_states.all()
+ if s in transitions:
+ n = transitions[s].next_states.all()
+ else:
+ n = default_n
+
+ s.next_states_checkboxes = [(x in n, x in default_n, x) for x in states]
+ s.used_next_states = [x for x in n if x.slug not in unused_states]
+
+ return render_to_response('wginfo/customize_workflow.html', {
+ 'group': group,
+ 'states': states,
+ 'tags': tags,
+ }, RequestContext(request))
diff --git a/ietf/wginfo/mails.py b/ietf/wginfo/mails.py
index 96b8bb6e8..02606d30e 100644
--- a/ietf/wginfo/mails.py
+++ b/ietf/wginfo/mails.py
@@ -16,7 +16,7 @@ def email_milestones_changed(request, group, changes):
def wrap_up_email(to, text):
text = wrap(strip_tags(text), 70)
text += "\n\n"
- text += u"URL: %s" % (settings.IDTRACKER_BASE_URL + urlreverse("wg_charter", kwargs=dict(acronym=group.acronym)))
+ text += u"URL: %s" % (settings.IDTRACKER_BASE_URL + urlreverse("group_charter", kwargs=dict(acronym=group.acronym)))
send_mail_text(request, to, None,
u"Milestones changed for %s %s" % (group.acronym, group.type.name),
@@ -95,7 +95,7 @@ def email_milestones_due(group, early_warning_days):
milestones=milestones,
today=today,
early_warning_days=early_warning_days,
- url=settings.IDTRACKER_BASE_URL + urlreverse("wg_charter", kwargs=dict(acronym=group.acronym))
+ url=settings.IDTRACKER_BASE_URL + urlreverse("group_charter", kwargs=dict(acronym=group.acronym))
))
def groups_needing_milestones_due_reminder(early_warning_days):
@@ -120,7 +120,7 @@ def email_milestones_overdue(group):
"wginfo/reminder_milestones_overdue.txt",
dict(group=group,
milestones=milestones,
- url=settings.IDTRACKER_BASE_URL + urlreverse("wg_charter", kwargs=dict(acronym=group.acronym))
+ url=settings.IDTRACKER_BASE_URL + urlreverse("group_charter", kwargs=dict(acronym=group.acronym))
))
def groups_needing_milestones_overdue_reminder(grace_period=30):
diff --git a/ietf/wginfo/milestones.py b/ietf/wginfo/milestones.py
index 3e7b3df64..dee1765cc 100644
--- a/ietf/wginfo/milestones.py
+++ b/ietf/wginfo/milestones.py
@@ -313,7 +313,7 @@ def edit_milestones(request, acronym, milestone_set="current"):
if milestone_set == "charter":
return redirect('doc_view', name=group.charter.canonical_name())
else:
- return redirect('wg_charter', acronym=group.acronym)
+ return redirect('group_charter', acronym=group.acronym)
else:
for m in milestones:
forms.append(MilestoneForm(instance=m, needs_review=needs_review))
diff --git a/ietf/wginfo/tests.py b/ietf/wginfo/tests.py
index 4eaf80586..fad2fcf67 100644
--- a/ietf/wginfo/tests.py
+++ b/ietf/wginfo/tests.py
@@ -50,42 +50,168 @@ from ietf.name.models import *
from ietf.person.models import *
from ietf.wginfo.mails import *
+class GroupPagesTests(TestCase):
+ def setUp(self):
+ self.charter_dir = os.path.abspath("tmp-charter-dir")
+ os.mkdir(self.charter_dir)
+ settings.CHARTER_PATH = self.charter_dir
-class WgInfoUrlTestCase(SimpleUrlTestCase):
- def testUrls(self):
- self.doTestUrls(__file__)
+ def tearDown(self):
+ shutil.rmtree(self.charter_dir)
-class WgFileTestCase(unittest.TestCase):
- def testFileExistence(self):
- fpath = os.path.join(settings.IETFWG_DESCRIPTIONS_PATH, "tls.desc.txt")
- if not os.path.exists(fpath):
- print "\nERROR: charter files not found in "+settings.IETFWG_DESCRIPTIONS_PATH
- print "They are needed for testing WG charter pages."
- print "Download them to a local directory with:"
- print "wget -nd -nc -np -r http://www.ietf.org/wg-descriptions/"
- print "And set IETFWG_DESCRIPTIONS_PATH in settings_local.py\n"
+ def test_active_wgs(self):
+ draft = make_test_data()
+ group = draft.group
-class WgOverviewTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ["names"]
+ url = urlreverse('ietf.wginfo.views.active_wgs')
+ r = self.client.get(url)
+ self.assertEquals(r.status_code, 200)
+ self.assertTrue(group.parent.name in r.content)
+ self.assertTrue(group.acronym in r.content)
+ self.assertTrue(group.name in r.content)
+ self.assertTrue(group.ad.plain_name() in r.content)
- def test_overview(self):
- make_test_data()
+ def test_wg_summaries(self):
+ draft = make_test_data()
+ group = draft.group
- wg = Group.objects.get(acronym="mars")
- wg.charter.set_state(State.objects.get(used=True, type="charter", slug="intrev"))
+ chair = Email.objects.filter(role__group=group, role__name="chair")[0]
+
+ with open(os.path.join(self.charter_dir, "%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev)), "w") as f:
+ f.write("This is a charter.")
+
+ url = urlreverse('ietf.wginfo.views.wg_summary_area')
+ r = self.client.get(url)
+ self.assertEquals(r.status_code, 200)
+ self.assertTrue(group.parent.name in r.content)
+ self.assertTrue(group.acronym in r.content)
+ self.assertTrue(group.name in r.content)
+ self.assertTrue(chair.address in r.content)
+
+ url = urlreverse('ietf.wginfo.views.wg_summary_acronym')
+ r = self.client.get(url)
+ self.assertEquals(r.status_code, 200)
+ self.assertTrue(group.acronym in r.content)
+ self.assertTrue(group.name in r.content)
+ self.assertTrue(chair.address in r.content)
+
+ url = urlreverse('ietf.wginfo.views.wg_charters')
+ r = self.client.get(url)
+ self.assertEquals(r.status_code, 200)
+ self.assertTrue(group.acronym in r.content)
+ self.assertTrue(group.name in r.content)
+ self.assertTrue(group.ad.plain_name() in r.content)
+ self.assertTrue(chair.address in r.content)
+ self.assertTrue("This is a charter." in r.content)
+
+ url = urlreverse('ietf.wginfo.views.wg_charters_by_acronym')
+ r = self.client.get(url)
+ self.assertEquals(r.status_code, 200)
+ self.assertTrue(group.acronym in r.content)
+ self.assertTrue(group.name in r.content)
+ self.assertTrue(group.ad.plain_name() in r.content)
+ self.assertTrue(chair.address in r.content)
+ self.assertTrue("This is a charter." in r.content)
+
+ def test_chartering_wgs(self):
+ draft = make_test_data()
+ group = draft.group
+ group.charter.set_state(State.objects.get(used=True, type="charter", slug="intrev"))
url = urlreverse('ietf.wginfo.views.chartering_wgs')
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
- self.assertEquals(len(q('table.ietf-doctable td.acronym a:contains("mars")')), 1)
+ self.assertEquals(len(q('table.ietf-doctable td.acronym a:contains("%s")' % group.acronym)), 1)
+ def test_bofs(self):
+ draft = make_test_data()
+ group = draft.group
+ group.state_id = "bof"
+ group.save()
-class WgEditTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ["names"]
+ url = urlreverse('ietf.wginfo.views.bofs')
+ r = self.client.get(url)
+ self.assertEquals(r.status_code, 200)
+ q = PyQuery(r.content)
+ self.assertEquals(len(q('table.ietf-doctable td.acronym a:contains("%s")' % group.acronym)), 1)
+
+ def test_group_documents(self):
+ draft = make_test_data()
+ group = draft.group
+ draft2 = Document.objects.create(
+ name="draft-somebody-mars-test",
+ time=datetime.datetime.now(),
+ type_id="draft",
+ title="Test By Somebody",
+ stream_id="ietf",
+ group=Group.objects.get(type="individ"),
+ abstract="Abstract.",
+ rev="01",
+ pages=2,
+ intended_std_level_id="ps",
+ shepherd=None,
+ ad=None,
+ expires=datetime.datetime.now() + datetime.timedelta(days=10),
+ notify="",
+ note="",
+ )
+
+ draft2.set_state(State.objects.get(used=True, type="draft", slug="active"))
+ DocAlias.objects.create(
+ document=draft2,
+ name=draft2.name,
+ )
+
+ url = urlreverse('ietf.wginfo.views.group_documents', kwargs=dict(acronym=group.acronym))
+ r = self.client.get(url)
+ self.assertEquals(r.status_code, 200)
+ self.assertTrue(draft.name in r.content)
+ self.assertTrue(group.name in r.content)
+ self.assertTrue(group.acronym in r.content)
+
+ self.assertTrue(draft2.name in r.content)
+
+ def test_group_charter(self):
+ draft = make_test_data()
+ group = draft.group
+
+ with open(os.path.join(self.charter_dir, "%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev)), "w") as f:
+ f.write("This is a charter.")
+
+ milestone = GroupMilestone.objects.create(
+ group=group,
+ state_id="active",
+ desc="Get Work Done",
+ due=datetime.date.today() + datetime.timedelta(days=100))
+ milestone.docs.add(draft)
+
+ url = urlreverse('ietf.wginfo.views.group_charter', kwargs=dict(acronym=group.acronym))
+ r = self.client.get(url)
+ self.assertEquals(r.status_code, 200)
+ self.assertTrue(group.name in r.content)
+ self.assertTrue(group.acronym in r.content)
+ self.assertTrue("This is a charter." in r.content)
+ self.assertTrue(milestone.desc in r.content)
+ self.assertTrue(milestone.docs.all()[0].name in r.content)
+
+ def test_history(self):
+ draft = make_test_data()
+ group = draft.group
+
+ e = GroupEvent.objects.create(
+ group=group,
+ desc="Something happened.",
+ type="added_comment",
+ by=Person.objects.get(name="(System)"))
+
+ url = urlreverse('ietf.wginfo.views.history', kwargs=dict(acronym=group.acronym))
+ r = self.client.get(url)
+ self.assertEquals(r.status_code, 200)
+ self.assertTrue(e.desc in r.content)
+
+class GroupEditTests(TestCase):
def setUp(self):
self.charter_dir = os.path.abspath("tmp-charter-dir")
os.mkdir(self.charter_dir)
@@ -180,7 +306,7 @@ class WgEditTestCase(TestCase):
make_test_data()
group = Group.objects.get(acronym="mars")
- url = urlreverse('wg_edit', kwargs=dict(acronym=group.acronym))
+ url = urlreverse('group_edit', kwargs=dict(acronym=group.acronym))
login_testing_unauthorized(self, "secretary", url)
# normal get
@@ -225,6 +351,7 @@ class WgEditTestCase(TestCase):
chairs="aread@ietf.org, ad1@ietf.org",
secretaries="aread@ietf.org, ad1@ietf.org, ad2@ietf.org",
techadv="aread@ietf.org",
+ delegates="ad2@ietf.org",
list_email="mars@mail",
list_subscribe="subscribe.mars",
list_archive="archive.mars",
@@ -245,6 +372,7 @@ class WgEditTestCase(TestCase):
self.assertEquals(group.ad, ad)
for k in ("chair", "secr", "techadv"):
self.assertTrue(group.role_set.filter(name=k, email__address="aread@ietf.org"))
+ self.assertTrue(group.role_set.filter(name="delegate", email__address="ad2@ietf.org"))
self.assertEquals(group.list_email, "mars@mail")
self.assertEquals(group.list_subscribe, "subscribe.mars")
self.assertEquals(group.list_archive, "archive.mars")
@@ -281,10 +409,7 @@ class WgEditTestCase(TestCase):
group = Group.objects.get(acronym=group.acronym)
self.assertEquals(group.state_id, "active")
-class MilestoneTestCase(TestCase):
- # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures
- perma_fixtures = ["names"]
-
+class MilestoneTests(TestCase):
def create_test_milestones(self):
draft = make_test_data()
@@ -688,3 +813,73 @@ class MilestoneTestCase(TestCase):
self.assertTrue(group.acronym in outbox[-1]["Subject"])
self.assertTrue(m1.desc in unicode(outbox[-1]))
self.assertTrue(m2.desc in unicode(outbox[-1]))
+
+class CustomizeWorkflowTests(TestCase):
+ def test_customize_workflow(self):
+ make_test_data()
+
+ group = Group.objects.get(acronym="mars")
+
+ url = urlreverse('ietf.wginfo.edit.customize_workflow', kwargs=dict(acronym=group.acronym))
+ login_testing_unauthorized(self, "secretary", url)
+
+ state = State.objects.get(used=True, type="draft-stream-ietf", slug="wg-lc")
+ self.assertTrue(state not in group.unused_states.all())
+
+ # get
+ r = self.client.get(url)
+ self.assertEquals(r.status_code, 200)
+ q = PyQuery(r.content)
+ self.assertEquals(len(q("form.set-state").find("input[name=state][value='%s']" % state.pk).parents("form").find("input[name=active][value='0']")), 1)
+
+ # deactivate state
+ r = self.client.post(url,
+ dict(action="setstateactive",
+ state=state.pk,
+ active="0"))
+ self.assertEquals(r.status_code, 302)
+ r = self.client.get(url)
+ q = PyQuery(r.content)
+ self.assertEquals(len(q("form.set-state").find("input[name=state][value='%s']" % state.pk).parents("form").find("input[name=active][value='1']")), 1)
+ group = Group.objects.get(acronym=group.acronym)
+ self.assertTrue(state in group.unused_states.all())
+
+ # change next states
+ state = State.objects.get(used=True, type="draft-stream-ietf", slug="wg-doc")
+ next_states = State.objects.filter(used=True, type=b"draft-stream-ietf", slug__in=["parked", "dead", "wait-wgw", 'sub-pub']).values_list('pk', flat=True)
+ r = self.client.post(url,
+ dict(action="setnextstates",
+ state=state.pk,
+ next_states=next_states))
+ self.assertEquals(r.status_code, 302)
+ r = self.client.get(url)
+ q = PyQuery(r.content)
+ self.assertEquals(len(q("form.set-next-states").find("input[name=state][value='%s']" % state.pk).parents('form').find("input[name=next_states][checked=checked]")), len(next_states))
+ transitions = GroupStateTransitions.objects.filter(group=group, state=state)
+ self.assertEquals(len(transitions), 1)
+ self.assertEquals(set(transitions[0].next_states.values_list("pk", flat=True)), set(next_states))
+
+ # change them back to default
+ next_states = state.next_states.values_list("pk", flat=True)
+ r = self.client.post(url,
+ dict(action="setnextstates",
+ state=state.pk,
+ next_states=next_states))
+ self.assertEquals(r.status_code, 302)
+ r = self.client.get(url)
+ q = PyQuery(r.content)
+ transitions = GroupStateTransitions.objects.filter(group=group, state=state)
+ self.assertEquals(len(transitions), 0)
+
+ # deactivate tag
+ tag = DocTagName.objects.get(slug="w-expert")
+ r = self.client.post(url,
+ dict(action="settagactive",
+ tag=tag.pk,
+ active="0"))
+ self.assertEquals(r.status_code, 302)
+ r = self.client.get(url)
+ q = PyQuery(r.content)
+ self.assertEquals(len(q('form').find('input[name=tag][value="%s"]' % tag.pk).parents("form").find("input[name=active]")), 1)
+ group = Group.objects.get(acronym=group.acronym)
+ self.assertTrue(tag in group.unused_tags.all())
diff --git a/ietf/wginfo/testurl.list b/ietf/wginfo/testurl.list
deleted file mode 100644
index 2cf38b30a..000000000
--- a/ietf/wginfo/testurl.list
+++ /dev/null
@@ -1,19 +0,0 @@
-200 /wg/
-404 /wg/nosuchgroup/
-200 /wg/tls/
-200 /wg/tls/charter/
-200 /wg/mobike/ # concluded
-200 /wg/mobike/charter/
-200 /wg/catnip/ # concluded very long time ago
-200 /wg/catnip/charter/ # concluded very long time ago
-404 /wg/saag/ # not a WG
-404 /wg/saag/charter/ # not a WG
-
-200 /wg/1wg-summary.txt
-200 /wg/1wg-summary-by-acronym.txt
-301 /wg/summary.txt
-301 /wg/summary-by-area.txt
-301 /wg/summary-by-acronym.txt
-200,heavy /wg/1wg-charters.txt
-200,heavy /wg/1wg-charters-by-acronym.txt
-
diff --git a/ietf/wginfo/urls.py b/ietf/wginfo/urls.py
index 80651befd..b0106f144 100644
--- a/ietf/wginfo/urls.py
+++ b/ietf/wginfo/urls.py
@@ -6,7 +6,7 @@ from django.views.generic.simple import redirect_to
urlpatterns = patterns('',
- (r'^$', views.wg_dir),
+ (r'^$', views.active_wgs),
(r'^summary.txt', redirect_to, { 'url':'/wg/1wg-summary.txt' }),
(r'^summary-by-area.txt', redirect_to, { 'url':'/wg/1wg-summary.txt' }),
(r'^summary-by-acronym.txt', redirect_to, { 'url':'/wg/1wg-summary-by-acronym.txt' }),
@@ -18,17 +18,16 @@ urlpatterns = patterns('',
(r'^bofs/$', views.bofs),
(r'^chartering/create/$', edit.edit, {'action': "charter"}, "wg_create"),
(r'^bofs/create/$', edit.edit, {'action': "create"}, "bof_create"),
- (r'^(?P[a-zA-Z0-9-]+)/documents/txt/$', views.wg_documents_txt),
- (r'^(?P[a-zA-Z0-9-]+)/$', views.wg_documents_html, None, "wg_docs"),
- (r'^(?P[a-zA-Z0-9-]+)/charter/$', views.wg_charter, None, 'wg_charter'),
+ (r'^(?P[a-zA-Z0-9-]+)/documents/txt/$', views.group_documents_txt),
+ (r'^(?P[a-zA-Z0-9-]+)/$', views.group_documents, None, "wg_docs"),
+ (r'^(?P[a-zA-Z0-9-]+)/charter/$', views.group_charter, None, 'group_charter'),
(r'^(?P[a-zA-Z0-9-]+)/init-charter/', edit.submit_initial_charter, None, "wg_init_charter"),
(r'^(?P[a-zA-Z0-9-]+)/history/$', views.history),
- (r'^(?P[a-zA-Z0-9-]+)/edit/$', edit.edit, {'action': "edit"}, "wg_edit"),
+ (r'^(?P[a-zA-Z0-9-]+)/edit/$', edit.edit, {'action': "edit"}, "group_edit"),
(r'^(?P[a-zA-Z0-9-]+)/conclude/$', edit.conclude, None, "wg_conclude"),
(r'^(?P[a-zA-Z0-9-]+)/milestones/$', milestones.edit_milestones, {'milestone_set': "current"}, "wg_edit_milestones"),
(r'^(?P[a-zA-Z0-9-]+)/milestones/charter/$', milestones.edit_milestones, {'milestone_set': "charter"}, "wg_edit_charter_milestones"),
(r'^(?P[a-zA-Z0-9-]+)/milestones/charter/reset/$', milestones.reset_charter_milestones, None, "wg_reset_charter_milestones"),
(r'^(?P[a-zA-Z0-9-]+)/ajax/searchdocs/$', milestones.ajax_search_docs, None, "wg_ajax_search_docs"),
- (r'^(?P[^/]+)/management/', include('ietf.wgchairs.urls')),
-
+ (r'^(?P[a-zA-Z0-9-]+)/workflow/$', edit.customize_workflow),
)
diff --git a/ietf/wginfo/views.py b/ietf/wginfo/views.py
index 6caaf6f7d..5d5c12795 100644
--- a/ietf/wginfo/views.py
+++ b/ietf/wginfo/views.py
@@ -32,106 +32,126 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+import itertools
+
from django.shortcuts import get_object_or_404, render_to_response
-from django.template import RequestContext, loader
+from django.template import RequestContext
from django.http import HttpResponse
from django.conf import settings
from django.core.urlresolvers import reverse as urlreverse
-from ietf.idtracker.models import Area, IETFWG
+
from ietf.doc.views_search import SearchForm, retrieve_search_results
-from ietf.idrfc.idrfc_wrapper import IdRfcWrapper
-from ietf.ipr.models import IprDetail
-from ietf.group.models import Group
-from ietf.doc.models import State
+from ietf.group.models import Group, GroupURL, Role
+from ietf.doc.models import State, DocAlias, RelatedDocument
from ietf.doc.utils import get_chartering_type
+from ietf.group.utils import get_charter_text
+from ietf.doc.templatetags.ietf_filters import clean_whitespace
+from ietf.ietfauth.utils import has_role
+def roles(group, role_name):
+ return Role.objects.filter(group=group, name=role_name).select_related("email", "person")
-def fill_in_charter_info(wg, include_drafts=False):
- from ietf.person.models import Email
- from ietf.doc.models import DocAlias, RelatedDocument
+def fill_in_charter_info(group, include_drafts=False):
+ group.areadirector = group.ad.role_email("ad", group.parent) if group.ad else None
+ group.chairs = roles(group, "chair")
+ group.techadvisors = roles(group, "techadv")
+ group.editors = roles(group, "editor")
+ group.secretaries = roles(group, "secr")
+ milestone_state = "charter" if group.state_id == "proposed" else "active"
+ group.milestones = group.groupmilestone_set.filter(state=milestone_state).order_by('due')
- wg.areadirector = wg.ad.role_email("ad", wg.parent) if wg.ad else None
- wg.chairs = Email.objects.filter(role__group=wg, role__name="chair")
- wg.techadvisors = Email.objects.filter(role__group=wg, role__name="techadv")
- wg.editors = Email.objects.filter(role__group=wg, role__name="editor")
- wg.secretaries = Email.objects.filter(role__group=wg, role__name="secr")
- milestone_state = "charter" if wg.state_id == "proposed" else "active"
- wg.milestones = wg.groupmilestone_set.filter(state=milestone_state).order_by('due')
+ if group.charter:
+ group.charter_text = get_charter_text(group)
+ else:
+ group.charter_text = u"Not chartered yet."
if include_drafts:
- aliases = DocAlias.objects.filter(document__type="draft", document__group=wg).select_related('document').order_by("name")
- wg.drafts = []
- wg.rfcs = []
+ aliases = DocAlias.objects.filter(document__type="draft", document__group=group).select_related('document').order_by("name")
+ group.drafts = []
+ group.rfcs = []
for a in aliases:
if a.name.startswith("draft"):
- wg.drafts.append(a)
+ group.drafts.append(a)
else:
- wg.rfcs.append(a)
+ group.rfcs.append(a)
a.rel = RelatedDocument.objects.filter(source=a.document).distinct()
a.invrel = RelatedDocument.objects.filter(target=a).distinct()
-def wg_summary_acronym(request):
- areas = Area.active_areas()
- wgs = IETFWG.objects.filter(status=IETFWG.ACTIVE)
- return HttpResponse(loader.render_to_string('wginfo/1wg-summary-by-acronym.txt', {'area_list': areas, 'wg_list': wgs}),mimetype='text/plain; charset=UTF-8')
+def extract_last_name(role):
+ return role.person.name_parts()[3]
def wg_summary_area(request):
- wgs = IETFWG.objects.filter(status='1',group_type='1',start_date__isnull=False).exclude(parent=None)
- return HttpResponse(loader.render_to_string('wginfo/1wg-summary.txt', {'wg_list': wgs}),mimetype='text/plain; charset=UTF-8')
-
-def wg_charters(request):
- wgs = IETFWG.objects.filter(status='1',group_type='1',start_date__isnull=False).exclude(parent=None)
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- for wg in wgs:
- fill_in_charter_info(wg, include_drafts=True)
- return HttpResponse(loader.render_to_string('wginfo/1wg-charters.txt', {'wg_list': wgs, 'USE_DB_REDESIGN_PROXY_CLASSES': settings.USE_DB_REDESIGN_PROXY_CLASSES}),mimetype='text/plain; charset=UTF-8')
-
-def wg_charters_by_acronym(request):
- wgs = IETFWG.objects.filter(status='1',group_type='1',start_date__isnull=False)
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- for wg in wgs:
- fill_in_charter_info(wg, include_drafts=True)
- return HttpResponse(loader.render_to_string('wginfo/1wg-charters-by-acronym.txt', {'wg_list': wgs, 'USE_DB_REDESIGN_PROXY_CLASSES': settings.USE_DB_REDESIGN_PROXY_CLASSES}),mimetype='text/plain; charset=UTF-8')
-
-def wg_dir(request):
- areas = Area.active_areas()
- return render_to_response('wginfo/wg-dir.html', {'areas':areas}, RequestContext(request))
-
-def wg_dirREDESIGN(request):
- from ietf.group.models import Group, GroupURL
- from ietf.person.models import Email
-
areas = Group.objects.filter(type="area", state="active").order_by("name")
for area in areas:
- area.ads = []
- for e in Email.objects.filter(role__group=area, role__name="ad").select_related("person"):
- e.incoming = False
- area.ads.append(e)
+ area.ads = sorted(roles(area, "ad"), key=extract_last_name)
+ area.groups = Group.objects.filter(parent=area, type="wg", state="active").order_by("acronym")
+ for group in area.groups:
+ group.chairs = sorted(roles(group, "chair"), key=extract_last_name)
- for e in Email.objects.filter(role__group=area, role__name="pre-ad").select_related("person"):
- e.incoming = True
- area.ads.append(e)
+ areas = [a for a in areas if a.groups]
- area.ads.sort(key=lambda e: (e.incoming, e.person.name_parts()[3]))
- area.wgs = Group.objects.filter(parent=area, type="wg", state="active").order_by("acronym")
+ return render_to_response('wginfo/1wg-summary.txt',
+ { 'areas': areas },
+ mimetype='text/plain; charset=UTF-8')
+
+def wg_summary_acronym(request):
+ areas = Group.objects.filter(type="area", state="active").order_by("name")
+ groups = Group.objects.filter(type="wg", state="active").order_by("acronym").select_related("parent")
+ for group in groups:
+ group.chairs = sorted(roles(group, "chair"), key=extract_last_name)
+ return render_to_response('wginfo/1wg-summary-by-acronym.txt',
+ { 'areas': areas,
+ 'groups': groups },
+ mimetype='text/plain; charset=UTF-8')
+
+def wg_charters(request):
+ areas = Group.objects.filter(type="area", state="active").order_by("name")
+ for area in areas:
+ area.ads = sorted(roles(area, "ad"), key=extract_last_name)
+ area.groups = Group.objects.filter(parent=area, type="wg", state="active").order_by("name")
+ for group in area.groups:
+ fill_in_charter_info(group, include_drafts=True)
+ group.area = area
+ return render_to_response('wginfo/1wg-charters.txt',
+ { 'areas': areas },
+ mimetype='text/plain; charset=UTF-8')
+
+def wg_charters_by_acronym(request):
+ areas = dict((a.id, a) for a in Group.objects.filter(type="area", state="active").order_by("name"))
+
+ for area in areas.itervalues():
+ area.ads = sorted(roles(area, "ad"), key=extract_last_name)
+
+ groups = Group.objects.filter(type="wg", state="active").exclude(parent=None).order_by("acronym")
+ for group in groups:
+ fill_in_charter_info(group, include_drafts=True)
+ group.area = areas.get(group.parent_id)
+ return render_to_response('wginfo/1wg-charters-by-acronym.txt',
+ { 'groups': groups },
+ mimetype='text/plain; charset=UTF-8')
+
+def active_wgs(request):
+ areas = Group.objects.filter(type="area", state="active").order_by("name")
+ for area in areas:
+ # dig out information for template
+ area.ads = (list(sorted(roles(area, "ad"), key=extract_last_name))
+ + list(sorted(roles(area, "pre-ad"), key=extract_last_name)))
+
+ area.groups = Group.objects.filter(parent=area, type="wg", state="active").order_by("acronym")
area.urls = area.groupurl_set.all().order_by("name")
- for wg in area.wgs:
- wg.chairs = sorted(Email.objects.filter(role__group=wg, role__name="chair").select_related("person"), key=lambda e: e.person.name_parts()[3])
-
- return render_to_response('wginfo/wg-dirREDESIGN.html', {'areas':areas}, RequestContext(request))
+ for group in area.groups:
+ group.chairs = sorted(roles(group, "chair"), key=extract_last_name)
-if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- wg_dir = wg_dirREDESIGN
+ return render_to_response('wginfo/active_wgs.html', {'areas':areas}, RequestContext(request))
def bofs(request):
groups = Group.objects.filter(type="wg", state="bof")
- return render_to_response('wginfo/bofs.html',dict(groups=groups),RequestContext(request))
+ return render_to_response('wginfo/bofs.html',dict(groups=groups), RequestContext(request))
def chartering_wgs(request):
charter_states = State.objects.filter(used=True, type="charter").exclude(slug__in=("approved", "notrev"))
groups = Group.objects.filter(type="wg", charter__states__in=charter_states).select_related("state", "charter")
-
for g in groups:
g.chartering_type = get_chartering_type(g.charter)
@@ -141,32 +161,56 @@ def chartering_wgs(request):
RequestContext(request))
-def wg_documents(request, acronym):
- wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1)
- concluded = wg.status_id in [ 2, 3, ]
- proposed = (wg.status_id == 4)
- form = SearchForm({'by':'group', 'group':str(wg.group_acronym.acronym),
- 'rfcs':'on', 'activedrafts':'on'})
+def construct_group_menu_context(request, group, selected, others):
+ """Return context with info for the group menu filled in."""
+ actions = []
+
+ is_chair = group.has_role(request.user, "chair")
+ is_ad_or_secretariat = has_role(request.user, ("Area Director", "Secretariat"))
+
+ if group.state_id != "proposed" and (is_chair or is_ad_or_secretariat):
+ actions.append((u"Add or edit milestones", urlreverse("wg_edit_milestones", kwargs=dict(acronym=group.acronym))))
+
+ if group.state_id != "conclude" and is_ad_or_secretariat:
+ actions.append((u"Edit group", urlreverse("group_edit", kwargs=dict(acronym=group.acronym))))
+
+ if is_chair or is_ad_or_secretariat:
+ actions.append((u"Customize workflow", urlreverse("ietf.wginfo.edit.customize_workflow", kwargs=dict(acronym=group.acronym))))
+
+ if group.state_id in ("active", "dormant") and is_ad_or_secretariat:
+ actions.append((u"Request closing group", urlreverse("wg_conclude", kwargs=dict(acronym=group.acronym))))
+
+ d = {
+ "group": group,
+ "selected": selected,
+ "menu_actions": actions,
+ }
+
+ d.update(others)
+
+ return d
+
+def search_for_group_documents(group):
+ form = SearchForm({ 'by':'group', 'group': group.acronym or "", 'rfcs':'on', 'activedrafts': 'on' })
docs, meta = retrieve_search_results(form)
# get the related docs
- form_related = SearchForm({'by':'group', 'name':'-'+str(wg.group_acronym.acronym)+'-', 'activedrafts':'on'})
- docs_related, meta_related = retrieve_search_results(form_related)
- docs_related_pruned = []
- for d in docs_related:
+ form_related = SearchForm({ 'by':'group', 'name': u'-%s-' % group.acronym, 'activedrafts': 'on' })
+ raw_docs_related, meta_related = retrieve_search_results(form_related)
+
+ docs_related = []
+ for d in raw_docs_related:
parts = d.name.split("-", 2);
# canonical form draft--wg-etc
- if len(parts) >= 3 and parts[1] != "ietf" and parts[2].startswith(wg.group_acronym.acronym + "-"):
+ if len(parts) >= 3 and parts[1] != "ietf" and parts[2].startswith(group.acronym + "-"):
d.search_heading = "Related Internet-Draft"
- docs_related_pruned.append(d)
-
- docs_related = docs_related_pruned
+ docs_related.append(d)
# move call for WG adoption to related
cleaned_docs = []
docs_related_names = set(d.name for d in docs_related)
for d in docs:
- if d.stream_id == "ietf" and d.get_state_slug("draft-stream-ietf") in [ "c-adopt", "wg-cand" ]:
+ if d.stream_id and d.get_state_slug("draft-stream-%s" % d.stream_id) in ("c-adopt", "wg-cand"):
if d.name not in docs_related_names:
d.search_heading = "Related Internet-Draft"
docs_related.append(d)
@@ -177,59 +221,68 @@ def wg_documents(request, acronym):
docs_related.sort(key=lambda d: d.name)
- return wg, concluded, proposed, docs, meta, docs_related, meta_related
+ return docs, meta, docs_related, meta_related
-def wg_documents_txt(request, acronym):
- wg, concluded, proposed, docs, meta, docs_related, meta_related = wg_documents(request, acronym)
- return HttpResponse(loader.render_to_string('wginfo/wg_documents.txt', {'wg': wg, 'concluded':concluded, 'proposed':proposed, 'selected':'documents', 'docs':docs, 'meta':meta, 'docs_related':docs_related, 'meta_related':meta_related}),mimetype='text/plain; charset=UTF-8')
+def group_documents(request, acronym):
+ group = get_object_or_404(Group, type="wg", acronym=acronym)
-def wg_documents_html(request, acronym):
- wg, concluded, proposed, docs, meta, docs_related, meta_related = wg_documents(request, acronym)
- return render_to_response('wginfo/wg_documents.html', {'wg': wg, 'concluded':concluded, 'proposed':proposed, 'selected':'documents', 'docs':docs, 'meta':meta, 'docs_related':docs_related, 'meta_related':meta_related}, RequestContext(request))
+ docs, meta, docs_related, meta_related = search_for_group_documents(group)
-def wg_charter(request, acronym):
- wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1)
- concluded = wg.status_id in [ 2, 3, ]
- proposed = (wg.status_id == 4)
+ return render_to_response('wginfo/group_documents.html',
+ construct_group_menu_context(request, group, "documents", {
+ 'docs': docs,
+ 'meta': meta,
+ 'docs_related': docs_related,
+ 'meta_related': meta_related
+ }), RequestContext(request))
- fill_in_charter_info(wg)
- actions = []
- if wg.state_id != "conclude":
- actions.append(("Edit WG", urlreverse("wg_edit", kwargs=dict(acronym=wg.acronym))))
+def group_documents_txt(request, acronym):
+ """Return tabulator-separated rows with documents for group."""
+ group = get_object_or_404(Group, type="wg", acronym=acronym)
- e = wg.latest_event(type__in=("changed_state", "requested_close",))
- requested_close = wg.state_id != "conclude" and e and e.type == "requested_close"
+ docs, meta, docs_related, meta_related = search_for_group_documents(group)
- if wg.state_id in ("active", "dormant"):
- actions.append(("Request closing WG", urlreverse("wg_conclude", kwargs=dict(acronym=wg.acronym))))
+ for d in docs:
+ d.prefix = d.get_state().name
- context = get_wg_menu_context(wg, "charter")
- context.update(dict(
- actions=actions,
- is_chair=request.user.is_authenticated() and wg.role_set.filter(name="chair", person__user=request.user),
- milestones_in_review=wg.groupmilestone_set.filter(state="review"),
- requested_close=requested_close,
- ))
+ for d in docs_related:
+ d.prefix = u"Related %s" % d.get_state().name
- return render_to_response('wginfo/wg_charter.html',
- context,
- RequestContext(request))
+ rows = []
+ for d in itertools.chain(docs, docs_related):
+ rfc_number = d.rfc_number()
+ if rfc_number != None:
+ name = rfc_number
+ else:
+ name = "%s-%s" % (d.name, d.rev)
-def get_wg_menu_context(wg, selected):
- # it would probably be better to refactor wginfo into rendering
- # the menu separately instead of each view having to include the information
+ rows.append(u"\t".join((d.prefix, name, clean_whitespace(d.title))))
+
+ return HttpResponse(u"\n".join(rows), mimetype='text/plain; charset=UTF-8')
+
+
+def group_charter(request, acronym):
+ group = get_object_or_404(Group, type="wg", acronym=acronym)
+
+ fill_in_charter_info(group, include_drafts=False)
+ group.delegates = roles(group, "delegate")
+
+ e = group.latest_event(type__in=("changed_state", "requested_close",))
+ requested_close = group.state_id != "conclude" and e and e.type == "requested_close"
+
+ return render_to_response('wginfo/group_charter.html',
+ construct_group_menu_context(request, group, "charter", {
+ "milestones_in_review": group.groupmilestone_set.filter(state="review"),
+ "requested_close": requested_close,
+ }), RequestContext(request))
- return dict(wg=wg, concluded=wg.state_id == "conclude", proposed=wg.state_id == "proposed", selected=selected)
def history(request, acronym):
- wg = get_object_or_404(Group, acronym=acronym)
+ group = get_object_or_404(Group, acronym=acronym)
- events = wg.groupevent_set.all().select_related('by').order_by('-time', '-id')
+ events = group.groupevent_set.all().select_related('by').order_by('-time', '-id')
- context = get_wg_menu_context(wg, "history")
- context.update(dict(events=events,
- ))
-
- wg.group_acronym = wg # hack for compatibility with old templates
-
- return render_to_response('wginfo/history.html', context, RequestContext(request))
+ return render_to_response('wginfo/history.html',
+ construct_group_menu_context(request, group, "history", {
+ "events": events,
+ }), RequestContext(request))
diff --git a/permissions/.gitignore b/permissions/.gitignore
deleted file mode 100644
index a74b07aee..000000000
--- a/permissions/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/*.pyc
diff --git a/permissions/__init__.py b/permissions/__init__.py
deleted file mode 100644
index b81c7766c..000000000
--- a/permissions/__init__.py
+++ /dev/null
@@ -1,148 +0,0 @@
-import permissions.utils
-
-class PermissionBase(object):
- """Mix-in class for permissions.
- """
- def grant_permission(self, role, permission):
- """Grants passed permission to passed role. Returns True if the
- permission was able to be added, otherwise False.
-
- **Parameters:**
-
- role
- The role for which the permission should be granted.
-
- permission
- The permission which should be granted. Either a permission
- object or the codename of a permission.
- """
- return permissions.utils.grant_permission(self, role, permission)
-
- def remove_permission(self, role, permission):
- """Removes passed permission from passed role. Returns True if the
- permission has been removed.
-
- **Parameters:**
-
- role
- The role for which a permission should be removed.
-
- permission
- The permission which should be removed. Either a permission object
- or the codename of a permission.
- """
- return permissions.utils.remove_permission(self, role, permission)
-
- def has_permission(self, user, permission, roles=[]):
- """Returns True if the passed user has passed permission for this
- instance. Otherwise False.
-
- **Parameters:**
-
- permission
- The permission's codename which should be checked. Must be a
- string with a valid codename.
-
- user
- The user for which the permission should be checked.
-
- roles
- If passed, these roles will be assigned to the user temporarily
- before the permissions are checked.
- """
- return permissions.utils.has_permission(self, user, permission, roles)
-
- def check_permission(self, user, permission, roles=[]):
- """Raise Unauthorized if the the passed user hasn't passed permission
- for this instance.
-
- **Parameters:**
-
- permission
- The permission's codename which should be checked. Must be a
- string with a valid codename.
-
- user
- The user for which the permission should be checked.
-
- roles
- If passed, these roles will be assigned to the user temporarily
- before the permissions are checked.
- """
- if not self.has_permission(user, permission, roles):
- raise Unauthorized("User %s doesn't have permission %s for object %s" % (user, permission, obj.slug))
-
- def add_inheritance_block(self, permission):
- """Adds an inheritance block for the passed permission.
-
- **Parameters:**
-
- permission
- The permission for which an inheritance block should be added.
- Either a permission object or the codename of a permission.
- """
- return permissions.utils.add_inheritance_block(self, permission)
-
- def remove_inheritance_block(self, permission):
- """Removes a inheritance block for the passed permission.
-
- **Parameters:**
-
- permission
- The permission for which an inheritance block should be removed.
- Either a permission object or the codename of a permission.
- """
- return permissions.utils.remove_inheritance_block(self, permission)
-
- def is_inherited(self, codename):
- """Returns True if the passed permission is inherited.
-
- **Parameters:**
-
- codename
- The permission which should be checked. Must be the codename of
- the permission.
- """
- return permissions.utils.is_inherited(self, codename)
-
- def add_role(self, principal, role):
- """Adds a local role for the principal.
-
- **Parameters:**
-
- principal
- The principal (user or group) which gets the role.
-
- role
- The role which is assigned.
- """
- return permissions.utils.add_local_role(self, principal, role)
-
- def get_roles(self, principal):
- """Returns *direct* local roles for passed principal (user or group).
- """
- return permissions.utils.get_local_roles(self, principal)
-
- def remove_role(self, principal, role):
- """Adds a local role for the principal to the object.
-
- **Parameters:**
-
- principal
- The principal (user or group) from which the role is removed.
-
- role
- The role which is removed.
- """
- return permissions.utils.remove_local_role(self, principal, role)
-
- def remove_roles(self, principal):
- """Removes all local roles for the passed principal from the object.
-
- **Parameters:**
-
- principal
- The principal (user or group) from which all local roles are
- removed.
- """
- return permissions.utils.remove_local_roles(self, principal)
\ No newline at end of file
diff --git a/permissions/backend.py b/permissions/backend.py
deleted file mode 100644
index a0c2d8ec6..000000000
--- a/permissions/backend.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# permissions imports
-import permissions.utils
-
-class ObjectPermissionsBackend(object):
- """Django backend for object permissions. Needs Django 1.2.
-
-
- Use it together with the default ModelBackend like so::
-
- AUTHENTICATION_BACKENDS = (
- 'django.contrib.auth.backends.ModelBackend',
- 'permissions.backend.ObjectPermissionsBackend',
- )
-
- Then you can use it like:
-
- user.has_perm("view", your_object)
-
- """
- supports_object_permissions = True
- supports_anonymous_user = True
-
- def authenticate(self, username, password):
- return None
-
- def has_perm(self, user_obj, perm, obj=None):
- """Checks whether the passed user has passed permission for passed
- object (obj).
-
- This should be the primary method to check wether a user has a certain
- permission.
-
- Parameters
- ==========
-
- perm
- The permission's codename which should be checked.
-
- user_obj
- The user for which the permission should be checked.
-
- obj
- The object for which the permission should be checked.
- """
- return permissions.utils.has_permission(obj, user_obj, perm)
\ No newline at end of file
diff --git a/permissions/exceptions.py b/permissions/exceptions.py
deleted file mode 100644
index 72c24a317..000000000
--- a/permissions/exceptions.py
+++ /dev/null
@@ -1,3 +0,0 @@
-class Unauthorized(Exception):
- def __init__(self, str):
- super(Unauthorized, self).__init__(str)
\ No newline at end of file
diff --git a/permissions/fixtures/initial.xml b/permissions/fixtures/initial.xml
deleted file mode 100644
index 914abf8f1..000000000
--- a/permissions/fixtures/initial.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
- View
- view
-
-
- Edit
- edit
-
-
- Delete
- delete
-
-
- Cut
- cut
-
-
- Copy
- copy
-
-
diff --git a/permissions/locale/de/LC_MESSAGES/django.mo b/permissions/locale/de/LC_MESSAGES/django.mo
deleted file mode 100644
index 37b653476..000000000
Binary files a/permissions/locale/de/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/permissions/locale/de/LC_MESSAGES/django.po b/permissions/locale/de/LC_MESSAGES/django.po
deleted file mode 100644
index 0a7e9e9cb..000000000
--- a/permissions/locale/de/LC_MESSAGES/django.po
+++ /dev/null
@@ -1,52 +0,0 @@
-# German translations for django-permissions
-# Copyright (C) 2010 Kai Diefenbach
-# This file is distributed under the same license as the PACKAGE package.
-# Kai Diefenbach , 2010.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: 1.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-03-30 23:12+0200\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: LANGUAGE \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: models.py:154
-msgid "Name"
-msgstr "Name"
-
-#: models.py:155
-msgid "Codename"
-msgstr "Codename"
-
-#: models.py:156
-msgid "Content Types"
-msgstr "Inhaltstypen"
-
-#: models.py:175 models.py:280
-msgid "Role"
-msgstr "Rolle"
-
-#: models.py:176 models.py:216
-msgid "Permission"
-msgstr "Recht"
-
-#: models.py:178 models.py:218 models.py:282
-msgid "Content type"
-msgstr "Inhaltstyp"
-
-#: models.py:179 models.py:219 models.py:283
-msgid "Content id"
-msgstr "Inhalts-ID"
-
-#: models.py:278
-msgid "User"
-msgstr "Benutzer"
-
-#: models.py:279
-msgid "Group"
-msgstr "Gruppe"
diff --git a/permissions/migrations/.gitignore b/permissions/migrations/.gitignore
deleted file mode 100644
index a74b07aee..000000000
--- a/permissions/migrations/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/*.pyc
diff --git a/permissions/migrations/0001_initial.py b/permissions/migrations/0001_initial.py
deleted file mode 100644
index 38e36b067..000000000
--- a/permissions/migrations/0001_initial.py
+++ /dev/null
@@ -1,154 +0,0 @@
-
-from south.db import db
-from django.db import models
-from permissions.models import *
-
-class Migration:
-
- def forwards(self, orm):
-
- # Adding model 'Role'
- db.create_table('permissions_role', (
- ('id', orm['permissions.Role:id']),
- ('name', orm['permissions.Role:name']),
- ))
- db.send_create_signal('permissions', ['Role'])
-
- # Adding model 'ObjectPermissionInheritanceBlock'
- db.create_table('permissions_objectpermissioninheritanceblock', (
- ('id', orm['permissions.ObjectPermissionInheritanceBlock:id']),
- ('permission', orm['permissions.ObjectPermissionInheritanceBlock:permission']),
- ('content_type', orm['permissions.ObjectPermissionInheritanceBlock:content_type']),
- ('content_id', orm['permissions.ObjectPermissionInheritanceBlock:content_id']),
- ))
- db.send_create_signal('permissions', ['ObjectPermissionInheritanceBlock'])
-
- # Adding model 'ObjectPermission'
- db.create_table('permissions_objectpermission', (
- ('id', orm['permissions.ObjectPermission:id']),
- ('role', orm['permissions.ObjectPermission:role']),
- ('permission', orm['permissions.ObjectPermission:permission']),
- ('content_type', orm['permissions.ObjectPermission:content_type']),
- ('content_id', orm['permissions.ObjectPermission:content_id']),
- ))
- db.send_create_signal('permissions', ['ObjectPermission'])
-
- # Adding model 'Permission'
- db.create_table('permissions_permission', (
- ('id', orm['permissions.Permission:id']),
- ('name', orm['permissions.Permission:name']),
- ('codename', orm['permissions.Permission:codename']),
- ))
- db.send_create_signal('permissions', ['Permission'])
-
- # Adding model 'PrincipalRoleRelation'
- db.create_table('permissions_principalrolerelation', (
- ('id', orm['permissions.PrincipalRoleRelation:id']),
- ('user', orm['permissions.PrincipalRoleRelation:user']),
- ('group', orm['permissions.PrincipalRoleRelation:group']),
- ('role', orm['permissions.PrincipalRoleRelation:role']),
- ('content_type', orm['permissions.PrincipalRoleRelation:content_type']),
- ('content_id', orm['permissions.PrincipalRoleRelation:content_id']),
- ))
- db.send_create_signal('permissions', ['PrincipalRoleRelation'])
-
- # Adding ManyToManyField 'Permission.content_types'
- db.create_table('permissions_permission_content_types', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('permission', models.ForeignKey(orm.Permission, null=False)),
- ('contenttype', models.ForeignKey(orm['contenttypes.ContentType'], null=False))
- ))
-
-
-
- def backwards(self, orm):
-
- # Deleting model 'Role'
- db.delete_table('permissions_role')
-
- # Deleting model 'ObjectPermissionInheritanceBlock'
- db.delete_table('permissions_objectpermissioninheritanceblock')
-
- # Deleting model 'ObjectPermission'
- db.delete_table('permissions_objectpermission')
-
- # Deleting model 'Permission'
- db.delete_table('permissions_permission')
-
- # Deleting model 'PrincipalRoleRelation'
- db.delete_table('permissions_principalrolerelation')
-
- # Dropping ManyToManyField 'Permission.content_types'
- db.delete_table('permissions_permission_content_types')
-
-
-
- models = {
- 'auth.group': {
- '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': "orm['auth.Permission']", 'blank': 'True'})
- },
- 'auth.permission': {
- 'Meta': {'unique_together': "(('content_type', 'codename'),)"},
- 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
- },
- 'auth.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', [], {'to': "orm['auth.Group']", 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
- 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
- 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
- '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', [], {'to': "orm['auth.Permission']", 'blank': 'True'}),
- 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
- },
- 'contenttypes.contenttype': {
- 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"},
- 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- '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'})
- },
- 'permissions.objectpermission': {
- 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
- 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}),
- 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Role']", 'null': 'True', 'blank': 'True'})
- },
- 'permissions.objectpermissioninheritanceblock': {
- 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
- 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"})
- },
- 'permissions.permission': {
- 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
- 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'})
- },
- 'permissions.principalrolerelation': {
- 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
- 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}),
- 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Role']"}),
- 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
- },
- 'permissions.role': {
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'})
- }
- }
-
- complete_apps = ['permissions']
diff --git a/permissions/migrations/__init__.py b/permissions/migrations/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/permissions/models.py b/permissions/models.py
deleted file mode 100644
index 478fbdf9d..000000000
--- a/permissions/models.py
+++ /dev/null
@@ -1,190 +0,0 @@
-# django imports
-from django.db import models
-from django.contrib.auth.models import User
-from django.contrib.auth.models import Group
-from django.contrib.contenttypes import generic
-from django.contrib.contenttypes.models import ContentType
-from django.utils.translation import ugettext_lazy as _
-
-class Permission(models.Model):
- """A permission which can be granted to users/groups and objects.
-
- **Attributes:**
-
- name
- The unique name of the permission. This is displayed to users.
-
- codename
- The unique codename of the permission. This is used internal to
- identify a permission.
-
- content_types
- The content types for which the permission is active. This can be
- used to display only reasonable permissions for an object.
- """
- name = models.CharField(_(u"Name"), max_length=100, unique=True)
- codename = models.CharField(_(u"Codename"), max_length=100, unique=True)
- content_types = models.ManyToManyField(ContentType, verbose_name=_(u"Content Types"), blank=True, null=True, related_name="content_types")
-
- def __unicode__(self):
- return "%s (%s)" % (self.name, self.codename)
-
-class ObjectPermission(models.Model):
- """Grants permission for specific user/group and object.
-
- **Attributes:**
-
- role
- The role for which the permission is granted.
-
- permission
- The permission which is granted.
-
- content
- The object for which the permission is granted.
- """
- role = models.ForeignKey("Role", verbose_name=_(u"Role"), blank=True, null=True)
- permission = models.ForeignKey(Permission, verbose_name=_(u"Permission"))
-
- content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"))
- content_id = models.PositiveIntegerField(verbose_name=_(u"Content id"))
- content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id")
-
- def __unicode__(self):
- if self.role:
- principal = self.role
- else:
- principal = self.user
-
- return "%s / %s / %s - %s" % (self.permission.name, principal, self.content_type, self.content_id)
-
- def get_principal(self):
- """Returns the principal.
- """
- return self.user or self.group
-
- def set_principal(self, principal):
- """Sets the principal.
- """
- if isinstance(principal, User):
- self.user = principal
- else:
- self.group = principal
-
- principal = property(get_principal, set_principal)
-
-class ObjectPermissionInheritanceBlock(models.Model):
- """Blocks the inheritance for specific permission and object.
-
- **Attributes:**
-
- permission
- The permission for which inheritance is blocked.
-
- content
- The object for which the inheritance is blocked.
- """
- permission = models.ForeignKey(Permission, verbose_name=_(u"Permission"))
-
- content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"))
- content_id = models.PositiveIntegerField(verbose_name=_(u"Content id"))
- content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id")
-
- def __unicode__(self):
- return "%s / %s - %s" % (self.permission, self.content_type, self.content_id)
-
-class Role(models.Model):
- """A role gets permissions to do something. Principals (users and groups)
- can only get permissions via roles.
-
- **Attributes:**
-
- name
- The unique name of the role
- """
- name = models.CharField(max_length=100, unique=True)
-
- class Meta:
- ordering = ("name", )
-
- def __unicode__(self):
- return self.name
-
- def add_principal(self, principal, content=None):
- """Addes the given principal (user or group) ot the Role.
- """
- if isinstance(principal, User):
- PrincipalRoleRelation.objects.create(user=principal, role=self)
- else:
- PrincipalRoleRelation.objects.create(group=principal, role=self)
-
- def get_groups(self, content=None):
- """Returns all groups which has this role assigned. If content is given
- it returns also the local roles.
- """
- if content:
- ctype = ContentType.objects.get_for_model(content)
- prrs = PrincipalRoleRelation.objects.filter(role=self,
- content_id__in = (None, content.pk),
- content_type__in = (None, ctype)).exclude(group=None)
- else:
- prrs = PrincipalRoleRelation.objects.filter(role=self,
- content_id=None, content_type=None).exclude(group=None)
-
- return [prr.group for prr in prrs]
-
- def get_users(self, content=None):
- """Returns all users which has this role assigned. If content is given
- it returns also the local roles.
- """
- if content:
- ctype = ContentType.objects.get_for_model(content)
- prrs = PrincipalRoleRelation.objects.filter(role=self,
- content_id__in = (None, content.pk),
- content_type__in = (None, ctype)).exclude(user=None)
- else:
- prrs = PrincipalRoleRelation.objects.filter(role=self,
- content_id=None, content_type=None).exclude(user=None)
-
- return [prr.user for prr in prrs]
-
-class PrincipalRoleRelation(models.Model):
- """A role given to a principal (user or group). If a content object is
- given this is a local role, i.e. the principal has this role only for this
- content object. Otherwise it is a global role, i.e. the principal has
- this role generally.
-
- user
- A user instance. Either a user xor a group needs to be given.
-
- group
- A group instance. Either a user xor a group needs to be given.
-
- role
- The role which is given to the principal for content.
-
- content
- The content object which gets the local role (optional).
- """
- user = models.ForeignKey(User, verbose_name=_(u"User"), blank=True, null=True)
- group = models.ForeignKey(Group, verbose_name=_(u"Group"), blank=True, null=True)
- role = models.ForeignKey(Role, verbose_name=_(u"Role"))
-
- content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"), blank=True, null=True)
- content_id = models.PositiveIntegerField(verbose_name=_(u"Content id"), blank=True, null=True)
- content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id")
-
- def get_principal(self):
- """Returns the principal.
- """
- return self.user or self.group
-
- def set_principal(self, principal):
- """Sets the principal.
- """
- if isinstance(principal, User):
- self.user = principal
- else:
- self.group = principal
-
- principal = property(get_principal, set_principal)
diff --git a/permissions/templatetags/.gitignore b/permissions/templatetags/.gitignore
deleted file mode 100644
index a74b07aee..000000000
--- a/permissions/templatetags/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/*.pyc
diff --git a/permissions/templatetags/__init__.py b/permissions/templatetags/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/permissions/templatetags/permissions_tags.py b/permissions/templatetags/permissions_tags.py
deleted file mode 100644
index 94116381f..000000000
--- a/permissions/templatetags/permissions_tags.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# django imports
-from django import template
-from django.core.exceptions import ImproperlyConfigured
-from django.contrib.auth.models import User, AnonymousUser
-
-import permissions.utils
-register = template.Library()
-
-class PermissionComparisonNode(template.Node):
- """Implements a node to provide an if current user has passed permission
- for current object.
- """
- @classmethod
- def handle_token(cls, parser, token):
- bits = token.contents.split()
- if len(bits) != 2:
- raise template.TemplateSyntaxError(
- "'%s' tag takes one argument" % bits[0])
- end_tag = 'endifhasperm'
- nodelist_true = parser.parse(('else', end_tag))
- token = parser.next_token()
- if token.contents == 'else': # there is an 'else' clause in the tag
- nodelist_false = parser.parse((end_tag,))
- parser.delete_first_token()
- else:
- nodelist_false = ""
-
- return cls(bits[1], nodelist_true, nodelist_false)
-
- def __init__(self, permission, nodelist_true, nodelist_false):
- self.permission = permission
- self.nodelist_true = nodelist_true
- self.nodelist_false = nodelist_false
-
- def render(self, context):
- obj = context.get("obj")
- request = context.get("request")
- if permissions.utils.has_permission(self.permission, request.user, obj):
- return self.nodelist_true.render(context)
- else:
- return self.nodelist_false
-
-@register.tag
-def ifhasperm(parser, token):
- """This function provides functionality for the 'ifhasperm' template tag.
- """
- return PermissionComparisonNode.handle_token(parser, token)
-
diff --git a/permissions/tests.py b/permissions/tests.py
deleted file mode 100644
index 91bbf51bd..000000000
--- a/permissions/tests.py
+++ /dev/null
@@ -1,783 +0,0 @@
-# django imports
-from django.contrib.flatpages.models import FlatPage
-from django.contrib.auth.models import Group
-from django.contrib.auth.models import User
-from django.conf import settings
-from django.core.urlresolvers import reverse
-from django.test import TestCase
-from django.test.client import Client
-
-# permissions imports
-from permissions.models import Permission
-from permissions.models import ObjectPermission
-from permissions.models import ObjectPermissionInheritanceBlock
-from permissions.models import Role
-
-import permissions.utils
-
-class BackendTestCase(TestCase):
- """
- """
- def setUp(self):
- """
- """
- settings.AUTHENTICATION_BACKENDS = (
- 'django.contrib.auth.backends.ModelBackend',
- 'permissions.backend.ObjectPermissionsBackend',
- )
-
- self.role_1 = permissions.utils.register_role("Role 1")
- self.user = User.objects.create(username="john")
- self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1")
- self.view = permissions.utils.register_permission("View", "view")
-
- # Add user to role
- self.role_1.add_principal(self.user)
-
- def test_has_perm(self):
- """Tests has perm of the backend.
- """
- result = self.user.has_perm(self.view, self.page_1)
- self.assertEqual(result, False)
-
- # assign view permission to role 1
- permissions.utils.grant_permission(self.page_1, self.role_1, self.view)
-
- result = self.user.has_perm("view", self.page_1)
- self.assertEqual(result, True)
-
-class RoleTestCase(TestCase):
- """
- """
- def setUp(self):
- """
- """
- self.role_1 = permissions.utils.register_role("Role 1")
- self.role_2 = permissions.utils.register_role("Role 2")
-
- self.user = User.objects.create(username="john")
- self.group = Group.objects.create(name="brights")
-
- self.user.groups.add(self.group)
-
- self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1")
- self.page_2 = FlatPage.objects.create(url="/page-1/", title="Page 2")
-
- def test_getter(self):
- """
- """
- result = permissions.utils.get_group(self.group.id)
- self.assertEqual(result, self.group)
-
- result = permissions.utils.get_group(42)
- self.assertEqual(result, None)
-
- result = permissions.utils.get_role(self.role_1.id)
- self.assertEqual(result, self.role_1)
-
- result = permissions.utils.get_role(42)
- self.assertEqual(result, None)
-
- result = permissions.utils.get_user(self.user.id)
- self.assertEqual(result, self.user)
-
- result = permissions.utils.get_user(42)
- self.assertEqual(result, None)
-
- def test_global_roles_user(self):
- """
- """
- # Add role 1
- result = permissions.utils.add_role(self.user, self.role_1)
- self.assertEqual(result, True)
-
- # Add role 1 again
- result = permissions.utils.add_role(self.user, self.role_1)
- self.assertEqual(result, False)
-
- result = permissions.utils.get_roles(self.user)
- self.assertEqual(result, [self.role_1])
-
- # Add role 2
- result = permissions.utils.add_role(self.user, self.role_2)
- self.assertEqual(result, True)
-
- result = permissions.utils.get_roles(self.user)
- self.assertEqual(result, [self.role_1, self.role_2])
-
- # Remove role 1
- result = permissions.utils.remove_role(self.user, self.role_1)
- self.assertEqual(result, True)
-
- # Remove role 1 again
- result = permissions.utils.remove_role(self.user, self.role_1)
- self.assertEqual(result, False)
-
- result = permissions.utils.get_roles(self.user)
- self.assertEqual(result, [self.role_2])
-
- # Remove role 2
- result = permissions.utils.remove_role(self.user, self.role_2)
- self.assertEqual(result, True)
-
- result = permissions.utils.get_roles(self.user)
- self.assertEqual(result, [])
-
- def test_global_roles_group(self):
- """
- """
- # Add role 1
- result = permissions.utils.add_role(self.group, self.role_1)
- self.assertEqual(result, True)
-
- # Add role 1 again
- result = permissions.utils.add_role(self.group, self.role_1)
- self.assertEqual(result, False)
-
- result = permissions.utils.get_roles(self.group)
- self.assertEqual(result, [self.role_1])
-
- # Add role 2
- result = permissions.utils.add_role(self.group, self.role_2)
- self.assertEqual(result, True)
-
- result = permissions.utils.get_roles(self.group)
- self.assertEqual(result, [self.role_1, self.role_2])
-
- # Remove role 1
- result = permissions.utils.remove_role(self.group, self.role_1)
- self.assertEqual(result, True)
-
- # Remove role 1 again
- result = permissions.utils.remove_role(self.group, self.role_1)
- self.assertEqual(result, False)
-
- result = permissions.utils.get_roles(self.group)
- self.assertEqual(result, [self.role_2])
-
- # Remove role 2
- result = permissions.utils.remove_role(self.group, self.role_2)
- self.assertEqual(result, True)
-
- result = permissions.utils.get_roles(self.group)
- self.assertEqual(result, [])
-
- def test_remove_roles_user(self):
- """
- """
- # Add role 1
- result = permissions.utils.add_role(self.user, self.role_1)
- self.assertEqual(result, True)
-
- # Add role 2
- result = permissions.utils.add_role(self.user, self.role_2)
- self.assertEqual(result, True)
-
- result = permissions.utils.get_roles(self.user)
- self.assertEqual(result, [self.role_1, self.role_2])
-
- # Remove roles
- result = permissions.utils.remove_roles(self.user)
- self.assertEqual(result, True)
-
- result = permissions.utils.get_roles(self.user)
- self.assertEqual(result, [])
-
- # Remove roles
- result = permissions.utils.remove_roles(self.user)
- self.assertEqual(result, False)
-
- def test_remove_roles_group(self):
- """
- """
- # Add role 1
- result = permissions.utils.add_role(self.group, self.role_1)
- self.assertEqual(result, True)
-
- # Add role 2
- result = permissions.utils.add_role(self.group, self.role_2)
- self.assertEqual(result, True)
-
- result = permissions.utils.get_roles(self.group)
- self.assertEqual(result, [self.role_1, self.role_2])
-
- # Remove roles
- result = permissions.utils.remove_roles(self.group)
- self.assertEqual(result, True)
-
- result = permissions.utils.get_roles(self.group)
- self.assertEqual(result, [])
-
- # Remove roles
- result = permissions.utils.remove_roles(self.group)
- self.assertEqual(result, False)
-
- def test_local_role_user(self):
- """
- """
- # Add local role to page 1
- result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1)
- self.assertEqual(result, True)
-
- # Again
- result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1)
- self.assertEqual(result, False)
-
- result = permissions.utils.get_local_roles(self.page_1, self.user)
- self.assertEqual(result, [self.role_1])
-
- # Add local role 2
- result = permissions.utils.add_local_role(self.page_1, self.user, self.role_2)
- self.assertEqual(result, True)
-
- result = permissions.utils.get_local_roles(self.page_1, self.user)
- self.assertEqual(result, [self.role_1, self.role_2])
-
- # Remove role 1
- result = permissions.utils.remove_local_role(self.page_1, self.user, self.role_1)
- self.assertEqual(result, True)
-
- # Remove role 1 again
- result = permissions.utils.remove_local_role(self.page_1, self.user, self.role_1)
- self.assertEqual(result, False)
-
- result = permissions.utils.get_local_roles(self.page_1, self.user)
- self.assertEqual(result, [self.role_2])
-
- # Remove role 2
- result = permissions.utils.remove_local_role(self.page_1, self.user, self.role_2)
- self.assertEqual(result, True)
-
- result = permissions.utils.get_local_roles(self.page_1, self.user)
- self.assertEqual(result, [])
-
- def test_local_role_group(self):
- """
- """
- # Add local role to page 1
- result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1)
- self.assertEqual(result, True)
-
- # Again
- result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1)
- self.assertEqual(result, False)
-
- result = permissions.utils.get_local_roles(self.page_1, self.group)
- self.assertEqual(result, [self.role_1])
-
- # Add local role 2
- result = permissions.utils.add_local_role(self.page_1, self.group, self.role_2)
- self.assertEqual(result, True)
-
- result = permissions.utils.get_local_roles(self.page_1, self.group)
- self.assertEqual(result, [self.role_1, self.role_2])
-
- # Remove role 1
- result = permissions.utils.remove_local_role(self.page_1, self.group, self.role_1)
- self.assertEqual(result, True)
-
- # Remove role 1 again
- result = permissions.utils.remove_local_role(self.page_1, self.group, self.role_1)
- self.assertEqual(result, False)
-
- result = permissions.utils.get_local_roles(self.page_1, self.group)
- self.assertEqual(result, [self.role_2])
-
- # Remove role 2
- result = permissions.utils.remove_local_role(self.page_1, self.group, self.role_2)
- self.assertEqual(result, True)
-
- result = permissions.utils.get_local_roles(self.page_1, self.group)
- self.assertEqual(result, [])
-
- def test_remove_local_roles_user(self):
- """
- """
- # Add local role to page 1
- result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1)
- self.assertEqual(result, True)
-
- # Add local role 2
- result = permissions.utils.add_local_role(self.page_1, self.user, self.role_2)
- self.assertEqual(result, True)
-
- result = permissions.utils.get_local_roles(self.page_1, self.user)
- self.assertEqual(result, [self.role_1, self.role_2])
-
- # Remove all local roles
- result = permissions.utils.remove_local_roles(self.page_1, self.user)
- self.assertEqual(result, True)
-
- result = permissions.utils.get_local_roles(self.page_1, self.user)
- self.assertEqual(result, [])
-
- # Remove all local roles again
- result = permissions.utils.remove_local_roles(self.page_1, self.user)
- self.assertEqual(result, False)
-
- def test_get_groups_1(self):
- """Tests global roles for groups.
- """
- result = self.role_1.get_groups()
- self.assertEqual(len(result), 0)
-
- result = permissions.utils.add_role(self.group, self.role_1)
- self.assertEqual(result, True)
-
- result = self.role_1.get_groups()
- self.assertEqual(result[0].name, "brights")
-
- # Add another group
- self.group_2 = Group.objects.create(name="atheists")
- result = permissions.utils.add_role(self.group_2, self.role_1)
-
- result = self.role_1.get_groups()
- self.assertEqual(result[0].name, "brights")
- self.assertEqual(result[1].name, "atheists")
- self.assertEqual(len(result), 2)
-
- # Add the role to an user
- result = permissions.utils.add_role(self.user, self.role_1)
- self.assertEqual(result, True)
-
- # This shouldn't have an effect on the result
- result = self.role_1.get_groups()
- self.assertEqual(result[0].name, "brights")
- self.assertEqual(result[1].name, "atheists")
- self.assertEqual(len(result), 2)
-
- def test_get_groups_2(self):
- """Tests local roles for groups.
- """
- result = self.role_1.get_groups(self.page_1)
- self.assertEqual(len(result), 0)
-
- result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1)
- self.assertEqual(result, True)
-
- result = self.role_1.get_groups(self.page_1)
- self.assertEqual(result[0].name, "brights")
-
- # Add another local group
- self.group_2 = Group.objects.create(name="atheists")
- result = permissions.utils.add_local_role(self.page_1, self.group_2, self.role_1)
-
- result = self.role_1.get_groups(self.page_1)
- self.assertEqual(result[0].name, "brights")
- self.assertEqual(result[1].name, "atheists")
-
- # A the global role to group
- result = permissions.utils.add_role(self.group, self.role_1)
- self.assertEqual(result, True)
-
- # Nontheless there are just two groups returned (and no duplicate)
- result = self.role_1.get_groups(self.page_1)
- self.assertEqual(result[0].name, "brights")
- self.assertEqual(result[1].name, "atheists")
- self.assertEqual(len(result), 2)
-
- # Andere there should one global role
- result = self.role_1.get_groups()
- self.assertEqual(result[0].name, "brights")
-
- # Add the role to an user
- result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1)
- self.assertEqual(result, True)
-
- # This shouldn't have an effect on the result
- result = self.role_1.get_groups(self.page_1)
- self.assertEqual(result[0].name, "brights")
- self.assertEqual(result[1].name, "atheists")
- self.assertEqual(len(result), 2)
-
- def test_get_users_1(self):
- """Tests global roles for users.
- """
- result = self.role_1.get_users()
- self.assertEqual(len(result), 0)
-
- result = permissions.utils.add_role(self.user, self.role_1)
- self.assertEqual(result, True)
-
- result = self.role_1.get_users()
- self.assertEqual(result[0].username, "john")
-
- # Add another role to an user
- self.user_2 = User.objects.create(username="jane")
- result = permissions.utils.add_role(self.user_2, self.role_1)
-
- result = self.role_1.get_users()
- self.assertEqual(result[0].username, "john")
- self.assertEqual(result[1].username, "jane")
- self.assertEqual(len(result), 2)
-
- # Add the role to an user
- result = permissions.utils.add_role(self.group, self.role_1)
- self.assertEqual(result, True)
-
- # This shouldn't have an effect on the result
- result = self.role_1.get_users()
- self.assertEqual(result[0].username, "john")
- self.assertEqual(result[1].username, "jane")
- self.assertEqual(len(result), 2)
-
- def test_get_users_2(self):
- """Tests local roles for users.
- """
- result = self.role_1.get_users(self.page_1)
- self.assertEqual(len(result), 0)
-
- result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1)
- self.assertEqual(result, True)
-
- result = self.role_1.get_users(self.page_1)
- self.assertEqual(result[0].username, "john")
-
- # Add another local role to an user
- self.user_2 = User.objects.create(username="jane")
- result = permissions.utils.add_local_role(self.page_1, self.user_2, self.role_1)
-
- result = self.role_1.get_users(self.page_1)
- self.assertEqual(result[0].username, "john")
- self.assertEqual(result[1].username, "jane")
-
- # A the global role to user
- result = permissions.utils.add_role(self.user, self.role_1)
- self.assertEqual(result, True)
-
- # Nontheless there are just two users returned (and no duplicate)
- result = self.role_1.get_users(self.page_1)
- self.assertEqual(result[0].username, "john")
- self.assertEqual(result[1].username, "jane")
- self.assertEqual(len(result), 2)
-
- # Andere there should one user for the global role
- result = self.role_1.get_users()
- self.assertEqual(result[0].username, "john")
-
- # Add the role to an group
- result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1)
- self.assertEqual(result, True)
-
- # This shouldn't have an effect on the result
- result = self.role_1.get_users(self.page_1)
- self.assertEqual(result[0].username, "john")
- self.assertEqual(result[1].username, "jane")
- self.assertEqual(len(result), 2)
-
-class PermissionTestCase(TestCase):
- """
- """
- def setUp(self):
- """
- """
- self.role_1 = permissions.utils.register_role("Role 1")
- self.role_2 = permissions.utils.register_role("Role 2")
-
- self.user = User.objects.create(username="john")
- permissions.utils.add_role(self.user, self.role_1)
- self.user.save()
-
- self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1")
- self.page_2 = FlatPage.objects.create(url="/page-1/", title="Page 2")
-
- self.permission = permissions.utils.register_permission("View", "view")
-
- def test_add_permissions(self):
- """
- """
- # Add per object
- result = permissions.utils.grant_permission(self.page_1, self.role_1, self.permission)
- self.assertEqual(result, True)
-
- # Add per codename
- result = permissions.utils.grant_permission(self.page_1, self.role_1, "view")
- self.assertEqual(result, True)
-
- # Add ermission which does not exist
- result = permissions.utils.grant_permission(self.page_1, self.role_1, "hurz")
- self.assertEqual(result, False)
-
- def test_remove_permission(self):
- """
- """
- # Add
- result = permissions.utils.grant_permission(self.page_1, self.role_1, "view")
- self.assertEqual(result, True)
-
- # Remove
- result = permissions.utils.remove_permission(self.page_1, self.role_1, "view")
- self.assertEqual(result, True)
-
- # Remove again
- result = permissions.utils.remove_permission(self.page_1, self.role_1, "view")
- self.assertEqual(result, False)
-
- def test_has_permission_role(self):
- """
- """
- result = permissions.utils.has_permission(self.page_1, self.user, "view")
- self.assertEqual(result, False)
-
- result = permissions.utils.grant_permission(self.page_1, self.role_1, self.permission)
- self.assertEqual(result, True)
-
- result = permissions.utils.has_permission(self.page_1, self.user, "view")
- self.assertEqual(result, True)
-
- result = permissions.utils.remove_permission(self.page_1, self.role_1, "view")
- self.assertEqual(result, True)
-
- result = permissions.utils.has_permission(self.page_1, self.user, "view")
- self.assertEqual(result, False)
-
- def test_has_permission_owner(self):
- """
- """
- creator = User.objects.create(username="jane")
-
- result = permissions.utils.has_permission(self.page_1, creator, "view")
- self.assertEqual(result, False)
-
- owner = permissions.utils.register_role("Owner")
- permissions.utils.grant_permission(self.page_1, owner, "view")
-
- result = permissions.utils.has_permission(self.page_1, creator, "view", [owner])
- self.assertEqual(result, True)
-
- def test_local_role(self):
- """
- """
- result = permissions.utils.has_permission(self.page_1, self.user, "view")
- self.assertEqual(result, False)
-
- permissions.utils.grant_permission(self.page_1, self.role_2, self.permission)
- permissions.utils.add_local_role(self.page_1, self.user, self.role_2)
-
- result = permissions.utils.has_permission(self.page_1, self.user, "view")
- self.assertEqual(result, True)
-
- def test_ineritance(self):
- """
- """
- result = permissions.utils.is_inherited(self.page_1, "view")
- self.assertEqual(result, True)
-
- # per permission
- permissions.utils.add_inheritance_block(self.page_1, self.permission)
-
- result = permissions.utils.is_inherited(self.page_1, "view")
- self.assertEqual(result, False)
-
- permissions.utils.remove_inheritance_block(self.page_1, self.permission)
-
- result = permissions.utils.is_inherited(self.page_1, "view")
- self.assertEqual(result, True)
-
- # per codename
- permissions.utils.add_inheritance_block(self.page_1, "view")
-
- result = permissions.utils.is_inherited(self.page_1, "view")
- self.assertEqual(result, False)
-
- permissions.utils.remove_inheritance_block(self.page_1, "view")
-
- result = permissions.utils.is_inherited(self.page_1, "view")
- self.assertEqual(result, True)
-
- def test_unicode(self):
- """
- """
- # Permission
- self.assertEqual(self.permission.__unicode__(), "View (view)")
-
- # ObjectPermission
- permissions.utils.grant_permission(self.page_1, self.role_1, self.permission)
- opr = ObjectPermission.objects.get(permission=self.permission, role=self.role_1)
- self.assertEqual(opr.__unicode__(), "View / Role 1 / flat page - 1")
-
- # ObjectPermissionInheritanceBlock
- permissions.utils.add_inheritance_block(self.page_1, self.permission)
- opb = ObjectPermissionInheritanceBlock.objects.get(permission=self.permission)
-
- self.assertEqual(opb.__unicode__(), "View (view) / flat page - 1")
-
- def test_reset(self):
- """
- """
- result = permissions.utils.grant_permission(self.page_1, self.role_1, "view")
- self.assertEqual(result, True)
-
- result = permissions.utils.has_permission(self.page_1, self.user, "view")
- self.assertEqual(result, True)
-
- permissions.utils.add_inheritance_block(self.page_1, "view")
-
- result = permissions.utils.is_inherited(self.page_1, "view")
- self.assertEqual(result, False)
-
- permissions.utils.reset(self.page_1)
-
- result = permissions.utils.has_permission(self.page_1, self.user, "view")
- self.assertEqual(result, False)
-
- result = permissions.utils.is_inherited(self.page_1, "view")
- self.assertEqual(result, True)
-
- permissions.utils.reset(self.page_1)
-
-class RegistrationTestCase(TestCase):
- """Tests the registration of different components.
- """
- def test_group(self):
- """Tests registering/unregistering of a group.
- """
- # Register a group
- result = permissions.utils.register_group("Brights")
- self.failUnless(isinstance(result, Group))
-
- # It's there
- group = Group.objects.get(name="Brights")
- self.assertEqual(group.name, "Brights")
-
- # Trying to register another group with same name
- result = permissions.utils.register_group("Brights")
- self.assertEqual(result, False)
-
- group = Group.objects.get(name="Brights")
- self.assertEqual(group.name, "Brights")
-
- # Unregister the group
- result = permissions.utils.unregister_group("Brights")
- self.assertEqual(result, True)
-
- # It's not there anymore
- self.assertRaises(Group.DoesNotExist, Group.objects.get, name="Brights")
-
- # Trying to unregister the group again
- result = permissions.utils.unregister_group("Brights")
- self.assertEqual(result, False)
-
- def test_role(self):
- """Tests registering/unregistering of a role.
- """
- # Register a role
- result = permissions.utils.register_role("Editor")
- self.failUnless(isinstance(result, Role))
-
- # It's there
- role = Role.objects.get(name="Editor")
- self.assertEqual(role.name, "Editor")
-
- # Trying to register another role with same name
- result = permissions.utils.register_role("Editor")
- self.assertEqual(result, False)
-
- role = Role.objects.get(name="Editor")
- self.assertEqual(role.name, "Editor")
-
- # Unregister the role
- result = permissions.utils.unregister_role("Editor")
- self.assertEqual(result, True)
-
- # It's not there anymore
- self.assertRaises(Role.DoesNotExist, Role.objects.get, name="Editor")
-
- # Trying to unregister the role again
- result = permissions.utils.unregister_role("Editor")
- self.assertEqual(result, False)
-
- def test_permission(self):
- """Tests registering/unregistering of a permission.
- """
- # Register a permission
- result = permissions.utils.register_permission("Change", "change")
- self.failUnless(isinstance(result, Permission))
-
- # Is it there?
- p = Permission.objects.get(codename="change")
- self.assertEqual(p.name, "Change")
-
- # Register a permission with the same codename
- result = permissions.utils.register_permission("Change2", "change")
- self.assertEqual(result, False)
-
- # Is it there?
- p = Permission.objects.get(codename="change")
- self.assertEqual(p.name, "Change")
-
- # Register a permission with the same name
- result = permissions.utils.register_permission("Change", "change2")
- self.assertEqual(result, False)
-
- # Is it there?
- p = Permission.objects.get(codename="change")
- self.assertEqual(p.name, "Change")
-
- # Unregister the permission
- result = permissions.utils.unregister_permission("change")
- self.assertEqual(result, True)
-
- # Is it not there anymore?
- self.assertRaises(Permission.DoesNotExist, Permission.objects.get, codename="change")
-
- # Unregister the permission again
- result = permissions.utils.unregister_permission("change")
- self.assertEqual(result, False)
-
-# django imports
-from django.core.handlers.wsgi import WSGIRequest
-from django.contrib.auth.models import User
-from django.contrib.sessions.backends.file import SessionStore
-from django.test.client import Client
-
-# Taken from "http://www.djangosnippets.org/snippets/963/"
-class RequestFactory(Client):
- """
- Class that lets you create mock Request objects for use in testing.
-
- Usage:
-
- rf = RequestFactory()
- get_request = rf.get('/hello/')
- post_request = rf.post('/submit/', {'foo': 'bar'})
-
- This class re-uses the django.test.client.Client interface, docs here:
- http://www.djangoproject.com/documentation/testing/#the-test-client
-
- Once you have a request object you can pass it to any view function,
- just as if that view had been hooked up using a URLconf.
-
- """
- def request(self, **request):
- """
- Similar to parent class, but returns the request object as soon as it
- has created it.
- """
- environ = {
- 'HTTP_COOKIE': self.cookies,
- 'PATH_INFO': '/',
- 'QUERY_STRING': '',
- 'REQUEST_METHOD': 'GET',
- 'SCRIPT_NAME': '',
- 'SERVER_NAME': 'testserver',
- 'SERVER_PORT': 80,
- 'SERVER_PROTOCOL': 'HTTP/1.1',
- }
- environ.update(self.defaults)
- environ.update(request)
- return WSGIRequest(environ)
-
-def create_request():
- """
- """
- rf = RequestFactory()
- request = rf.get('/')
- request.session = SessionStore()
-
- user = User()
- user.is_superuser = True
- user.save()
- request.user = user
-
- return request
\ No newline at end of file
diff --git a/permissions/utils.py b/permissions/utils.py
deleted file mode 100644
index 06183e946..000000000
--- a/permissions/utils.py
+++ /dev/null
@@ -1,665 +0,0 @@
-# django imports
-from django.db import IntegrityError
-from django.db.models import Q
-from django.db import connection
-from django.contrib.auth.models import User
-from django.contrib.auth.models import Group
-from django.contrib.contenttypes.models import ContentType
-from django.core.cache import cache
-from django.core.exceptions import ObjectDoesNotExist
-
-# permissions imports
-from permissions.exceptions import Unauthorized
-from permissions.models import ObjectPermission
-from permissions.models import ObjectPermissionInheritanceBlock
-from permissions.models import Permission
-from permissions.models import PrincipalRoleRelation
-from permissions.models import Role
-
-
-# Roles ######################################################################
-
-def add_role(principal, role):
- """Adds a global role to a principal.
-
- **Parameters:**
-
- principal
- The principal (user or group) which gets the role added.
-
- role
- The role which is assigned.
- """
- if isinstance(principal, User):
- try:
- ppr = PrincipalRoleRelation.objects.get(user=principal, role=role, content_id=None, content_type=None)
- except PrincipalRoleRelation.DoesNotExist:
- PrincipalRoleRelation.objects.create(user=principal, role=role)
- return True
- else:
- try:
- ppr = PrincipalRoleRelation.objects.get(group=principal, role=role, content_id=None, content_type=None)
- except PrincipalRoleRelation.DoesNotExist:
- PrincipalRoleRelation.objects.create(group=principal, role=role)
- return True
-
- return False
-
-def add_local_role(obj, principal, role):
- """Adds a local role to a principal.
-
- **Parameters:**
-
- obj
- The object for which the principal gets the role.
-
- principal
- The principal (user or group) which gets the role.
-
- role
- The role which is assigned.
- """
- ctype = ContentType.objects.get_for_model(obj)
- if isinstance(principal, User):
- try:
- ppr = PrincipalRoleRelation.objects.get(user=principal, role=role, content_id=obj.pk, content_type=ctype)
- except PrincipalRoleRelation.DoesNotExist:
- PrincipalRoleRelation.objects.create(user=principal, role=role, content=obj)
- return True
- else:
- try:
- ppr = PrincipalRoleRelation.objects.get(group=principal, role=role, content_id=obj.pk, content_type=ctype)
- except PrincipalRoleRelation.DoesNotExist:
- PrincipalRoleRelation.objects.create(group=principal, role=role, content=obj)
- return True
-
- return False
-
-def remove_role(principal, role):
- """Removes role from passed principal.
-
- **Parameters:**
-
- principal
- The principal (user or group) from which the role is removed.
-
- role
- The role which is removed.
- """
- try:
- if isinstance(principal, User):
- ppr = PrincipalRoleRelation.objects.get(
- user=principal, role=role, content_id=None, content_type=None)
- else:
- ppr = PrincipalRoleRelation.objects.get(
- group=principal, role=role, content_id=None, content_type=None)
-
- except PrincipalRoleRelation.DoesNotExist:
- return False
- else:
- ppr.delete()
-
- return True
-
-def remove_local_role(obj, principal, role):
- """Removes role from passed object and principle.
-
- **Parameters:**
-
- obj
- The object from which the role is removed.
-
- principal
- The principal (user or group) from which the role is removed.
-
- role
- The role which is removed.
- """
- try:
- ctype = ContentType.objects.get_for_model(obj)
-
- if isinstance(principal, User):
- ppr = PrincipalRoleRelation.objects.get(
- user=principal, role=role, content_id=obj.pk, content_type=ctype)
- else:
- ppr = PrincipalRoleRelation.objects.get(
- group=principal, role=role, content_id=obj.pk, content_type=ctype)
-
- except PrincipalRoleRelation.DoesNotExist:
- return False
- else:
- ppr.delete()
-
- return True
-
-def remove_roles(principal):
- """Removes all roles passed principal (user or group).
-
- **Parameters:**
-
- principal
- The principal (user or group) from which all roles are removed.
- """
- if isinstance(principal, User):
- ppr = PrincipalRoleRelation.objects.filter(
- user=principal, content_id=None, content_type=None)
- else:
- ppr = PrincipalRoleRelation.objects.filter(
- group=principal, content_id=None, content_type=None)
-
- if ppr:
- ppr.delete()
- return True
- else:
- return False
-
-def remove_local_roles(obj, principal):
- """Removes all local roles from passed object and principal (user or
- group).
-
- **Parameters:**
-
- obj
- The object from which the roles are removed.
-
- principal
- The principal (user or group) from which the roles are removed.
- """
- ctype = ContentType.objects.get_for_model(obj)
-
- if isinstance(principal, User):
- ppr = PrincipalRoleRelation.objects.filter(
- user=principal, content_id=obj.pk, content_type=ctype)
- else:
- ppr = PrincipalRoleRelation.objects.filter(
- group=principal, content_id=obj.pk, content_type=ctype)
-
- if ppr:
- ppr.delete()
- return True
- else:
- return False
-
-def get_roles(user, obj=None):
- """Returns *all* roles of the passed user.
-
- This takes direct roles and roles via the user's groups into account.
-
- If an object is passed local roles will also added. Then all local roles
- from all ancestors and all user's groups are also taken into account.
-
- This is the method to use if one want to know whether the passed user
- has a role in general (for the passed object).
-
- **Parameters:**
-
- user
- The user for which the roles are returned.
-
- obj
- The object for which local roles will returned.
-
- """
- roles = []
- groups = user.groups.all()
- groups_ids_str = ", ".join([str(g.id) for g in groups])
-
- # Gobal roles for user and the user's groups
- cursor = connection.cursor()
- cursor.execute("""SELECT role_id
- FROM permissions_principalrolerelation
- WHERE (user_id=%s OR group_id IN (%s))
- AND content_id is Null""" % (user.id, groups_ids_str))
-
- for row in cursor.fetchall():
- roles.append(row[0])
-
- # Local roles for user and the user's groups and all ancestors of the
- # passed object.
- while obj:
- ctype = ContentType.objects.get_for_model(obj)
- cursor.execute("""SELECT role_id
- FROM permissions_principalrolerelation
- WHERE (user_id='%s' OR group_id IN (%s))
- AND content_id='%s'
- AND content_type_id='%s'""" % (user.id, groups_ids_str, obj.pk, ctype.id))
-
- for row in cursor.fetchall():
- roles.append(row[0])
-
- try:
- obj = obj.get_parent_for_permissions()
- except AttributeError:
- obj = None
-
- return roles
-
-def get_global_roles(principal):
- """Returns *direct* global roles of passed principal (user or group).
- """
- if isinstance(principal, User):
- return [prr.role for prr in PrincipalRoleRelation.objects.filter(
- user=principal, content_id=None, content_type=None)]
- else:
- if isinstance(principal, Group):
- principal = (principal,)
- return [prr.role for prr in PrincipalRoleRelation.objects.filter(
- group__in=principal, content_id=None, content_type=None)]
-
-def get_local_roles(obj, principal):
- """Returns *direct* local roles for passed principal and content object.
- """
- ctype = ContentType.objects.get_for_model(obj)
-
- if isinstance(principal, User):
- return [prr.role for prr in PrincipalRoleRelation.objects.filter(
- user=principal, content_id=obj.pk, content_type=ctype)]
- else:
- return [prr.role for prr in PrincipalRoleRelation.objects.filter(
- group=principal, content_id=obj.pk, content_type=ctype)]
-
-# Permissions ################################################################
-
-def check_permission(obj, user, codename, roles=None):
- """Checks whether passed user has passed permission for passed obj.
-
- **Parameters:**
-
- obj
- The object for which the permission should be checked.
-
- codename
- The permission's codename which should be checked.
-
- user
- The user for which the permission should be checked.
-
- roles
- If given these roles will be assigned to the user temporarily before
- the permissions are checked.
- """
- if not has_permission(obj, user, codename):
- raise Unauthorized("User '%s' doesn't have permission '%s' for object '%s' (%s)"
- % (user, codename, obj.slug, obj.__class__.__name__))
-
-def grant_permission(obj, role, permission):
- """Grants passed permission to passed role. Returns True if the permission
- was able to be added, otherwise False.
-
- **Parameters:**
-
- obj
- The content object for which the permission should be granted.
-
- role
- The role for which the permission should be granted.
-
- permission
- The permission which should be granted. Either a permission
- object or the codename of a permission.
- """
- if not isinstance(permission, Permission):
- try:
- permission = Permission.objects.get(codename = permission)
- except Permission.DoesNotExist:
- return False
-
- ct = ContentType.objects.get_for_model(obj)
- try:
- ObjectPermission.objects.get(role=role, content_type = ct, content_id=obj.pk, permission=permission)
- except ObjectPermission.DoesNotExist:
- ObjectPermission.objects.create(role=role, content=obj, permission=permission)
-
- return True
-
-def remove_permission(obj, role, permission):
- """Removes passed permission from passed role and object. Returns True if
- the permission has been removed.
-
- **Parameters:**
-
- obj
- The content object for which a permission should be removed.
-
- role
- The role for which a permission should be removed.
-
- permission
- The permission which should be removed. Either a permission object
- or the codename of a permission.
- """
- if not isinstance(permission, Permission):
- try:
- permission = Permission.objects.get(codename = permission)
- except Permission.DoesNotExist:
- return False
-
- ct = ContentType.objects.get_for_model(obj)
-
- try:
- op = ObjectPermission.objects.get(role=role, content_type = ct, content_id=obj.pk, permission = permission)
- except ObjectPermission.DoesNotExist:
- return False
-
- op.delete()
- return True
-
-def has_permission(obj, user, codename, roles=None):
- """Checks whether the passed user has passed permission for passed object.
-
- **Parameters:**
-
- obj
- The object for which the permission should be checked.
-
- codename
- The permission's codename which should be checked.
-
- request
- The current request.
-
- roles
- If given these roles will be assigned to the user temporarily before
- the permissions are checked.
- """
- cache_key = "%s-%s-%s" % (obj.content_type, obj.pk, codename)
- result = _get_cached_permission(user, cache_key)
- if result is not None:
- return result
-
- if roles is None:
- roles = []
-
- if user.is_superuser:
- return True
-
- if not user.is_anonymous():
- roles.extend(get_roles(user, obj))
-
- ct = ContentType.objects.get_for_model(obj)
-
- result = False
- while obj is not None:
- p = ObjectPermission.objects.filter(
- content_type=ct, content_id=obj.pk, role__in=roles, permission__codename = codename).values("id")
-
- if len(p) > 0:
- result = True
- break
-
- if is_inherited(obj, codename) == False:
- result = False
- break
-
- try:
- obj = obj.get_parent_for_permissions()
- ct = ContentType.objects.get_for_model(obj)
- except AttributeError:
- result = False
- break
-
- _cache_permission(user, cache_key, result)
- return result
-
-# Inheritance ################################################################
-
-def add_inheritance_block(obj, permission):
- """Adds an inheritance for the passed permission on the passed obj.
-
- **Parameters:**
-
- permission
- The permission for which an inheritance block should be added.
- Either a permission object or the codename of a permission.
- obj
- The content object for which an inheritance block should be added.
- """
- if not isinstance(permission, Permission):
- try:
- permission = Permission.objects.get(codename = permission)
- except Permission.DoesNotExist:
- return False
-
- ct = ContentType.objects.get_for_model(obj)
- try:
- ObjectPermissionInheritanceBlock.objects.get(content_type = ct, content_id=obj.pk, permission=permission)
- except ObjectPermissionInheritanceBlock.DoesNotExist:
- try:
- result = ObjectPermissionInheritanceBlock.objects.create(content=obj, permission=permission)
- except IntegrityError:
- return False
- return True
-
-def remove_inheritance_block(obj, permission):
- """Removes a inheritance block for the passed permission from the passed
- object.
-
- **Parameters:**
-
- obj
- The content object for which an inheritance block should be added.
-
- permission
- The permission for which an inheritance block should be removed.
- Either a permission object or the codename of a permission.
- """
- if not isinstance(permission, Permission):
- try:
- permission = Permission.objects.get(codename = permission)
- except Permission.DoesNotExist:
- return False
-
- ct = ContentType.objects.get_for_model(obj)
- try:
- opi = ObjectPermissionInheritanceBlock.objects.get(content_type = ct, content_id=obj.pk, permission=permission)
- except ObjectPermissionInheritanceBlock.DoesNotExist:
- return False
-
- opi.delete()
- return True
-
-def is_inherited(obj, codename):
- """Returns True if the passed permission is inherited for passed object.
-
- **Parameters:**
-
- obj
- The content object for which the permission should be checked.
-
- codename
- The permission which should be checked. Must be the codename of
- the permission.
- """
- ct = ContentType.objects.get_for_model(obj)
- try:
- ObjectPermissionInheritanceBlock.objects.get(
- content_type=ct, content_id=obj.pk, permission__codename = codename)
- except ObjectDoesNotExist:
- return True
- else:
- return False
-
-def get_group(id):
- """Returns the group with passed id or None.
- """
- try:
- return Group.objects.get(pk=id)
- except Group.DoesNotExist:
- return None
-
-def get_role(id):
- """Returns the role with passed id or None.
- """
- try:
- return Role.objects.get(pk=id)
- except Role.DoesNotExist:
- return None
-
-def get_user(id):
- """Returns the user with passed id or None.
- """
- try:
- return User.objects.get(pk=id)
- except User.DoesNotExist:
- return None
-
-def has_group(user, group):
- """Returns True if passed user has passed group.
- """
- if isinstance(group, str):
- group = Group.objects.get(name=group)
-
- return group in user.groups.all()
-
-def reset(obj):
- """Resets all permissions and inheritance blocks of passed object.
- """
- ctype = ContentType.objects.get_for_model(obj)
- ObjectPermissionInheritanceBlock.objects.filter(content_id=obj.pk, content_type=ctype).delete()
- ObjectPermission.objects.filter(content_id=obj.pk, content_type=ctype).delete()
-
-# Registering ################################################################
-
-def register_permission(name, codename, ctypes=[]):
- """Registers a permission to the framework. Returns the permission if the
- registration was successfully, otherwise False.
-
- **Parameters:**
-
- name
- The unique name of the permission. This is displayed to the
- customer.
- codename
- The unique codename of the permission. This is used internally to
- identify the permission.
- content_types
- The content type for which the permission is active. This can be
- used to display only reasonable permissions for an object. This
- must be a Django ContentType
- """
- try:
- p = Permission.objects.create(name=name, codename=codename)
-
- ctypes = [ContentType.objects.get_for_model(ctype) for ctype in ctypes]
- if ctypes:
- p.content_types = ctypes
- p.save()
- except IntegrityError:
- return False
- return p
-
-def unregister_permission(codename):
- """Unregisters a permission from the framework
-
- **Parameters:**
-
- codename
- The unique codename of the permission.
- """
- try:
- permission = Permission.objects.get(codename=codename)
- except Permission.DoesNotExist:
- return False
- permission.delete()
- return True
-
-def register_role(name):
- """Registers a role with passed name to the framework. Returns the new
- role if the registration was successfully, otherwise False.
-
- **Parameters:**
-
- name
- The unique role name.
- """
- try:
- role = Role.objects.create(name=name)
- except IntegrityError:
- return False
- return role
-
-def unregister_role(name):
- """Unregisters the role with passed name.
-
- **Parameters:**
-
- name
- The unique role name.
- """
- try:
- role = Role.objects.get(name=name)
- except Role.DoesNotExist:
- return False
-
- role.delete()
- return True
-
-def register_group(name):
- """Registers a group with passed name to the framework. Returns the new
- group if the registration was successfully, otherwise False.
-
- Actually this creates just a default Django Group.
-
- **Parameters:**
-
- name
- The unique group name.
- """
- try:
- group = Group.objects.create(name=name)
- except IntegrityError:
- return False
- return group
-
-def unregister_group(name):
- """Unregisters the group with passed name. Returns True if the
- unregistration was succesfull otherwise False.
-
- Actually this deletes just a default Django Group.
-
- **Parameters:**
-
- name
- The unique role name.
- """
- try:
- group = Group.objects.get(name=name)
- except Group.DoesNotExist:
- return False
-
- group.delete()
- return True
-
-def _cache_permission(user, cache_key, data):
- """Stores the passed data on the passed user object.
-
- **Parameters:**
-
- user
- The user on which the data is stored.
-
- cache_key
- The key under which the data is stored.
-
- data
- The data which is stored.
- """
- if not getattr(user, "permissions", None):
- user.permissions = {}
- user.permissions[cache_key] = data
-
-def _get_cached_permission(user, cache_key):
- """Returns the stored data from passed user object for passed cache_key.
-
- **Parameters:**
-
- user
- The user from which the data is retrieved.
-
- cache_key
- The key under which the data is stored.
-
- """
- permissions = getattr(user, "permissions", None)
- if permissions:
- return user.permissions.get(cache_key, None)
diff --git a/static/css/base2.css b/static/css/base2.css
index 53cec6094..520c1efb1 100644
--- a/static/css/base2.css
+++ b/static/css/base2.css
@@ -76,6 +76,7 @@ a img { border: 0; }
.ietf-navset .selected { font-weight:bold; padding: 0 3px; }
.ietf-navset a, .ietf-navset a:visited { color: white; padding:0 3px; }
+.ietf-navset .actions { margin-top: 0.5em; font-style: italic; font-size: 90%; }
.ietf-ballot .left { background: #edf5ff; width:160px; padding-left: 10px; }
.ietf-ballot .right { padding-left: 15px; padding-right:15px; width:610px;padding-top:0px;}
@@ -187,7 +188,7 @@ form table .help {
.color2 { color: #00ffff; }
.bgcolor1 { background-color: #ffb000; }
.bgcolor2 { background-color: #00ffff; }
-.square { width: 0.8ex; height: 0.8ex; margin: 0; padding: 0; display: inline-block; position: relative; top: 0.8ex; text-align: top;}
+.square { width: 0.8ex; height: 0.8ex; margin: 0; padding: 0; display: inline-block; position: relative; top: 0.8ex; }
.big { font-size: 109.5%; margin: 0; padding: 0; }
.large { font-size: 120%; margin: 0; padding: 0; }
.huge { font-size: 144%; margin: 0; padding: 0; }
@@ -237,6 +238,8 @@ li.error { margin: 0.5em; background-color: #f44; }
font-family: Arial, sans-serif;
}
+.errorlist a { color: #fff; }
+
.group-documents .search-results { margin-top: 1.5em; }
table.milestones td.due { vertical-align: top; width: 80px; }
@@ -374,6 +377,24 @@ span.fieldRequired {
background-color: #ffcc66;
}
+.state-help-icon {
+ display: inline-block;
+ margin-left: 0.2em;
+ padding: 0 0.2em;
+ font-weight: bold;
+ font-style: normal;
+ font-size: 90%;
+ color: #999;
+ background-color: #ddd;
+ text-decoration: none;
+}
+
+.state-help-icon:hover {
+ color: #eee;
+ background-color: #bbb;
+ transition-duration: 0.2s;
+}
+
/* js styles */
.js-info {
background-color: #FFDD88;
diff --git a/static/css/submit.css b/static/css/submit.css
new file mode 100644
index 000000000..052987d8c
--- /dev/null
+++ b/static/css/submit.css
@@ -0,0 +1,32 @@
+.ietf-navset {
+ background:#214197 url(/images/yui/sprite.png) repeat-x left -1400px;
+ color:white;
+ border:1px solid black;
+ padding:4px;
+}
+.ietf-navset .selected { font-weight:bold; padding: 0 3px; }
+.ietf-navset a, .ietf-navset a:visited { color: white; padding:0 3px; }
+.cutoff-warning { border: 1px dashed red; background-color: #ffeeaa; padding: 1em 2em; margin: 1em 0px; }
+.problem-reports-footer { font-style: italic; margin-top: 2em; }
+
+div.metadata-errors { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; margin: 1em 0px; max-width: 50em; }
+div.info-message-error { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; margin: 1em 0px; color: red; }
+div.info-message-warning { border: 1px solid orange; background-color: #ffffaa; padding: 5px 10px; margin: 1em 0px; color: black; }
+div.info-message-success { border: 1px solid green; background-color: #eeffbb; padding: 5px 10px; margin: 1em 0px; color: green; }
+
+table.metadata-table th { white-space: nowrap; font-weight: bold; }
+table.metadata-table th, table.metadata-table td { text-align: left; background: #ddddff; padding: 5px 10px; }
+table.metadata-table th.author { text-align: right; }
+table.metadata-table tr { vertical-align: top; }
+table.metadata-table tr.error td, table.metadata-table tr.error th { background-color: #ffaaaa; }
+table.metadata-table div.error-msg { color: red; }
+table.metadata-table td.author-button-help { max-width: 40em; }
+table.metadata-table input.name, table.metadata-table input.email { width: 25em; }
+
+pre.twopages { margin: 0px; }
+
+.idnits-popup .content,
+.twopages-popup .content { background-color: #fff; width: 55em; height: 30em; overflow: auto; padding: 1em; }
+a.idnits-trigger, a.twopages-trigger, a.idnits-trigger:visited, a.twopages-trigger:visited { color: #000; }
+
+table.history { max-width: 50em; }
diff --git a/static/js/draft-submit.js b/static/js/draft-submit.js
index fa9917143..0d51b657a 100644
--- a/static/js/draft-submit.js
+++ b/static/js/draft-submit.js
@@ -1,12 +1,60 @@
-$(function (){
+jQuery(function (){
// fill in submitter info when an author button is clicked
- $("input[type=button]").click(function () {
- var name = $(this).data("name");
- if (name == null) // backwards compatibility
- return;
- var email = $(this).data("email");
+ jQuery("input[type=button].author").click(function () {
+ var name = jQuery(this).data("name");
+ var email = jQuery(this).data("email");
- $(this).parents("form").find("input[name=name]").val(name || "");
- $(this).parents("form").find("input[name=email]").val(email || "");
+ jQuery(this).parents("form").find("input[name=submitter-name]").val(name || "");
+ jQuery(this).parents("form").find("input[name=submitter-email]").val(email || "");
+ });
+
+ jQuery("form").submit(function() {
+ if (this.submittedAlready)
+ return false;
+ else {
+ this.submittedAlready = true;
+ return true;
+ }
+ });
+
+ jQuery("form#cancel-submission").submit(function () {
+ return confirm("Cancel this submission?");
+ });
+
+ jQuery(".idnits-trigger").click(function (e) {
+ e.preventDefault();
+ var popup = jQuery(".idnits-popup").clone().show();
+ showModalBox(popup);
+ });
+
+ jQuery(".twopages-trigger").click(function (e) {
+ e.preventDefault();
+ var popup = jQuery(".twopages-popup").clone().show();
+ showModalBox(popup);
+ });
+
+ jQuery("form .add-author").click(function (e) {
+ e.preventDefault();
+
+ var table = jQuery("table.authors tbody");
+ var row = table.find("tr.empty").clone();
+
+ row.removeClass("empty");
+ var prefixInput = row.find('input[name=authors-prefix]');
+
+ // figure out a prefix
+ var i = 0, prefix;
+ do {
+ ++i;
+ prefix = prefixInput.val() + i;
+ }
+ while (table.find('input[name=authors-prefix][value="' + prefix +'"]').length > 0);
+
+ prefixInput.val(prefix);
+ row.find('input').not(prefixInput).each(function () {
+ this.name = prefix + "-" + this.name;
+ });
+
+ table.append(row);
});
});
diff --git a/static/js/iesg-discusses.js b/static/js/iesg-discusses.js
new file mode 100644
index 000000000..f3b98e151
--- /dev/null
+++ b/static/js/iesg-discusses.js
@@ -0,0 +1,33 @@
+jQuery(function () {
+ var radioButtons = jQuery('input[name="discusses"]');
+
+ var url = window.location.hash.replace("#", "");
+ if (url == "byme" || url == "forme")
+ radioButtons.filter("[value=" + url + "]").click();
+
+ function updateDisplayedRows() {
+ var rows = jQuery(".discuss-row");
+
+ var val = radioButtons.filter(":checked").val();
+
+ if (val == "all") {
+ rows.show();
+ if (window.location.hash)
+ window.location.hash = "";
+ }
+ else if (val == "forme" || val == "byme") {
+ console.log(rows.filter("." + val))
+ rows.filter("." + val).show();
+ rows.not("." + val).hide();
+ window.location.hash = val;
+ }
+
+ // odd/even are swapped because the jQuery filter is 0-indexed
+ rows.filter(":visible").filter(":even").removeClass("even").addClass("odd");
+ rows.filter(":visible").filter(":odd").removeClass("odd").addClass("even");
+ }
+
+ radioButtons.click(updateDisplayedRows);
+
+ updateDisplayedRows();
+});
diff --git a/static/secretariat/css/custom.css b/static/secretariat/css/custom.css
index 0205d85ff..07be32d83 100644
--- a/static/secretariat/css/custom.css
+++ b/static/secretariat/css/custom.css
@@ -661,6 +661,10 @@ ul.session-buttons {
list-style-type: circle;
}
+#telechat-sidebar ul.doc-list {
+ margin-bottom: 0.8em;
+}
+
ul.doc-list li {
list-style-type: circle;
}
@@ -685,6 +689,18 @@ ul.doc-list li {
font-size: 110%;
}
+#telechat-sidebar li.level3 + li.level2 {
+ margin-top: 1em;
+}
+
+#telechat-sidebar li.level3 + li.level1 {
+ margin-top: 1.5em;
+}
+
+#telechat-sidebar li div {
+ font-style: italic;
+}
+
#telechat-positions-table td {
text-align: center;
}
diff --git a/workflows/.gitignore b/workflows/.gitignore
deleted file mode 100644
index a74b07aee..000000000
--- a/workflows/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/*.pyc
diff --git a/workflows/__init__.py b/workflows/__init__.py
deleted file mode 100644
index 271167dbe..000000000
--- a/workflows/__init__.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# workflows imports
-import workflows.utils
-
-class WorkflowBase(object):
- """Mixin class to make objects workflow aware.
- """
- def get_workflow(self):
- """Returns the current workflow of the object.
- """
- return workflows.utils.get_workflow(self)
-
- def remove_workflow(self):
- """Removes the workflow from the object. After this function has been
- called the object has no *own* workflow anymore (it might have one via
- its content type).
-
- """
- return workflows.utils.remove_workflow_from_object(self)
-
- def set_workflow(self, workflow):
- """Sets the passed workflow to the object. This will set the local
- workflow for the object.
-
- If the object has already the given workflow nothing happens.
- Otherwise the object gets the passed workflow and the state is set to
- the workflow's initial state.
-
- **Parameters:**
-
- workflow
- The workflow which should be set to the object. Can be a Workflow
- instance or a string with the workflow name.
- obj
- The object which gets the passed workflow.
- """
- return workflows.utils.set_workflow_for_object(workflow)
-
- def get_state(self):
- """Returns the current workflow state of the object.
- """
- return workflows.utils.get_state(self)
-
- def set_state(self, state):
- """Sets the workflow state of the object.
- """
- return workflows.utils.set_state(self, state)
-
- def set_initial_state(self):
- """Sets the initial state of the current workflow to the object.
- """
- return self.set_state(self.get_workflow().initial_state)
-
- def get_allowed_transitions(self, user):
- """Returns allowed transitions for the current state.
- """
- return workflows.utils.get_allowed_transitions(self, user)
-
- def do_transition(self, transition, user):
- """Processes the passed transition (if allowed).
- """
- return workflows.utils.do_transition(self, transition, user)
\ No newline at end of file
diff --git a/workflows/locale/de/LC_MESSAGES/django.mo b/workflows/locale/de/LC_MESSAGES/django.mo
deleted file mode 100644
index e4512512f..000000000
Binary files a/workflows/locale/de/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/workflows/locale/de/LC_MESSAGES/django.po b/workflows/locale/de/LC_MESSAGES/django.po
deleted file mode 100644
index 2b97ddef5..000000000
--- a/workflows/locale/de/LC_MESSAGES/django.po
+++ /dev/null
@@ -1,60 +0,0 @@
-# German translations for django-workflows
-# Copyright (C) 2010 Kai Diefenbach
-# This file is distributed under the same license as the PACKAGE package.
-# Kai Diefenbach , 2010.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: 1.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-04-02 09:16+0200\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: LANGUAGE \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: models.py:98 models.py:199 models.py:237
-msgid "Name"
-msgstr "Name"
-
-#: models.py:200 models.py:238 models.py:285 models.py:307
-msgid "Workflow"
-msgstr "Arbeitsablauf"
-
-#: models.py:201
-msgid "Transitions"
-msgstr "Übergänge"
-
-#: models.py:239
-msgid "Destination"
-msgstr "Ziel"
-
-#: models.py:240
-msgid "Condition"
-msgstr "Kondition"
-
-#: models.py:241 models.py:350 models.py:373
-msgid "Permission"
-msgstr "Recht"
-
-#: models.py:258 models.py:282
-msgid "Content type"
-msgstr "Inhaltstyp"
-
-#: models.py:259 models.py:283
-msgid "Content id"
-msgstr "Inhalts-ID"
-
-#: models.py:261 models.py:349 models.py:372
-msgid "State"
-msgstr "Status"
-
-#: models.py:306
-msgid "Content Type"
-msgstr "Inhaltstyp"
-
-#: models.py:374
-msgid "Role"
-msgstr "Rolle"
diff --git a/workflows/migrations/.gitignore b/workflows/migrations/.gitignore
deleted file mode 100644
index a74b07aee..000000000
--- a/workflows/migrations/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/*.pyc
diff --git a/workflows/migrations/0001_initial.py b/workflows/migrations/0001_initial.py
deleted file mode 100644
index d12f59ff3..000000000
--- a/workflows/migrations/0001_initial.py
+++ /dev/null
@@ -1,225 +0,0 @@
-
-from south.db import db
-from django.db import models
-from workflows.models import *
-
-class Migration:
-
- def forwards(self, orm):
-
- # Adding model 'Workflow'
- db.create_table('workflows_workflow', (
- ('id', orm['workflows.Workflow:id']),
- ('name', orm['workflows.Workflow:name']),
- ('initial_state', orm['workflows.Workflow:initial_state']),
- ))
- db.send_create_signal('workflows', ['Workflow'])
-
- # Adding model 'StatePermissionRelation'
- db.create_table('workflows_statepermissionrelation', (
- ('id', orm['workflows.StatePermissionRelation:id']),
- ('state', orm['workflows.StatePermissionRelation:state']),
- ('permission', orm['workflows.StatePermissionRelation:permission']),
- ('role', orm['workflows.StatePermissionRelation:role']),
- ))
- db.send_create_signal('workflows', ['StatePermissionRelation'])
-
- # Adding model 'StateInheritanceBlock'
- db.create_table('workflows_stateinheritanceblock', (
- ('id', orm['workflows.StateInheritanceBlock:id']),
- ('state', orm['workflows.StateInheritanceBlock:state']),
- ('permission', orm['workflows.StateInheritanceBlock:permission']),
- ))
- db.send_create_signal('workflows', ['StateInheritanceBlock'])
-
- # Adding model 'WorkflowModelRelation'
- db.create_table('workflows_workflowmodelrelation', (
- ('id', orm['workflows.WorkflowModelRelation:id']),
- ('content_type', orm['workflows.WorkflowModelRelation:content_type']),
- ('workflow', orm['workflows.WorkflowModelRelation:workflow']),
- ))
- db.send_create_signal('workflows', ['WorkflowModelRelation'])
-
- # Adding model 'WorkflowPermissionRelation'
- db.create_table('workflows_workflowpermissionrelation', (
- ('id', orm['workflows.WorkflowPermissionRelation:id']),
- ('workflow', orm['workflows.WorkflowPermissionRelation:workflow']),
- ('permission', orm['workflows.WorkflowPermissionRelation:permission']),
- ))
- db.send_create_signal('workflows', ['WorkflowPermissionRelation'])
-
- # Adding model 'State'
- db.create_table('workflows_state', (
- ('id', orm['workflows.State:id']),
- ('name', orm['workflows.State:name']),
- ('workflow', orm['workflows.State:workflow']),
- ))
- db.send_create_signal('workflows', ['State'])
-
- # Adding model 'Transition'
- db.create_table('workflows_transition', (
- ('id', orm['workflows.Transition:id']),
- ('name', orm['workflows.Transition:name']),
- ('workflow', orm['workflows.Transition:workflow']),
- ('destination', orm['workflows.Transition:destination']),
- ('condition', orm['workflows.Transition:condition']),
- ('permission', orm['workflows.Transition:permission']),
- ))
- db.send_create_signal('workflows', ['Transition'])
-
- # Adding model 'WorkflowObjectRelation'
- db.create_table('workflows_workflowobjectrelation', (
- ('id', orm['workflows.WorkflowObjectRelation:id']),
- ('content_type', orm['workflows.WorkflowObjectRelation:content_type']),
- ('content_id', orm['workflows.WorkflowObjectRelation:content_id']),
- ('workflow', orm['workflows.WorkflowObjectRelation:workflow']),
- ))
- db.send_create_signal('workflows', ['WorkflowObjectRelation'])
-
- # Adding model 'StateObjectRelation'
- db.create_table('workflows_stateobjectrelation', (
- ('id', orm['workflows.StateObjectRelation:id']),
- ('content_type', orm['workflows.StateObjectRelation:content_type']),
- ('content_id', orm['workflows.StateObjectRelation:content_id']),
- ('state', orm['workflows.StateObjectRelation:state']),
- ))
- db.send_create_signal('workflows', ['StateObjectRelation'])
-
- # Adding ManyToManyField 'State.transitions'
- db.create_table('workflows_state_transitions', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('state', models.ForeignKey(orm.State, null=False)),
- ('transition', models.ForeignKey(orm.Transition, null=False))
- ))
-
- # Creating unique_together for [content_type, content_id] on WorkflowObjectRelation.
- db.create_unique('workflows_workflowobjectrelation', ['content_type_id', 'content_id'])
-
- # Creating unique_together for [content_type, content_id, state] on StateObjectRelation.
- db.create_unique('workflows_stateobjectrelation', ['content_type_id', 'content_id', 'state_id'])
-
- # Creating unique_together for [workflow, permission] on WorkflowPermissionRelation.
- db.create_unique('workflows_workflowpermissionrelation', ['workflow_id', 'permission_id'])
-
-
-
- def backwards(self, orm):
-
- # Deleting unique_together for [workflow, permission] on WorkflowPermissionRelation.
- db.delete_unique('workflows_workflowpermissionrelation', ['workflow_id', 'permission_id'])
-
- # Deleting unique_together for [content_type, content_id, state] on StateObjectRelation.
- db.delete_unique('workflows_stateobjectrelation', ['content_type_id', 'content_id', 'state_id'])
-
- # Deleting unique_together for [content_type, content_id] on WorkflowObjectRelation.
- db.delete_unique('workflows_workflowobjectrelation', ['content_type_id', 'content_id'])
-
- # Deleting model 'Workflow'
- db.delete_table('workflows_workflow')
-
- # Deleting model 'StatePermissionRelation'
- db.delete_table('workflows_statepermissionrelation')
-
- # Deleting model 'StateInheritanceBlock'
- db.delete_table('workflows_stateinheritanceblock')
-
- # Deleting model 'WorkflowModelRelation'
- db.delete_table('workflows_workflowmodelrelation')
-
- # Deleting model 'WorkflowPermissionRelation'
- db.delete_table('workflows_workflowpermissionrelation')
-
- # Deleting model 'State'
- db.delete_table('workflows_state')
-
- # Deleting model 'Transition'
- db.delete_table('workflows_transition')
-
- # Deleting model 'WorkflowObjectRelation'
- db.delete_table('workflows_workflowobjectrelation')
-
- # Deleting model 'StateObjectRelation'
- db.delete_table('workflows_stateobjectrelation')
-
- # Dropping ManyToManyField 'State.transitions'
- db.delete_table('workflows_state_transitions')
-
-
-
- models = {
- 'contenttypes.contenttype': {
- 'Meta': {'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'"},
- 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- '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'})
- },
- 'permissions.permission': {
- 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
- 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'})
- },
- 'permissions.role': {
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'})
- },
- 'workflows.state': {
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}),
- 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"})
- },
- 'workflows.stateinheritanceblock': {
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}),
- 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"})
- },
- 'workflows.stateobjectrelation': {
- 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"},
- 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
- 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"})
- },
- 'workflows.statepermissionrelation': {
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}),
- 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Role']"}),
- 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"})
- },
- 'workflows.transition': {
- 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
- 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}),
- 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"})
- },
- 'workflows.workflow': {
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
- 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'})
- },
- 'workflows.workflowmodelrelation': {
- 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'unique': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wmrs'", 'to': "orm['workflows.Workflow']"})
- },
- 'workflows.workflowobjectrelation': {
- 'Meta': {'unique_together': "(('content_type', 'content_id'),)"},
- 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
- 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wors'", 'to': "orm['workflows.Workflow']"})
- },
- 'workflows.workflowpermissionrelation': {
- 'Meta': {'unique_together': "(('workflow', 'permission'),)"},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'permission': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'to': "orm['permissions.Permission']"}),
- 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.Workflow']"})
- }
- }
-
- complete_apps = ['workflows']
diff --git a/workflows/migrations/__init__.py b/workflows/migrations/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/workflows/models.py b/workflows/models.py
deleted file mode 100644
index d7b5ac68d..000000000
--- a/workflows/models.py
+++ /dev/null
@@ -1,357 +0,0 @@
-from django.db import models
-
-# django imports
-from django.contrib.contenttypes import generic
-from django.contrib.contenttypes.models import ContentType
-from django.utils.translation import ugettext_lazy as _
-
-# permissions imports
-import permissions.utils
-from permissions.models import Permission
-from permissions.models import Role
-
-class Workflow(models.Model):
- """A workflow consists of a sequence of connected (through transitions)
- states. It can be assigned to a model and / or model instances. If a
- model instance has a workflow it takes precendence over the model's
- workflow.
-
- **Attributes:**
-
- model
- The model the workflow belongs to. Can be any
-
- content
- The object the workflow belongs to.
-
- name
- The unique name of the workflow.
-
- states
- The states of the workflow.
-
- initial_state
- The initial state the model / content gets if created.
-
- """
- name = models.CharField(_(u"Name"), max_length=100, unique=True)
- initial_state = models.ForeignKey("State", related_name="workflow_state", blank=True, null=True)
- permissions = models.ManyToManyField(Permission, symmetrical=False, through="WorkflowPermissionRelation")
-
- def __unicode__(self):
- return self.name
-
- def get_initial_state(self):
- """Returns the initial state of the workflow. Takes the first one if
- no state has been defined.
- """
- if self.initial_state:
- return self.initial_state
- else:
- try:
- return self.states.all()[0]
- except IndexError:
- return None
-
- def get_objects(self):
- """Returns all objects which have this workflow assigned. Globally
- (via the object's content type) or locally (via the object itself).
- """
- import workflows.utils
- objs = []
-
- # Get all objects whose content type has this workflow
- for wmr in WorkflowModelRelation.objects.filter(workflow=self):
- ctype = wmr.content_type
- # We have also to check whether the global workflow is not
- # overwritten.
- for obj in ctype.model_class().objects.all():
- if workflows.utils.get_workflow(obj) == self:
- objs.append(obj)
-
- # Get all objects whose local workflow this workflow
- for wor in WorkflowObjectRelation.objects.filter(workflow=self):
- if wor.content not in objs:
- objs.append(wor.content)
-
- return objs
-
- def set_to(self, ctype_or_obj):
- """Sets the workflow to passed content type or object. See the specific
- methods for more information.
-
- **Parameters:**
-
- ctype_or_obj
- The content type or the object to which the workflow should be set.
- Can be either a ContentType instance or any Django model instance.
- """
- if isinstance(ctype_or_obj, ContentType):
- return self.set_to_model(ctype_or_obj)
- else:
- return self.set_to_object(ctype_or_obj)
-
- def set_to_model(self, ctype):
- """Sets the workflow to the passed content type. If the content
- type has already an assigned workflow the workflow is overwritten.
-
- **Parameters:**
-
- ctype
- The content type which gets the workflow. Can be any Django model
- instance.
- """
- try:
- wor = WorkflowModelRelation.objects.get(content_type=ctype)
- except WorkflowModelRelation.DoesNotExist:
- WorkflowModelRelation.objects.create(content_type=ctype, workflow=self)
- else:
- wor.workflow = self
- wor.save()
-
- def set_to_object(self, obj):
- """Sets the workflow to the passed object.
-
- If the object has already the given workflow nothing happens. Otherwise
- the workflow is set to the objectthe state is set to the workflow's
- initial state.
-
- **Parameters:**
-
- obj
- The object which gets the workflow.
- """
- import workflows.utils
-
- ctype = ContentType.objects.get_for_model(obj)
- try:
- wor = WorkflowObjectRelation.objects.get(content_type=ctype, content_id=obj.pk)
- except WorkflowObjectRelation.DoesNotExist:
- WorkflowObjectRelation.objects.create(content = obj, workflow=self)
- workflows.utils.set_state(obj, self.initial_state)
- else:
- if wor.workflow != self:
- wor.workflow = self
- wor.save()
- workflows.utils.set_state(self.initial_state)
-
-class State(models.Model):
- """A certain state within workflow.
-
- **Attributes:**
-
- name
- The unique name of the state within the workflow.
-
- workflow
- The workflow to which the state belongs.
-
- transitions
- The transitions of a workflow state.
-
- """
- name = models.CharField(_(u"Name"), max_length=100)
- workflow = models.ForeignKey(Workflow, verbose_name=_(u"Workflow"), related_name="states")
- transitions = models.ManyToManyField("Transition", verbose_name=_(u"Transitions"), blank=True, null=True, related_name="states")
-
- class Meta:
- ordering = ("name", )
-
- def __unicode__(self):
- return "%s (%s)" % (self.name, self.workflow.name)
-
- def get_allowed_transitions(self, obj, user):
- """Returns all allowed transitions for passed object and user.
- """
- transitions = []
- for transition in self.transitions.all():
- permission = transition.permission
- if permission is None:
- transitions.append(transition)
- else:
- # First we try to get the objects specific has_permission
- # method (in case the object inherits from the PermissionBase
- # class).
- try:
- if obj.has_permission(user, permission.codename):
- transitions.append(transition)
- except AttributeError:
- if permissions.utils.has_permission(obj, user, permission.codename):
- transitions.append(transition)
- return transitions
-
-class Transition(models.Model):
- """A transition from a source to a destination state. The transition can
- be used from several source states.
-
- **Attributes:**
-
- name
- The unique name of the transition within a workflow.
-
- workflow
- The workflow to which the transition belongs. Must be a Workflow
- instance.
-
- destination
- The state after a transition has been processed. Must be a State
- instance.
-
- condition
- The condition when the transition is available. Can be any python
- expression.
-
- permission
- The necessary permission to process the transition. Must be a
- Permission instance.
-
- """
- name = models.CharField(_(u"Name"), max_length=100)
- workflow = models.ForeignKey(Workflow, verbose_name=_(u"Workflow"), related_name="transitions")
- destination = models.ForeignKey(State, verbose_name=_(u"Destination"), null=True, blank=True, related_name="destination_state")
- condition = models.CharField(_(u"Condition"), blank=True, max_length=100)
- permission = models.ForeignKey(Permission, verbose_name=_(u"Permission"), blank=True, null=True)
-
- def __unicode__(self):
- return self.name
-
-class StateObjectRelation(models.Model):
- """Stores the workflow state of an object.
-
- Provides a way to give any object a workflow state without changing the
- object's model.
-
- **Attributes:**
-
- content
- The object for which the state is stored. This can be any instance of
- a Django model.
-
- state
- The state of content. This must be a State instance.
- """
- content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"), related_name="state_object", blank=True, null=True)
- content_id = models.PositiveIntegerField(_(u"Content id"), blank=True, null=True)
- content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id")
- state = models.ForeignKey(State, verbose_name = _(u"State"))
-
- def __unicode__(self):
- return "%s %s - %s" % (self.content_type.name, self.content_id, self.state.name)
-
- class Meta:
- unique_together = ("content_type", "content_id", "state")
-
-class WorkflowObjectRelation(models.Model):
- """Stores an workflow of an object.
-
- Provides a way to give any object a workflow without changing the object's
- model.
-
- **Attributes:**
-
- content
- The object for which the workflow is stored. This can be any instance of
- a Django model.
-
- workflow
- The workflow which is assigned to an object. This needs to be a workflow
- instance.
- """
- content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"), related_name="workflow_object", blank=True, null=True)
- content_id = models.PositiveIntegerField(_(u"Content id"), blank=True, null=True)
- content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id")
- workflow = models.ForeignKey(Workflow, verbose_name=_(u"Workflow"), related_name="wors")
-
- class Meta:
- unique_together = ("content_type", "content_id")
-
- def __unicode__(self):
- return "%s %s - %s" % (self.content_type.name, self.content_id, self.workflow.name)
-
-class WorkflowModelRelation(models.Model):
- """Stores an workflow for a model (ContentType).
-
- Provides a way to give any object a workflow without changing the model.
-
- **Attributes:**
-
- Content Type
- The content type for which the workflow is stored. This can be any
- instance of a Django model.
-
- workflow
- The workflow which is assigned to an object. This needs to be a
- workflow instance.
- """
- content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content Type"), unique=True)
- workflow = models.ForeignKey(Workflow, verbose_name=_(u"Workflow"), related_name="wmrs")
-
- def __unicode__(self):
- return "%s - %s" % (self.content_type.name, self.workflow.name)
-
-# Permissions relation #######################################################
-
-class WorkflowPermissionRelation(models.Model):
- """Stores the permissions for which a workflow is responsible.
-
- **Attributes:**
-
- workflow
- The workflow which is responsible for the permissions. Needs to be a
- Workflow instance.
-
- permission
- The permission for which the workflow is responsible. Needs to be a
- Permission instance.
- """
- workflow = models.ForeignKey(Workflow)
- permission = models.ForeignKey(Permission, related_name="permissions")
-
- class Meta:
- unique_together = ("workflow", "permission")
-
- def __unicode__(self):
- return "%s %s" % (self.workflow.name, self.permission.name)
-
-class StateInheritanceBlock(models.Model):
- """Stores inheritance block for state and permission.
-
- **Attributes:**
-
- state
- The state for which the inheritance is blocked. Needs to be a State
- instance.
-
- permission
- The permission for which the instance is blocked. Needs to be a
- Permission instance.
- """
- state = models.ForeignKey(State, verbose_name=_(u"State"))
- permission = models.ForeignKey(Permission, verbose_name=_(u"Permission"))
-
- def __unicode__(self):
- return "%s %s" % (self.state.name, self.permission.name)
-
-class StatePermissionRelation(models.Model):
- """Stores granted permission for state and role.
-
- **Attributes:**
-
- state
- The state for which the role has the permission. Needs to be a State
- instance.
-
- permission
- The permission for which the workflow is responsible. Needs to be a
- Permission instance.
-
- role
- The role for which the state has the permission. Needs to be a lfc
- Role instance.
- """
- state = models.ForeignKey(State, verbose_name=_(u"State"))
- permission = models.ForeignKey(Permission, verbose_name=_(u"Permission"))
- role = models.ForeignKey(Role, verbose_name=_(u"Role"))
-
- def __unicode__(self):
- return "%s %s %s" % (self.state.name, self.role.name, self.permission.name)
diff --git a/workflows/templates/workflows/transitions.html b/workflows/templates/workflows/transitions.html
deleted file mode 100644
index 635cbba02..000000000
--- a/workflows/templates/workflows/transitions.html
+++ /dev/null
@@ -1,10 +0,0 @@
-{% load i18n %}
-
-
diff --git a/workflows/templatetags/.gitignore b/workflows/templatetags/.gitignore
deleted file mode 100644
index a74b07aee..000000000
--- a/workflows/templatetags/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/*.pyc
diff --git a/workflows/templatetags/__init__.py b/workflows/templatetags/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/workflows/templatetags/workflows_tags.py b/workflows/templatetags/workflows_tags.py
deleted file mode 100644
index e95357a32..000000000
--- a/workflows/templatetags/workflows_tags.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# django imports
-from django import template
-
-# workflows imports
-import workflows.utils
-
-register = template.Library()
-
-@register.inclusion_tag('workflows/transitions.html', takes_context=True)
-def transitions(context, obj):
- """
- """
- request = context.get("request")
-
- return {
- "transitions" : workflows.utils.get_allowed_transitions(obj, request.user),
- "state" : workflows.utils.get_state(obj),
- }
diff --git a/workflows/tests.py b/workflows/tests.py
deleted file mode 100644
index 2fda0ad4b..000000000
--- a/workflows/tests.py
+++ /dev/null
@@ -1,600 +0,0 @@
-# django imports
-from django.contrib.contenttypes.models import ContentType
-from django.contrib.flatpages.models import FlatPage
-from django.test import TestCase
-from django.contrib.auth.models import User
-from django.contrib.sessions.backends.file import SessionStore
-from django.core.handlers.wsgi import WSGIRequest
-from django.test.client import Client
-
-# workflows import
-import permissions.utils
-import workflows.utils
-from workflows.models import State
-from workflows.models import StateInheritanceBlock
-from workflows.models import StatePermissionRelation
-from workflows.models import StateObjectRelation
-from workflows.models import Transition
-from workflows.models import Workflow
-from workflows.models import WorkflowModelRelation
-from workflows.models import WorkflowObjectRelation
-from workflows.models import WorkflowPermissionRelation
-
-class WorkflowTestCase(TestCase):
- """Tests a simple workflow without permissions.
- """
- def setUp(self):
- """
- """
- create_workflow(self)
-
- def test_get_states(self):
- """
- """
- states = self.w.states.all()
- self.assertEqual(states[0], self.private)
- self.assertEqual(states[1], self.public)
-
- def test_unicode(self):
- """
- """
- self.assertEqual(self.w.__unicode__(), u"Standard")
-
-class PermissionsTestCase(TestCase):
- """Tests a simple workflow with permissions.
- """
- def setUp(self):
- """
- """
- create_workflow(self)
-
- # Register roles
- self.anonymous = permissions.utils.register_role("Anonymous")
- self.owner = permissions.utils.register_role("Owner")
-
- self.user = User.objects.create(username="john")
- permissions.utils.add_role(self.user, self.owner)
-
- # Example content type
- self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1")
-
- # Registers permissions
- self.view = permissions.utils.register_permission("View", "view")
- self.edit = permissions.utils.register_permission("Edit", "edit")
-
- # Add all permissions which are managed by the workflow
- wpr = WorkflowPermissionRelation.objects.create(workflow=self.w, permission=self.view)
- wpr = WorkflowPermissionRelation.objects.create(workflow=self.w, permission=self.edit)
-
- # Add permissions for single states
- spr = StatePermissionRelation.objects.create(state=self.public, permission=self.view, role=self.owner)
- spr = StatePermissionRelation.objects.create(state=self.private, permission=self.view, role=self.owner)
- spr = StatePermissionRelation.objects.create(state=self.private, permission=self.edit, role=self.owner)
-
- # Add inheritance block for single states
- sib = StateInheritanceBlock.objects.create(state=self.private, permission=self.view)
- sib = StateInheritanceBlock.objects.create(state=self.private, permission=self.edit)
- sib = StateInheritanceBlock.objects.create(state=self.public, permission=self.edit)
-
- workflows.utils.set_workflow(self.page_1, self.w)
-
- def test_set_state(self):
- """
- """
- # Permissions
- result = permissions.utils.has_permission(self.page_1, self.user, "edit")
- self.assertEqual(result, True)
-
- result = permissions.utils.has_permission(self.page_1, self.user, "view")
- self.assertEqual(result, True)
-
- # Inheritance
- result = permissions.utils.is_inherited(self.page_1, "view")
- self.assertEqual(result, False)
-
- result = permissions.utils.is_inherited(self.page_1, "edit")
- self.assertEqual(result, False)
-
- # Change state
- workflows.utils.set_state(self.page_1, self.public)
-
- # Permissions
- result = permissions.utils.has_permission(self.page_1, self.user, "edit")
- self.assertEqual(result, False)
-
- result = permissions.utils.has_permission(self.page_1, self.user, "view")
- self.assertEqual(result, True)
-
- # Inheritance
- result = permissions.utils.is_inherited(self.page_1, "view")
- self.assertEqual(result, True)
-
- result = permissions.utils.is_inherited(self.page_1, "edit")
- self.assertEqual(result, False)
-
- def test_set_initial_state(self):
- """
- """
- state = workflows.utils.get_state(self.page_1)
- self.assertEqual(state.name, self.private.name)
-
- workflows.utils.do_transition(self.page_1, self.make_public, self.user)
- state = workflows.utils.get_state(self.page_1)
- self.assertEqual(state.name, self.public.name)
-
- workflows.utils.set_initial_state(self.page_1)
- state = workflows.utils.get_state(self.page_1)
- self.assertEqual(state.name, self.private.name)
-
- def test_do_transition(self):
- """
- """
- state = workflows.utils.get_state(self.page_1)
- self.assertEqual(state.name, self.private.name)
-
- # by transition
- workflows.utils.do_transition(self.page_1, self.make_public, self.user)
-
- state = workflows.utils.get_state(self.page_1)
- self.assertEqual(state.name, self.public.name)
-
- # by name
- workflows.utils.do_transition(self.page_1, "Make private", self.user)
-
- state = workflows.utils.get_state(self.page_1)
- self.assertEqual(state.name, self.private.name)
-
- # name which does not exist
- result = workflows.utils.do_transition(self.page_1, "Make pending", self.user)
- self.assertEqual(result, False)
-
- wrong = Transition.objects.create(name="Wrong", workflow=self.w, destination = self.public)
-
- # name which does not exist
- result = workflows.utils.do_transition(self.page_1, wrong, self.user)
- self.assertEqual(result, False)
-
-class UtilsTestCase(TestCase):
- """Tests various methods of the utils module.
- """
- def setUp(self):
- """
- """
- create_workflow(self)
- self.user = User.objects.create()
-
- def test_workflow(self):
- """
- """
- workflows.utils.set_workflow(self.user, self.w)
- result = workflows.utils.get_workflow(self.user)
- self.assertEqual(result, self.w)
-
- def test_state(self):
- """
- """
- result = workflows.utils.get_state(self.user)
- self.assertEqual(result, None)
-
- workflows.utils.set_workflow(self.user, self.w)
- result = workflows.utils.get_state(self.user)
- self.assertEqual(result, self.w.initial_state)
-
- def test_set_workflow_1(self):
- """Set worklow by object
- """
- ctype = ContentType.objects.get_for_model(self.user)
-
- result = workflows.utils.get_workflow(self.user)
- self.assertEqual(result, None)
-
- wp = Workflow.objects.create(name="Portal")
-
- # Set for model
- workflows.utils.set_workflow_for_model(ctype, wp)
-
- result = workflows.utils.get_workflow_for_model(ctype)
- self.assertEqual(result, wp)
-
- result = workflows.utils.get_workflow(self.user)
- self.assertEqual(result, wp)
-
- # Set for object
- workflows.utils.set_workflow_for_object(self.user, self.w)
- result = workflows.utils.get_workflow(self.user)
- self.assertEqual(result, self.w)
-
- # The model still have wp
- result = workflows.utils.get_workflow_for_model(ctype)
- self.assertEqual(result, wp)
-
- def test_set_workflow_2(self):
- """Set worklow by name
- """
- ctype = ContentType.objects.get_for_model(self.user)
-
- result = workflows.utils.get_workflow(self.user)
- self.assertEqual(result, None)
-
- wp = Workflow.objects.create(name="Portal")
-
- # Set for model
- workflows.utils.set_workflow_for_model(ctype, "Portal")
-
- result = workflows.utils.get_workflow_for_model(ctype)
- self.assertEqual(result, wp)
-
- result = workflows.utils.get_workflow(self.user)
- self.assertEqual(result, wp)
-
- # Set for object
- workflows.utils.set_workflow_for_object(self.user, "Standard")
- result = workflows.utils.get_workflow(self.user)
- self.assertEqual(result, self.w)
-
- # The model still have wp
- result = workflows.utils.get_workflow_for_model(ctype)
- self.assertEqual(result, wp)
-
- # Workflow which does not exist
- result = workflows.utils.set_workflow_for_model(ctype, "Wrong")
- self.assertEqual(result, False)
-
- result = workflows.utils.set_workflow_for_object(self.user, "Wrong")
- self.assertEqual(result, False)
-
- def test_get_objects_for_workflow_1(self):
- """Workflow is added to object.
- """
- result = workflows.utils.get_objects_for_workflow(self.w)
- self.assertEqual(result, [])
-
- workflows.utils.set_workflow(self.user, self.w)
- result = workflows.utils.get_objects_for_workflow(self.w)
- self.assertEqual(result, [self.user])
-
- def test_get_objects_for_workflow_2(self):
- """Workflow is added to content type.
- """
- result = workflows.utils.get_objects_for_workflow(self.w)
- self.assertEqual(result, [])
-
- ctype = ContentType.objects.get_for_model(self.user)
- workflows.utils.set_workflow(ctype, self.w)
- result = workflows.utils.get_objects_for_workflow(self.w)
- self.assertEqual(result, [self.user])
-
- def test_get_objects_for_workflow_3(self):
- """Workflow is added to content type and object.
- """
- result = workflows.utils.get_objects_for_workflow(self.w)
- self.assertEqual(result, [])
-
- workflows.utils.set_workflow(self.user, self.w)
- result = workflows.utils.get_objects_for_workflow(self.w)
- self.assertEqual(result, [self.user])
-
- ctype = ContentType.objects.get_for_model(self.user)
- workflows.utils.set_workflow(ctype, self.w)
- result = workflows.utils.get_objects_for_workflow(self.w)
- self.assertEqual(result, [self.user])
-
- def test_get_objects_for_workflow_4(self):
- """Get workflow by name
- """
- result = workflows.utils.get_objects_for_workflow("Standard")
- self.assertEqual(result, [])
-
- workflows.utils.set_workflow(self.user, self.w)
- result = workflows.utils.get_objects_for_workflow("Standard")
- self.assertEqual(result, [self.user])
-
- # Workflow which does not exist
- result = workflows.utils.get_objects_for_workflow("Wrong")
- self.assertEqual(result, [])
-
- def test_remove_workflow_from_model(self):
- """
- """
- ctype = ContentType.objects.get_for_model(self.user)
-
- result = workflows.utils.get_workflow(ctype)
- self.assertEqual(result, None)
-
- workflows.utils.set_workflow_for_model(ctype, self.w)
-
- result = workflows.utils.get_workflow_for_model(ctype)
- self.assertEqual(result, self.w)
-
- result = workflows.utils.get_workflow(self.user)
- self.assertEqual(result, self.w)
-
- workflows.utils.remove_workflow_from_model(ctype)
-
- result = workflows.utils.get_workflow_for_model(ctype)
- self.assertEqual(result, None)
-
- result = workflows.utils.get_workflow_for_object(self.user)
- self.assertEqual(result, None)
-
- def test_remove_workflow_from_object(self):
- """
- """
- result = workflows.utils.get_workflow(self.user)
- self.assertEqual(result, None)
-
- workflows.utils.set_workflow_for_object(self.user, self.w)
-
- result = workflows.utils.get_workflow(self.user)
- self.assertEqual(result, self.w)
-
- result = workflows.utils.remove_workflow_from_object(self.user)
- self.assertEqual(result, None)
-
- def test_remove_workflow_1(self):
- """Removes workflow from model
- """
- ctype = ContentType.objects.get_for_model(self.user)
-
- result = workflows.utils.get_workflow(ctype)
- self.assertEqual(result, None)
-
- workflows.utils.set_workflow_for_model(ctype, self.w)
-
- result = workflows.utils.get_workflow_for_model(ctype)
- self.assertEqual(result, self.w)
-
- result = workflows.utils.get_workflow(self.user)
- self.assertEqual(result, self.w)
-
- workflows.utils.remove_workflow(ctype)
-
- result = workflows.utils.get_workflow_for_model(ctype)
- self.assertEqual(result, None)
-
- result = workflows.utils.get_workflow_for_object(self.user)
- self.assertEqual(result, None)
-
- def test_remove_workflow_2(self):
- """Removes workflow from object
- """
- result = workflows.utils.get_workflow(self.user)
- self.assertEqual(result, None)
-
- workflows.utils.set_workflow_for_object(self.user, self.w)
-
- result = workflows.utils.get_workflow(self.user)
- self.assertEqual(result, self.w)
-
- result = workflows.utils.remove_workflow(self.user)
- self.assertEqual(result, None)
-
- def test_get_allowed_transitions(self):
- """Tests get_allowed_transitions method
- """
- page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1")
- role_1 = permissions.utils.register_role("Role 1")
- permissions.utils.add_role(self.user, role_1)
-
- view = permissions.utils.register_permission("Publish", "publish")
-
- transitions = self.private.get_allowed_transitions(page_1, self.user)
- self.assertEqual(len(transitions), 1)
-
- # protect the transition with a permission
- self.make_public.permission = view
- self.make_public.save()
-
- # user has no transition
- transitions = self.private.get_allowed_transitions(page_1, self.user)
- self.assertEqual(len(transitions), 0)
-
- # grant permission
- permissions.utils.grant_permission(page_1, role_1, view)
-
- # user has transition again
- transitions = self.private.get_allowed_transitions(page_1, self.user)
- self.assertEqual(len(transitions), 1)
-
- def test_get_workflow_for_object(self):
- """
- """
- result = workflows.utils.get_workflow(self.user)
- self.assertEqual(result, None)
-
- # Set workflow for a user
- workflows.utils.set_workflow_for_object(self.user, self.w)
-
- # Get workflow for the user
- result = workflows.utils.get_workflow_for_object(self.user)
- self.assertEqual(result, self.w)
-
- # Set workflow for a FlatPage
- page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1")
- workflows.utils.set_workflow_for_object(page_1, self.w)
-
- result = workflows.utils.get_workflow_for_object(self.user)
- self.assertEqual(result, self.w)
-
- result = workflows.utils.get_workflow_for_object(page_1)
- self.assertEqual(result, self.w)
-
-class StateTestCase(TestCase):
- """Tests the State model
- """
- def setUp(self):
- """
- """
- create_workflow(self)
- self.user = User.objects.create()
- self.role_1 = permissions.utils.register_role("Role 1")
- permissions.utils.add_role(self.user, self.role_1)
- self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1")
-
- def test_unicode(self):
- """
- """
- self.assertEqual(self.private.__unicode__(), u"Private (Standard)")
-
- def test_transitions(self):
- """
- """
- transitions = self.public.transitions.all()
- self.assertEqual(len(transitions), 1)
- self.assertEqual(transitions[0], self.make_private)
-
- transitions = self.private.transitions.all()
- self.assertEqual(len(transitions), 1)
- self.assertEqual(transitions[0], self.make_public)
-
- def test_get_transitions(self):
- """
- """
- transitions = self.private.get_allowed_transitions(self.page_1, self.user)
- self.assertEqual(len(transitions), 1)
- self.assertEqual(transitions[0], self.make_public)
-
- transitions = self.public.get_allowed_transitions(self.page_1, self.user)
- self.assertEqual(len(transitions), 1)
- self.assertEqual(transitions[0], self.make_private)
-
- def test_get_allowed_transitions(self):
- """
- """
- self.view = permissions.utils.register_permission("Publish", "publish")
- transitions = self.private.get_allowed_transitions(self.page_1, self.user)
- self.assertEqual(len(transitions), 1)
-
- # protect the transition with a permission
- self.make_public.permission = self.view
- self.make_public.save()
-
- # user has no transition
- transitions = self.private.get_allowed_transitions(self.page_1, self.user)
- self.assertEqual(len(transitions), 0)
-
- # grant permission
- permissions.utils.grant_permission(self.page_1, self.role_1, self.view)
-
- # user has transition again
- transitions = self.private.get_allowed_transitions(self.page_1, self.user)
- self.assertEqual(len(transitions), 1)
-
-class TransitionTestCase(TestCase):
- """Tests the Transition model
- """
- def setUp(self):
- """
- """
- create_workflow(self)
-
- def test_unicode(self):
- """
- """
- self.assertEqual(self.make_private.__unicode__(), u"Make private")
-
-class RelationsTestCase(TestCase):
- """Tests various Relations models.
- """
- def setUp(self):
- """
- """
- create_workflow(self)
- self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1")
-
- def test_unicode(self):
- """
- """
- # WorkflowObjectRelation
- workflows.utils.set_workflow(self.page_1, self.w)
- wor = WorkflowObjectRelation.objects.filter()[0]
- self.assertEqual(wor.__unicode__(), "flat page 1 - Standard")
-
- # StateObjectRelation
- workflows.utils.set_state(self.page_1, self.public)
- sor = StateObjectRelation.objects.filter()[0]
- self.assertEqual(sor.__unicode__(), "flat page 1 - Public")
-
- # WorkflowModelRelation
- ctype = ContentType.objects.get_for_model(self.page_1)
- workflows.utils.set_workflow(ctype, self.w)
- wmr = WorkflowModelRelation.objects.filter()[0]
- self.assertEqual(wmr.__unicode__(), "flat page - Standard")
-
- # WorkflowPermissionRelation
- self.view = permissions.utils.register_permission("View", "view")
- wpr = WorkflowPermissionRelation.objects.create(workflow=self.w, permission=self.view)
- self.assertEqual(wpr.__unicode__(), "Standard View")
-
- # StatePermissionRelation
- self.owner = permissions.utils.register_role("Owner")
- spr = StatePermissionRelation.objects.create(state=self.public, permission=self.view, role=self.owner)
- self.assertEqual(spr.__unicode__(), "Public Owner View")
-
-# Helpers ####################################################################
-
-def create_workflow(self):
- self.w = Workflow.objects.create(name="Standard")
-
- self.private = State.objects.create(name="Private", workflow= self.w)
- self.public = State.objects.create(name="Public", workflow= self.w)
-
- self.make_public = Transition.objects.create(name="Make public", workflow=self.w, destination = self.public)
- self.make_private = Transition.objects.create(name="Make private", workflow=self.w, destination = self.private)
-
- self.private.transitions.add(self.make_public)
- self.public.transitions.add(self.make_private)
-
- self.w.initial_state = self.private
- self.w.save()
-
-# Taken from "http://www.djangosnippets.org/snippets/963/"
-class RequestFactory(Client):
- """
- Class that lets you create mock Request objects for use in testing.
-
- Usage:
-
- rf = RequestFactory()
- get_request = rf.get('/hello/')
- post_request = rf.post('/submit/', {'foo': 'bar'})
-
- This class re-uses the django.test.client.Client interface, docs here:
- http://www.djangoproject.com/documentation/testing/#the-test-client
-
- Once you have a request object you can pass it to any view function,
- just as if that view had been hooked up using a URLconf.
-
- """
- def request(self, **request):
- """
- Similar to parent class, but returns the request object as soon as it
- has created it.
- """
- environ = {
- 'HTTP_COOKIE': self.cookies,
- 'PATH_INFO': '/',
- 'QUERY_STRING': '',
- 'REQUEST_METHOD': 'GET',
- 'SCRIPT_NAME': '',
- 'SERVER_NAME': 'testserver',
- 'SERVER_PORT': 80,
- 'SERVER_PROTOCOL': 'HTTP/1.1',
- }
- environ.update(self.defaults)
- environ.update(request)
- return WSGIRequest(environ)
-
-def create_request():
- """
- """
- rf = RequestFactory()
- request = rf.get('/')
- request.session = SessionStore()
-
- user = User()
- user.is_superuser = True
- user.save()
- request.user = user
-
- return request
diff --git a/workflows/urls.py b/workflows/urls.py
deleted file mode 100644
index 949c2346c..000000000
--- a/workflows/urls.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from django.conf.urls.defaults import *
-
-# URL patterns for django-workflows
-
-urlpatterns = patterns('django-workflows.views',
- # Add url patterns here
-)
diff --git a/workflows/utils.py b/workflows/utils.py
deleted file mode 100644
index ef2d32596..000000000
--- a/workflows/utils.py
+++ /dev/null
@@ -1,351 +0,0 @@
-# django imports
-from django.contrib.contenttypes.models import ContentType
-
-# workflows imports
-from permissions.models import ObjectPermission
-from permissions.models import ObjectPermissionInheritanceBlock
-from workflows.models import StateInheritanceBlock
-from workflows.models import StateObjectRelation
-from workflows.models import StatePermissionRelation
-from workflows.models import Transition
-from workflows.models import Workflow
-from workflows.models import WorkflowModelRelation
-from workflows.models import WorkflowObjectRelation
-from workflows.models import WorkflowPermissionRelation
-
-# permissions imports
-import permissions.utils
-
-def get_objects_for_workflow(workflow):
- """Returns all objects which have passed workflow.
-
- **Parameters:**
-
- workflow
- The workflow for which the objects are returned. Can be a Workflow
- instance or a string with the workflow name.
- """
- if not isinstance(workflow, Workflow):
- try:
- workflow = Workflow.objects.get(name=workflow)
- except Workflow.DoesNotExist:
- return []
-
- return workflow.get_objects()
-
-def remove_workflow(ctype_or_obj):
- """Removes the workflow from the passed content type or object. After this
- function has been called the content type or object has no workflow
- anymore.
-
- If ctype_or_obj is an object the workflow is removed from the object not
- from the belonging content type.
-
- If ctype_or_obj is an content type the workflow is removed from the
- content type not from instances of the content type (if they have an own
- workflow)
-
- ctype_or_obj
- The content type or the object to which the passed workflow should be
- set. Can be either a ContentType instance or any LFC Django model
- instance.
- """
- if isinstance(ctype_or_obj, ContentType):
- remove_workflow_from_model(ctype_or_obj)
- else:
- remove_workflow_from_object(ctype_or_obj)
-
-def remove_workflow_from_model(ctype):
- """Removes the workflow from passed content type. After this function has
- been called the content type has no workflow anymore (the instances might
- have own ones).
-
- ctype
- The content type from which the passed workflow should be removed.
- Must be a ContentType instance.
- """
- # First delete all states, inheritance blocks and permissions from ctype's
- # instances which have passed workflow.
- workflow = get_workflow_for_model(ctype)
- for obj in get_objects_for_workflow(workflow):
- try:
- ctype = ContentType.objects.get_for_model(obj)
- sor = StateObjectRelation.objects.get(content_id=obj.pk, content_type=ctype)
- except StateObjectRelation.DoesNotExist:
- pass
- else:
- sor.delete()
-
- # Reset all permissions
- permissions.utils.reset(obj)
-
- try:
- wmr = WorkflowModelRelation.objects.get(content_type=ctype)
- except WorkflowModelRelation.DoesNotExist:
- pass
- else:
- wmr.delete()
-
-def remove_workflow_from_object(obj):
- """Removes the workflow from the passed object. After this function has
- been called the object has no *own* workflow anymore (it might have one
- via its content type).
-
- obj
- The object from which the passed workflow should be set. Must be a
- Django Model instance.
- """
- try:
- wor = WorkflowObjectRelation.objects.get(content_type=obj)
- except WorkflowObjectRelation.DoesNotExist:
- pass
- else:
- wor.delete()
-
- # Reset all permissions
- permissions.utils.reset(obj)
-
- # Set initial of object's content types workflow (if there is one)
- set_initial_state(obj)
-
-def set_workflow(ctype_or_obj, workflow):
- """Sets the workflow for passed content type or object. See the specific
- methods for more information.
-
- **Parameters:**
-
- workflow
- The workflow which should be set to the object or model.
-
- ctype_or_obj
- The content type or the object to which the passed workflow should be
- set. Can be either a ContentType instance or any Django model
- instance.
- """
- return workflow.set_to(ctype_or_obj)
-
-def set_workflow_for_object(obj, workflow):
- """Sets the passed workflow to the passed object.
-
- If the object has already the given workflow nothing happens. Otherwise
- the object gets the passed workflow and the state is set to the workflow's
- initial state.
-
- **Parameters:**
-
- workflow
- The workflow which should be set to the object. Can be a Workflow
- instance or a string with the workflow name.
-
- obj
- The object which gets the passed workflow.
- """
- if isinstance(workflow, Workflow) == False:
- try:
- workflow = Workflow.objects.get(name=workflow)
- except Workflow.DoesNotExist:
- return False
-
- workflow.set_to_object(obj)
-
-def set_workflow_for_model(ctype, workflow):
- """Sets the passed workflow to the passed content type. If the content
- type has already an assigned workflow the workflow is overwritten.
-
- The objects which had the old workflow must updated explicitely.
-
- **Parameters:**
-
- workflow
- The workflow which should be set to passend content type. Must be a
- Workflow instance.
-
- ctype
- The content type to which the passed workflow should be assigned. Can
- be any Django model instance
- """
- if isinstance(workflow, Workflow) == False:
- try:
- workflow = Workflow.objects.get(name=workflow)
- except Workflow.DoesNotExist:
- return False
-
- workflow.set_to_model(ctype)
-
-def get_workflow(obj):
- """Returns the workflow for the passed object. It takes it either from
- the passed object or - if the object doesn't have a workflow - from the
- passed object's ContentType.
-
- **Parameters:**
-
- object
- The object for which the workflow should be returend. Can be any
- Django model instance.
- """
- workflow = get_workflow_for_object(obj)
- if workflow is not None:
- return workflow
-
- ctype = ContentType.objects.get_for_model(obj)
- return get_workflow_for_model(ctype)
-
-def get_workflow_for_object(obj):
- """Returns the workflow for the passed object.
-
- **Parameters:**
-
- obj
- The object for which the workflow should be returned. Can be any
- Django model instance.
- """
- try:
- ctype = ContentType.objects.get_for_model(obj)
- wor = WorkflowObjectRelation.objects.get(content_id=obj.pk, content_type=ctype)
- except WorkflowObjectRelation.DoesNotExist:
- return None
- else:
- return wor.workflow
-
-def get_workflow_for_model(ctype):
- """Returns the workflow for the passed model.
-
- **Parameters:**
-
- ctype
- The content type for which the workflow should be returned. Must be
- a Django ContentType instance.
- """
- try:
- wor = WorkflowModelRelation.objects.get(content_type=ctype)
- except WorkflowModelRelation.DoesNotExist:
- return None
- else:
- return wor.workflow
-
-def get_state(obj):
- """Returns the current workflow state for the passed object.
-
- **Parameters:**
-
- obj
- The object for which the workflow state should be returned. Can be any
- Django model instance.
- """
- ctype = ContentType.objects.get_for_model(obj)
- try:
- sor = StateObjectRelation.objects.get(content_type=ctype, content_id=obj.pk)
- except StateObjectRelation.DoesNotExist:
- return None
- else:
- return sor.state
-
-def set_state(obj, state):
- """Sets the state for the passed object to the passed state and updates
- the permissions for the object.
-
- **Parameters:**
-
- obj
- The object for which the workflow state should be set. Can be any
- Django model instance.
-
- state
- The state which should be set to the passed object.
- """
- if not state:
- remove_state(obj)
- else:
- ctype = ContentType.objects.get_for_model(obj)
- try:
- sor = StateObjectRelation.objects.get(content_type=ctype, content_id=obj.pk)
- except StateObjectRelation.DoesNotExist:
- sor = StateObjectRelation.objects.create(content=obj, state=state)
- else:
- sor.state = state
- sor.save()
- update_permissions(obj)
-
-def remove_state(obj):
- """Removes the current state for the passed object.
-
- **Parameters:**
-
- obj
- The object for which the workflow state should be set. Can be any
- Django model instance.
-
- """
- ctype = ContentType.objects.get_for_model(obj)
- try:
- sor = StateObjectRelation.objects.get(content_type=ctype, content_id=obj.pk)
- sor.delete()
- except StateObjectRelation.DoesNotExist:
- pass
- update_permissions(obj)
-
-def set_initial_state(obj):
- """Sets the initial state to the passed object.
- """
- wf = get_workflow(obj)
- if wf is not None:
- set_state(obj, wf.get_initial_state())
-
-def get_allowed_transitions(obj, user):
- """Returns all allowed transitions for passed object and user. Takes the
- current state of the object into account.
-
- **Parameters:**
-
- obj
- The object for which the transitions should be returned.
-
- user
- The user for which the transitions are allowed.
- """
- state = get_state(obj)
- if state is None:
- return []
-
- return state.get_allowed_transitions(obj, user)
-
-def do_transition(obj, transition, user):
- """Processes the passed transition to the passed object (if allowed).
- """
- if not isinstance(transition, Transition):
- try:
- transition = Transition.objects.get(name=transition)
- except Transition.DoesNotExist:
- return False
-
- transitions = get_allowed_transitions(obj, user)
- if transition in transitions:
- set_state(obj, transition.destination)
- return True
- else:
- return False
-
-def update_permissions(obj):
- """Updates the permissions of the passed object according to the object's
- current workflow state.
- """
- workflow = get_workflow(obj)
- state = get_state(obj)
-
- # Remove all permissions for the workflow
- ct = ContentType.objects.get_for_model(obj)
- ps = [wpr.permission for wpr in WorkflowPermissionRelation.objects.filter(workflow=workflow)]
-
- ObjectPermission.objects.filter(content_type = ct, content_id=obj.pk, permission__in=ps).delete()
-
- # Grant permission for the state
- for spr in StatePermissionRelation.objects.filter(state=state):
- permissions.utils.grant_permission(obj, spr.role, spr.permission)
-
- # Remove all inheritance blocks from the object
- ObjectPermissionInheritanceBlock.objects.filter(
- content_type = ct, content_id=obj.pk, permission__in=ps).delete()
-
- # Add inheritance blocks of this state to the object
- for sib in StateInheritanceBlock.objects.filter(state=state):
- permissions.utils.add_inheritance_block(obj, sib.permission)
diff --git a/workflows/views.py b/workflows/views.py
deleted file mode 100644
index e69de29bb..000000000