Add confirmation step so that the new charter effort page allows one
to continue from a BoF (with a warning with confirmation being ticked) and continue from a WG (with another warning and confirmation not being ticked). Also allow overriding a historically used acronym (one that's in the GroupHistory) after confirmation. - Legacy-Id: 4402
This commit is contained in:
parent
2dbed6a439
commit
79b4c0843b
|
@ -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 %}
|
||||
|
|
|
@ -33,7 +33,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
|
||||
|
@ -42,16 +44,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("Acronym used in an existing group. Please pick another.")
|
||||
if GroupHistory.objects.filter(acronym__iexact=acronym):
|
||||
raise forms.ValidationError("Acronym used in 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):
|
||||
|
@ -71,10 +106,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
|
||||
|
@ -85,27 +117,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:
|
||||
|
@ -139,9 +178,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
|
||||
|
||||
|
@ -167,7 +206,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,
|
||||
|
@ -178,7 +217,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))
|
||||
|
@ -223,7 +262,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,
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue