Skip to content

Commit d3efeb4

Browse files
committed
Introduce basic charter support for non-WG groups
- Legacy-Id: 7550
1 parent 25625a0 commit d3efeb4

8 files changed

Lines changed: 82 additions & 54 deletions

File tree

ietf/doc/views_charter.py

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os, datetime, shutil, textwrap, json
22

3-
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound, Http404
3+
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound, HttpResponseForbidden, Http404
44
from django.shortcuts import render_to_response, get_object_or_404, redirect
55
from django.core.urlresolvers import reverse as urlreverse
66
from django.template import RequestContext
@@ -9,6 +9,7 @@
99
from django.utils.safestring import mark_safe
1010
from django.conf import settings
1111
from django.contrib import messages
12+
from django.contrib.auth.decorators import login_required
1213

1314
import debug # pyflakes:ignore
1415

@@ -21,7 +22,7 @@
2122
approved_revision, default_review_text, default_action_text, email_state_changed,
2223
generate_ballot_writeup, generate_issue_ballot_mail, next_approved_revision, next_revision )
2324
from ietf.group.models import ChangeStateGroupEvent, MilestoneGroupEvent
24-
from ietf.group.utils import save_group_in_history, save_milestone_in_history
25+
from ietf.group.utils import save_group_in_history, save_milestone_in_history, can_manage_group_type
2526
from ietf.iesg.models import TelechatDate
2627
from ietf.ietfauth.utils import has_role, role_required
2728
from ietf.name.models import GroupStateName
@@ -33,25 +34,34 @@
3334

3435

3536
class ChangeStateForm(forms.Form):
36-
charter_state = forms.ModelChoiceField(State.objects.filter(used=True, type="charter", slug__in=["infrev", "intrev", "extrev", "iesgrev"]), label="Charter state", empty_label=None, required=False)
37+
charter_state = forms.ModelChoiceField(State.objects.filter(used=True, type="charter"), label="Charter state", empty_label=None, required=False)
3738
initial_time = forms.IntegerField(initial=0, label="Review time", help_text="(in weeks)", required=False)
3839
message = forms.CharField(widget=forms.Textarea, help_text="Leave blank to change state without notifying the Secretariat", required=False, label=mark_safe("Message to<br> Secretariat"))
3940
comment = forms.CharField(widget=forms.Textarea, help_text="Optional comment for the charter history", required=False)
4041
def __init__(self, *args, **kwargs):
4142
self.hide = kwargs.pop('hide', None)
43+
group = kwargs.pop('group')
4244
super(ChangeStateForm, self).__init__(*args, **kwargs)
45+
state_field = self.fields["charter_state"]
46+
if group.type_id == "wg":
47+
state_field.queryset = state_field.queryset.filter(slug__in=("infrev", "intrev", "extrev", "iesgrev"))
48+
else:
49+
state_field.queryset = state_field.queryset.filter(slug__in=("notrev", "approved"))
4350
# hide requested fields
4451
if self.hide:
4552
for f in self.hide:
4653
self.fields[f].widget = forms.HiddenInput
4754

48-
@role_required("Area Director", "Secretariat")
55+
@login_required
4956
def change_state(request, name, option=None):
5057
"""Change state of charter, notifying parties as necessary and
5158
logging the change as a comment."""
5259
charter = get_object_or_404(Document, type="charter", name=name)
5360
group = charter.group
5461

62+
if not can_manage_group_type(request.user, group.type_id):
63+
return HttpResponseForbidden("You don't have permission to access this view")
64+
5565
chartering_type = get_chartering_type(charter)
5666

5767
initial_review = charter.latest_event(InitialReviewDocEvent, type="initial_review")
@@ -61,13 +71,17 @@ def change_state(request, name, option=None):
6171
login = request.user.person
6272

6373
if request.method == 'POST':
64-
form = ChangeStateForm(request.POST)
74+
form = ChangeStateForm(request.POST, group=group)
6575
if form.is_valid():
6676
clean = form.cleaned_data
6777
charter_rev = charter.rev
6878

