Skip to content

Commit 57a4c9f

Browse files
committed
Added 9 new group features, and changed list-like char fields to json fields, to get better support for using the values as lists. Modified code to use the group features instead of explicit lists of group types in many places in the code.
- Legacy-Id: 15908
1 parent d59eb23 commit 57a4c9f

31 files changed

Lines changed: 829 additions & 184 deletions

ietf/community/utils.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,8 @@ def can_manage_community_list(user, clist):
4747
if has_role(user, 'Secretariat'):
4848
return True
4949

50-
if clist.group.type_id == 'area':
51-
return Role.objects.filter(name__slug='ad', person__user=user, group=clist.group).exists()
52-
elif clist.group.type_id in ('wg', 'rg', 'ag'):
53-
return Role.objects.filter(name__slug='chair', person__user=user, group=clist.group).exists()
54-
elif clist.group.type_id in ('program'):
55-
return Role.objects.filter(name__slug='lead', person__user=user, group=clist.group).exists()
50+
if clist.group.type_id in ['area', 'wg', 'rg', 'ag', 'program', ]:
51+
return Role.objects.filter(name__slug__in=clist.group.features.admin_roles, person__user=user, group=clist.group).exists()
5652

5753
return False
5854

ietf/doc/mails.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,9 @@ def generate_approval_mail_rfc_editor(request, doc):
269269
def generate_publication_request(request, doc):
270270
group_description = ""
271271
if doc.group and doc.group.acronym != "none":
272-
group_description = doc.group.name_with_acronym()
272+
group_description = doc.group.name
273+
if doc.group.type_id not in ("ietf", "irtf", "iab",):
274+
group_description += " %s (%s)" % (doc.group.type, doc.group.acronym)
273275

274276
e = doc.latest_event(ConsensusDocEvent, type="changed_consensus")
275277
consensus = e.consensus if e else None

ietf/doc/templatetags/managed_groups.py

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from django import template
22

3-
from ietf.group.models import Group
3+
from ietf.group.models import Group, Role
44

55
register = template.Library()
66

@@ -9,18 +9,11 @@ def managed_groups(user):
99
if not (user and hasattr(user, "is_authenticated") and user.is_authenticated):
1010
return []
1111

12-
groups = []
13-
# groups.extend(Group.objects.filter(
14-
# role__name__slug='ad',
15-
# role__person__user=user,
16-
# type__slug='area',
17-
# state__slug='active').select_related("type"))
18-
19-
groups.extend(Group.objects.filter(
20-
role__name__slug__in=['chair', 'delegate', 'ad', ],
21-
role__person__user=user,
22-
type__slug__in=('rg', 'wg', 'ag', 'ietf'),
23-
state__slug__in=('active', 'bof')).select_related("type"))
12+
groups = [ g for g in Group.objects.filter(
13+
role__person__user=user,
14+
type__features__has_session_materials=True,
15+
state__slug__in=('active', 'bof')).select_related("type")
16+
if Role.objects.filter(group=g, person__user=user, name__slug__in=g.type.features.matman_roles) ]
2417

2518
return groups
2619

ietf/doc/tests.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -887,17 +887,17 @@ def test_document_nonietf_pubreq_button(self):
887887
self.client.login(username='iab-chair', password='iab-chair+password')
888888
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=doc.name)))
889889
self.assertEqual(r.status_code, 200)
890-
self.assertTrue("Request publication" not in unicontent(r))
890+
self.assertNotIn("Request publication", unicontent(r))
891891

892892
Document.objects.filter(pk=doc.pk).update(stream='iab')
893893
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=doc.name)))
894894
self.assertEqual(r.status_code, 200)
895-
self.assertTrue("Request publication" in unicontent(r))
895+
self.assertIn("Request publication", unicontent(r))
896896

897897
doc.states.add(State.objects.get(type_id='draft-stream-iab',slug='rfc-edit'))
898898
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=doc.name)))
899899
self.assertEqual(r.status_code, 200)
900-
self.assertTrue("Request publication" not in unicontent(r))
900+
self.assertNotIn("Request publication", unicontent(r))
901901

902902

903903
def test_document_bibtex(self):

ietf/doc/views_doc.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
# Copyright The IETF Trust 2016, All Rights Reserved
1+
# Copyright The IETF Trust 2016-2018, All Rights Reserved
2+
23

