Fix edit pages and milestones functionality to work with RGs - currently the IRTF Chair takes the same role as the AD

- Legacy-Id: 7541
This commit is contained in:
Ole Laursen 2014-03-25 11:50:24 +00:00
parent cb1e72ad21
commit c125ca1c64
16 changed files with 155 additions and 111 deletions

View file

@ -5,6 +5,7 @@ from django.conf import settings
from ietf.group.models import Group, RoleHistory
from ietf.person.models import Email
from ietf.utils.history import get_history_object_for, copy_many_to_many_for_history
from ietf.ietfauth.utils import has_role
def save_group_in_history(group):
@ -98,3 +99,18 @@ def save_milestone_in_history(milestone):
copy_many_to_many_for_history(h, milestone)
return h
def can_manage_group_type(user, group_type):
if group_type == "rg":
return has_role(user, ('IRTF Chair', 'Secretariat'))
elif group_type == "wg":
return has_role(user, ('Area Director', 'Secretariat'))
return has_role(user, 'Secretariat')
def milestone_reviewer_for_group_type(group_type):
if group_type == "rg":
return "IRTF Chair"
else:
return "Area Director"

View file

@ -73,7 +73,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</div></div></li>
<li style="padding-top:0;"><a href="/wg/">Active WGs</a></li>
<li><a href="{% url "ietf.wginfo.views.chartering_wgs" group_type="wg" %}">Chartering WGs</a></li>
<li><a href="{% url "ietf.wginfo.views.chartering_groups" group_type="wg" %}">Chartering WGs</a></li>
<li><a href="{% url "ietf.wginfo.views.bofs" group_type="wg" %}">BoFs</a></li>
<li><a href="http://tools.ietf.org/wg/concluded">Concluded WGs</a></li>
<li><a href="http://www.ietf.org/list/nonwg.html">Non-WG Lists</a></li>

View file

@ -1,12 +1,12 @@
{% extends "base.html" %}
{% block title %}Chartering or Re-Chartering Working Groups{% endblock %}
{% block title %}Chartering or Re-Chartering Groups{% endblock %}
{% block content %}
{% load ietf_filters %}
{% load ballot_icon %}
<h1>Chartering or Re-Chartering Working Groups</h1>
<h1>Chartering or Re-Chartering Groups</h1>
<p>Groups with a charter in state
{% for s in charter_states %}{% if not forloop.first %}, {% if forloop.last %}or {% endif %}{% endif %}<i>{{ s.name }}</i>{% endfor %}.</p>

View file

@ -37,9 +37,10 @@
<a href="{% url "group_charter" group_type=group.type_id acronym=group.acronym %}">{{ group.acronym }} {{ group.type.name }}</a>. Note that some states are
mandatory for group operation and cannot be deactivated.</p>
{% if group.type_id == "wg" %}
<p>You can see the default Working Group I-D State Diagram
in <a href="http://tools.ietf.org/html/rfc6174#section-4.1">Section 4.1 of RFC6174</a>.</p>
{% endif %}
<h3>States</h3>

View file

@ -43,13 +43,14 @@ chairs and delegates, need a Datatracker account to actually do
so. New accounts can be <a href="{% url "create_account" %}">created here</a>.</p>
<form class="edit" action="" method="post">{% csrf_token %}
{% for field in form.hidden_fields %}{{ field }}{% endfor %}
<table>
{% for field in form.visible_fields %}
<tr>
<th>{{ field.label_tag }} {% if field.field.required %}*{% endif %}</th>
<td>{{ field }}
{% if field.name == "ad" and user|has_role:"Area Director" %}
<label><input type="checkbox" name="ad" value="{{ login.pk }}" /> Assign to me</label>
{% if field.name == "ad" and request.user|has_role:"Area Director" %}
<label><input type="checkbox" name="ad" value="{{ request.user.person.pk }}" /> Assign to me</label>
{% endif %}
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
{{ field.errors }}

View file

