Allow editing of group non-chartered group descriptions through UI. Fixes #3388. Commit ready for merge.

- Legacy-Id: 19838
This commit is contained in:
Jennifer Richards 2022-01-12 21:04:13 +00:00
parent 66687a5a37
commit d4dbb1bc1f
4 changed files with 123 additions and 3 deletions

View file

@ -66,6 +66,7 @@ class GroupForm(forms.Form):
list_email = forms.CharField(max_length=64, required=False) list_email = forms.CharField(max_length=64, required=False)
list_subscribe = forms.CharField(max_length=255, required=False) list_subscribe = forms.CharField(max_length=255, required=False)
list_archive = forms.CharField(max_length=255, required=False) list_archive = forms.CharField(max_length=255, required=False)
description = forms.CharField(widget=forms.Textarea, required=False, help_text='Text that appears on the "about" page.')
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) 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)
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) 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)
closing_note = forms.CharField(widget=forms.Textarea, label="Closing note", required=False) closing_note = forms.CharField(widget=forms.Textarea, label="Closing note", required=False)
@ -103,6 +104,9 @@ class GroupForm(forms.Form):
super(self.__class__, self).__init__(*args, **kwargs) super(self.__class__, self).__init__(*args, **kwargs)
if not group_features or group_features.has_chartering_process:
self.fields.pop('description') # do not show the description field for chartered groups
for role_slug in self.used_roles: for role_slug in self.used_roles:
role_name = RoleName.objects.get(slug=role_slug) role_name = RoleName.objects.get(slug=role_slug)
fieldname = '%s_roles'%role_slug fieldname = '%s_roles'%role_slug

View file