34
# Parts Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
45
# All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
@@ -160,11 +161,14 @@ def document_main(request, name, rev=None):
160161
iesg_state_summary = doc.friendly_state()
161162
can_edit = has_role(request.user, ("Area Director", "Secretariat"))
162163
stream_slugs = StreamName.objects.values_list("slug", flat=True)
163-
can_change_stream = bool(can_edit or (
164-
request.user.is_authenticated and
165-
Role.objects.filter(name__in=("chair", "secr", "auth", "delegate"),
166-
group__acronym__in=stream_slugs,
167-
person__user=request.user)))
164+
# For some reason, AnonymousUser has __iter__, but is not iterable,
165+
# which causes problems in the filter() below. Work around this:
166+
if request.user.is_authenticated:
167+
roles = [ r for r in Role.objects.filter(group__acronym__in=stream_slugs, person__user=request.user)
168+
if r.name.slug in r.group.type.features.matman_roles ]
169+
else:
170+
roles = []
171+
can_change_stream = bool(can_edit or roles)
168172
can_edit_iana_state = has_role(request.user, ("Secretariat", "IANA"))
169173

170174
can_edit_replaces = has_role(request.user, ("Area Director", "Secretariat", "IRTF Chair", "WG Chair", "RG Chair", "WG Secretary", "RG Secretary"))

ietf/doc/views_draft.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# Copyright The IETF Trust 2010-2019, All Rights Reserved
2+
13
# changing state and metadata on Internet Drafts
24

35
import datetime
@@ -31,7 +33,7 @@
3133
set_replaces_for_document, default_consensus, tags_suffix, )
3234
from ietf.doc.lastcall import request_last_call
3335
from ietf.doc.fields import SearchableDocAliasesField
34-
from ietf.group.models import Group, Role
36+
from ietf.group.models import Group, Role, GroupFeatures
3537
from ietf.iesg.models import TelechatDate
3638
from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream, user_is_person, is_individual_draft_author
3739
from ietf.ietfauth.utils import role_required
@@ -1294,31 +1296,37 @@ class PublicationForm(forms.Form):
12941296
)
12951297

12961298
class AdoptDraftForm(forms.Form):
1297-
group = forms.ModelChoiceField(queryset=Group.objects.filter(type__in=["wg", "rg"], state="active").order_by("-type", "acronym"), required=True, empty_label=None)
1299+
group = forms.ModelChoiceField(queryset=Group.objects.filter(type__features__acts_like_wg=True, state="active").order_by("-type", "acronym"), required=True, empty_label=None)
12981300
newstate = forms.ModelChoiceField(queryset=State.objects.filter(type__in=['draft-stream-ietf','draft-stream-irtf'], used=True).exclude(slug__in=settings.GROUP_STATES_WITH_EXTRA_PROCESSING), required=True, label="State")
12991301
comment = forms.CharField(widget=forms.Textarea, required=False, label="Comment", help_text="Optional comment explaining the reasons for the adoption.", strip=False)
13001302
weeks = forms.IntegerField(required=False, label="Expected weeks in adoption state")
13011303

13021304
def __init__(self, *args, **kwargs):
13031305
user = kwargs.pop("user")
1306+
rg_features = GroupFeatures.objects.get(type_id='rg')
1307+
wg_features = GroupFeatures.objects.get(type_id='wg')
13041308

13051309
super(AdoptDraftForm, self).__init__(*args, **kwargs)
13061310

13071311
state_types = set()
13081312
if has_role(user, "Secretariat"):
13091313
state_types.update(['draft-stream-ietf','draft-stream-irtf'])
13101314
else:
1311-
if has_role(user, "IRTF Chair") or Group.objects.filter(type="rg", state="active", role__person__user=user, role__name__in=("chair", "delegate", "secr")).exists():
1315+
if has_role(user, "IRTF Chair") or Group.objects.filter(type="rg", state="active", role__person__user=user, role__name__in=rg_features.matman_roles).exists():
13121316
state_types.add('draft-stream-irtf')
1313-
if Group.objects.filter(type="wg", state="active", role__person__user=user, role__name__in=("chair", "delegate", "secr")).exists():
1317+
if Group.objects.filter(type="wg", state="active", role__person__user=user, role__name__in=wg_features.matman_roles).exists():
13141318
state_types.add('draft-stream-ietf')
1319+
1320+
1321+
1322+
13151323
state_choices = State.objects.filter(type__in=state_types, used=True).exclude(slug__in=settings.GROUP_STATES_WITH_EXTRA_PROCESSING)
13161324

