Skip to content

Commit d4dbb1b

Browse files
Allow editing of group non-chartered group descriptions through UI. Fixes ietf-tools#3388. Commit ready for merge.
- Legacy-Id: 19838
1 parent 66687a5 commit d4dbb1b

File tree

4 files changed

+123
-3
lines changed

4 files changed

+123
-3
lines changed

ietf/group/forms.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class GroupForm(forms.Form):
6666
list_email = forms.CharField(max_length=64, required=False)
6767
list_subscribe = forms.CharField(max_length=255, required=False)
6868
list_archive = forms.CharField(max_length=255, required=False)
69+
description = forms.CharField(widget=forms.Textarea, required=False, help_text='Text that appears on the "about" page.')
6970
urls = forms.CharField(widget=forms.Textarea, label="Additional URLs", help_text="Format: https://site/path (Optional description). Separate multiple entries with newline. Prefer HTTPS URLs where possible.", required=False)
7071
resources = forms.CharField(widget=forms.Textarea, label="Additional Resources", help_text="Format: tag value (Optional description). Separate multiple entries with newline. Prefer HTTPS URLs where possible.", required=False)
7172
closing_note = forms.CharField(widget=forms.Textarea, label="Closing note", required=False)
@@ -103,6 +104,9 @@ def __init__(self, *args, **kwargs):
103104

104105
super(self.__class__, self).__init__(*args, **kwargs)
105106

107+
if not group_features or group_features.has_chartering_process:
108+
self.fields.pop('description') # do not show the description field for chartered groups
109+
106110
for role_slug in self.used_roles:
107111
role_name = RoleName.objects.get(slug=role_slug)
108112
fieldname = '%s_roles'%role_slug

ietf/group/tests_info.py

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import io
99
import bleach
1010

11+
from unittest.mock import patch
1112
from pathlib import Path
1213
from pyquery import PyQuery
1314
from tempfile import NamedTemporaryFile
@@ -515,12 +516,16 @@ def test_create(self):
515516
self.assertTrue(len(q('form .has-error')) > 0)
516517

517518
# Ok creation
518-
r = self.client.post(url, dict(acronym="testwg", name="Testing WG", state=bof_state.pk, parent=area.pk))
519+
r = self.client.post(
520+
url,
521+
dict(acronym="testwg", name="Testing WG", state=bof_state.pk, parent=area.pk, description="ignored"),
522+
)
519523
self.assertEqual(r.status_code, 302)
520524
self.assertEqual(len(Group.objects.filter(type="wg")), num_wgs + 1)
521525
group = Group.objects.get(acronym="testwg")
522526
self.assertEqual(group.name, "Testing WG")
523527
self.assertEqual(charter_name_for_group(group), "charter-ietf-testwg")
528+
self.assertEqual(group.description, '', 'Description should be ignored for a WG')
524529

525530
def test_create_rg(self):
526531

@@ -579,6 +584,28 @@ def test_create_based_on_existing_bof(self):
579584
# self.assertEqual(Group.objects.get(acronym=group.acronym).state_id, "proposed")
580585
# self.assertEqual(Group.objects.get(acronym=group.acronym).name, "Test")
581586

587+
588+
def test_create_non_chartered_includes_description(self):
589+
parent = GroupFactory(type_id='area')
590+
group_type = GroupTypeName.objects.filter(used=True, features__has_chartering_process=False).first()
591+
self.assertIsNotNone(group_type)
592+
url = urlreverse('ietf.group.views.edit', kwargs=dict(group_type=group_type.slug, action="create"))
593+
login_testing_unauthorized(self, "secretary", url)
594+
r = self.client.post(
595+
url,
596+
{
597+
'acronym': "testgrp",
598+
'name': "Testing",
599+
'state': GroupStateName.objects.get(slug='active').pk,
600+
'parent': parent.pk,
601+
'description': "not ignored",
602+
},
603+
)
604+
self.assertEqual(r.status_code, 302)
605+
group = Group.objects.get(acronym="testgrp")
606+
self.assertEqual(group.name, "Testing")
607+
self.assertEqual(group.description, 'not ignored', 'Description should not be ignored')
608+
582609
def test_edit_info(self):
583610
group = GroupFactory(acronym='mars',parent=GroupFactory(type_id='area'))
584611
CharterFactory(group=group)
@@ -640,6 +667,7 @@ def test_edit_info(self):
640667
list_email="mars@mail",
641668
list_subscribe="subscribe.mars",
642669
list_archive="archive.mars",
670+
description='ignored'
643671
))
644672
self.assertEqual(r.status_code, 302)
645673