6979
if option in ("initcharter", "recharter"):
70-
charter_state = State.objects.get(used=True, type="charter", slug="infrev")
80+
if group.type_id == "wg":
81+
charter_state = State.objects.get(used=True, type="charter", slug="infrev")
82+
else:
83+
charter_state = clean['charter_state']
84+
7185
# make sure we have the latest revision set, if we
7286
# abandoned a charter before, we could have reset the
7387
# revision to latest approved
@@ -142,6 +156,8 @@ def change_state(request, name, option=None):
142156
default_action_text(group, charter, login)
143157
elif charter_state.slug == "iesgrev":
144158
create_ballot_if_not_open(charter, login, "approve")
159+
elif charter_state.slug == "approved":
160+
fix_charter_revision_after_approval(charter, login)
145161

146162
if charter_state.slug == "infrev" and clean["initial_time"] and clean["initial_time"] != 0:
147163
e = InitialReviewDocEvent(type="initial_review", by=login, doc=charter)
@@ -151,10 +167,10 @@ def change_state(request, name, option=None):
151167

152168
return redirect('doc_view', name=charter.name)
153169
else:
154-
if option == "recharter":
170+
if option == "recharter" and group.type_id == "wg":
155171
hide = ['initial_time', 'charter_state', 'message']
156172
init = dict()
157-
elif option == "initcharter":
173+
elif option == "initcharter" and group.type_id == "wg":
158174
hide = ['charter_state']
159175
init = dict(initial_time=1, message='%s has initiated chartering of the proposed %s:\n "%s" (%s).' % (login.plain_name(), group.type.name, group.name, group.acronym))
160176
elif option == "abandon":
@@ -164,7 +180,7 @@ def change_state(request, name, option=None):
164180
hide = ['initial_time']
165181
s = charter.get_state()
166182
init = dict(charter_state=s.pk if s else None)
167-
form = ChangeStateForm(hide=hide, initial=init)
183+
form = ChangeStateForm(hide=hide, initial=init, group=group)
168184

169185
prev_charter_state = None
170186
charter_hists = DocHistory.objects.filter(doc=charter).exclude(states__type="charter", states__slug=charter.get_state_slug()).order_by("-time")[:1]
@@ -352,17 +368,16 @@ def save(self, group, rev):
352368
else:
353369
destination.write(self.cleaned_data['content'].encode("utf-8"))
354370

355-
@role_required('Area Director','Secretariat')
356-
def submit(request, name=None, acronym=None, option=None):
357-
if name:
358-
if not name.startswith('charter-'):
359-
name = "charter-ietf-" + name
360-
elif acronym:
361-
name = "charter-ietf-" + acronym
371+
@login_required
372+
def submit(request, name=None, option=None):
373+
if not name.startswith('charter-'):
374+
raise Http404
375+
362376
charter = get_object_or_404(Document, type="charter", name=name)
363377
group = charter.group
364378

365-
login = request.user.person
379+
if not can_manage_group_type(request.user, group.type_id):
380+
return HttpResponseForbidden("You don't have permission to access this view")
366381

367382
path = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (charter.canonical_name(), charter.rev))
368383
not_uploaded_yet = charter.rev.endswith("-00") and not os.path.exists(path)
@@ -386,7 +401,7 @@ def submit(request, name=None, acronym=None, option=None):
386401

387402
charter.rev = next_rev
388403

389-
e = NewRevisionDocEvent(doc=charter, by=login, type="new_revision")
404+
e = NewRevisionDocEvent(doc=charter, by=request.user.person, type="new_revision")
390405
e.desc = "New version available: <b>%s-%s.txt</b>" % (charter.canonical_name(), charter.rev)
391406
e.rev = charter.rev
392407
e.save()
@@ -423,7 +438,8 @@ def submit(request, name=None, acronym=None, option=None):
423438
return render_to_response('doc/charter/submit.html',
424439
{'form': form,
425440
'next_rev': next_rev,
426-
'group': group },
441+
'group': group,
442+
'name': name },
427443
context_instance=RequestContext(request))
428444

