Introduce basic charter support for non-WG groups

- Legacy-Id: 7550
This commit is contained in:
Ole Laursen 2014-03-27 17:06:56 +00:00
parent 25625a0241
commit d3efeb421f
8 changed files with 82 additions and 54 deletions

View file

@ -1,6 +1,6 @@
import os, datetime, shutil, textwrap, json
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound, Http404
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound, HttpResponseForbidden, Http404
from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.core.urlresolvers import reverse as urlreverse
from django.template import RequestContext
@ -9,6 +9,7 @@ from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
import debug # pyflakes:ignore
@ -21,7 +22,7 @@ from ietf.doc.utils_charter import ( historic_milestones_for_charter,
approved_revision, default_review_text, default_action_text, email_state_changed,
generate_ballot_writeup, generate_issue_ballot_mail, next_approved_revision, next_revision )
from ietf.group.models import ChangeStateGroupEvent, MilestoneGroupEvent
from ietf.group.utils import save_group_in_history, save_milestone_in_history
from ietf.group.utils import save_group_in_history, save_milestone_in_history, can_manage_group_type
from ietf.iesg.models import TelechatDate
from ietf.ietfauth.utils import has_role, role_required
from ietf.name.models import GroupStateName
@ -33,25 +34,34 @@ from ietf.wginfo.mails import email_secretariat
class ChangeStateForm(forms.Form):
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)
charter_state = forms.ModelChoiceField(State.objects.filter(used=True, type="charter"), label="Charter state", empty_label=None, required=False)
initial_time = forms.IntegerField(initial=0, label="Review time", help_text="(in weeks)", required=False)
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"))
comment = forms.CharField(widget=forms.Textarea, help_text="Optional comment for the charter history", required=False)
def __init__(self, *args, **kwargs):
self.hide = kwargs.pop('hide', None)
group = kwargs.pop('group')
super(ChangeStateForm, self).__init__(*args, **kwargs)
state_field = self.fields["charter_state"]
if group.type_id == "wg":
state_field.queryset = state_field.queryset.filter(slug__in=("infrev", "intrev", "extrev", "iesgrev"))
else:
state_field.queryset = state_field.queryset.filter(slug__in=("notrev", "approved"))
# hide requested fields
if self.hide:
for f in self.hide:
self.fields[f].widget = forms.HiddenInput
@role_required("Area Director", "Secretariat")
@login_required
def change_state(request, name, option=None):
"""Change state of charter, notifying parties as necessary and
logging the change as a comment."""
charter = get_object_or_404(Document, type="charter", name=name)
group = charter.group
if not can_manage_group_type(request.user, group.type_id):
return HttpResponseForbidden("You don't have permission to access this view")
chartering_type = get_chartering_type(charter)
initial_review = charter.latest_event(InitialReviewDocEvent, type="initial_review")
@ -61,13 +71,17 @@ def change_state(request, name, option=None):
login = request.user.person
if request.method == 'POST':
form = ChangeStateForm(request.POST)
form = ChangeStateForm(request.POST, group=group)
if form.is_valid():
clean = form.cleaned_data
charter_rev = charter.rev
if option in ("initcharter", "recharter"):
charter_state = State.objects.get(used=True, type="charter", slug="infrev")
if group.type_id == "wg":
charter_state = State.objects.get(used=True, type="charter", slug="infrev")
else:
charter_state = clean['charter_state']
# make sure we have the latest revision set, if we
# abandoned a charter before, we could have reset the
# revision to latest approved
@ -142,6 +156,8 @@ def change_state(request, name, option=None):
default_action_text(group, charter, login)
elif charter_state.slug == "iesgrev":
create_ballot_if_not_open(charter, login, "approve")
elif charter_state.slug == "approved":
fix_charter_revision_after_approval(charter, login)
if charter_state.slug == "infrev" and clean["initial_time"] and clean["initial_time"] != 0:
e = InitialReviewDocEvent(type="initial_review", by=login, doc=charter)
@ -151,10 +167,10 @@ def change_state(request, name, option=None):
return redirect('doc_view', name=charter.name)
else:
if option == "recharter":
if option == "recharter" and group.type_id == "wg":
hide = ['initial_time', 'charter_state', 'message']
init = dict()
elif option == "initcharter":
elif option == "initcharter" and group.type_id == "wg":
hide = ['charter_state']
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))
elif option == "abandon":
@ -164,7 +180,7 @@ def change_state(request, name, option=None):
hide = ['initial_time']
s = charter.get_state()
init = dict(charter_state=s.pk if s else None)
form = ChangeStateForm(hide=hide, initial=init)
form = ChangeStateForm(hide=hide, initial=init, group=group)
prev_charter_state = None
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 @@ class UploadForm(forms.Form):
else:
destination.write(self.cleaned_data['content'].encode("utf-8"))
@role_required('Area Director','Secretariat')
def submit(request, name=None, acronym=None, option=None):
if name:
if not name.startswith('charter-'):
name = "charter-ietf-" + name
elif acronym:
name = "charter-ietf-" + acronym
@login_required
def submit(request, name=None, option=None):
if not name.startswith('charter-'):
raise Http404
charter = get_object_or_404(Document, type="charter", name=name)
group = charter.group
login = request.user.person
if not can_manage_group_type(request.user, group.type_id):
return HttpResponseForbidden("You don't have permission to access this view")
path = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (charter.canonical_name(), charter.rev))
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):
charter.rev = next_rev
e = NewRevisionDocEvent(doc=charter, by=login, type="new_revision")
e = NewRevisionDocEvent(doc=charter, by=request.user.person, type="new_revision")
e.desc = "New version available: <b>%s-%s.txt</b>" % (charter.canonical_name(), charter.rev)
e.rev = charter.rev
e.save()
@ -423,7 +438,8 @@ def submit(request, name=None, acronym=None, option=None):
return render_to_response('doc/charter/submit.html',
{'form': form,
'next_rev': next_rev,
'group': group },
'group': group,
'name': name },
context_instance=RequestContext(request))
class AnnouncementTextForm(forms.Form):
@ -568,6 +584,26 @@ def ballot_writeupnotes(request, name):
),
context_instance=RequestContext(request))
def fix_charter_revision_after_approval(charter, by):
# according to spec, 00-02 becomes 01, so copy file and record new revision
try:
old = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.canonical_name(), charter.rev))
new = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.canonical_name(), next_approved_revision(charter.rev)))
shutil.copy(old, new)
except IOError:
return HttpResponse("There was an error copying %s to %s" %
('%s-%s.txt' % (charter.canonical_name(), charter.rev),
'%s-%s.txt' % (charter.canonical_name(), next_approved_revision(charter.rev))))
e = NewRevisionDocEvent(doc=charter, by=by, type="new_revision")
e.rev = next_approved_revision(charter.rev)
e.desc = "New version available: <b>%s-%s.txt</b>" % (charter.canonical_name(), e.rev)
e.save()
charter.rev = e.rev
charter.time = e.time
charter.save()
@role_required("Secretariat")
def approve(request, name):
"""Approve charter, changing state, fixing revision, copying file to final location."""
@ -618,24 +654,7 @@ def approve(request, name):
e = add_state_change_event(charter, login, prev_charter_state, new_charter_state)
# according to spec, 00-02 becomes 01, so copy file and record new revision
try:
old = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.canonical_name(), charter.rev))
new = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.canonical_name(), next_approved_revision(charter.rev)))
shutil.copy(old, new)
except IOError:
return HttpResponse("There was an error copying %s to %s" %
('%s-%s.txt' % (charter.canonical_name(), charter.rev),
'%s-%s.txt' % (charter.canonical_name(), next_approved_revision(charter.rev))))
e = NewRevisionDocEvent(doc=charter, by=login, type="new_revision")
e.rev = next_approved_revision(charter.rev)
e.desc = "New version available: <b>%s-%s.txt</b>" % (charter.canonical_name(), e.rev)
e.save()
charter.rev = e.rev
charter.time = e.time
charter.save()
fix_charter_revision_after_approval(charter, login)
email_secretariat(request, group, "Charter state changed to %s" % new_charter_state.name, change_description)