@ -8,6 +8,7 @@ import datetime
import io import io
import bleach import bleach
from unittest.mock import patch
from pathlib import Path from pathlib import Path
from pyquery import PyQuery from pyquery import PyQuery
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
@ -515,12 +516,16 @@ class GroupEditTests(TestCase):
self.assertTrue(len(q('form .has-error')) > 0) self.assertTrue(len(q('form .has-error')) > 0)
# Ok creation # Ok creation
r = self.client.post(url, dict(acronym="testwg", name="Testing WG", state=bof_state.pk, parent=area.pk)) r = self.client.post(
url,
dict(acronym="testwg", name="Testing WG", state=bof_state.pk, parent=area.pk, description="ignored"),
)
self.assertEqual(r.status_code, 302) self.assertEqual(r.status_code, 302)
self.assertEqual(len(Group.objects.filter(type="wg")), num_wgs + 1) self.assertEqual(len(Group.objects.filter(type="wg")), num_wgs + 1)
group = Group.objects.get(acronym="testwg") group = Group.objects.get(acronym="testwg")
self.assertEqual(group.name, "Testing WG") self.assertEqual(group.name, "Testing WG")
self.assertEqual(charter_name_for_group(group), "charter-ietf-testwg") self.assertEqual(charter_name_for_group(group), "charter-ietf-testwg")
self.assertEqual(group.description, '', 'Description should be ignored for a WG')
def test_create_rg(self): def test_create_rg(self):
@ -579,6 +584,28 @@ class GroupEditTests(TestCase):
# self.assertEqual(Group.objects.get(acronym=group.acronym).state_id, "proposed") # self.assertEqual(Group.objects.get(acronym=group.acronym).state_id, "proposed")
# self.assertEqual(Group.objects.get(acronym=group.acronym).name, "Test") # self.assertEqual(Group.objects.get(acronym=group.acronym).name, "Test")
def test_create_non_chartered_includes_description(self):
parent = GroupFactory(type_id='area')
group_type = GroupTypeName.objects.filter(used=True, features__has_chartering_process=False).first()
self.assertIsNotNone(group_type)
url = urlreverse('ietf.group.views.edit', kwargs=dict(group_type=group_type.slug, action="create"))
login_testing_unauthorized(self, "secretary", url)
r = self.client.post(
url,
{
'acronym': "testgrp",
'name': "Testing",
'state': GroupStateName.objects.get(slug='active').pk,
'parent': parent.pk,
'description': "not ignored",
},
)
self.assertEqual(r.status_code, 302)
group = Group.objects.get(acronym="testgrp")
self.assertEqual(group.name, "Testing")
self.assertEqual(group.description, 'not ignored', 'Description should not be ignored')
def test_edit_info(self): def test_edit_info(self):
group = GroupFactory(acronym='mars',parent=GroupFactory(type_id='area')) group = GroupFactory(acronym='mars',parent=GroupFactory(type_id='area'))
CharterFactory(group=group) CharterFactory(group=group)
@ -640,6 +667,7 @@ class GroupEditTests(TestCase):
list_email="mars@mail", list_email="mars@mail",
list_subscribe="subscribe.mars", list_subscribe="subscribe.mars",
list_archive="archive.mars", list_archive="archive.mars",
description='ignored'
)) ))
self.assertEqual(r.status_code, 302) self.assertEqual(r.status_code, 302)
@ -658,6 +686,7 @@ class GroupEditTests(TestCase):
self.assertEqual(group.list_email, "mars@mail") self.assertEqual(group.list_email, "mars@mail")
self.assertEqual(group.list_subscribe, "subscribe.mars") self.assertEqual(group.list_subscribe, "subscribe.mars")
self.assertEqual(group.list_archive, "archive.mars") self.assertEqual(group.list_archive, "archive.mars")
self.assertEqual(group.description, '')
self.assertTrue((Path(settings.CHARTER_PATH) / ("%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev))).exists()) self.assertTrue((Path(settings.CHARTER_PATH) / ("%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev))).exists())
self.assertEqual(len(outbox), 2) self.assertEqual(len(outbox), 2)
@ -843,6 +872,60 @@ class GroupEditTests(TestCase):
self.assertEqual(review_assignment.state_id, 'accepted') self.assertEqual(review_assignment.state_id, 'accepted')
self.assertEqual(other_review_assignment.state_id, 'assigned') self.assertEqual(other_review_assignment.state_id, 'assigned')
def test_edit_info_non_chartered_includes_description(self):
group_type = GroupTypeName.objects.filter(used=True, features__has_chartering_process=False).first()
self.assertIsNotNone(group_type)
group = GroupFactory(type_id=group_type.pk, description='Original description')
url = urlreverse('ietf.group.views.edit', kwargs={'acronym': group.acronym, 'action': 'edit'})
PersonFactory(user__username='plain')
self.client.login(username='plain', password='plain+password')
# mock the auth check so we don't have to delve into details of GroupFeatures for testing
with patch('ietf.group.views.can_manage_group', return_value=True):
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(q('textarea[name="description"]'))
with patch('ietf.group.views.can_manage_group', return_value=True):
r = self.client.post(url, {
'name': group.name,
'acronym': group.acronym,
'state': group.state.pk,
'description': 'Updated description',
})
self.assertEqual(r.status_code, 302)
group = Group.objects.get(pk=group.pk) # refresh
self.assertEqual(group.description, 'Updated description')
def test_edit_description_field(self):
group_type = GroupTypeName.objects.filter(used=True, features__has_chartering_process=False).first()
self.assertIsNotNone(group_type)
group = GroupFactory(type_id=group_type.pk, description='Original description')
url = urlreverse('ietf.group.views.edit',
kwargs={'acronym': group.acronym, 'action': 'edit', 'field': 'description'})
PersonFactory(user__username='plain')
self.client.login(username='plain', password='plain+password')
# mock the auth check so we don't have to delve into details of GroupFeatures for testing
with patch('ietf.group.views.can_manage_group', return_value=True):
r = self.client.post(url, {
'description': 'Updated description',
})
self.assertEqual(r.status_code, 302)
group = Group.objects.get(pk=group.pk) # refresh
self.assertEqual(group.description, 'Updated description')
# Convert the group to a chartered type and repeat - should no longer be able to edit the desc
group.type = GroupTypeName.objects.filter(used=True, features__has_chartering_process=True).first()
group.save()
with patch('ietf.group.views.can_manage_group', return_value=True):
r = self.client.post(url, {
'description': 'Ignored description',
})
self.assertEqual(r.status_code, 302)
group = Group.objects.get(pk=group.pk) # refresh
self.assertEqual(group.description, 'Updated description')
def test_conclude(self): def test_conclude(self):
group = GroupFactory(acronym="mars") group = GroupFactory(acronym="mars")
@ -1037,6 +1120,32 @@ class GroupFormTests(TestCase):
self.assertTrue(form.is_valid()) self.assertTrue(form.is_valid())
self._assert_cleaned_data_equal(form.cleaned_data, data) self._assert_cleaned_data_equal(form.cleaned_data, data)
def test_no_description_field_for_chartered_groups(self):
group = GroupFactory()
self.assertTrue(
group.features.has_chartering_process,
'Group type must have has_chartering_process=True for this test',
)
self.assertNotIn('description', GroupForm(group=group).fields)
self.assertNotIn('description', GroupForm(group_type=group.type).fields)
self.assertNotIn('description', GroupForm(group=group, group_type=group.type).fields)
self.assertNotIn('description', GroupForm(data={'description': 'blah'}, group=group).fields)
self.assertNotIn('description', GroupForm(data={'description': 'blah'}, group_type=group.type).fields)
self.assertNotIn('description', GroupForm(data={'description': 'blah'}, group=group, group_type=group.type).fields)
def test_have_description_field_for_non_chartered_groups(self):
group = GroupFactory(type_id='dir')
self.assertFalse(
group.features.has_chartering_process,
'Group type must have has_chartering_process=False for this test',
)
self.assertIn('description', GroupForm(group=group).fields)
self.assertIn('description', GroupForm(group_type=group.type).fields)
self.assertIn('description', GroupForm(group=group, group_type=group.type).fields)
self.assertIn('description', GroupForm(data={'description': 'blah'}, group=group).fields)
self.assertIn('description', GroupForm(data={'description': 'blah'}, group_type=group.type).fields)
self.assertIn('description', GroupForm(data={'description': 'blah'}, group=group, group_type=group.type).fields)
class MilestoneTests(TestCase): class MilestoneTests(TestCase):
def create_test_milestones(self): def create_test_milestones(self):

