Merged in fixes from Ole, from branch/charter:r4382-4407, and some changes from Henrik (removing the use of pos.blocked in views_ballot.py, and setting a ballot id in idrfc/testsREDESIGN.py) to make idrfc.EditPositionTestCase succeed.
- Legacy-Id: 4444
This commit is contained in:
commit
afe3ee71b8
|
@ -86,6 +86,7 @@ GROUP_EVENT_CHOICES = [
|
|||
("changed_state", "Changed state"),
|
||||
("added_comment", "Added comment"),
|
||||
("info_changed", "Changed metadata"),
|
||||
("requested_close", "Requested closing group"),
|
||||
]
|
||||
|
||||
class GroupEvent(models.Model):
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
from ietf.idtracker.models import InternetDraft, IDInternal, BallotInfo, IESGDiscuss, IESGLogin, DocumentComment, Acronym, IDState
|
||||
from ietf.idrfc.models import RfcEditorQueue
|
||||
from ietf.ipr.models import IprRfc, IprDraft, IprDetail
|
||||
from ietf.doc.models import BallotDocEvent
|
||||
|
||||
import re
|
||||
from datetime import date
|
||||
|
|
|
@ -40,6 +40,7 @@ from django.core.urlresolvers import reverse as urlreverse
|
|||
from django.conf import settings
|
||||
|
||||
from pyquery import PyQuery
|
||||
import debug
|
||||
|
||||
from ietf.doc.models import *
|
||||
from ietf.name.models import *
|
||||
|
@ -505,7 +506,8 @@ class EditPositionTestCase(django.test.TestCase):
|
|||
|
||||
def test_cannot_edit_position_as_pre_ad(self):
|
||||
draft = make_test_data()
|
||||
url = urlreverse('doc_edit_position', kwargs=dict(name=draft.name))
|
||||
url = urlreverse('doc_edit_position', kwargs=dict(name=draft.name,
|
||||
ballot_id=draft.latest_event(BallotDocEvent, type="created_ballot").pk))
|
||||
|
||||
# transform to pre-ad
|
||||
ad_role = Role.objects.filter(name="ad")[0]
|
||||
|
|
|
@ -13,6 +13,8 @@ from django.utils.html import strip_tags
|
|||
from django.utils import simplejson
|
||||
from django.conf import settings
|
||||
|
||||
import debug
|
||||
|
||||
from ietf.utils.mail import send_mail_text, send_mail_preformatted
|
||||
from ietf.ietfauth.decorators import group_required, role_required
|
||||
from ietf.idtracker.templatetags.ietf_filters import in_group
|
||||
|
@ -274,8 +276,10 @@ def edit_positionREDESIGN(request, name, ballot_id):
|
|||
pos.comment = clean["comment"].rstrip()
|
||||
pos.comment_time = old_pos.comment_time if old_pos else None
|
||||
pos.discuss = clean["discuss"].rstrip()
|
||||
if not pos.pos.blocking:
|
||||
if not pos.pos_id == "discuss":
|
||||
pos.discuss = ""
|
||||
# if not pos.pos.blocking:
|
||||
# pos.discuss = ""
|
||||
pos.discuss_time = old_pos.discuss_time if old_pos else None
|
||||
|
||||
changes = []
|
||||
|
@ -350,7 +354,6 @@ def edit_positionREDESIGN(request, name, ballot_id):
|
|||
form = EditPositionForm(initial=initial, ballot_type=ballot.ballot_type)
|
||||
|
||||
blocking_positions = dict((p.pk, p.name) for p in form.fields["position"].queryset.all() if p.blocking)
|
||||
print blocking_positions, form.fields["position"].queryset.all()
|
||||
|
||||
ballot_deferred = None
|
||||
if doc.get_state_slug("%s-iesg" % doc.type_id) == "defer":
|
||||
|
@ -363,7 +366,7 @@ def edit_positionREDESIGN(request, name, ballot_id):
|
|||
return_to_url=return_to_url,
|
||||
old_pos=old_pos,
|
||||
ballot_deferred=ballot_deferred,
|
||||
show_discuss_text=old_pos and old_pos.pos.blocking,
|
||||
show_discuss_text=old_pos and old_pos.pos_id=="discuss",
|
||||
blocking_positions=simplejson.dumps(blocking_positions),
|
||||
),
|
||||
context_instance=RequestContext(request))
|
||||
|
@ -476,7 +479,7 @@ def send_ballot_commentREDESIGN(request, name, ballot_id):
|
|||
subj = []
|
||||
d = ""
|
||||
blocking_name = "DISCUSS"
|
||||
if pos.pos.blocking and pos.discuss:
|
||||
if pos.pos_id == "discuss" and pos.discuss:
|
||||
d = pos.discuss
|
||||
blocking_name = pos.pos.name.upper()
|
||||
subj.append(blocking_name)
|
||||
|
|
|
@ -68,11 +68,14 @@ class SearchForm(forms.Form):
|
|||
def clean(self):
|
||||
q = self.cleaned_data
|
||||
# Reset query['by'] if needed
|
||||
for k in ('author','group','area','ad'):
|
||||
if (q['by'] == k) and not q[k]:
|
||||
if 'by' not in q:
|
||||
q['by'] = None
|
||||
else:
|
||||
for k in ('author','group','area','ad'):
|
||||
if (q['by'] == k) and not q[k]:
|
||||
q['by'] = None
|
||||
if (q['by'] == 'state') and not (q['state'] or q['subState']):
|
||||
q['by'] = None
|
||||
if (q['by'] == 'state') and not (q['state'] or q['subState']):
|
||||
q['by'] = None
|
||||
# Reset other fields
|
||||
for k in ('author','group','area','ad'):
|
||||
if q['by'] != k:
|
||||
|
@ -296,11 +299,14 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
|||
def clean(self):
|
||||
q = self.cleaned_data
|
||||
# Reset query['by'] if needed
|
||||
for k in ('author','group','area','ad'):
|
||||
if (q['by'] == k) and (k not in q or not q[k]):
|
||||
if 'by' not in q:
|
||||
q['by'] = None
|
||||
else:
|
||||
for k in ('author','group','area','ad'):
|
||||
if (q['by'] == k) and (k not in q or not q[k]):
|
||||
q['by'] = None
|
||||
if (q['by'] == 'state') and (not 'state' in q or not 'subState' in q or not (q['state'] or q['subState'])):
|
||||
q['by'] = None
|
||||
if (q['by'] == 'state') and (not 'state' in q or not 'subState' in q or not (q['state'] or q['subState'])):
|
||||
q['by'] = None
|
||||
# Reset other fields
|
||||
for k in ('author','group','area','ad'):
|
||||
if q['by'] != k:
|
||||
|
|
|
@ -16,7 +16,7 @@ from ietf.utils.mail import outbox
|
|||
|
||||
from ietf.person.models import Person, Email
|
||||
from ietf.group.models import Group, Role
|
||||
from ietf.doc.models import Document, BallotPositionDocEvent
|
||||
from ietf.doc.models import Document, BallotDocEvent, BallotPositionDocEvent
|
||||
from ietf.submit.models import IdSubmissionDetail
|
||||
|
||||
class SubmitTestCase(django.test.TestCase):
|
||||
|
@ -163,6 +163,7 @@ class SubmitTestCase(django.test.TestCase):
|
|||
|
||||
# make a discuss to see if the AD gets an email
|
||||
ballot_position = BallotPositionDocEvent()
|
||||
ballot_position.ballot = draft.latest_event(BallotDocEvent, type="created_ballot")
|
||||
ballot_position.pos_id = "discuss"
|
||||
ballot_position.type = "changed_ballot_position"
|
||||
ballot_position.doc = draft
|
||||
|
@ -234,7 +235,7 @@ class SubmitTestCase(django.test.TestCase):
|
|||
self.assertTrue(name in unicode(outbox[-2]))
|
||||
self.assertTrue("mars" in unicode(outbox[-2]))
|
||||
self.assertTrue(draft.ad.role_email("ad").address in unicode(outbox[-2]))
|
||||
self.assertTrue(ballot_position.ad.email_address() in unicode(outbox[-2]))
|
||||
self.assertTrue(ballot_position.ad.role_email("ad").address 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]))
|
||||
|
|
|
@ -53,9 +53,9 @@
|
|||
|
||||
{% else %}
|
||||
{% if group.state_id == "proposed" or group.state_id == "bof" %}
|
||||
- <a href="{% url charter_startstop_process name=doc.name option='initcharter' %}">Charter</a>
|
||||
- <a href="{% url charter_submit name=doc.name option='initcharter' %}">Start chartering</a>
|
||||
{% else %}
|
||||
- <a href="{% url charter_startstop_process name=doc.name option='recharter' %}">Recharter</a>
|
||||
- <a href="{% url charter_submit name=doc.name option='recharter' %}">Recharter</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
@ -88,16 +88,24 @@
|
|||
</tr>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
|
||||
|
||||
<tr>
|
||||
<td>Send notices to:</td>
|
||||
<td>{{ doc.notify|default:"none" }}
|
||||
{% if user|has_role:"Area Director,Secretariat" %}
|
||||
- <a href="{% url charter_edit_notify name=doc.name %}">Change</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><td>Last updated:</td><td> {{ doc.time|date:"Y-m-d" }}</td></tr>
|
||||
|
||||
<tr><td colspan='2'><hr size='1' noshade /></td></tr>
|
||||
</table>
|
||||
|
||||
<div class="actions">
|
||||
<a href="/feed/wgchanges/{{ group.acronym }}/">Atom feed</a>
|
||||
<a href="/feed/group-changes/{{ group.acronym }}/">Atom feed</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -20,7 +20,9 @@ form.change-state .actions {
|
|||
{% block content %}
|
||||
<h1>{{ title }}</h1>
|
||||
|
||||
{% if "Change state" in title %}<p class="helptext">For help on the states, see the <a href="{% url help_charter_states %}">state table</a>.</p>{% endif %}
|
||||
{% if not option %}
|
||||
<p class="helptext">For help on the states, see the <a href="{% url help_charter_states %}">state table</a>.</p>
|
||||
{% endif %}
|
||||
|
||||
<form class="change-state" action="" method="post">
|
||||
<table>
|
||||
|
@ -60,9 +62,10 @@ form.change-state .actions {
|
|||
{% endif %}
|
||||
<tr>
|
||||
<td colspan="2" class="actions">
|
||||
{% if option %}
|
||||
<input type="submit" value="Submit"/>
|
||||
{% else %}
|
||||
{% if option == "initcharter" or option == "recharter" %}
|
||||
<input type="submit" value="Initiate chartering"/>
|
||||
{% endif %}
|
||||
{% if not option or option == "abandon" %}
|
||||
<a href="{% url doc_view name=doc.name %}">Back</a>
|
||||
<input type="submit" value="Save and (possibly) notify Secretariat"/>
|
||||
{% endif %}
|
||||
|
|
32
ietf/templates/wgcharter/edit_notify.html
Normal file
32
ietf/templates/wgcharter/edit_notify.html
Normal file
|
@ -0,0 +1,32 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Set notification list for {{ doc.name }}{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
form.edit-notify td input#id_notify { width: 40em; }
|
||||
form.edit-notify td.actions { padding-top: 1em; }
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% load ietf_filters %}
|
||||
<h1>Set notification list for {{ doc.name }}</h1>
|
||||
|
||||
<form class="edit-notify" action="" method="POST">
|
||||
<table>
|
||||
<tr>
|
||||
<th>{{ form.notify.label_tag }}:</th>
|
||||
<td>{{ form.notify }}
|
||||
<div class="help">{{ form.notify.help_text }}</div>
|
||||
{{ form.notify.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="actions">
|
||||
<a href="{% url doc_view name=doc.name %}">Back</a>
|
||||
<input type="submit" value="Save"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -4,35 +4,20 @@
|
|||
{% if wg %}
|
||||
Edit WG {{ wg.acronym }}
|
||||
{% else %}
|
||||
Create WG
|
||||
Start chartering new WG
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
form.edit-info #id_name {
|
||||
width: 396px;
|
||||
}
|
||||
|
||||
form.edit-info #id_list_email {
|
||||
width: 396px;
|
||||
}
|
||||
|
||||
form.edit-info #id_list_subscribe {
|
||||
width: 396px;
|
||||
}
|
||||
|
||||
form.edit-info #id_list_archive {
|
||||
width: 396px;
|
||||
}
|
||||
|
||||
form.edit-info #id_urls {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
form.edit-info #id_comments {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
form.edit #id_name,
|
||||
form.edit #id_list_email,
|
||||
form.edit #id_list_subscribe,
|
||||
form.edit #id_list_archive,
|
||||
form.edit #id_urls,
|
||||
form.edit #id_comments { width: 400px; }
|
||||
form.edit input[type=checkbox] { vertical-align: middle; }
|
||||
ul.errorlist { border-width: 0px; padding: 0px; margin: 0px;}
|
||||
ul.errorlist li { color: #a00; margin: 0px; padding: 0px; list-style: none; }
|
||||
{% endblock %}
|
||||
|
||||
{% block pagehead %}
|
||||
|
@ -44,11 +29,11 @@ form.edit-info #id_comments {
|
|||
<h1>{% if wg %}
|
||||
Edit WG {{ wg.acronym }}
|
||||
{% else %}
|
||||
Create WG
|
||||
Start chartering new WG
|
||||
{% endif %}
|
||||
</h1>
|
||||
|
||||
<form class="edit-info" action="" method="POST">
|
||||
<form class="edit" action="" method="POST">
|
||||
<table>
|
||||
{% for field in form.visible_fields %}
|
||||
<tr>
|
||||
|
@ -63,6 +48,14 @@ Create WG
|
|||
{{ field.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
{% if field.name == "acronym" and form.confirm_msg %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><input name="confirmed" type="checkbox"{% if form.autoenable_confirm %} checked="checked"{% endif %}/>
|
||||
{{ form.confirm_msg }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td></td>
|
||||
|
@ -71,7 +64,7 @@ Create WG
|
|||
<a href="{% url wg_charter acronym=wg.acronym %}">Back</a>
|
||||
<input type="submit" value="Save"/>
|
||||
{% else %}
|
||||
<input type="submit" value="Create"/>
|
||||
<input type="submit" value="Start chartering"/>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -83,4 +76,15 @@ Create WG
|
|||
<script type="text/javascript" src="/js/lib/jquery.tokeninput.js"></script>
|
||||
<script type="text/javascript" src="/js/lib/json2.js"></script>
|
||||
<script type="text/javascript" src="/js/emails-field.js"></script>
|
||||
<script>
|
||||
jQuery(function () {
|
||||
if (jQuery('input[name="confirmed"]').length > 0) {
|
||||
jQuery('input[name="acronym"]').change(function() {
|
||||
// make sure we don't accidentally confirm another acronym
|
||||
jQuery('input[name="confirmed"]').closest("tr").remove();
|
||||
jQuery('input[name="acronym"]').siblings(".errorlist").remove();
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -55,7 +55,14 @@ is occasionally incorrect.</span>
|
|||
<tr><td>Area:</td><td>{{ wg.parent.name }} ({{ wg.parent.acronym }})</td></tr>
|
||||
{% endif %}
|
||||
|
||||
<tr><td>State:</td><td>{{ wg.state.name }}</td></tr>
|
||||
<tr>
|
||||
<td>State:</td>
|
||||
<td>{{ wg.state.name }}
|
||||
{% if requested_close %}
|
||||
(but in the process of being closed)
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Charter:</td>
|
||||
|
|
|
@ -37,6 +37,20 @@ def email_secretariat(request, wg, type, text):
|
|||
)
|
||||
)
|
||||
|
||||
def email_state_changed(request, doc, text):
|
||||
to = [e.strip() for e in doc.notify.replace(';', ',').split(',')]
|
||||
if not to:
|
||||
return
|
||||
|
||||
text = strip_tags(text)
|
||||
text += "\n\n"
|
||||
text += "URL: %s" % (settings.IDTRACKER_BASE_URL + doc.get_absolute_url())
|
||||
|
||||
send_mail_text(request, to, None,
|
||||
"State changed: %s-%s" % (doc.canonical_name(), doc.rev),
|
||||
text)
|
||||
|
||||
|
||||
def generate_ballot_writeup(request, doc):
|
||||
e = WriteupDocEvent()
|
||||
e.type = "changed_ballot_writeup_text"
|
||||
|
|
|
@ -116,6 +116,22 @@ class EditCharterTestCase(django.test.TestCase):
|
|||
charter = Document.objects.get(name=charter.name)
|
||||
self.assertTrue(not charter.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date)
|
||||
|
||||
def test_edit_notify(self):
|
||||
make_test_data()
|
||||
|
||||
charter = Group.objects.get(acronym="mars").charter
|
||||
|
||||
url = urlreverse('charter_edit_notify', kwargs=dict(name=charter.name))
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
# post
|
||||
self.assertTrue(not charter.notify)
|
||||
r = self.client.post(url, dict(notify="someone@example.com, someoneelse@example.com"))
|
||||
self.assertEquals(r.status_code, 302)
|
||||
|
||||
charter = Document.objects.get(name=charter.name)
|
||||
self.assertEquals(charter.notify, "someone@example.com, someoneelse@example.com")
|
||||
|
||||
def test_submit_charter(self):
|
||||
make_test_data()
|
||||
|
||||
|
|
|
@ -6,9 +6,10 @@ urlpatterns = patterns('',
|
|||
url(r'^state/$', "ietf.wgcharter.views.change_state", name='charter_change_state'),
|
||||
url(r'^(?P<option>initcharter|recharter|abandon)/$', "ietf.wgcharter.views.change_state", name='charter_startstop_process'),
|
||||
url(r'^telechat/$', "ietf.wgcharter.views.telechat_date", name='charter_telechat_date'),
|
||||
url(r'^notify/$', "ietf.wgcharter.views.edit_notify", name='charter_edit_notify'),
|
||||
url(r'^(?P<ann>action|review)/$', "ietf.wgcharter.views.announcement_text"),
|
||||
url(r'^ballotwriteupnotes/$', "ietf.wgcharter.views.ballot_writeupnotes"),
|
||||
url(r'^approve/$', "ietf.wgcharter.views.approve", name='charter_approve'),
|
||||
url(r'^submit/$', "ietf.wgcharter.views.submit", name='charter_submit'),
|
||||
|
||||
url(r'^submit/(?P<option>initcharter|recharter)/$', "ietf.wgcharter.views.submit", name='charter_submit'),
|
||||
)
|
||||
|
|
|
@ -9,7 +9,7 @@ from django.template import RequestContext
|
|||
from django import forms
|
||||
from django.forms.util import ErrorList
|
||||
from django.utils import simplejson
|
||||
from django.utils.html import strip_tags
|
||||
from django.utils.html import strip_tags, escape
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.conf import settings
|
||||
|
||||
|
@ -47,8 +47,10 @@ def change_state(request, name, option=None):
|
|||
charter = get_object_or_404(Document, type="charter", name=name)
|
||||
wg = charter.group
|
||||
|
||||
chartering_type = get_chartering_type(charter)
|
||||
|
||||
initial_review = charter.latest_event(InitialReviewDocEvent, type="initial_review")
|
||||
if charter.get_state_slug() != "infrev" or (initial_review and initial_review.expires < datetime.datetime.now()):
|
||||
if charter.get_state_slug() != "infrev" or (initial_review and initial_review.expires < datetime.datetime.now()) or chartering_type == "rechartering":
|
||||
initial_review = None
|
||||
|
||||
login = request.user.get_profile()
|
||||
|
@ -112,6 +114,8 @@ def change_state(request, name, option=None):
|
|||
if message:
|
||||
email_secretariat(request, wg, "state-%s" % charter_state.slug, message)
|
||||
|
||||
email_state_changed(request, charter, "State changed to %s." % charter_state)
|
||||
|
||||
if charter_state.slug == "intrev":
|
||||
if request.POST.get("ballot_wo_extern"):
|
||||
create_ballot_if_not_open(charter, login, "r-wo-ext")
|
||||
|
@ -128,19 +132,17 @@ def change_state(request, name, option=None):
|
|||
e.desc = "Initial review time expires %s" % e.expires.strftime("%Y-%m-%d")
|
||||
e.save()
|
||||
|
||||
if option in ("initcharter", "recharter"):
|
||||
return redirect('charter_submit', name=charter.name)
|
||||
return redirect('doc_view', name=charter.name)
|
||||
else:
|
||||
if option == "recharter":
|
||||
hide = ['charter_state']
|
||||
init = dict(initial_time=1, message='%s has initiated a recharter effort on the WG:\n "%s" (%s)' % (login.plain_name(), wg.name, wg.acronym))
|
||||
hide = ['initial_time', 'charter_state', 'message']
|
||||
init = dict()
|
||||
elif option == "initcharter":
|
||||
hide = ['charter_state']
|
||||
init = dict(initial_time=1, message='%s has initiated chartering of the proposed WG:\n "%s" (%s)' % (login.plain_name(), wg.name, wg.acronym))
|
||||
init = dict(initial_time=1, message='%s has initiated chartering of the proposed WG:\n "%s" (%s).' % (login.plain_name(), wg.name, wg.acronym))
|
||||
elif option == "abandon":
|
||||
hide = ['initial_time', 'charter_state']
|
||||
init = dict(message='%s has abandoned the chartering effort on the WG: "%s" (%s)' % (login.plain_name(), wg.name, wg.acronym))
|
||||
init = dict(message='%s has abandoned the chartering effort on the WG:\n "%s" (%s).' % (login.plain_name(), wg.name, wg.acronym))
|
||||
else:
|
||||
hide = ['initial_time']
|
||||
s = charter.get_state()
|
||||
|
@ -165,8 +167,8 @@ def change_state(request, name, option=None):
|
|||
|
||||
messages = {
|
||||
state_pk("infrev"): 'The WG "%s" (%s) has been set to Informal IESG review by %s.' % (wg.name, wg.acronym, login.plain_name()),
|
||||
state_pk("intrev"): 'The WG "%s" (%s) has been set to Internal review by %s. Please place it on the next IESG telechat and inform the IAB.' % (wg.name, wg.acronym, login.plain_name()),
|
||||
state_pk("extrev"): 'The WG "%s" (%s) has been set to External review by %s. Please send out the external review announcement to the appropriate lists.\n\nSend the announcement to other SDOs: Yes\nAdditional recipients of the announcement: ' % (wg.name, wg.acronym, login.plain_name()),
|
||||
state_pk("intrev"): 'The WG "%s" (%s) has been set to Internal review by %s.\nPlease place it on the next IESG telechat and inform the IAB.' % (wg.name, wg.acronym, login.plain_name()),
|
||||
state_pk("extrev"): 'The WG "%s" (%s) has been set to External review by %s.\nPlease send out the external review announcement to the appropriate lists.\n\nSend the announcement to other SDOs: Yes\nAdditional recipients of the announcement: ' % (wg.name, wg.acronym, login.plain_name()),
|
||||
}
|
||||
|
||||
states_for_ballot_wo_extern = State.objects.filter(type="charter", slug="intrev").values_list("pk", flat=True)
|
||||
|
@ -179,7 +181,7 @@ def change_state(request, name, option=None):
|
|||
prev_charter_state=prev_charter_state,
|
||||
title=title,
|
||||
initial_review=initial_review,
|
||||
chartering_type=get_chartering_type(charter),
|
||||
chartering_type=chartering_type,
|
||||
messages=simplejson.dumps(messages),
|
||||
states_for_ballot_wo_extern=simplejson.dumps(list(states_for_ballot_wo_extern)),
|
||||
),
|
||||
|
@ -223,6 +225,49 @@ def telechat_date(request, name):
|
|||
login=login),
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
class NotifyForm(forms.Form):
|
||||
notify = forms.CharField(max_length=255, help_text="List of email addresses to receive state notifications, separated by comma", label="Notification list", required=False)
|
||||
|
||||
def clean_notify(self):
|
||||
return self.cleaned_data["notify"].strip()
|
||||
|
||||
@role_required("Area Director", "Secretariat")
|
||||
def edit_notify(request, name):
|
||||
doc = get_object_or_404(Document, type="charter", name=name)
|
||||
login = request.user.get_profile()
|
||||
|
||||
init = {'notify': doc.notify}
|
||||
|
||||
if request.method == "POST":
|
||||
form = NotifyForm(request.POST, initial=init)
|
||||
if form.is_valid():
|
||||
n = form.cleaned_data["notify"]
|
||||
if n != doc.notify:
|
||||
save_document_in_history(doc)
|
||||
|
||||
e = DocEvent(doc=doc, by=login)
|
||||
e.desc = "Notification list changed to %s" % (escape(n) or "none")
|
||||
if doc.notify:
|
||||
e.desc += " from %s" % escape(doc.notify)
|
||||
e.type = "changed_document"
|
||||
e.save()
|
||||
|
||||
doc.notify = n
|
||||
doc.time = e.time
|
||||
doc.save()
|
||||
|
||||
return redirect("doc_view", name=doc.name)
|
||||
else:
|
||||
form = NotifyForm(initial=init)
|
||||
|
||||
return render_to_response('wgcharter/edit_notify.html',
|
||||
dict(doc=doc,
|
||||
form=form,
|
||||
user=request.user,
|
||||
login=login),
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
|
||||
class UploadForm(forms.Form):
|
||||
content = forms.CharField(widget=forms.Textarea, label="Charter text", help_text="Edit the charter text", required=False)
|
||||
txt = forms.FileField(label=".txt format", help_text="Or upload a .txt file", required=False)
|
||||
|
@ -242,7 +287,7 @@ class UploadForm(forms.Form):
|
|||
destination.write(self.cleaned_data['content'])
|
||||
|
||||
@role_required('Area Director','Secretariat')
|
||||
def submit(request, name):
|
||||
def submit(request, name, option=None):
|
||||
charter = get_object_or_404(Document, type="charter", name=name)
|
||||
wg = charter.group
|
||||
|
||||
|
@ -281,7 +326,10 @@ def submit(request, name):
|
|||
charter.time = datetime.datetime.now()
|
||||
charter.save()
|
||||
|
||||
return HttpResponseRedirect(reverse('doc_view', kwargs={'name': charter.name}))
|
||||
if option:
|
||||
return redirect('charter_startstop_process', name=charter.name, option=option)
|
||||
else:
|
||||
return redirect("doc_view", name=charter.name)
|
||||
else:
|
||||
init = { "content": ""}
|
||||
c = charter
|
||||
|
|
|
@ -34,7 +34,9 @@ class WGForm(forms.Form):
|
|||
urls = forms.CharField(widget=forms.Textarea, label="Additional URLs", help_text="Format: http://site/path (Optional description). Separate multiple entries with newline.", required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.cur_acronym = kwargs.pop('cur_acronym')
|
||||
self.wg = kwargs.pop('wg', None)
|
||||
self.confirmed = kwargs.pop('confirmed', False)
|
||||
|
||||
super(self.__class__, self).__init__(*args, **kwargs)
|
||||
|
||||
# if previous AD is now ex-AD, append that person to the list
|
||||
|
@ -43,16 +45,49 @@ class WGForm(forms.Form):
|
|||
if ad_pk and ad_pk not in [pk for pk, name in choices]:
|
||||
self.fields['ad'].choices = list(choices) + [("", "-------"), (ad_pk, Person.objects.get(pk=ad_pk).plain_name())]
|
||||
|
||||
self.confirm_msg = ""
|
||||
self.autoenable_confirm = False
|
||||
|
||||
def clean_acronym(self):
|
||||
self.confirm_msg = ""
|
||||
self.autoenable_confirm = False
|
||||
|
||||
acronym = self.cleaned_data['acronym'].strip().lower()
|
||||
|
||||
if not re.match(r'^[-\w]+$', acronym):
|
||||
# be careful with acronyms, requiring confirmation to take existing or override historic
|
||||
|
||||
if self.wg and acronym == self.wg.acronym:
|
||||
return acronym # no change, no check
|
||||
|
||||
if not re.match(r'^[-a-z0-9]+$', acronym):
|
||||
raise forms.ValidationError("Acronym is invalid, may only contain letters, numbers and dashes.")
|
||||
if acronym != self.cur_acronym:
|
||||
if Group.objects.filter(acronym__iexact=acronym):
|
||||
raise forms.ValidationError(mark_safe("The acronym belongs to an existing group. Please pick another,<br/> or go to <a href='%s'>recharter %s</a>" % (reverse("doc_view", None, kwargs={"name":"charter-ietf-%s"%acronym, }), acronym)))
|
||||
if GroupHistory.objects.filter(acronym__iexact=acronym):
|
||||
raise forms.ValidationError("The acronym has been used by a previous group. Please pick another.")
|
||||
|
||||
existing = Group.objects.filter(acronym__iexact=acronym)
|
||||
if existing:
|
||||
existing = existing[0]
|
||||
|
||||
if not self.wg and existing and existing.type_id == "wg":
|
||||
if self.confirmed:
|
||||
return acronym # take over confirmed
|
||||
|
||||
if existing.state_id == "bof":
|
||||
self.confirm_msg = "Turn BoF %s into proposed WG and start chartering it" % existing.acronym
|
||||
self.autoenable_confirm = True
|
||||
raise forms.ValidationError("Warning: Acronym used for an existing BoF (%s)." % existing.name)
|
||||
else:
|
||||
self.confirm_msg = "Set state of %s WG to proposed and start chartering it" % existing.acronym
|
||||
self.autoenable_confirm = False
|
||||
raise forms.ValidationError("Warning: Acronym used for an existing WG (%s, %s)." % (existing.name, existing.state.name if existing.state else "unknown state"))
|
||||
|
||||
if existing:
|
||||
raise forms.ValidationError("Acronym used for an existing group (%s)." % existing.name)
|
||||
|
||||
old = GroupHistory.objects.filter(acronym__iexact=acronym, type="wg")
|
||||
if old and not self.confirmed:
|
||||
self.confirm_msg = "Confirm reusing acronym %s" % old[0].acronym
|
||||
self.autoenable_confirm = False
|
||||
raise forms.ValidationError("Warning: Acronym used for a historic WG.")
|
||||
|
||||
return acronym
|
||||
|
||||
def clean_urls(self):
|
||||
|
@ -72,10 +107,7 @@ def edit(request, acronym=None, action="edit"):
|
|||
"""Edit or create a WG, notifying parties as
|
||||
necessary and logging changes as group events."""
|
||||
if action == "edit":
|
||||
# Editing. Get group
|
||||
wg = get_object_or_404(Group, acronym=acronym)
|
||||
if not wg.charter:
|
||||
raise Http404
|
||||
new_wg = False
|
||||
elif action == "create":
|
||||
wg = None
|
||||
|
@ -86,27 +118,34 @@ def edit(request, acronym=None, action="edit"):
|
|||
login = request.user.get_profile()
|
||||
|
||||
if request.method == 'POST':
|
||||
form = WGForm(request.POST, cur_acronym=wg.acronym if wg else None)
|
||||
form = WGForm(request.POST, wg=wg, confirmed=request.POST.get("confirmed", False))
|
||||
if form.is_valid():
|
||||
r = form.cleaned_data
|
||||
clean = form.cleaned_data
|
||||
if new_wg:
|
||||
# Create WG
|
||||
wg = Group(name=r["name"],
|
||||
acronym=r["acronym"],
|
||||
type=GroupTypeName.objects.get(slug="wg"),
|
||||
state=GroupStateName.objects.get(slug="proposed"))
|
||||
wg.save()
|
||||
|
||||
# get ourselves a proposed WG
|
||||
try:
|
||||
wg = Group.objects.get(acronym=clean["acronym"])
|
||||
|
||||
save_group_in_history(wg)
|
||||
wg.state = GroupStateName.objects.get(slug="proposed")
|
||||
wg.time = datetime.datetime.now()
|
||||
wg.save()
|
||||
except Group.DoesNotExist:
|
||||
wg = Group.objects.create(name=clean["name"],
|
||||
acronym=clean["acronym"],
|
||||
type=GroupTypeName.objects.get(slug="wg"),
|
||||
state=GroupStateName.objects.get(slug="proposed"))
|
||||
|
||||
e = ChangeStateGroupEvent(group=wg, type="changed_state")
|
||||
e.time = datetime.datetime.now()
|
||||
e.time = wg.time
|
||||
e.by = login
|
||||
e.state_id = "proposed"
|
||||
e.desc = "Proposed group"
|
||||
e.save()
|
||||
else:
|
||||
gh = save_group_in_history(wg)
|
||||
save_group_in_history(wg)
|
||||
|
||||
if not wg.charter:
|
||||
if not wg.charter: # make sure we have a charter
|
||||
try:
|
||||
charter = Document.objects.get(docalias__name="charter-ietf-%s" % wg.acronym)
|
||||
except Document.DoesNotExist:
|
||||
|
@ -140,9 +179,9 @@ def edit(request, acronym=None, action="edit"):
|
|||
|
||||
def diff(attr, name):
|
||||
v = getattr(wg, attr)
|
||||
if r[attr] != v:
|
||||
changes.append(desc(name, r[attr], v))
|
||||
setattr(wg, attr, r[attr])
|
||||
if clean[attr] != v:
|
||||
changes.append(desc(name, clean[attr], v))
|
||||
setattr(wg, attr, clean[attr])
|
||||
|
||||
prev_acronym = wg.acronym
|
||||
|
||||
|
@ -168,7 +207,7 @@ def edit(request, acronym=None, action="edit"):
|
|||
|
||||
# update roles
|
||||
for attr, slug, title in [('chairs', 'chair', "Chairs"), ('secretaries', 'secr', "Secretaries"), ('techadv', 'techadv', "Tech Advisors")]:
|
||||
new = r[attr]
|
||||
new = clean[attr]
|
||||
old = Email.objects.filter(role__group=wg, role__name=slug).select_related("person")
|
||||
if set(new) != set(old):
|
||||
changes.append(desc(title,
|
||||
|
@ -179,7 +218,7 @@ def edit(request, acronym=None, action="edit"):
|
|||
Role.objects.get_or_create(name_id=slug, email=e, group=wg, person=e.person)
|
||||
|
||||
# update urls
|
||||
new_urls = r['urls']
|
||||
new_urls = clean['urls']
|
||||
old_urls = format_urls(wg.groupurl_set.order_by('url'), ", ")
|
||||
if ", ".join(sorted(new_urls)) != old_urls:
|
||||
changes.append(desc('Urls', ", ".join(sorted(new_urls)), old_urls))
|
||||
|
@ -203,7 +242,7 @@ def edit(request, acronym=None, action="edit"):
|
|||
wg.save()
|
||||
|
||||
if new_wg:
|
||||
return redirect('charter_startstop_process', name=wg.charter.name, option="initcharter")
|
||||
return redirect('charter_submit', name=wg.charter.name, option="initcharter")
|
||||
|
||||
return redirect('wg_charter', acronym=wg.acronym)
|
||||
else: # form.is_valid()
|
||||
|
@ -224,7 +263,7 @@ def edit(request, acronym=None, action="edit"):
|
|||
else:
|
||||
init = dict(ad=login.id if has_role(request.user, "Area Director") else None,
|
||||
)
|
||||
form = WGForm(initial=init, cur_acronym=wg.acronym if wg else None)
|
||||
form = WGForm(initial=init, wg=wg)
|
||||
|
||||
return render_to_response('wginfo/edit.html',
|
||||
dict(wg=wg,
|
||||
|
@ -236,19 +275,12 @@ def edit(request, acronym=None, action="edit"):
|
|||
|
||||
|
||||
class ConcludeForm(forms.Form):
|
||||
instructions = forms.CharField(widget=forms.Textarea, required=True)
|
||||
instructions = forms.CharField(widget=forms.Textarea(attrs={'rows': 30}), required=True)
|
||||
|
||||
@role_required('Area Director','Secretariat')
|
||||
def conclude(request, acronym):
|
||||
"""Request the closing of a WG, prompting for instructions."""
|
||||
try:
|
||||
wg = Group.objects.get(acronym=acronym)
|
||||
except Group.DoesNotExist:
|
||||
wglist = GroupHistory.objects.filter(acronym=acronym)
|
||||
if wglist:
|
||||
return redirect('wg_conclude', acronym=wglist[0].group.acronym)
|
||||
else:
|
||||
raise Http404
|
||||
wg = get_object_or_404(Group, acronym=acronym)
|
||||
|
||||
login = request.user.get_profile()
|
||||
|
||||
|
@ -259,6 +291,11 @@ def conclude(request, acronym):
|
|||
|
||||
email_secretariat(request, wg, "conclude", instructions)
|
||||
|
||||
e = GroupEvent(group=wg, by=login)
|
||||
e.type = "requested_close"
|
||||
e.desc = "Requested closing group"
|
||||
e.save()
|
||||
|
||||
return redirect('wg_charter', acronym=wg.acronym)
|
||||
else:
|
||||
form = ConcludeForm()
|
||||
|
|
|
@ -120,6 +120,38 @@ class WgEditTestCase(django.test.TestCase):
|
|||
self.assertEquals(group.charter.name, "charter-ietf-testwg")
|
||||
self.assertEquals(group.charter.rev, "00-00")
|
||||
|
||||
def test_create_based_on_existing(self):
|
||||
make_test_data()
|
||||
|
||||
url = urlreverse('wg_create')
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
group = Group.objects.get(acronym="mars")
|
||||
|
||||
# try hijacking area - faulty
|
||||
r = self.client.post(url, dict(name="Test", acronym=group.parent.acronym))
|
||||
self.assertEquals(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertEquals(len(q('form input[name="confirmed"]')), 0) # can't confirm us out of this
|
||||
|
||||
# try elevating BoF to WG
|
||||
group.state_id = "bof"
|
||||
group.save()
|
||||
|
||||
r = self.client.post(url, dict(name="Test", acronym=group.acronym))
|
||||
self.assertEquals(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q('form ul.errorlist')) > 0)
|
||||
self.assertEquals(len(q('form input[name="confirmed"]')), 1)
|
||||
|
||||
self.assertEquals(Group.objects.get(acronym=group.acronym).state_id, "bof")
|
||||
|
||||
# confirm elevation
|
||||
r = self.client.post(url, dict(name="Test", acronym=group.acronym, confirmed="1"))
|
||||
self.assertEquals(r.status_code, 302)
|
||||
self.assertEquals(Group.objects.get(acronym=group.acronym).state_id, "proposed")
|
||||
self.assertEquals(Group.objects.get(acronym=group.acronym).name, "Test")
|
||||
|
||||
def test_edit_info(self):
|
||||
make_test_data()
|
||||
|
|
|
@ -194,15 +194,20 @@ def wg_charter(request, acronym):
|
|||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
fill_in_charter_info(wg)
|
||||
actions = []
|
||||
|
||||
e = wg.latest_event(type__in=("changed_state", "requested_close",))
|
||||
requested_close = wg.state_id != "conclude" and e and e.type == "requested_close"
|
||||
|
||||
if wg.state_id != "conclude":
|
||||
actions.append(("Edit WG", urlreverse("wg_edit", kwargs=dict(acronym=wg.acronym))))
|
||||
|
||||
if wg.state_id == "active" and (not wg.charter or wg.charter.get_state_slug() == "approved"):
|
||||
actions.append(("Conclude WG", urlreverse("wg_conclude", kwargs=dict(acronym=wg.acronym))))
|
||||
if wg.state_id in ("active", "dormant"):
|
||||
actions.append(("Request closing WG", urlreverse("wg_conclude", kwargs=dict(acronym=wg.acronym))))
|
||||
|
||||
context = get_wg_menu_context(wg, "charter")
|
||||
context.update(dict(
|
||||
actions=actions))
|
||||
actions=actions,
|
||||
requested_close=requested_close,
|
||||
))
|
||||
|
||||
return render_to_response('wginfo/wg_charterREDESIGN.html',
|
||||
context,
|
||||
|
|
Loading…
Reference in a new issue