Skip to content

Commit 04f5b4a

Browse files
committed
Retain strict acronym validation, specifically disallowing hyphens, for new groups of types that create documents, while allowing existing groups and new non-document-creating groups to validate when they contain hyphens. Fixes ietf-tools#3236. Commitready for merge.
- Legacy-Id: 18936
1 parent 130e953 commit 04f5b4a

4 files changed

Lines changed: 63 additions & 4 deletions

File tree

ietf/group/admin.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
from functools import update_wrapper
77

8+
import debug # pyflakes:ignore
9+
810
from django import forms
911

1012
from django.contrib import admin
@@ -40,8 +42,14 @@ def clean_acronym(self):
4042
See ietf.group.forms.GroupForm.clean_acronym()
4143
'''
4244
acronym = self.cleaned_data['acronym'].strip().lower()
43-
if not re.match(r'^[a-z][a-z0-9]+$', acronym):
44-
raise forms.ValidationError("Acronym is invalid, must be at least two characters and only contain lowercase letters and numbers starting with a letter.")
45+
if not self.instance.pk:
46+
type = self.cleaned_data['type']
47+
if GroupFeatures.objects.get(type=type).has_documents:
48+
if not re.match(r'^[a-z][a-z0-9]+$', acronym):
49+
raise forms.ValidationError("Acronym is invalid, for groups that create documents, the acronym must be at least two characters and only contain lowercase letters and numbers starting with a letter.")
50+
else:
51+
if not re.match(r'^[a-z][a-z0-9-]*[a-z0-9]$', acronym):
52+
raise forms.ValidationError("Acronym is invalid, must be at least two characters and only contain lowercase letters and numbers starting with a letter. It may contain hyphens, but that is discouraged.")
4553
return acronym
4654

4755
def clean_used_roles(self):

ietf/group/factories.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import debug # pyflakes:ignore
44
import factory
55

6+
from typing import List # pyflakes:ignore
7+
68
from ietf.group.models import Group, Role, GroupEvent, GroupMilestone
79
from ietf.review.factories import ReviewTeamSettingsFactory
810

@@ -17,6 +19,7 @@ class Meta:
1719
type_id = 'wg'
1820
list_email = factory.LazyAttribute(lambda a: '%s@ietf.org'% a.acronym)
1921
uses_milestone_dates = True
22+
used_roles = [] # type: List[str]
2023

2124
@factory.lazy_attribute
2225
def parent(self):

ietf/group/forms.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,12 @@ def clean_acronym(self):
148148

149149
acronym = self.cleaned_data['acronym'].strip().lower()
150150

151-
if not re.match(r'^[a-z][a-z0-9]+$', acronym):
152-
raise forms.ValidationError("Acronym is invalid, must be at least two characters and only contain lowercase letters and numbers starting with a letter.")
151+
if self.group_type and GroupFeatures.objects.get(type=self.group_type).has_documents:
152+
if not re.match(r'^[a-z][a-z0-9]+$', acronym):
153+
raise forms.ValidationError("Acronym is invalid, for groups that create documents, the acronym must be at least two characters and only contain lowercase letters and numbers starting with a letter.")
154+
else:
155+
if not re.match(r'^[a-z][a-z0-9-]*[a-z0-9]$', acronym):
156+
raise forms.ValidationError("Acronym is invalid, must be at least two characters and only contain lowercase letters and numbers starting with a letter. It may contain hyphens, but that is discouraged.")
153157

154158
# be careful with acronyms, requiring confirmation to take existing or override historic
155159
existing = Group.objects.filter(acronym__iexact=acronym)

ietf/group/tests_info.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from django.conf import settings
1818
from django.urls import reverse as urlreverse
1919
from django.urls import NoReverseMatch
20+
from django.utils import timezone
2021
from django.contrib.auth.models import User
2122

2223
from django.utils.html import escape
@@ -26,8 +27,10 @@
2627
from ietf.doc.factories import WgDraftFactory, CharterFactory, BallotDocEventFactory
2728
from ietf.doc.models import Document, DocAlias, DocEvent, State
2829
from ietf.doc.utils_charter import charter_name_for_group
30+
from ietf.group.admin import GroupForm as AdminGroupForm
2931
from ietf.group.factories import (GroupFactory, RoleFactory, GroupEventFactory,
3032
DatedGroupMilestoneFactory, DatelessGroupMilestoneFactory)
33+
from ietf.group.forms import GroupForm
3134
from ietf.group.models import Group, GroupEvent, GroupMilestone, GroupStateTransitions, Role
3235
from ietf.group.utils import save_group_in_history, setup_default_community_list_for_group
3336
from ietf.meeting.factories import SessionFactory
@@ -1638,3 +1641,44 @@ def timeout_handler(signum, frame):
16381641

16391642
# If we get here, then there is not an infinite loop
16401643
return
1644+
1645+
class AcronymValidationTests(TestCase):
1646+
1647+
def test_admin_acronym_validation(self):
1648+
now = timezone.now()
1649+
form = AdminGroupForm({'acronym':'shouldpass','name':'should pass','type':'wg','state':'active','used_roles':'[]','time':now})
1650+
self.assertTrue(form.is_valid())
1651+
form = AdminGroupForm({'acronym':'should-fail','name':'should fail','type':'wg','state':'active','used_roles':'[]','time':now})
1652+
self.assertIn('acronym',form.errors)
1653+
form = AdminGroupForm({'acronym':'f','name':'should fail','type':'wg','state':'active','used_roles':'[]','time':now})
1654+
self.assertIn('acronym',form.errors)
1655+
# For the ITU we have a heirarchy of group names that use hyphens as delimeters
1656+
form = AdminGroupForm({'acronym':'should-pass','name':'should pass','type':'sdo','state':'active','used_roles':'[]','time':now})
1657+
self.assertTrue(form.is_valid())
1658+
form = AdminGroupForm({'acronym':'shouldfail-','name':'should fail','type':'wg','state':'active','used_roles':'[]','time':now})
1659+
self.assertIn('acronym',form.errors)
1660+
form = AdminGroupForm({'acronym':'-shouldfail','name':'should fail','type':'wg','state':'active','used_roles':'[]','time':now})
1661+
self.assertIn('acronym',form.errors)
1662+
1663+
wg = GroupFactory(acronym='bad-idea', type_id='wg') # There are some existing wg and programs with hyphens in their acronyms.
1664+
form = AdminGroupForm({'acronym':wg.acronym,'name':wg.name,'type':wg.type_id,'state':wg.state_id,'used_roles':str(wg.used_roles),'time':now},instance=wg)
1665+
self.assertTrue(form.is_valid())
1666+
1667+
def test_groupform_acronym_validation(self):
1668+
form = GroupForm({'acronym':'shouldpass','name':'should pass','state':'active'},group_type='wg')
1669+
self.assertTrue(form.is_valid())
1670+
form = GroupForm({'acronym':'should-fail','name':'should fail','state':'active'},group_type='wg')
1671+
self.assertIn('acronym',form.errors)
1672+
form = GroupForm({'acronym':'f','name':'should fail','state':'active'},group_type='wg')
1673+
self.assertIn('acronym',form.errors)
1674+
form = GroupForm({'acronym':'should-pass','name':'should pass','state':'active'},group_type='sdo')
1675+
self.assertTrue(form.is_valid())
1676+
form = GroupForm({'acronym':'shouldfail-','name':'should fail','state':'active'},group_type='sdo')
1677+
self.assertIn('acronym',form.errors)
1678+
form = GroupForm({'acronym':'-shouldfail','name':'should fail','state':'active'},group_type='sdo')
1679+
self.assertIn('acronym',form.errors)
1680+
1681+
wg = GroupFactory(acronym='bad-idea', type_id='wg')
1682+
form = GroupForm({'acronym':wg.acronym,'name':wg.name,'state':wg.state_id},group=wg, group_type=wg.type_id)
1683+
self.assertTrue(form.is_valid())
1684+

0 commit comments

Comments
 (0)