View file

@ -884,7 +884,7 @@ def edit(request, group_type=None, acronym=None, action="edit", field=None):
return fs.join(res) return fs.join(res)
def diff(attr, name): def diff(attr, name):
if field and attr != field: if attr not in clean or (field and attr != field):
return return
v = getattr(group, attr) v = getattr(group, attr)
if clean[attr] != v: if clean[attr] != v:
@ -951,6 +951,7 @@ def edit(request, group_type=None, acronym=None, action="edit", field=None):
diff('name', "Name") diff('name', "Name")
diff('acronym', "Acronym") diff('acronym', "Acronym")
diff('state', "State") diff('state', "State")
diff('description', "Description")
diff('parent', "IETF Area" if group.type=="wg" else "Group parent") diff('parent', "IETF Area" if group.type=="wg" else "Group parent")
diff('list_email', "Mailing list email") diff('list_email', "Mailing list email")
diff('list_subscribe', "Mailing list subscribe address") diff('list_subscribe', "Mailing list subscribe address")
@ -1067,6 +1068,7 @@ def edit(request, group_type=None, acronym=None, action="edit", field=None):
init = dict(name=group.name, init = dict(name=group.name,
acronym=group.acronym, acronym=group.acronym,
state=group.state, state=group.state,
description = group.description,
parent=group.parent.id if group.parent else None, parent=group.parent.id if group.parent else None,
list_email=group.list_email if group.list_email else None, list_email=group.list_email if group.list_email else None,
list_subscribe=group.list_subscribe if group.list_subscribe else None, list_subscribe=group.list_subscribe if group.list_subscribe else None,

View file

@ -246,7 +246,12 @@
{# the linebreaks filter adds <p/>, no surrounding <p/> necessary: #} {# the linebreaks filter adds <p/>, no surrounding <p/> necessary: #}
{{ group.charter_text|linebreaks }} {{ group.charter_text|linebreaks }}
{% else %} {% else %}
<h2>About</h2> <h2>
About
{% if can_edit_group %}
<a class="btn btn-default btn-xs" href="{% url 'ietf.group.views.edit' acronym=group.acronym field='description' %}">Edit</a>&nbsp;
{% endif %}
</h2>
{% comment %}{{ group.description|default:"No description yet."|linebreaks }}{% endcomment %} {% comment %}{{ group.description|default:"No description yet."|linebreaks }}{% endcomment %}
{{ group.description|default:"No description yet."| apply_markup:"restructuredtext" }} {{ group.description|default:"No description yet."| apply_markup:"restructuredtext" }}
{% endif %} {% endif %}