429445
class AnnouncementTextForm(forms.Form):
@@ -568,6 +584,26 @@ def ballot_writeupnotes(request, name):
568584
),
569585
context_instance=RequestContext(request))
570586

587+
def fix_charter_revision_after_approval(charter, by):
588+
# according to spec, 00-02 becomes 01, so copy file and record new revision
589+
try:
590+
old = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.canonical_name(), charter.rev))
591+
new = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.canonical_name(), next_approved_revision(charter.rev)))
592+
shutil.copy(old, new)
593+
except IOError:
594+
return HttpResponse("There was an error copying %s to %s" %
595+
('%s-%s.txt' % (charter.canonical_name(), charter.rev),
596+
'%s-%s.txt' % (charter.canonical_name(), next_approved_revision(charter.rev))))
597+
598+
e = NewRevisionDocEvent(doc=charter, by=by, type="new_revision")
599+
e.rev = next_approved_revision(charter.rev)
600+
e.desc = "New version available: <b>%s-%s.txt</b>" % (charter.canonical_name(), e.rev)
601+
e.save()
602+
603+
charter.rev = e.rev
604+
charter.time = e.time
605+
charter.save()
606+
571607
@role_required("Secretariat")
572608
def approve(request, name):
573609
"""Approve charter, changing state, fixing revision, copying file to final location."""
@@ -618,24 +654,7 @@ def approve(request, name):
618654

619655
e = add_state_change_event(charter, login, prev_charter_state, new_charter_state)
620656

621-
# according to spec, 00-02 becomes 01, so copy file and record new revision
622-
try:
623-
old = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.canonical_name(), charter.rev))
624-
new = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.canonical_name(), next_approved_revision(charter.rev)))
625-
shutil.copy(old, new)
626-
except IOError:
627-
return HttpResponse("There was an error copying %s to %s" %
628-
('%s-%s.txt' % (charter.canonical_name(), charter.rev),
629-
'%s-%s.txt' % (charter.canonical_name(), next_approved_revision(charter.rev))))
630-
631-
e = NewRevisionDocEvent(doc=charter, by=login, type="new_revision")
632-
e.rev = next_approved_revision(charter.rev)
633-
e.desc = "New version available: <b>%s-%s.txt</b>" % (charter.canonical_name(), e.rev)
634-
e.save()
635-
636-
charter.rev = e.rev
637-
charter.time = e.time
638-
charter.save()
657+
fix_charter_revision_after_approval(charter, login)
639658

640659
email_secretariat(request, group, "Charter state changed to %s" % new_charter_state.name, change_description)
641660

ietf/doc/views_doc.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
from ietf.doc.mails import email_ad
5454
from ietf.doc.views_status_change import RELATION_SLUGS as status_change_relationships
5555
from ietf.group.models import Role
56+
from ietf.group.utils import can_manage_group_type
5657
from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream, user_is_person, role_required
5758
from ietf.name.models import StreamName, BallotPositionName
5859
from ietf.person.models import Email
@@ -401,6 +402,8 @@ def document_main(request, name, rev=None):
401402
if chartering and not snapshot:
402403
milestones = doc.group.groupmilestone_set.filter(state="charter")
403404

405+
can_manage = can_manage_group_type(request.user, doc.group.type_id)
406+
404407
return render_to_response("doc/document_charter.html",
405408
dict(doc=doc,
406409
top=top,
@@ -413,6 +416,7 @@ def document_main(request, name, rev=None):
413416
ballot_summary=ballot_summary,
414417
group=group,
415418
milestones=milestones,
419+
can_manage=can_manage,
416420
),
417421
context_instance=RequestContext(request))
418422

ietf/templates/doc/charter/submit.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
{% block content %}
1515
<h1>Charter submission for {{ group.acronym }} {{ group.type.name }}</h1>
1616

17-
<p>The text will be submitted as <strong>charter-ietf-{{ group.acronym }}-{{ next_rev }}</strong></p>
17+
<p>The text will be submitted as <strong>{{ name }}-{{ next_rev }}</strong></p>
1818
<form class="edit-info" action="" enctype="multipart/form-data" method="post">{% csrf_token %}
1919
<table>
2020
{% for field in form.visible_fields %}

ietf/templates/doc/document_charter.html

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
{% if snapshot %}Snapshot of{% endif %}
2626
{% if doc.get_state_slug != "approved" %}Proposed{% endif %}
2727
Charter for "{{ group.name }}"
28-
(<a {% if group.type.slug == "wg" %}href="{% url "ietf.wginfo.views.group_charter" group_type=group.type_id acronym=group.acronym %}"{% endif %}>{{ group.acronym }}</a>) {{ group.type.name }}
28+
(<a {% if group.type_id == "wg" or group.type_id == "rg" %}href="{% url "ietf.wginfo.views.group_charter" group_type=group.type_id acronym=group.acronym %}"{% endif %}>{{ group.acronym }}</a>) {{ group.type.name }}
2929
</div>
3030

3131
<table id="metatable" width="100%">
@@ -38,7 +38,7 @@
3838
<td>
3939
<div>
4040
<a title="{{ doc.get_state.desc }}"
41-
{% if not snapshot and user|has_role:"Area Director,Secretariat" %}
41+
{% if not snapshot and can_manage %}
4242
class="editlink" href="{% url "charter_change_state" name=doc.name %}"
4343
{% endif %}>
4444
{{ doc.get_state.name }}
@@ -69,7 +69,7 @@
6969

7070
<tr>
7171
<td>Responsible AD:</td>
72-
<td><a {% if request.user|has_role:"Area Director,Secretariat" %}class="editlink" href="{% url "charter_edit_ad" name=doc.name %}"{% endif %}>{{ doc.ad|default:"none" }}</a> </td>
72+
<td><a {% if user|has_role:"Area Director,Secretariat" %}class="editlink" href="{% url "charter_edit_ad" name=doc.name %}"{% endif %}>{{ doc.ad|default:"none" }}</a> </td>
7373
</tr>
7474

7575
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
@@ -94,13 +94,13 @@
9494
</div>
9595

9696
<div class="actions">
97-
{% if not snapshot and user|has_role:"Area Director,Secretariat" %}
97+
{% if not snapshot and can_manage %}
9898
{% if chartering %}
9999
{% url "charter_startstop_process" name=doc.name option='abandon' as abandon_url %}{% if abandon_url %}<a class="button" href="{{ abandon_url }}">Abandon Effort</a>{% endif %}
100100

101-
{% if request.user|has_role:"Secretariat" %}
102-
{% url "charter_approve" name=doc.name as approve_url %}{% if approve_url %}<a class="button" href="{{ approve_url }}">Approve Charter</a>{% endif %}
103-
{% endif %}
101+
{% if user|has_role:"Secretariat" %}
102+
{% url "charter_approve" name=doc.name as approve_url %}{% if approve_url %}<a class="button" href="{{ approve_url }}">Approve Charter</a>{% endif %}
103+
{% endif %}
104104

105105
{% else %}
106106
{% if group.state_id == "proposed" or group.state_id == "bof" %}
@@ -118,7 +118,7 @@
118118

119119
<h3>Charter {{ doc.canonical_name }}-{{ doc.rev }}
120120

121-
{% if not snapshot and user|has_role:"Area Director,Secretariat" and chartering and group.state_id != "conclude" %}
121+
{% if not snapshot and can_manage and chartering and group.state_id != "conclude" %}
122122
<a class="edit" href="{% url "charter_submit" name=doc.name %}">Change charter text</a>
123123
{% endif %}
124124
</h3>
@@ -131,7 +131,7 @@ <h3>Charter {{ doc.canonical_name }}-{{ doc.rev }}
131131

132132
{% if not snapshot and chartering %}
133133
<h3>Proposed Milestones
134-
{% if user|has_role:"Area Director,Secretariat" %}
134+
{% if can_manage %}
135135
<a class="edit" href="{% url "group_edit_charter_milestones" group_type=doc.group.type_id acronym=doc.group.acronym %}">Edit charter milestones</a>
136136
{% endif %}
137137
</h3>