@ -51,8 +51,7 @@ tr.milestone.add { font-style: italic; }
{% if needs_review %}
Note that as {{ group.type.name }} Chair you cannot edit descriptions of existing
milestones and milestones you add are subject to review by the Area
Director.
milestones and milestones you add are subject to review by the {{ reviewer }}.
{% endif %}
</p>
@ -86,7 +85,7 @@ this list</a> to the milestones currently in use for the {{ group.acronym }} {{
<tr class="edit-milestone{% if form.changed %} changed{% endif %}"><td colspan="2">{% include "wginfo/milestone_form.html" %}</td></tr>
{% endfor %}
<tr class="milestone add"><td></td><td>Add {% if milestone_set == "chartering" %}charter{% endif%} milestone {% if needs_review %}for AD review{% endif %}</td></tr>
<tr class="milestone add"><td></td><td>Add {% if milestone_set == "chartering" %}charter{% endif%} milestone {% if needs_review %}for {{ reviewer }} review{% endif %}</td></tr>
<tr class="edit-milestone template"><td colspan="2">{% include "wginfo/milestone_form.html" with form=empty_form %}</td></tr>
</table>

View file

@ -74,7 +74,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
| <a href="{{ group.list_archive }}">List Archive &raquo;</a>
{% endif %}
{% if group.has_tools_page %}
| <a href="http://tools.ietf.org/wg/{{ group.acronym }}/">Tools WG Page &raquo;</a>
| <a href="http://tools.ietf.org/{{ group.type_id }}/{{ group.acronym }}/">Tools {{ group.type.name }} Page &raquo;</a>
{% endif %}
</div>

View file

@ -12,7 +12,7 @@ h2 a.button { margin-left: 0.5em; font-size: 13px; }
<div class="ietf-box ietf-group-details">
{% if group.state_id == "conclude" %}
<span class="ietf-concluded-warning">Note: The data for concluded WGs
<span class="ietf-concluded-warning">Note: The data for concluded {{ group.type.name }}s
is occasionally incorrect.</span>
{% endif %}
@ -162,7 +162,7 @@ is occasionally incorrect.</span>
{% if milestones_in_review %}
<p>+ {{ milestones_in_review|length }} new milestone{{ milestones_in_review|pluralize }}
currently in Area Director review.</p>
currently in {{ milestone_reviewer }} review.</p>
{% endif %}
{% endblock %}

View file

@ -1,4 +1,4 @@
{# assumes group, form, needs_review are in the context #}
{# assumes group, form, needs_review, reviewer are in the context #}
<input type="hidden" name="prefix" value="{{ form.prefix|default:"" }}"/>
{{ form.id }}
@ -35,7 +35,7 @@
<td>Review:</td>
<td class="accept">
This milestone is not active yet, awaiting
AD acceptance{% if needs_review %}.{% else %}: {{ form.accept }}{% endif %}
{{ reviewer }} acceptance{% if needs_review %}.{% else %}: {{ form.accept }}{% endif %}
</td>
</tr>
{% endif %}

View file

@ -1,4 +1,4 @@
{% autoescape off %}{% filter wordwrap:73 %}{{ milestones|length }} new milestone{{ milestones|pluralize }} in "{{ group.name }}" {% if milestones|length > 1 %}need{% else %}needs{%endif %} an AD review:
{% autoescape off %}{% filter wordwrap:73 %}{{ milestones|length }} new milestone{{ milestones|pluralize }} in "{{ group.name }}" {% if milestones|length > 1 %}need{% else %}needs{%endif %} review by the {{ reviewer }}:
{% for m in milestones %}"{{ m.desc }}"{% if m.days_ready != None %}
Waiting for {{ m.days_ready }} day{{ m.days_ready|pluralize }}.{% endif %}

View file

@ -10,6 +10,7 @@ from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponseForbidden
from django.utils.html import mark_safe
from django.http import Http404, HttpResponse
from django.contrib.auth.decorators import login_required
import debug # pyflakes:ignore
@ -17,7 +18,7 @@ from ietf.doc.models import DocAlias, DocTagName, Document, State, save_document
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
from ietf.group.utils import save_group_in_history, can_manage_group_type
from ietf.ietfauth.utils import role_required, has_role
from ietf.person.forms import EmailsField
from ietf.person.models import Person, Email
@ -34,7 +35,7 @@ class GroupForm(forms.Form):
techadv = EmailsField(label="Technical Advisors", required=False)
delegates = EmailsField(label="Delegates", required=False, help_text=mark_safe("Type in name to search for person<br>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)
parent = forms.ModelChoiceField(Group.objects.filter(state="active").order_by('name'), empty_label="(None)", required=False)
list_email = forms.CharField(max_length=64, required=False)
list_subscribe = forms.CharField(max_length=255, required=False)
list_archive = forms.CharField(max_length=255, required=False)
@ -43,6 +44,7 @@ class GroupForm(forms.Form):
def __init__(self, *args, **kwargs):
self.group = kwargs.pop('group', None)
self.confirmed = kwargs.pop('confirmed', False)
self.group_type = kwargs.pop('group_type', False)
super(self.__class__, self).__init__(*args, **kwargs)
@ -57,11 +59,19 @@ class GroupForm(forms.Form):
if self.group:
self.fields['acronym'].widget.attrs['readonly'] = True
if self.group_type == "rg":
self.fields['ad'].widget = forms.HiddenInput()
self.fields['parent'].queryset = self.fields['parent'].queryset.filter(acronym="irtf")
self.fields['parent'].widget = forms.HiddenInput()
else:
self.fields['parent'].queryset = self.fields['parent'].queryset.filter(type="area")
self.fields['parent'].label = "IETF Area"
def clean_acronym(self):
self.confirm_msg = ""
self.autoenable_confirm = False
# Changing the acronym of an already existing GROUP will cause 404s all
# Changing the acronym of an already existing group will cause 404s all
# over the place, loose history, and generally muck up a lot of
# things, so we don't permit it
if self.group:
@ -69,15 +79,15 @@ class GroupForm(forms.Form):
acronym = self.cleaned_data['acronym'].strip().lower()
# be careful with acronyms, requiring confirmation to take existing or override historic
if not re.match(r'^[a-z][a-z0-9]+$', acronym):
raise forms.ValidationError("Acronym is invalid, must be at least two characters and only contain lowercase letters and numbers starting with a letter.")
# be careful with acronyms, requiring confirmation to take existing or override historic
existing = Group.objects.filter(acronym__iexact=acronym)
if existing:
existing = existing[0]
if existing and existing.type_id == "wg":
if existing and existing.type_id == self.group_type:
if self.confirmed:
return acronym # take over confirmed
@ -93,7 +103,7 @@ class GroupForm(forms.Form):
if existing:
raise forms.ValidationError("Acronym used for an existing group (%s)." % existing.name)
old = GroupHistory.objects.filter(acronym__iexact=acronym, type="wg")
old = GroupHistory.objects.filter(acronym__iexact=acronym, type__in=("wg", "rg"))
if old and not self.confirmed:
self.confirm_msg = "Confirm reusing acronym %s" % old[0].acronym
self.autoenable_confirm = False
@ -120,12 +130,19 @@ def format_urls(urls, fs="\n"):
res.append(u.url)
return fs.join(res)
def get_or_create_initial_charter(group):
def get_or_create_initial_charter(group, group_type):
if group_type == "rg":
top_org = "irtf"
else:
top_org = "ietf"
charter_name = "charter-%s-%s" % (top_org, group.acronym)
try:
charter = Document.objects.get(docalias__name="charter-ietf-%s" % group.acronym)
charter = Document.objects.get(docalias__name=charter_name)
except Document.DoesNotExist:
charter = Document(
name="charter-ietf-" + group.acronym,
name=charter_name,
type_id="charter",
title=group.name,
group=group,
@ -135,26 +152,29 @@ def get_or_create_initial_charter(group):
charter.save()
charter.set_state(State.objects.get(used=True, type="charter", slug="notrev"))
# Create an alias as well
DocAlias.objects.create(
name=charter.name,
document=charter
)
# Create an alias as well
DocAlias.objects.create(name=charter.name, document=charter)
return charter
@role_required('Area Director', 'Secretariat')
@login_required
def submit_initial_charter(request, group_type, acronym=None):
if not can_manage_group_type(request.user, group_type):
return HttpResponseForbidden("You don't have permission to access this view")
group = get_object_or_404(Group, acronym=acronym)
if not group.charter:
group.charter = get_or_create_initial_charter(group)
group.charter = get_or_create_initial_charter(group, group_type)
group.save()
return redirect('charter_submit', name=group.charter.name, option="initcharter")
@role_required('Area Director', 'Secretariat')
@login_required
def edit(request, group_type, acronym=None, action="edit"):
"""Edit or create a GROUP, notifying parties as
"""Edit or create a group, notifying parties as
necessary and logging changes as group events."""
if not can_manage_group_type(request.user, group_type):
return HttpResponseForbidden("You don't have permission to access this view")
if action == "edit":
group = get_object_or_404(Group, acronym=acronym)
new_group = False
@ -164,10 +184,8 @@ def edit(request, group_type, acronym=None, action="edit"):
else:
raise Http404
login = request.user.person
if request.method == 'POST':
form = GroupForm(request.POST, group=group, confirmed=request.POST.get("confirmed", False))
form = GroupForm(request.POST, group=group, confirmed=request.POST.get("confirmed", False), group_type=group_type)
if form.is_valid():
clean = form.cleaned_data
if new_group:
@ -185,7 +203,7 @@ def edit(request, group_type, acronym=None, action="edit"):
e = ChangeStateGroupEvent(group=group, type="changed_state")
e.time = group.time
e.by = login
e.by = request.user.person
e.state_id = clean["state"].slug
e.desc = "Group created in state %s" % clean["state"].name
e.save()
@ -193,8 +211,8 @@ def edit(request, group_type, acronym=None, action="edit"):
save_group_in_history(group)
if action=="charter" and not group.charter: # make sure we have a charter
group.charter = get_or_create_initial_charter(group)
if action == "charter" and not group.charter: # make sure we have a charter
group.charter = get_or_create_initial_charter(group, group_type)
changes = []
@ -266,7 +284,7 @@ def edit(request, group_type, acronym=None, action="edit"):
if changes and not new_group:
for c in changes:
GroupEvent.objects.create(group=group, by=login, type="info_changed", desc=c)
GroupEvent.objects.create(group=group, by=request.user.person, type="info_changed", desc=c)
group.save()
@ -291,28 +309,27 @@ def edit(request, group_type, acronym=None, action="edit"):
urls=format_urls(group.groupurl_set.all()),
)
else:
init = dict(ad=login.id if has_role(request.user, "Area Director") else None,
init = dict(ad=request.user.person.id if group_type == "wg" and has_role(request.user, "Area Director") else None,
)
form = GroupForm(initial=init, group=group)
form = GroupForm(initial=init, group=group, group_type=group_type)
return render(request, 'wginfo/edit.html',
dict(group=group,
form=form,
action=action,
user=request.user,
login=login))
action=action))
class ConcludeForm(forms.Form):
instructions = forms.CharField(widget=forms.Textarea(attrs={'rows': 30}), required=True)
@role_required('Area Director','Secretariat')
@login_required
def conclude(request, group_type, acronym):
"""Request the closing of group, prompting for instructions."""
group = get_object_or_404(Group, type=group_type, acronym=acronym)
login = request.user.person
if not can_manage_group_type(request.user, group_type):
return HttpResponseForbidden("You don't have permission to access this view")
if request.method == 'POST':
form = ConcludeForm(request.POST)
@ -321,7 +338,7 @@ def conclude(request, group_type, acronym):
email_secretariat(request, group, "Request closing of group", instructions)
e = GroupEvent(group=group, by=login)
e = GroupEvent(group=group, by=request.user.person)
e.type = "requested_close"
e.desc = "Requested closing group"
e.save()
@ -334,13 +351,20 @@ def conclude(request, group_type, acronym):
dict(form=form, group=group))
@login_required
def customize_workflow(request, group_type, acronym):
MANDATORY_STATES = ('c-adopt', 'wg-doc', 'sub-pub')
group = get_object_or_404(Group, type=group_type, acronym=acronym)
if not request.user.is_authenticated() or not (has_role(request.user, "Secretariat") or group.role_set.filter(name="chair", person__user=request.user)):
if (not has_role(request.user, "Secretariat") and
not group.role_set.filter(name="chair", person__user=request.user)):
return HttpResponseForbidden("You don't have permission to access this view")
if group_type == "rg":
stream_id = "irtf"
MANDATORY_STATES = ('candidat', 'active', 'rfc-edit', 'pub', 'dead')
else:
stream_id = "ietf"
MANDATORY_STATES = ('c-adopt', 'wg-doc', 'sub-pub')
if request.method == 'POST':
action = request.POST.get("action")
if action == "setstateactive":
@ -365,7 +389,7 @@ def customize_workflow(request, group_type, acronym):
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"))
next_states = State.objects.filter(used=True, type='draft-stream-%s' % stream_id, 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
@ -390,15 +414,14 @@ def customize_workflow(request, group_type, acronym):
return redirect("ietf.wginfo.edit.customize_workflow", group_type=group.type_id, 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"))
tags = DocTagName.objects.filter(slug__in=get_tags_for_stream_id(stream_id))
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")
states = State.objects.filter(used=True, type="draft-stream-%s" % stream_id)
transitions = dict((o.state, o) for o in group.groupstatetransitions_set.all())
for s in states:
s.used = s.slug not in unused_states

View file

@ -10,8 +10,8 @@ from django.conf import settings
from django.core.urlresolvers import reverse as urlreverse
from ietf.utils.mail import send_mail, send_mail_text
from ietf.group.models import Group
from ietf.group.utils import milestone_reviewer_for_group_type
def email_secretariat(request, group, subject, text):
to = ["iesg-secretary@ietf.org"]
@ -37,10 +37,12 @@ def email_milestones_changed(request, group, changes):
u"Milestones changed for %s %s" % (group.acronym, group.type.name),
text)
# first send to AD and chairs
# first send to management and chairs
to = []
if group.ad:
to.append(group.ad.role_email("ad").formatted_email())
elif group.type_id == "rg":
to.append("IRTF Chair <irtf-chair@irtf.org>")
for r in group.role_set.filter(name="chair"):
to.append(r.formatted_email())
@ -48,7 +50,7 @@ def email_milestones_changed(request, group, changes):
if to:
wrap_up_email(to, u"\n\n".join(c + "." for c in changes))
# then send to WG
# then send to group
if group.list_email:
review_re = re.compile("Added .* for review, due")
to = [ group.list_email ]
@ -56,11 +58,17 @@ def email_milestones_changed(request, group, changes):
def email_milestone_review_reminder(group, grace_period=7):
"""Email reminders about milestones needing review to AD."""
if not group.ad:
"""Email reminders about milestones needing review to management."""
to = []
if group.ad:
to.append(group.ad.role_email("ad").formatted_email())
elif group.type_id == "rg":
to.append("IRTF Chair <irtf-chair@irtf.org>")
if not to:
return False
to = [group.ad.role_email("ad").formatted_email()]
cc = [r.formatted_email() for r in group.role_set.filter(name="chair")]
now = datetime.datetime.now()
@ -84,6 +92,7 @@ def email_milestone_review_reminder(group, grace_period=7):
"wginfo/reminder_milestones_need_review.txt",
dict(group=group,
milestones=milestones,
reviewer=milestone_reviewer_for_group_type(group.type_id),
url=settings.IDTRACKER_BASE_URL + urlreverse("group_edit_milestones", kwargs=dict(group_type=group.type_id, acronym=group.acronym)),
cc=cc,
)

View file

@ -6,13 +6,13 @@ import json
from django import forms
from django.http import HttpResponse, HttpResponseForbidden, HttpResponseBadRequest
from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.template import RequestContext
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
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
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
@ -108,23 +108,19 @@ class MilestoneForm(forms.Form):
return r
@role_required('WG Chair', 'Area Director', 'Secretariat')
@login_required
def edit_milestones(request, group_type, acronym, milestone_set="current"):
# milestones_set + needs_review: we have several paths into this view
# AD/Secr. -> all actions on current + add new
# management (IRTF chair/AD/...)/Secr. -> all actions on current + add new
# group chair -> limited actions on current + add new for review
# (re)charter -> all actions on existing in state charter + add new in state charter
#
# For charters we store the history on the charter document to not confuse people.
login = request.user.person
group = get_object_or_404(Group, type=group_type, acronym=acronym)
needs_review = False
if not has_role(request.user, ("Area Director", "Secretariat")):
if group.role_set.filter(name="chair", person=login):
if not can_manage_group_type(request.user, group_type):
if group.role_set.filter(name="chair", person__user=request.user):
if milestone_set == "current":
needs_review = True
else:
@ -298,10 +294,10 @@ def edit_milestones(request, group_type, acronym, milestone_set="current"):
if milestone_set == "charter":
DocEvent.objects.create(doc=group.charter, type="changed_charter_milestone",
by=login, desc=change)
by=request.user.person, desc=change)
else:
MilestoneGroupEvent.objects.create(group=group, type="changed_milestone",
by=login, desc=change, milestone=f.milestone)
by=request.user.person, desc=change, milestone=f.milestone)
changes.append(change)
@ -322,27 +318,25 @@ def edit_milestones(request, group_type, acronym, milestone_set="current"):
forms.sort(key=lambda f: f.milestone.due if f.milestone else datetime.date.max)
return render_to_response('wginfo/edit_milestones.html',
dict(group=group,
title=title,
forms=forms,
form_errors=form_errors,
empty_form=empty_form,
milestone_set=milestone_set,
finished_milestone_text=finished_milestone_text,
needs_review=needs_review,
can_reset=can_reset),
context_instance=RequestContext(request))
return render(request, 'wginfo/edit_milestones.html',
dict(group=group,
title=title,
forms=forms,
form_errors=form_errors,
empty_form=empty_form,
milestone_set=milestone_set,
finished_milestone_text=finished_milestone_text,
needs_review=needs_review,
reviewer=milestone_reviewer_for_group_type(group_type),
can_reset=can_reset))
@role_required('WG Chair', 'Area Director', 'Secretariat')
@login_required
def reset_charter_milestones(request, group_type, acronym):
"""Reset charter milestones to the currently in-use milestones."""
login = request.user.person
group = get_object_or_404(Group, type=group_type, acronym=acronym)
if (not has_role(request.user, ("Area Director", "Secretariat")) and
not group.role_set.filter(name="chair", person=login)):
if (not can_manage_group_type(request.user, group_type) and
not group.role_set.filter(name="chair", person__user=request.user)):
return HttpResponseForbidden("You are not chair of this group.")
current_milestones = group.groupmilestone_set.filter(state="active")
@ -364,7 +358,7 @@ def reset_charter_milestones(request, group_type, acronym):
DocEvent.objects.create(type="changed_charter_milestone",
doc=group.charter,
desc='Deleted milestone "%s"' % m.desc,
by=login,
by=request.user.person,
)
# add current
@ -380,18 +374,17 @@ def reset_charter_milestones(request, group_type, acronym):
DocEvent.objects.create(type="changed_charter_milestone",
doc=group.charter,
desc='Added milestone "%s", due %s, from current group milestones' % (new.desc, new.due.strftime("%B %Y")),
by=login,
by=request.user.person,
)
return redirect('group_edit_charter_milestones', group_type=group.type_id, acronym=group.acronym)
return render_to_response('wginfo/reset_charter_milestones.html',
dict(group=group,
charter_milestones=charter_milestones,
current_milestones=current_milestones,
),
context_instance=RequestContext(request))
return render('wginfo/reset_charter_milestones.html',
dict(group=group,
charter_milestones=charter_milestones,
current_milestones=current_milestones,
))
def ajax_search_docs(request, group_type, acronym):

View file

@ -90,12 +90,12 @@ class GroupPagesTests(TestCase):
self.assertTrue(chair.address in r.content)
self.assertTrue("This is a charter." in r.content)
def test_chartering_wgs(self):
def test_chartering_groups(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', kwargs=dict(group_type="wg"))
url = urlreverse('ietf.wginfo.views.chartering_groups', kwargs=dict(group_type="wg"))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)

View file

@ -14,7 +14,7 @@ urlpatterns = patterns('',
(r'^1wg-summary-by-acronym.txt', views.wg_summary_acronym),
(r'^1wg-charters.txt', views.wg_charters),
(r'^1wg-charters-by-acronym.txt', views.wg_charters_by_acronym),
(r'^chartering/$', views.chartering_wgs),
(r'^chartering/$', views.chartering_groups),
(r'^bofs/$', views.bofs),
(r'^chartering/create/$', edit.edit, {'action': "charter"}, "group_create"),
(r'^bofs/create/$', edit.edit, {'action': "create"}, "bof_create"),

View file

@ -45,11 +45,11 @@ from django.views.decorators.cache import cache_page
from django.db.models import Q
from ietf.doc.views_search import SearchForm, retrieve_search_results
from ietf.group.models import Group, 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.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
@ -148,7 +148,8 @@ def active_groups(request, group_type):
return active_wgs(request)
elif group_type == "rg":
return active_rgs(request)
raise Http404
else:
raise Http404
def active_wgs(request):
areas = Group.objects.filter(type="area", state="active").order_by("name")
@ -178,14 +179,14 @@ def bofs(request, group_type):
groups = Group.objects.filter(type=group_type, state="bof")
return render(request, 'wginfo/bofs.html',dict(groups=groups))
def chartering_wgs(request, group_type):
def chartering_groups(request, group_type):
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")
for g in groups:
g.chartering_type = get_chartering_type(g.charter)
return render(request, 'wginfo/chartering_wgs.html',
return render(request, 'wginfo/chartering_groups.html',
dict(charter_states=charter_states,
groups=groups))
@ -195,18 +196,18 @@ def construct_group_menu_context(request, group, selected, others):
actions = []
is_chair = group.has_role(request.user, "chair")
is_ad_or_secretariat = has_role(request.user, ("Area Director", "Secretariat"))
can_manage = can_manage_group_type(request.user, group.type_id)
if group.state_id != "proposed" and (is_chair or is_ad_or_secretariat):
if group.state_id != "proposed" and (is_chair or can_manage):
actions.append((u"Add or edit milestones", urlreverse("group_edit_milestones", kwargs=dict(group_type=group.type_id, acronym=group.acronym))))
if group.state_id != "conclude" and is_ad_or_secretariat:
if group.state_id != "conclude" and can_manage:
actions.append((u"Edit group", urlreverse("group_edit", kwargs=dict(group_type=group.type_id, acronym=group.acronym))))
if is_chair or is_ad_or_secretariat:
if is_chair or can_manage:
actions.append((u"Customize workflow", urlreverse("ietf.wginfo.edit.customize_workflow", kwargs=dict(group_type=group.type_id, acronym=group.acronym))))
if group.state_id in ("active", "dormant") and is_ad_or_secretariat:
if group.state_id in ("active", "dormant") and can_manage:
actions.append((u"Request closing group", urlreverse("ietf.wginfo.edit.conclude", kwargs=dict(group_type=group.type_id, acronym=group.acronym))))
d = {
@ -307,6 +308,7 @@ def group_charter(request, group_type, acronym):
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")
}))