View file

@ -53,6 +53,7 @@ from ietf.community.models import CommunityList
from ietf.doc.mails import email_ad
from ietf.doc.views_status_change import RELATION_SLUGS as status_change_relationships
from ietf.group.models import Role
from ietf.group.utils import can_manage_group_type
from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream, user_is_person, role_required
from ietf.name.models import StreamName, BallotPositionName
from ietf.person.models import Email
@ -401,6 +402,8 @@ def document_main(request, name, rev=None):
if chartering and not snapshot:
milestones = doc.group.groupmilestone_set.filter(state="charter")
can_manage = can_manage_group_type(request.user, doc.group.type_id)
return render_to_response("doc/document_charter.html",
dict(doc=doc,
top=top,
@ -413,6 +416,7 @@ def document_main(request, name, rev=None):
ballot_summary=ballot_summary,
group=group,
milestones=milestones,
can_manage=can_manage,
),
context_instance=RequestContext(request))

View file

@ -14,7 +14,7 @@ Charter submission for {{ group.acronym }} {{ group.type.name }}
{% block content %}
<h1>Charter submission for {{ group.acronym }} {{ group.type.name }}</h1>
<p>The text will be submitted as <strong>charter-ietf-{{ group.acronym }}-{{ next_rev }}</strong></p>
<p>The text will be submitted as <strong>{{ name }}-{{ next_rev }}</strong></p>
<form class="edit-info" action="" enctype="multipart/form-data" method="post">{% csrf_token %}
<table>
{% for field in form.visible_fields %}