@@ -658,6 +686,7 @@ def test_edit_info(self):
658686
self.assertEqual(group.list_email, "mars@mail")
659687
self.assertEqual(group.list_subscribe, "subscribe.mars")
660688
self.assertEqual(group.list_archive, "archive.mars")
689+
self.assertEqual(group.description, '')
661690

662691
self.assertTrue((Path(settings.CHARTER_PATH) / ("%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev))).exists())
663692
self.assertEqual(len(outbox), 2)
@@ -843,6 +872,60 @@ def test_edit_reviewers(self):
843872
self.assertEqual(review_assignment.state_id, 'accepted')
844873
self.assertEqual(other_review_assignment.state_id, 'assigned')
845874

875+
def test_edit_info_non_chartered_includes_description(self):
876+
group_type = GroupTypeName.objects.filter(used=True, features__has_chartering_process=False).first()
877+
self.assertIsNotNone(group_type)
878+
group = GroupFactory(type_id=group_type.pk, description='Original description')
879+
url = urlreverse('ietf.group.views.edit', kwargs={'acronym': group.acronym, 'action': 'edit'})
880+
PersonFactory(user__username='plain')
881+
self.client.login(username='plain', password='plain+password')
882+
883+
# mock the auth check so we don't have to delve into details of GroupFeatures for testing
884+
with patch('ietf.group.views.can_manage_group', return_value=True):
885+
r = self.client.get(url)
886+
self.assertEqual(r.status_code, 200)
887+
q = PyQuery(r.content)
888+
self.assertTrue(q('textarea[name="description"]'))
889+
890+
with patch('ietf.group.views.can_manage_group', return_value=True):
891+
r = self.client.post(url, {
892+
'name': group.name,
893+
'acronym': group.acronym,
894+
'state': group.state.pk,
895+
'description': 'Updated description',
896+
})
897+
self.assertEqual(r.status_code, 302)
898+
group = Group.objects.get(pk=group.pk) # refresh
899+
self.assertEqual(group.description, 'Updated description')
900+
901+
def test_edit_description_field(self):
902+
group_type = GroupTypeName.objects.filter(used=True, features__has_chartering_process=False).first()
903+
self.assertIsNotNone(group_type)
904+
group = GroupFactory(type_id=group_type.pk, description='Original description')
905+
url = urlreverse('ietf.group.views.edit',
906+
kwargs={'acronym': group.acronym, 'action': 'edit', 'field': 'description'})
907+
PersonFactory(user__username='plain')
908+
self.client.login(username='plain', password='plain+password')
909+
910+
# mock the auth check so we don't have to delve into details of GroupFeatures for testing
911+
with patch('ietf.group.views.can_manage_group', return_value=True):
912+
r = self.client.post(url, {
913+
'description': 'Updated description',
914+
})
915+
self.assertEqual(r.status_code, 302)
916+
group = Group.objects.get(pk=group.pk) # refresh
917+
self.assertEqual(group.description, 'Updated description')
918+
919+
# Convert the group to a chartered type and repeat - should no longer be able to edit the desc
920+
group.type = GroupTypeName.objects.filter(used=True, features__has_chartering_process=True).first()
921+
group.save()
922+
with patch('ietf.group.views.can_manage_group', return_value=True):
923+
r = self.client.post(url, {
924+
'description': 'Ignored description',
925+
})
926+
self.assertEqual(r.status_code, 302)
927+
group = Group.objects.get(pk=group.pk) # refresh
928+
self.assertEqual(group.description, 'Updated description')
846929

847930
def test_conclude(self):
848931
group = GroupFactory(acronym="mars")
@@ -1037,6 +1120,32 @@ def test_need_parent(self):
10371120
self.assertTrue(form.is_valid())
10381121
self._assert_cleaned_data_equal(form.cleaned_data, data)
10391122

1123+
def test_no_description_field_for_chartered_groups(self):
1124+
group = GroupFactory()
1125+
self.assertTrue(
1126+
group.features.has_chartering_process,
1127+
'Group type must have has_chartering_process=True for this test',
1128+
)
1129+
self.assertNotIn('description', GroupForm(group=group).fields)
1130+
self.assertNotIn('description', GroupForm(group_type=group.type).fields)
1131+
self.assertNotIn('description', GroupForm(group=group, group_type=group.type).fields)
1132+
self.assertNotIn('description', GroupForm(data={'description': 'blah'}, group=group).fields)
1133+
self.assertNotIn('description', GroupForm(data={'description': 'blah'}, group_type=group.type).fields)
1134+
self.assertNotIn('description', GroupForm(data={'description': 'blah'}, group=group, group_type=group.type).fields)
1135+
1136+
def test_have_description_field_for_non_chartered_groups(self):
1137+
group = GroupFactory(type_id='dir')
1138+
self.assertFalse(
1139+
group.features.has_chartering_process,
1140+
'Group type must have has_chartering_process=False for this test',
1141+
)
1142+
self.assertIn('description', GroupForm(group=group).fields)
1143+
self.assertIn('description', GroupForm(group_type=group.type).fields)
1144+
self.assertIn('description', GroupForm(group=group, group_type=group.type).fields)
1145+
self.assertIn('description', GroupForm(data={'description': 'blah'}, group=group).fields)
1146+
self.assertIn('description', GroupForm(data={'description': 'blah'}, group_type=group.type).fields)
1147+
self.assertIn('description', GroupForm(data={'description': 'blah'}, group=group, group_type=group.type).fields)
1148+
10401149

10411150
class MilestoneTests(TestCase):
10421151
def create_test_milestones(self):

ietf/group/views.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -884,7 +884,7 @@ def format_resources(resources, fs="\n"):
884884
return fs.join(res)
885885

886886
def diff(attr, name):
887-
if field and attr != field:
887+
if attr not in clean or (field and attr != field):
888888
return
889889
v = getattr(group, attr)
890890
if clean[attr] != v:
@@ -951,6 +951,7 @@ def diff(attr, name):
951951
diff('name', "Name")
952952
diff('acronym', "Acronym")
953953
diff('state', "State")
954+
diff('description', "Description")
954955
diff('parent', "IETF Area" if group.type=="wg" else "Group parent")
955956
diff('list_email', "Mailing list email")
956957
diff('list_subscribe', "Mailing list subscribe address")
@@ -1067,6 +1068,7 @@ def diff(attr, name):
10671068
init = dict(name=group.name,
10681069
acronym=group.acronym,
10691070
state=group.state,
1071+
description = group.description,
10701072
parent=group.parent.id if group.parent else None,
10711073
list_email=group.list_email if group.list_email else None,
10721074
list_subscribe=group.list_subscribe if group.list_subscribe else None,

ietf/templates/group/group_about.html

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,12 @@ <h2>Charter for {% if group.state_id == "proposed" %}proposed{% endif %} {{ grou
246246
{# the linebreaks filter adds <p/>, no surrounding <p/> necessary: #}
247247
{{ group.charter_text|linebreaks }}
248248
{% else %}
249-
<h2>About</h2>
249+
<h2>
250+
About
251+
{% if can_edit_group %}
252+
<a class="btn btn-default btn-xs" href="{% url 'ietf.group.views.edit' acronym=group.acronym field='description' %}">Edit</a>&nbsp;
253+
{% endif %}
254+
</h2>
250255
{% comment %}{{ group.description|default:"No description yet."|linebreaks }}{% endcomment %}
251256
{{ group.description|default:"No description yet."| apply_markup:"restructuredtext" }}
252257
{% endif %}

0 commit comments

Comments
 (0)