ietf/templates/wginfo/group_charter.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
<a href="{% url "doc_view" name=group.charter.name %}">{{ group.charter.name }}-{{ group.charter.rev }}</a> ({{ group.charter.get_state.name }})
4747
{% else %}
4848
none
49-
{% if user|has_role:"Area Director,Secretariat" %}
49+
{% if can_manage %}
5050
- <a href="{% url "ietf.wginfo.edit.submit_initial_charter" group_type=group.type_id acronym=group.acronym %}">Submit Charter</a>
5151
{% endif %}
5252
{% endif %}

ietf/wginfo/edit.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from ietf.group.models import ( Group, Role, GroupEvent, GroupHistory, GroupStateName,
2020
GroupStateTransitions, GroupTypeName, GroupURL, ChangeStateGroupEvent )
2121
from ietf.group.utils import save_group_in_history, can_manage_group_type
22-
from ietf.ietfauth.utils import role_required, has_role
22+
from ietf.ietfauth.utils import has_role
2323
from ietf.person.forms import EmailsField
2424
from ietf.person.models import Person, Email
2525
from ietf.wginfo.mails import email_secretariat
@@ -166,8 +166,9 @@ def submit_initial_charter(request, group_type, acronym=None):
166166
if not group.charter:
167167
group.charter = get_or_create_initial_charter(group, group_type)
168168
group.save()
169+
169170
return redirect('charter_submit', name=group.charter.name, option="initcharter")
170-
171+
171172
@login_required
172173
def edit(request, group_type, acronym=None, action="edit"):
173174
"""Edit or create a group, notifying parties as

ietf/wginfo/milestones.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from ietf.doc.utils import get_chartering_type
1414
from ietf.group.models import Group, GroupMilestone, MilestoneGroupEvent
1515
from ietf.group.utils import save_milestone_in_history, can_manage_group_type, milestone_reviewer_for_group_type
16-
from ietf.ietfauth.utils import role_required, has_role
1716
from ietf.name.models import GroupMilestoneStateName
1817
from ietf.wginfo.mails import email_milestones_changed
1918

@@ -380,7 +379,7 @@ def reset_charter_milestones(request, group_type, acronym):
380379

381380
return redirect('group_edit_charter_milestones', group_type=group.type_id, acronym=group.acronym)
382381

383-
return render('wginfo/reset_charter_milestones.html',
382+
return render(request, 'wginfo/reset_charter_milestones.html',
384383
dict(group=group,
385384
charter_milestones=charter_milestones,
386385
current_milestones=current_milestones,

ietf/wginfo/views.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
from ietf.doc.templatetags.ietf_filters import clean_whitespace
5151
from ietf.group.models import Group, Role
5252
from ietf.group.utils import get_charter_text, can_manage_group_type, milestone_reviewer_for_group_type
53-
from ietf.ietfauth.utils import has_role
5453
from ietf.utils.pipe import pipe
5554

5655
def roles(group, role_name):
@@ -180,6 +179,9 @@ def bofs(request, group_type):
180179
return render(request, 'wginfo/bofs.html',dict(groups=groups))
181180

182181
def chartering_groups(request, group_type):
182+
if group_type != "wg":
183+
raise Http404
184+
183185
charter_states = State.objects.filter(used=True, type="charter").exclude(slug__in=("approved", "notrev"))
184186
groups = Group.objects.filter(type=group_type, charter__states__in=charter_states).select_related("state", "charter")
185187

@@ -305,12 +307,15 @@ def group_charter(request, group_type, acronym):
305307
rg="Research Group",
306308
)
307309

310+
can_manage = can_manage_group_type(request.user, group.type_id)
311+
308312
return render(request, 'wginfo/group_charter.html',
309313
construct_group_menu_context(request, group, "charter", {
310314
"milestones_in_review": group.groupmilestone_set.filter(state="review"),
311315
"milestone_reviewer": milestone_reviewer_for_group_type(group_type),
312316
"requested_close": requested_close,
313-
"long_group_type":long_group_types.get(group_type, "Group")
317+
"long_group_type":long_group_types.get(group_type, "Group"),
318+
"can_manage": can_manage,
314319
}))
315320

316321

0 commit comments

Comments
 (0)