View file

@ -25,7 +25,7 @@
{% if snapshot %}Snapshot of{% endif %}
{% if doc.get_state_slug != "approved" %}Proposed{% endif %}
Charter for "{{ group.name }}"
(<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 }}
(<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 }}
</div>
<table id="metatable" width="100%">
@ -38,7 +38,7 @@
<td>
<div>
<a title="{{ doc.get_state.desc }}"
{% if not snapshot and user|has_role:"Area Director,Secretariat" %}
{% if not snapshot and can_manage %}
class="editlink" href="{% url "charter_change_state" name=doc.name %}"
{% endif %}>
{{ doc.get_state.name }}
@ -69,7 +69,7 @@
<tr>
<td>Responsible AD:</td>
<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>
<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>
</tr>
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
@ -94,13 +94,13 @@
</div>
<div class="actions">
{% if not snapshot and user|has_role:"Area Director,Secretariat" %}
{% if not snapshot and can_manage %}
{% if chartering %}
{% 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 %}
{% if request.user|has_role:"Secretariat" %}
{% url "charter_approve" name=doc.name as approve_url %}{% if approve_url %}<a class="button" href="{{ approve_url }}">Approve Charter</a>{% endif %}
{% endif %}
{% if user|has_role:"Secretariat" %}
{% url "charter_approve" name=doc.name as approve_url %}{% if approve_url %}<a class="button" href="{{ approve_url }}">Approve Charter</a>{% endif %}
{% endif %}
{% else %}
{% if group.state_id == "proposed" or group.state_id == "bof" %}
@ -118,7 +118,7 @@
<h3>Charter {{ doc.canonical_name }}-{{ doc.rev }}
{% if not snapshot and user|has_role:"Area Director,Secretariat" and chartering and group.state_id != "conclude" %}
{% if not snapshot and can_manage and chartering and group.state_id != "conclude" %}
<a class="edit" href="{% url "charter_submit" name=doc.name %}">Change charter text</a>
{% endif %}
</h3>
@ -131,7 +131,7 @@
{% if not snapshot and chartering %}
<h3>Proposed Milestones
{% if user|has_role:"Area Director,Secretariat" %}
{% if can_manage %}
<a class="edit" href="{% url "group_edit_charter_milestones" group_type=doc.group.type_id acronym=doc.group.acronym %}">Edit charter milestones</a>
{% endif %}
</h3>