13171325
if not has_role(user, "Secretariat"):
13181326
if has_role(user, "IRTF Chair"):
1319-
group_queryset = self.fields["group"].queryset.filter(Q(role__person__user=user, role__name__in=("chair", "delegate", "secr"))|Q(type="rg", state="active")).distinct()
1327+
group_queryset = self.fields["group"].queryset.filter(Q(role__person__user=user, role__name__in=rg_features.matman_roles)|Q(type="rg", state="active")).distinct()
13201328
else:
1321-
group_queryset = self.fields["group"].queryset.filter(role__person__user=user, role__name__in=("chair", "delegate", "secr")).distinct()
1329+
group_queryset = self.fields["group"].queryset.filter(role__person__user=user, role__name__in=wg_features.matman_roles).distinct()
13221330
self.fields["group"].queryset = group_queryset
13231331

13241332
self.fields['group'].choices = [(g.pk, '%s - %s' % (g.acronym, g.name)) for g in self.fields["group"].queryset]

ietf/group/admin.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,21 +102,30 @@ def send_one_reminder(self, request, object_id):
102102

103103
class GroupFeaturesAdmin(admin.ModelAdmin):
104104
list_display = [
105+
105106
'type',
106-
'customize_workflow',
107+
'has_milestones',
107108
'has_chartering_process',
108-
'has_default_jabber',
109-
'has_dependencies',
110109
'has_documents',
110+
'has_dependencies',
111+
'has_session_materials',
111112
'has_nonsession_materials',
112-
'has_milestones',
113+
'has_meetings',
113114
'has_reviews',
114-
'material_types',
115+
'has_default_jabber',
116+
'acts_like_wg',
117+
'create_wiki',
118+
'custom_group_roles',
119+
'customize_workflow',
120+
'is_schedulable',
121+
'show_on_agenda',
122+
'req_subm_approval',
115123
'agenda_type',
124+
'material_types',
116125
'admin_roles',
117-
'about_page',
118-
'default_tab',
119-
]
126+
'matman_roles',
127+
'role_order',
128+
]
120129
admin.site.register(GroupFeatures, GroupFeaturesAdmin)
121130

