diff --git a/ietf/templates/wgchairs/manage_workflowREDESIGN.html b/ietf/templates/wgchairs/manage_workflowREDESIGN.html
new file mode 100644
index 000000000..2e88a7930
--- /dev/null
+++ b/ietf/templates/wgchairs/manage_workflowREDESIGN.html
@@ -0,0 +1,168 @@
+{% extends "wginfo/wg_base.html" %}
+
+{% block wg_titledetail %}Manage Workflow{% endblock %}
+
+{% block pagehead %}
+{{ block.super }}
+
+
+
+{% endblock pagehead %}
+
+{% block morecss %}
+{{ block.super }}
+.state-table .inactive,
+.tag-table .inactive {
+ font-style: italic;
+ color: #666;
+}
+.state-table .state {
+ padding-right: 0.6em;
+}
+.set-next-states {
+ margin-top: 1em;
+}
+.set-next-states label {
+ display: block;
+ cursor: pointer;
+}
+.set-next-states label input {
+ vertical-align: middle;
+}
+.set-state input, .set-tag input {
+ width: 6em;
+}
+.toggled {
+ display: none;
+}
+.toggler {
+ color: #000;
+ text-decoration: none;
+ padding: 0px 3px;
+ display: inline-block;
+ margin-left: 0.5em;
+ font-size: 15px;
+ font-weight: bold;
+}
+.inactive .toggler {
+ color: #666;
+}
+.toggler:hover {
+ background-color: #999;
+ color: #fff;
+}
+{% endblock %}
+
+{% block wg_content %}
+
+
Edit workflow
+
+
Below you can customize the draft states and tags used in the {{ wg.acronym }} WG. Note that some states are mandatory for WG operation and cannot be deactivated.
+
+
You can see the default Working Group I-D State Diagram in Section 4.1 of RFC6174.
+
+
States
+
+
+
+ State |
+ Turn on/off |
+ Next states |
+
+ {% for state in states %}
+
+
+
+ {{ state.name }} {% if not state.used %} (not used in {{ wg.acronym }}){% endif %}
+ +
+
+ {{ state.desc|safe|linebreaks }}
+ |
+
+ {% if state.mandatory %}
+ (mandatory)
+ {% else %}
+
+ {% endif %}
+ |
+
+
+ {% if state.used_next_states %}
+ {% for n in state.used_next_states %} {{ n.name }}{% if not forloop.last %} {% endif %}{% endfor %}
+ {% else %}
+ None
+ {% endif %}
+ +
+
+
+ |
+
+{% endfor %}
+
+
+
Tags
+
+
+
+ Tag |
+ Turn on/off |
+
+ {% for tag in tags %}
+
+ {{ tag.name }} {% if not tag.used %} (not used in {{ wg.acronym }}){% endif %} |
+
+
+ |
+
+ {% endfor %}
+
+
+
+
+{% endblock %}
diff --git a/ietf/wgchairs/forms.py b/ietf/wgchairs/forms.py
index 5519bf6db..a94f02d34 100644
--- a/ietf/wgchairs/forms.py
+++ b/ietf/wgchairs/forms.py
@@ -18,8 +18,10 @@ from ietf.utils.mail import send_mail_text
from workflows.models import Transition
+from redesign.doc.models import WriteupDocEvent
from redesign.person.models import Person, Email
from redesign.group.models import Role, RoleName
+from redesign.name.models import DocTagName
class RelatedWGForm(forms.Form):
@@ -194,17 +196,20 @@ def assign_shepherd(user, internetdraft, shepherd):
doc = Document.objects.get(name=internetdraft.name)
save_document_in_history(doc)
- e = DocEvent(doc=doc, by=user.get_profile())
+
+ 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.name
- e.type = "changed_document"
e.save()
-
- doc.time = e.time
- doc.shepherd = shepherd
- doc.save()
# update proxy too
internetdraft.shepherd = shepherd
@@ -402,14 +407,25 @@ class WriteUpEditForm(RelatedWGForm):
return self.data.get('writeup', self.doc_writeup and self.doc_writeup.writeup or '')
def save(self):
- if not self.doc_writeup:
- self.doc_writeup = ProtoWriteUp.objects.create(
- person=self.person,
- draft=self.doc,
- writeup=self.cleaned_data['writeup'])
+ 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:
- self.doc_writeup.writeup = self.cleaned_data['writeup']
- self.doc_writeup.save()
+ 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)
@@ -418,13 +434,20 @@ class WriteUpEditForm(RelatedWGForm):
except PersonOrOrgInfo.DoesNotExist:
shepherd = None
if shepherd:
- extra_notify = ['%s <%s>' % shepherd.email()]
+ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
+ extra_notify = [shepherd.formatted_email()]
+ else:
+ extra_notify = ['%s <%s>' % shepherd.email()]
else:
extra_notify = []
- if followup:
- update_tags(self.doc, comment, self.person, set_tags=[FOLLOWUP_TAG], extra_notify=extra_notify)
+ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
+ tags = DocTagName.objects.filter(slug="sheph-u")
else:
- update_tags(self.doc, comment, self.person, reset_tags=[FOLLOWUP_TAG], extra_notify=extra_notify)
+ tags = [FOLLOWUP_TAG]
+ if followup:
+ update_tags(self.doc, comment, self.person, set_tags=tags, extra_notify=extra_notify)
+ else:
+ update_tags(self.doc, comment, self.person, reset_tags=tags, extra_notify=extra_notify)
return self.doc_writeup
def is_valid(self):
diff --git a/ietf/wgchairs/models.py b/ietf/wgchairs/models.py
index a9367ff04..36115bde0 100644
--- a/ietf/wgchairs/models.py
+++ b/ietf/wgchairs/models.py
@@ -27,6 +27,9 @@ class ProtoWriteUp(models.Model):
null=False,
)
+ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
+ from ietf.idtracker.models import InternetDraftOld as InternetDraft
+
draft = models.ForeignKey(
InternetDraft,
blank=False,
@@ -59,16 +62,26 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
class Meta:
proxy = True
- from redesign.doc.models import DocEvent
- class ProtoWriteUpProxy(DocEvent):
+ from redesign.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
- ProtoWriteUpOld = ProtoWriteUp
- ProtoWriteUp = ProtoWriteUpProxy
diff --git a/ietf/wgchairs/templatetags/wgchairs_tags.py b/ietf/wgchairs/templatetags/wgchairs_tags.py
index 0bcb4cda4..df51dafae 100644
--- a/ietf/wgchairs/templatetags/wgchairs_tags.py
+++ b/ietf/wgchairs/templatetags/wgchairs_tags.py
@@ -24,12 +24,8 @@ def wgchairs_admin_options(context, wg):
@register.simple_tag
def writeup(doc):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- e = doc.latest_event(type="changed_protocol_writeup")
- return e.text if e else ""
-
writeup = doc.protowriteup_set.all()
- if not writeup.count():
+ if not writeup:
return ''
else:
return writeup[0].writeup
@@ -37,12 +33,8 @@ def writeup(doc):
@register.simple_tag
def writeupdate(doc):
- if settings.USE_DB_REDESIGN_PROXY_CLASSES:
- e = doc.latest_event(type="changed_protocol_writeup")
- return e.time if e else ""
-
writeup = doc.protowriteup_set.all()
- if not writeup.count():
+ if not writeup:
return ''
else:
return writeup[0].date
diff --git a/ietf/wgchairs/tests.py b/ietf/wgchairs/tests.py
index 9c40c79b9..b0e7d94b7 100644
--- a/ietf/wgchairs/tests.py
+++ b/ietf/wgchairs/tests.py
@@ -13,8 +13,9 @@ from ietf.utils.test_data import make_test_data
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from redesign.person.models import Person, Email
- from redesign.group.models import Group, Role
- from redesign.doc.models import Document
+ from redesign.group.models import Group, Role, GroupStateTransitions
+ from redesign.doc.models import Document, State, WriteupDocEvent
+ from redesign.name.models import DocTagName
class ManageDelegatesTestCase(django.test.TestCase):
fixtures = ['names']
@@ -181,7 +182,118 @@ class ManageShepherdsTestCase(django.test.TestCase):
self.assertTrue(Email.objects.get(address="plain@example.com").person.name in r.content)
self.assertEquals(draft.docevent_set.count(), events_before + 1)
+class ManageWorkflowTestCase(django.test.TestCase):
+ 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(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(type="draft-stream-ietf", slug="wg-doc")
+ next_states = State.objects.filter(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())
+
+class ManageWriteupTestCase(django.test.TestCase):
+ fixtures = ['names']
+
+ def test_manage_writeup(self):
+ draft = make_test_data()
+
+ self.assertTrue(not draft.tags.filter(slug="sheph-u"))
+
+ url = urlreverse('doc_managing_writeup', kwargs=dict(acronym=draft.group.acronym, name=draft.name))
+ 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*=Change]")), 1)
+
+ # post text
+ r = self.client.post(url,
+ dict(writeup="New writeup"))
+ self.assertEquals(r.status_code, 200)
+ q = PyQuery(r.content)
+ self.assertEquals(len(q("input[name=followup]")), 1)
+ self.assertEquals(len(q("input[name=confirm]")), 1)
+ self.assertEquals(q("input[name=writeup]").val(), "New writeup")
+
+ # update tag and confirm
+ r = self.client.post(url,
+ dict(writeup="New writeup",
+ confirm="1",
+ followup="1",
+ comment="Starting on write up",
+ modify_tag="Modify"))
+ self.assertEquals(r.status_code, 200)
+ e = draft.latest_event(WriteupDocEvent, type="changed_protocol_writeup")
+ self.assertTrue(e)
+ self.assertEquals(e.text, "New writeup")
+ self.assertEquals(e.by.user.username, "secretary")
+ self.assertTrue(draft.tags.filter(slug="sheph-u"))
+
+
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/views.py b/ietf/wgchairs/views.py
index 4f2344892..62a1c994f 100644
--- a/ietf/wgchairs/views.py
+++ b/ietf/wgchairs/views.py
@@ -7,7 +7,7 @@ from django.http import HttpResponseForbidden, Http404
from ietf.idrfc.views_search import SearchForm, search_query
from ietf.wgchairs.forms import (RemoveDelegateForm, add_form_factory,
workflow_form_factory, TransitionFormSet,
- WriteUpEditForm)
+ 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,
@@ -22,7 +22,9 @@ from ietf.ietfworkflows.utils import (get_workflow_for_wg,
get_annotation_tags_for_draft,
get_state_for_draft, WAITING_WRITEUP,
FOLLOWUP_TAG)
-
+from redesign.name.models import DocTagName
+from redesign.doc.models import State
+from redesign.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)
@@ -54,7 +56,7 @@ 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 have no permission to access this view')
+ 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
@@ -91,10 +93,92 @@ def manage_workflow(request, acronym):
'selected': 'manage_workflow',
}, RequestContext(request))
+def manage_workflowREDESIGN(request, acronym):
+ from redesign.doc.models import State
+ from redesign.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(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(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:
- from ietf.wgchairs.forms import assign_shepherd
-
+ manage_workflow = manage_workflowREDESIGN
+
def managing_shepherd(request, acronym, name):
"""
View for managing the assigned shepherd of a document.
@@ -168,13 +252,18 @@ def managing_writeup(request, acronym, name):
user = request.user
doc = get_object_or_404(InternetDraft, filename=name)
if not can_manage_writeup_of_a_document(user, doc):
- raise Http404
- current_state = get_state_for_draft(doc)
- can_edit = True
- if current_state != get_state_by_name(WAITING_WRITEUP) and not can_manage_writeup_of_a_document_no_state(user, doc):
- can_edit = False
+ return HttpResponseForbidden('You do not have permission to access this page')
+ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
+ from redesign.doc.models import State
+ state = doc.get_state("draft-stream-%s" % doc.stream_id)
+ can_edit = (state and state.slug == "writeupw") or can_manage_writeup_of_a_document_no_state(user, doc)
+ else:
+ can_edit = True
+ current_state = get_state_for_draft(doc)
+ if current_state != get_state_by_name(WAITING_WRITEUP) and not can_manage_writeup_of_a_document_no_state(user, doc):
+ can_edit = False
writeup = doc.protowriteup_set.all()
- if writeup.count():
+ if writeup:
writeup = writeup[0]
else:
writeup = None