View file

@ -46,7 +46,7 @@ is occasionally incorrect.</span>
<a href="{% url "doc_view" name=group.charter.name %}">{{ group.charter.name }}-{{ group.charter.rev }}</a> ({{ group.charter.get_state.name }})
{% else %}
none
{% if user|has_role:"Area Director,Secretariat" %}
{% if can_manage %}
- <a href="{% url "ietf.wginfo.edit.submit_initial_charter" group_type=group.type_id acronym=group.acronym %}">Submit Charter</a>
{% endif %}
{% endif %}

View file

@ -19,7 +19,7 @@ from ietf.doc.utils import get_tags_for_stream_id
from ietf.group.models import ( Group, Role, GroupEvent, GroupHistory, GroupStateName,
GroupStateTransitions, GroupTypeName, GroupURL, ChangeStateGroupEvent )
from ietf.group.utils import save_group_in_history, can_manage_group_type
from ietf.ietfauth.utils import role_required, has_role
from ietf.ietfauth.utils import has_role
from ietf.person.forms import EmailsField
from ietf.person.models import Person, Email
from ietf.wginfo.mails import email_secretariat
@ -166,8 +166,9 @@ def submit_initial_charter(request, group_type, acronym=None):
if not group.charter:
group.charter = get_or_create_initial_charter(group, group_type)
group.save()
return redirect('charter_submit', name=group.charter.name, option="initcharter")
@login_required
def edit(request, group_type, acronym=None, action="edit"):
"""Edit or create a group, notifying parties as

View file

@ -13,7 +13,6 @@ from ietf.doc.models import Document, DocEvent
from ietf.doc.utils import get_chartering_type
from ietf.group.models import Group, GroupMilestone, MilestoneGroupEvent
from ietf.group.utils import save_milestone_in_history, can_manage_group_type, milestone_reviewer_for_group_type
from ietf.ietfauth.utils import role_required, has_role
from ietf.name.models import GroupMilestoneStateName
from ietf.wginfo.mails import email_milestones_changed
@ -380,7 +379,7 @@ def reset_charter_milestones(request, group_type, acronym):
return redirect('group_edit_charter_milestones', group_type=group.type_id, acronym=group.acronym)
return render('wginfo/reset_charter_milestones.html',
return render(request, 'wginfo/reset_charter_milestones.html',
dict(group=group,
charter_milestones=charter_milestones,
current_milestones=current_milestones,

View file

@ -50,7 +50,6 @@ from ietf.doc.utils import get_chartering_type
from ietf.doc.templatetags.ietf_filters import clean_whitespace
from ietf.group.models import Group, Role
from ietf.group.utils import get_charter_text, can_manage_group_type, milestone_reviewer_for_group_type
from ietf.ietfauth.utils import has_role
from ietf.utils.pipe import pipe
def roles(group, role_name):
@ -180,6 +179,9 @@ def bofs(request, group_type):
return render(request, 'wginfo/bofs.html',dict(groups=groups))
def chartering_groups(request, group_type):
if group_type != "wg":
raise Http404
charter_states = State.objects.filter(used=True, type="charter").exclude(slug__in=("approved", "notrev"))
groups = Group.objects.filter(type=group_type, charter__states__in=charter_states).select_related("state", "charter")
@ -305,12 +307,15 @@ def group_charter(request, group_type, acronym):
rg="Research Group",
)
can_manage = can_manage_group_type(request.user, group.type_id)
return render(request, 'wginfo/group_charter.html',
construct_group_menu_context(request, group, "charter", {
"milestones_in_review": group.groupmilestone_set.filter(state="review"),
"milestone_reviewer": milestone_reviewer_for_group_type(group_type),
"requested_close": requested_close,
"long_group_type":long_group_types.get(group_type, "Group")
"long_group_type":long_group_types.get(group_type, "Group"),
"can_manage": can_manage,
}))