122131
class GroupHistoryAdmin(admin.ModelAdmin):
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Copyright The IETF Trust 2019, All Rights Reserved
2+
# -*- coding: utf-8 -*-
3+
# Generated by Django 1.11.16 on 2019-01-10 07:51
4+
from __future__ import unicode_literals
5+
6+
import django.core.validators
7+
from django.db import migrations, models
8+
9+
10+
class Migration(migrations.Migration):
11+
12+
dependencies = [
13+
('group', '0003_groupfeatures_data'),
14+
]
15+
16+
operations = [
17+
migrations.AddField(
18+
model_name='groupfeatures',
19+
name='acts_like_wg',
20+
field=models.BooleanField(default=False, verbose_name=b'WG-Like'),
21+
),
22+
migrations.AddField(
23+
model_name='groupfeatures',
24+
name='create_wiki',
25+
field=models.BooleanField(default=False, verbose_name=b'Wiki'),
26+
),
27+
migrations.AddField(
28+
model_name='groupfeatures',
29+
name='custom_group_roles',
30+
field=models.BooleanField(default=False, verbose_name=b'Group Roles'),
31+
),
32+
migrations.AddField(
33+
model_name='groupfeatures',
34+
name='has_session_materials',
35+
field=models.BooleanField(default=False, verbose_name=b'Materials'),
36+
),
37+
migrations.AddField(
38+
model_name='groupfeatures',
39+
name='is_schedulable',
40+
field=models.BooleanField(default=False, verbose_name=b'Schedulable'),
41+
),
42+
migrations.AddField(
43+
model_name='groupfeatures',
44+
name='role_order',
45+
field=models.CharField(default=b'chair,secr,member', help_text=b'The order in which roles are shown, for instance on photo pages', max_length=128, validators=[django.core.validators.RegexValidator(code=b'invalid', message=b'Enter a comma-separated list of role slugs', regex=b'[a-z0-9_-]+(,[a-z0-9_-]+)*')]),
46+
),
47+
migrations.AddField(
48+
model_name='groupfeatures',
49+
name='show_on_agenda',
50+
field=models.BooleanField(default=False, verbose_name=b'On Agenda'),
51+
),
52+
migrations.AddField(
53+
model_name='groupfeatures',
54+
name='req_subm_approval',
55+
field=models.BooleanField(default=False, verbose_name=b'Subm. Approval'),
56+
),
57+
migrations.AddField(
58+
model_name='groupfeatures',
59+
name='matman_roles',
60+
field=models.CharField(default=b'ad,chair,delegate,secr', max_length=64, validators=[django.core.validators.RegexValidator(code=b'invalid', message=b'Enter a comma-separated list of role slugs', regex=b'[a-z0-9_-]+(,[a-z0-9_-]+)*')]),
61+
),
62+
migrations.AddField(
63+
model_name='historicalgroupfeatures',
64+
name='acts_like_wg',
65+
field=models.BooleanField(default=False, verbose_name=b'WG-Like'),
66+
),
67+
migrations.AddField(
68+
model_name='historicalgroupfeatures',
69+
name='create_wiki',
70+
field=models.BooleanField(default=False, verbose_name=b'Wiki'),
71+
),
72+
migrations.AddField(
73+
model_name='historicalgroupfeatures',
74+
name='custom_group_roles',
75+
field=models.BooleanField(default=False, verbose_name=b'Group Roles'),
76+
),
77+
migrations.AddField(
78+
model_name='historicalgroupfeatures',
79+
name='has_session_materials',
80+
field=models.BooleanField(default=False, verbose_name=b'Materials'),
81+
),
82+
migrations.AddField(
83+
model_name='historicalgroupfeatures',
84+
name='is_schedulable',
85+
field=models.BooleanField(default=False, verbose_name=b'Schedulable'),
86+
),
87+
migrations.AddField(
88+
model_name='historicalgroupfeatures',
89+
name='role_order',
90+
field=models.CharField(default=b'chair,secr,member', help_text=b'The order in which roles are shown, for instance on photo pages', max_length=128, validators=[django.core.validators.RegexValidator(code=b'invalid', message=b'Enter a comma-separated list of role slugs', regex=b'[a-z0-9_-]+(,[a-z0-9_-]+)*')]),
91+
),
92+
migrations.AddField(
93+
model_name='historicalgroupfeatures',
94+
name='show_on_agenda',
95+
field=models.BooleanField(default=False, verbose_name=b'On Agenda'),
96+
),
97+
migrations.AddField(
98+
model_name='historicalgroupfeatures',
99+
name='req_subm_approval',
100+
field=models.BooleanField(default=False, verbose_name=b'Subm. Approval'),
101+
),
102+
migrations.AddField(
103+
model_name='historicalgroupfeatures',
104+
name='matman_roles',
105+
field=models.CharField(default=b'ad,chair,delegate,secr', max_length=64, validators=[django.core.validators.RegexValidator(code=b'invalid', message=b'Enter a comma-separated list of role slugs', regex=b'[a-z0-9_-]+(,[a-z0-9_-]+)*')]),
106+
),
107+
]

ietf/group/migrations/0004_auto_20181218_2349.py

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Copyright The IETF Trust 2019, All Rights Reserved
2+
# -*- coding: utf-8 -*-
3+
# Generated by Django 1.11.16 on 2019-01-09 09:02
4+
from __future__ import unicode_literals
5+
6+
import json
7+
import re
8+
9+
from django.db import migrations
10+
11+
import debug # pyflakes:ignore
12+
13+
def forward(apps, schema_editor):
14+
GroupFeatures = apps.get_model('group', 'GroupFeatures')
15+
for f in GroupFeatures.objects.all():
16+
for a in ['material_types', 'admin_roles', 'matman_roles', 'role_order']:
17+
v = getattr(f, a, None)
18+
if v != None:
19+
v = re.sub(r'[][\\"\' ]+', '', v)
20+
v = v.split(',')
21+
v = json.dumps(v)
22+
setattr(f, a, v)
23+
f.save()
24+
# This migration changes existing data fields in an incompatible manner, and
25+
# would not be interleavable if we hadn't added compatibility code in
26+
# Group.features() beforehand. With that patched in, we permit interleaving.
27+
forward.interleavable = True
28+
29+
def reverse(apps, schema_editor):
30+
GroupFeatures = apps.get_model('group', 'GroupFeatures')
31+
for f in GroupFeatures.objects.all():
32+
for a in ['material_types', 'admin_roles', 'matman_roles', 'role_order']:
33+
v = getattr(f, a, None)
34+
if v != None:
35+
v = getattr(f, a)
36+
v = json.loads(v)
37+
v = ','.join(v)
38+
setattr(f, a, v)
39+
f.save()
40+
41+
class Migration(migrations.Migration):
42+
43+
dependencies = [
44+
('group', '0004_add_group_feature_fields'),
45+
]
46+
47+
operations = [
48+
migrations.RunPython(forward, reverse),
49+
]

0 commit comments

Comments
 (0)