Merged in [19075] from jennifer@painless-security.com:
Clean up handling of non-WG groups on the group edit page; restrict parent/child group relationships by type. Fixes #3253.
- Legacy-Id: 19079
Note: SVN reference [19075] has been migrated to Git commit 2a2e5f0c24
This commit is contained in:
commit
5bb08bc103
|
@ -21,6 +21,7 @@ from django.utils.translation import ugettext as _
|
|||
from ietf.group.models import (Group, GroupFeatures, GroupHistory, GroupEvent, GroupURL, GroupMilestone,
|
||||
GroupMilestoneHistory, GroupStateTransitions, Role, RoleHistory, ChangeStateGroupEvent,
|
||||
MilestoneGroupEvent, GroupExtResource, )
|
||||
from ietf.name.models import GroupTypeName
|
||||
|
||||
from ietf.utils.validators import validate_external_resource_value
|
||||
from ietf.utils.response import permission_denied
|
||||
|
@ -139,10 +140,40 @@ class GroupAdmin(admin.ModelAdmin):
|
|||
|
||||
admin.site.register(Group, GroupAdmin)
|
||||
|
||||
class GroupFeaturesAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
|
||||
class GroupFeaturesAdminForm(forms.ModelForm):
|
||||
def clean_default_parent(self):
|
||||
# called before form clean() method -- cannot access other fields
|
||||
parent_acro = self.cleaned_data['default_parent'].strip().lower()
|
||||
if len(parent_acro) > 0:
|
||||
if Group.objects.filter(acronym=parent_acro).count() == 0:
|
||||
raise forms.ValidationError(
|
||||
'No group exists with acronym "%(acro)s"',
|
||||
params=dict(acro=parent_acro),
|
||||
)
|
||||
return parent_acro
|
||||
|
||||
def clean(self):
|
||||
# cleaning/validation that requires multiple fields
|
||||
parent_acro = self.cleaned_data['default_parent']
|
||||
if len(parent_acro) > 0:
|
||||
parent_type = GroupTypeName.objects.filter(group__acronym=parent_acro).first()
|
||||
if parent_type not in self.cleaned_data['parent_types']:
|
||||
self.add_error(
|
||||
'default_parent',
|
||||
forms.ValidationError(
|
||||
'Default parent group "%(acro)s" is type "%(gtype)s", which is not an allowed parent type.',
|
||||
params=dict(acro=parent_acro, gtype=parent_type),
|
||||
)
|
||||
)
|
||||
|
||||
class GroupFeaturesAdmin(admin.ModelAdmin):
|
||||
form = GroupFeaturesAdminForm
|
||||
list_display = [
|
||||
'type',
|
||||
'need_parent',
|
||||
'default_parent',
|
||||
'gf_parent_types',
|
||||
'has_milestones',
|
||||
'has_chartering_process',
|
||||
'has_documents',
|
||||
|
@ -165,8 +196,13 @@ class GroupFeaturesAdmin(admin.ModelAdmin):
|
|||
'groupman_roles',
|
||||
'matman_roles',
|
||||
'role_order',
|
||||
|
||||
]
|
||||
|
||||
def gf_parent_types(self, groupfeatures):
|
||||
"""Generate list of parent types; needed because many-to-many is not handled automatically"""
|
||||
return ', '.join([gtn.slug for gtn in groupfeatures.parent_types.all()])
|
||||
gf_parent_types.short_description = 'Parent Types' # type: ignore # https://github.com/python/mypy/issues/2087
|
||||
|
||||
admin.site.register(GroupFeatures, GroupFeaturesAdmin)
|
||||
|
||||
class GroupHistoryAdmin(admin.ModelAdmin):
|
||||
|
|
|
@ -18,10 +18,11 @@ from django.core.exceptions import ValidationError, ObjectDoesNotExist
|
|||
from ietf.group.models import Group, GroupHistory, GroupStateName, GroupFeatures
|
||||
from ietf.name.models import ReviewTypeName, RoleName, ExtResourceName
|
||||
from ietf.person.fields import SearchableEmailsField, PersonEmailChoiceField
|
||||
from ietf.person.models import Person, Email
|
||||
from ietf.person.models import Email
|
||||
from ietf.review.models import ReviewerSettings, UnavailablePeriod, ReviewSecretarySettings
|
||||
from ietf.review.policies import get_reviewer_queue_policy
|
||||
from ietf.review.utils import close_review_request_states
|
||||
from ietf.utils import log
|
||||
from ietf.utils.textupload import get_cleaned_text_file_content
|
||||
#from ietf.utils.ordereddict import insert_after_in_ordered_dict
|
||||
from ietf.utils.fields import DatepickerDateField, MultiEmailField
|
||||
|
@ -60,7 +61,6 @@ class GroupForm(forms.Form):
|
|||
acronym = forms.CharField(max_length=40, label="Acronym", required=True)
|
||||
state = forms.ModelChoiceField(GroupStateName.objects.all(), label="State", required=True)
|
||||
# Note that __init__ will add role fields here
|
||||
ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active", role__group__type='area').order_by('name'), label="Shepherding AD", empty_label="(None)", required=False)
|
||||
|
||||
parent = forms.ModelChoiceField(Group.objects.filter(state="active").order_by('name'), empty_label="(None)", required=False)
|
||||
list_email = forms.CharField(max_length=64, required=False)
|
||||
|
@ -74,9 +74,25 @@ class GroupForm(forms.Form):
|
|||
self.group = kwargs.pop('group', None)
|
||||
self.group_type = kwargs.pop('group_type', False)
|
||||
if self.group:
|
||||
self.used_roles = self.group.used_roles or self.group.features.default_used_roles
|
||||
group_features = self.group.features
|
||||
self.used_roles = self.group.used_roles or group_features.default_used_roles
|
||||
else:
|
||||
self.used_roles = GroupFeatures.objects.get(type=self.group_type).default_used_roles
|
||||
group_features = GroupFeatures.objects.filter(type_id=self.group_type).first()
|
||||
|
||||
log.assertion('group_features is not None')
|
||||
if group_features is not None:
|
||||
self.used_roles = group_features.default_used_roles
|
||||
parent_types = group_features.parent_types.all()
|
||||
need_parent = group_features.need_parent
|
||||
default_parent = group_features.default_parent
|
||||
else:
|
||||
# This should not happen, but in the absence of constraints that ensure it
|
||||
# cannot, prevent the form from breaking if it does.
|
||||
self.used_roles = []
|
||||
parent_types = GroupFeatures.objects.none()
|
||||
need_parent = False
|
||||
default_parent = None
|
||||
|
||||
if "field" in kwargs:
|
||||
field = kwargs["field"]
|
||||
del kwargs["field"]
|
||||
|
@ -109,22 +125,21 @@ class GroupForm(forms.Form):
|
|||
if self.group_type == "rg":
|
||||
self.fields["state"].queryset = self.fields["state"].queryset.exclude(slug__in=("bof", "bof-conc"))
|
||||
|
||||
# if previous AD is now ex-AD, append that person to the list
|
||||
ad_pk = self.initial.get('ad')
|
||||
choices = self.fields['ad'].choices
|
||||
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())]
|
||||
|
||||
if self.group:
|
||||
self.fields['acronym'].widget.attrs['readonly'] = ""
|
||||
|
||||
if self.group_type == "rg":
|
||||
self.fields['ad'].widget = forms.HiddenInput()
|
||||
self.fields['parent'].queryset = self.fields['parent'].queryset.filter(acronym="irtf")
|
||||
self.fields['parent'].initial = self.fields['parent'].queryset.first()
|
||||
self.fields['parent'].widget = forms.HiddenInput()
|
||||
else:
|
||||
self.fields['parent'].queryset = self.fields['parent'].queryset.filter(type="area")
|
||||
# Sort out parent options
|
||||
self.fields['parent'].queryset = self.fields['parent'].queryset.filter(type__in=parent_types)
|
||||
if need_parent:
|
||||
self.fields['parent'].required = True
|
||||
self.fields['parent'].empty_label = None
|
||||
# if this is a new group, fill in the default parent, if any
|
||||
if self.group is None or (not hasattr(self.group, 'pk')):
|
||||
self.fields['parent'].initial = self.fields['parent'].queryset.filter(
|
||||
acronym=default_parent
|
||||
).first()
|
||||
# label the parent field as 'IETF Area' if appropriate, for consistency with past behavior
|
||||
if parent_types.count() == 1 and parent_types.first().pk == 'area':
|
||||
self.fields['parent'].label = "IETF Area"
|
||||
|
||||
if field:
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# Generated by Django 2.2.19 on 2021-04-13 05:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('name', '0023_change_stream_descriptions'),
|
||||
('group', '0042_add_liaison_contact_roles_to_used_roles'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='groupfeatures',
|
||||
name='parent_types',
|
||||
field=models.ManyToManyField(blank=True, help_text='Group types allowed as parent of this group type', related_name='child_features', to='name.GroupTypeName'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='groupfeatures',
|
||||
name='req_parent',
|
||||
field=models.BooleanField(default=False, help_text='Does this group type require a parent group?', verbose_name='Need Parent'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='groupfeatures',
|
||||
name='default_parent',
|
||||
field=models.CharField(blank=True, default='', help_text='Default parent group acronym for this group type', max_length=40, verbose_name='Default Parent'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,91 @@
|
|||
# Generated by Django 2.2.19 on 2021-04-13 09:17
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
def populate_parent_types(apps, schema_editor):
|
||||
"""Add default parent_types entries
|
||||
|
||||
Data were determined from existing groups via this query:
|
||||
{t.slug: list(
|
||||
Group.objects.filter(type=t, parent__isnull=False).values_list('parent__type', flat=True).distinct()
|
||||
) for t in GroupTypeName.objects.all()}
|
||||
"""
|
||||
GroupFeatures = apps.get_model('group', 'GroupFeatures')
|
||||
GroupTypeName = apps.get_model('name', 'GroupTypeName')
|
||||
type_map = {
|
||||
'adhoc': ['ietf'],
|
||||
'admin': [],
|
||||
'ag': ['area', 'ietf'],
|
||||
'area': ['ietf'],
|
||||
'dir': ['area'],
|
||||
'iab': ['ietf'],
|
||||
'iana': [],
|
||||
'iesg': [],
|
||||
'ietf': ['ietf'],
|
||||
'individ': ['area'],
|
||||
'irtf': ['irtf'],
|
||||
'ise': [],
|
||||
'isoc': ['isoc'],
|
||||
'nomcom': ['area'],
|
||||
'program': ['ietf'],
|
||||
'rag': ['irtf'],
|
||||
'review': ['area'],
|
||||
'rfcedtyp': [],
|
||||
'rg': ['irtf'],
|
||||
'sdo': ['sdo', 'area'],
|
||||
'team': ['area'],
|
||||
'wg': ['area']
|
||||
}
|
||||
for type_slug, parent_slugs in type_map.items():
|
||||
if len(parent_slugs) > 0:
|
||||
features = GroupFeatures.objects.get(type__slug=type_slug)
|
||||
features.parent_types.add(*GroupTypeName.objects.filter(slug__in=parent_slugs))
|
||||
|
||||
# validate
|
||||
for gtn in GroupTypeName.objects.all():
|
||||
slugs_in_db = set(type.slug for type in gtn.features.parent_types.all())
|
||||
assert(slugs_in_db == set(type_map[gtn.slug]))
|
||||
|
||||
|
||||
def set_req_parent_values(apps, schema_editor):
|
||||
"""Set req_parent values
|
||||
|
||||
Data determined from existing groups using:
|
||||
|
||||
GroupTypeName.objects.exclude(pk__in=Group.objects.filter(parent__isnull=True).values('type'))
|
||||
|
||||
'iesg' has been removed because there are no groups of this type, so no parent types have
|
||||
been made available to it.
|
||||
"""
|
||||
GroupFeatures = apps.get_model('group', 'GroupFeatures')
|
||||
|
||||
GroupFeatures.objects.filter(
|
||||
type_id__in=('area', 'dir', 'individ', 'review', 'rg',)
|
||||
).update(req_parent=True)
|
||||
|
||||
|
||||
def set_default_parents(apps, schema_editor):
|
||||
GroupFeatures = apps.get_model('group', 'GroupFeatures')
|
||||
|
||||
# rg-typed groups are children of the irtf group
|
||||
rg_features = GroupFeatures.objects.filter(type_id='rg').first()
|
||||
if rg_features:
|
||||
rg_features.default_parent = 'irtf'
|
||||
rg_features.save()
|
||||
|
||||
|
||||
def empty_reverse(apps, schema_editor):
|
||||
pass # nothing to do, field will be dropped
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('group', '0043_add_groupfeatures_parent_type_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(populate_parent_types, empty_reverse),
|
||||
migrations.RunPython(set_req_parent_values, empty_reverse),
|
||||
migrations.RunPython(set_default_parents, empty_reverse),
|
||||
]
|
|
@ -95,6 +95,9 @@ class GroupInfo(models.Model):
|
|||
return self.parent
|
||||
return None
|
||||
|
||||
def get_used_roles(self):
|
||||
return self.used_roles if len(self.used_roles) > 0 else self.features.default_used_roles
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
@ -250,6 +253,14 @@ validate_comma_separated_roles = RegexValidator(
|
|||
class GroupFeatures(models.Model):
|
||||
type = OneToOneField(GroupTypeName, primary_key=True, null=False, related_name='features')
|
||||
#history = HistoricalRecords()
|
||||
|
||||
#
|
||||
need_parent = models.BooleanField("Need Parent", default=False, help_text="Does this group type require a parent group?")
|
||||
parent_types = models.ManyToManyField(GroupTypeName, blank=True, related_name='child_features',
|
||||
help_text="Group types allowed as parent of this group type")
|
||||
default_parent = models.CharField("Default Parent", max_length=40, blank=True, default="",
|
||||
help_text="Default parent group acronym for this group type")
|
||||
|
||||
#
|
||||
has_milestones = models.BooleanField("Milestones", default=False)
|
||||
has_chartering_process = models.BooleanField("Chartering", default=False)
|
||||
|
|
|
@ -541,7 +541,7 @@ class GroupEditTests(TestCase):
|
|||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('form input[name=acronym]')), 1)
|
||||
self.assertEqual(q('form input[name=parent]').attr('value'),'%s'%irtf.pk)
|
||||
self.assertEqual(q('form select[name=parent]')[0].value,'%s'%irtf.pk)
|
||||
|
||||
r = self.client.post(url, dict(acronym="testrg", name="Testing RG", state=proposed_state.pk, parent=irtf.pk))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
|
@ -632,6 +632,7 @@ class GroupEditTests(TestCase):
|
|||
parent=area.pk,
|
||||
ad=ad.pk,
|
||||
state=state.pk,
|
||||
ad_roles=ad.email().address,
|
||||
chair_roles="aread@example.org, ad1@example.org",
|
||||
secr_roles="aread@example.org, ad1@example.org, ad2@example.org",
|
||||
liaison_contact_roles="ad1@example.org",
|
||||
|
@ -886,6 +887,132 @@ class GroupEditTests(TestCase):
|
|||
self.assertEqual(r.status_code,403)
|
||||
self.assertEqual(len(outbox),5)
|
||||
|
||||
class GroupFormTests(TestCase):
|
||||
"""Tests of the GroupForm form"""
|
||||
@staticmethod
|
||||
def _format_resource(r):
|
||||
if r.display_name:
|
||||
return '{} {} ({})'.format(r.name.slug, r.value, r.display_name.strip('()'))
|
||||
else:
|
||||
return '{} {}'.format(r.name.slug, r.value)
|
||||
|
||||
def _group_post_data(self, group):
|
||||
data=dict(
|
||||
name=group.name,
|
||||
acronym=group.acronym,
|
||||
state=group.state_id,
|
||||
parent=group.parent_id or '',
|
||||
list_email=group.list_email if group.list_email else None,
|
||||
list_subscribe=group.list_subscribe if group.list_subscribe else '',
|
||||
list_archive=group.list_archive if group.list_archive else '',
|
||||
resources='\n'.join(self._format_resource(r) for r in group.groupextresource_set.all()),
|
||||
closing_note='', # not a group attribute, handled specially by the view; ignore in this test
|
||||
)
|
||||
# fill in original values
|
||||
for rslug in group.get_used_roles():
|
||||
data['{}_roles'.format(rslug)] = ','.join(
|
||||
group.role_set.filter(name_id=rslug).values_list('email__address', flat=True),
|
||||
)
|
||||
return data
|
||||
|
||||
def _assert_cleaned_data_equal(self, cleaned_data, post_data):
|
||||
for attr, expected in post_data.items():
|
||||
value = cleaned_data[attr]
|
||||
if attr.endswith('_roles'):
|
||||
actual = ','.join(value.values_list('address', flat=True))
|
||||
elif attr == 'resources':
|
||||
# must handle resources specially
|
||||
actual = '\n'.join(self._format_resource(r) for r in value)
|
||||
elif hasattr(value, 'pk'):
|
||||
actual = value.pk
|
||||
else:
|
||||
actual = '' if value is None else value
|
||||
self.assertEqual(actual, expected, 'unexpected value for {}'.format(attr))
|
||||
|
||||
def do_edit_roles_test(self, group):
|
||||
# get post_data for the group
|
||||
orig_data = self._group_post_data(group)
|
||||
|
||||
# create a user to be assigned roles
|
||||
new_email = EmailFactory()
|
||||
|
||||
# Now check that we can replace each used_role without disturbing the others.
|
||||
# This does not actually update group, so start with orig_data each time.
|
||||
for rslug in group.get_used_roles():
|
||||
data = orig_data.copy()
|
||||
edit_field = '{}_roles'.format(rslug)
|
||||
data[edit_field] = new_email.address # comma-separated list of addresses with only one
|
||||
|
||||
form = GroupForm(data, group=group, group_type=group.type_id, field=None)
|
||||
|
||||
self.assertTrue(form.is_valid())
|
||||
# Check that all cleaned values match what we passed to the form.
|
||||
self._assert_cleaned_data_equal(form.cleaned_data, data)
|
||||
|
||||
def test_edit_roles(self):
|
||||
"""Test that roles can be edited for all group types
|
||||
|
||||
N.B., the combinations of group type and parent group and the used_roles are
|
||||
obtained from the GroupFeatures in the database. The handling of these combinations
|
||||
is validated, but this test cannot check that the rules themselves are correct.
|
||||
As long as names.json is up to date, this will test what we want.
|
||||
"""
|
||||
# Test every parent type that is allowed for at least one group type
|
||||
for parent_type in GroupTypeName.objects.filter(child_features__isnull=False).distinct():
|
||||
parent = GroupFactory(type_id=parent_type.pk)
|
||||
for child_features in parent_type.child_features.all():
|
||||
# create a group of each child type for this parent and populate its roles
|
||||
group_type = child_features.type
|
||||
group = GroupFactory(type_id=group_type.pk, parent=parent)
|
||||
for rslug in group.get_used_roles():
|
||||
RoleFactory(name_id=rslug, group=group, person=PersonFactory())
|
||||
self.do_edit_roles_test(group)
|
||||
|
||||
def test_need_parent(self):
|
||||
"""GroupForm should enforce non-null parent when required"""
|
||||
group = GroupFactory()
|
||||
parent = group.parent
|
||||
other_parent = GroupFactory(type_id=parent.type_id)
|
||||
|
||||
for rslug in group.get_used_roles():
|
||||
RoleFactory(name_id=rslug, group=group, person=PersonFactory())
|
||||
|
||||
data = self._group_post_data(group)
|
||||
|
||||
# First, test with parent required
|
||||
group.type.features.need_parent = True
|
||||
group.type.features.save()
|
||||
group = Group.objects.get(pk=group.pk) # renew object to clear features cache
|
||||
|
||||
# should fail with empty parent
|
||||
data['parent'] = ''
|
||||
form = GroupForm(data, group=group, group_type=group.type_id, field=None)
|
||||
self.assertFalse(form.is_valid()) # cannot update to empty parent
|
||||
|
||||
# should succeed with non-empty parent
|
||||
data['parent'] = other_parent.pk
|
||||
form = GroupForm(data, group=group, group_type=group.type_id, field=None)
|
||||
self.assertTrue(form.is_valid())
|
||||
self._assert_cleaned_data_equal(form.cleaned_data, data)
|
||||
|
||||
# Second, test with parent not required
|
||||
group.type.features.need_parent = False
|
||||
group.type.features.save()
|
||||
group = Group.objects.get(pk=group.pk) # renew object to clear features cache
|
||||
|
||||
# should succeed with empty parent
|
||||
data['parent'] = ''
|
||||
form = GroupForm(data, group=group, group_type=group.type_id, field=None)
|
||||
self.assertTrue(form.is_valid())
|
||||
self._assert_cleaned_data_equal(form.cleaned_data, data)
|
||||
|
||||
# should succeed with non-empty parent
|
||||
data['parent'] = other_parent.pk
|
||||
form = GroupForm(data, group=group, group_type=group.type_id, field=None)
|
||||
self.assertTrue(form.is_valid())
|
||||
self._assert_cleaned_data_equal(form.cleaned_data, data)
|
||||
|
||||
|
||||
class MilestoneTests(TestCase):
|
||||
def create_test_milestones(self):
|
||||
group = GroupFactory(acronym='mars',parent=GroupFactory(type_id='area'),list_email='mars-wg@ietf.org')
|
||||
|
|
|
@ -942,7 +942,7 @@ def edit(request, group_type=None, acronym=None, action="edit", field=None):
|
|||
changed_personnel = set()
|
||||
# update roles
|
||||
for attr, f in form.fields.items():
|
||||
if not (attr.endswith("_roles") or attr == "ad"):
|
||||
if not attr.endswith("_roles"):
|
||||
continue
|
||||
|
||||
slug = attr
|
||||
|
@ -951,8 +951,6 @@ def edit(request, group_type=None, acronym=None, action="edit", field=None):
|
|||
title = f.label
|
||||
|
||||
new = clean[attr]
|
||||
if attr == 'ad':
|
||||
new = [ new.role_email('ad') ] if new else []
|
||||
old = Email.objects.filter(role__group=group, role__name=slug).select_related("person")
|
||||
if set(new) != set(old):
|
||||
changes.append((attr, new, desc(title,
|
||||
|
@ -1044,7 +1042,6 @@ def edit(request, group_type=None, acronym=None, action="edit", field=None):
|
|||
return HttpResponseRedirect(group.about_url())
|
||||
else: # Not POST:
|
||||
if not new_group:
|
||||
ad_role = group.ad_role()
|
||||
closing_note = ""
|
||||
e = group.latest_event(type='closing_note')
|
||||
if e:
|
||||
|
@ -1055,7 +1052,6 @@ def edit(request, group_type=None, acronym=None, action="edit", field=None):
|
|||
init = dict(name=group.name,
|
||||
acronym=group.acronym,
|
||||
state=group.state,
|
||||
ad=ad_role and ad_role.person and ad_role.person.id,
|
||||
parent=group.parent.id if group.parent else None,
|
||||
list_email=group.list_email if group.list_email else None,
|
||||
list_subscribe=group.list_subscribe if group.list_subscribe else None,
|
||||
|
@ -1065,8 +1061,7 @@ def edit(request, group_type=None, acronym=None, action="edit", field=None):
|
|||
)
|
||||
|
||||
else:
|
||||
init = dict(ad=request.user.person.id if group_type == "wg" and has_role(request.user, "Area Director") else None,
|
||||
)
|
||||
init = dict()
|
||||
form = GroupForm(initial=init, group=group, group_type=group_type, field=field)
|
||||
|
||||
return render(request, 'group/edit.html',
|
||||
|
|
|
@ -2482,6 +2482,7 @@
|
|||
"create_wiki": true,
|
||||
"custom_group_roles": false,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"matman\",\n \"ad\",\n \"chair\",\n \"lead\"\n]",
|
||||
"docman_roles": "[\n \"chair\"\n]",
|
||||
|
@ -2498,6 +2499,10 @@
|
|||
"is_schedulable": true,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"chair\",\n \"lead\",\n \"delegate\",\n \"matman\"\n]",
|
||||
"parent_types": [
|
||||
"ietf"
|
||||
],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"lead\",\n \"delegate\",\n \"matman\"\n]",
|
||||
"show_on_agenda": true
|
||||
|
@ -2514,6 +2519,7 @@
|
|||
"create_wiki": false,
|
||||
"custom_group_roles": false,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"member\",\n \"chair\"\n]",
|
||||
"docman_roles": "[\n \"chair\"\n]",
|
||||
|
@ -2530,6 +2536,8 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"chair\"\n]",
|
||||
"parent_types": [],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": false,
|
||||
"role_order": "[\n \"chair\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -2546,6 +2554,7 @@
|
|||
"create_wiki": true,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"ad\",\n \"chair\",\n \"secr\"\n]",
|
||||
"docman_roles": "[\n \"chair\",\n \"delegate\",\n \"secr\"\n]",
|
||||
|
@ -2562,6 +2571,11 @@
|
|||
"is_schedulable": true,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"ad\",\n \"chair\",\n \"delegate\",\n \"secr\"\n]",
|
||||
"parent_types": [
|
||||
"area",
|
||||
"ietf"
|
||||
],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"secr\"\n]",
|
||||
"show_on_agenda": true
|
||||
|
@ -2578,6 +2592,7 @@
|
|||
"create_wiki": true,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"ad\",\n \"liaison_contact\",\n \"liaison_cc_contact\"\n]",
|
||||
"docman_roles": "[\n \"ad\",\n \"delegate\",\n \"secr\"\n]",
|
||||
|
@ -2594,6 +2609,10 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"ad\",\n \"chair\",\n \"delegate\",\n \"secr\"\n]",
|
||||
"parent_types": [
|
||||
"ietf"
|
||||
],
|
||||
"need_parent": true,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"secr\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -2610,6 +2629,7 @@
|
|||
"create_wiki": true,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"ad\",\n \"chair\",\n \"reviewer\",\n \"secr\"\n]",
|
||||
"docman_roles": "[]",
|
||||
|
@ -2626,6 +2646,10 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"ad\",\n \"chair\",\n \"delegate\",\n \"secr\"\n]",
|
||||
"parent_types": [
|
||||
"area"
|
||||
],
|
||||
"need_parent": true,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"secr\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -2642,6 +2666,7 @@
|
|||
"create_wiki": false,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"chair\"\n]",
|
||||
"docman_roles": "[\n \"chair\"\n]",
|
||||
|
@ -2658,6 +2683,10 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"chair\",\n \"delegate\"\n]",
|
||||
"parent_types": [
|
||||
"ietf"
|
||||
],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"secr\"\n]",
|
||||
"show_on_agenda": true
|
||||
|
@ -2674,6 +2703,7 @@
|
|||
"create_wiki": false,
|
||||
"custom_group_roles": false,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"auth\"\n]",
|
||||
"docman_roles": "[\n \"chair\"\n]",
|
||||
|
@ -2690,6 +2720,8 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"chair\"\n]",
|
||||
"parent_types": [],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": false,
|
||||
"role_order": "[\n \"chair\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -2706,6 +2738,7 @@
|
|||
"create_wiki": false,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[]",
|
||||
"docman_roles": "[\n \"chair\"\n]",
|
||||
|
@ -2722,6 +2755,8 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "\"[]\"",
|
||||
"matman_roles": "[\n \"chair\",\n \"delegate\",\n \"member\"\n]",
|
||||
"parent_types": [],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"delegate\",\n \"member\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -2738,6 +2773,7 @@
|
|||
"create_wiki": false,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"ad\",\n \"member\",\n \"comdir\",\n \"delegate\",\n \"execdir\",\n \"recman\",\n \"secr\",\n \"trac-editor\",\n \"trac-admin\",\n \"chair\"\n]",
|
||||
"docman_roles": "[\n \"chair\"\n]",
|
||||
|
@ -2754,6 +2790,10 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"chair\",\n \"delegate\"\n]",
|
||||
"parent_types": [
|
||||
"ietf"
|
||||
],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"secr\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -2770,6 +2810,7 @@
|
|||
"create_wiki": false,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"ad\"\n]",
|
||||
"docman_roles": "[\n \"auth\"\n]",
|
||||
|
@ -2786,6 +2827,10 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[]",
|
||||
"parent_types": [
|
||||
"area"
|
||||
],
|
||||
"need_parent": true,
|
||||
"req_subm_approval": false,
|
||||
"role_order": "[\n \"chair\",\n \"secr\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -2802,6 +2847,7 @@
|
|||
"create_wiki": false,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"member\",\n \"atlarge\",\n \"chair\"\n]",
|
||||
"docman_roles": "[]",
|
||||
|
@ -2818,6 +2864,10 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"chair\",\n \"delegate\",\n \"secr\"\n]",
|
||||
"parent_types": [
|
||||
"irtf"
|
||||
],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"secr\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -2834,6 +2884,7 @@
|
|||
"create_wiki": false,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"chair\"\n]",
|
||||
"docman_roles": "[\n \"chair\"\n]",
|
||||
|
@ -2850,6 +2901,8 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"chair\",\n \"delegate\"\n]",
|
||||
"parent_types": [],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"delegate\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -2866,6 +2919,7 @@
|
|||
"create_wiki": false,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"chair\",\n \"ceo\"\n]",
|
||||
"docman_roles": "[]",
|
||||
|
@ -2882,6 +2936,10 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"chair\",\n \"secr\"\n]",
|
||||
"parent_types": [
|
||||
"isoc"
|
||||
],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"secr\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -2898,6 +2956,7 @@
|
|||
"create_wiki": true,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"member\",\n \"advisor\",\n \"liaison\",\n \"chair\",\n \"techadv\"\n]",
|
||||
"docman_roles": "[\n \"chair\"\n]",
|
||||
|
@ -2914,6 +2973,10 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"chair\"\n]",
|
||||
"parent_types": [
|
||||
"area"
|
||||
],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"member\",\n \"advisor\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -2930,6 +2993,7 @@
|
|||
"create_wiki": false,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"member\",\n \"chair\",\n \"lead\"\n]",
|
||||
"docman_roles": "[\n \"lead\",\n \"chair\",\n \"secr\"\n]",
|
||||
|
@ -2946,6 +3010,10 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"lead\",\n \"chair\",\n \"secr\"\n]",
|
||||
"parent_types": [
|
||||
"ietf"
|
||||
],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": false,
|
||||
"role_order": "[\n \"lead\",\n \"chair\",\n \"secr\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -2962,6 +3030,7 @@
|
|||
"create_wiki": true,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"chair\",\n \"secr\"\n]",
|
||||
"docman_roles": "[\n \"chair\",\n \"delegate\",\n \"secr\"\n]",
|
||||
|
@ -2978,6 +3047,10 @@
|
|||
"is_schedulable": true,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"chair\",\n \"delegate\",\n \"secr\"\n]",
|
||||
"parent_types": [
|
||||
"irtf"
|
||||
],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"secr\"\n]",
|
||||
"show_on_agenda": true
|
||||
|
@ -2994,6 +3067,7 @@
|
|||
"create_wiki": true,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.review_requests",
|
||||
"default_used_roles": "[\n \"ad\",\n \"chair\",\n \"reviewer\",\n \"secr\"\n]",
|
||||
"docman_roles": "[\n \"secr\"\n]",
|
||||
|
@ -3010,6 +3084,10 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"ad\",\n \"secr\"\n]",
|
||||
"parent_types": [
|
||||
"area"
|
||||
],
|
||||
"need_parent": true,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"secr\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -3026,6 +3104,7 @@
|
|||
"create_wiki": false,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"auth\",\n \"chair\"\n]",
|
||||
"docman_roles": "[]",
|
||||
|
@ -3042,6 +3121,8 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[]",
|
||||
"parent_types": [],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"secr\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -3058,6 +3139,7 @@
|
|||
"create_wiki": true,
|
||||
"custom_group_roles": false,
|
||||
"customize_workflow": true,
|
||||
"default_parent": "irtf",
|
||||
"default_tab": "ietf.group.views.group_documents",
|
||||
"default_used_roles": "[\n \"chair\",\n \"techadv\",\n \"secr\",\n \"delegate\"\n]",
|
||||
"docman_roles": "[\n \"chair\",\n \"delegate\",\n \"secr\"\n]",
|
||||
|
@ -3074,6 +3156,10 @@
|
|||
"is_schedulable": true,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"chair\",\n \"delegate\",\n \"secr\"\n]",
|
||||
"parent_types": [
|
||||
"irtf"
|
||||
],
|
||||
"need_parent": true,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"delegate\",\n \"secr\"\n]",
|
||||
"show_on_agenda": true
|
||||
|
@ -3090,6 +3176,7 @@
|
|||
"create_wiki": false,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"liaiman\",\n \"ceo\",\n \"coord\",\n \"auth\",\n \"chair\",\n \"liaison_contact\",\n \"liaison_cc_contact\"\n]",
|
||||
"docman_roles": "[\n \"liaiman\",\n \"matman\"\n]",
|
||||
|
@ -3106,6 +3193,11 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[]",
|
||||
"parent_types": [
|
||||
"area",
|
||||
"sdo"
|
||||
],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"liaiman\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -3122,6 +3214,7 @@
|
|||
"create_wiki": true,
|
||||
"custom_group_roles": true,
|
||||
"customize_workflow": false,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_about",
|
||||
"default_used_roles": "[\n \"ad\",\n \"member\",\n \"delegate\",\n \"secr\",\n \"liaison\",\n \"atlarge\",\n \"chair\",\n \"matman\",\n \"techadv\"\n]",
|
||||
"docman_roles": "[\n \"chair\"\n]",
|
||||
|
@ -3138,6 +3231,10 @@
|
|||
"is_schedulable": false,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"chair\",\n \"matman\"\n]",
|
||||
"parent_types": [
|
||||
"area"
|
||||
],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": false,
|
||||
"role_order": "[\n \"chair\",\n \"member\",\n \"matman\"\n]",
|
||||
"show_on_agenda": false
|
||||
|
@ -3154,6 +3251,7 @@
|
|||
"create_wiki": true,
|
||||
"custom_group_roles": false,
|
||||
"customize_workflow": true,
|
||||
"default_parent": "",
|
||||
"default_tab": "ietf.group.views.group_documents",
|
||||
"default_used_roles": "[\n \"ad\",\n \"editor\",\n \"delegate\",\n \"secr\",\n \"chair\",\n \"matman\",\n \"techadv\",\n \"liaison_contact\",\n \"liaison_cc_contact\"\n]",
|
||||
"docman_roles": "[\n \"chair\",\n \"delegate\",\n \"secr\"\n]",
|
||||
|
@ -3170,6 +3268,10 @@
|
|||
"is_schedulable": true,
|
||||
"material_types": "[\n \"slides\"\n]",
|
||||
"matman_roles": "[\n \"ad\",\n \"chair\",\n \"delegate\",\n \"secr\"\n]",
|
||||
"parent_types": [
|
||||
"area"
|
||||
],
|
||||
"need_parent": false,
|
||||
"req_subm_approval": true,
|
||||
"role_order": "[\n \"chair\",\n \"secr\",\n \"delegate\"\n]",
|
||||
"show_on_agenda": true
|
||||
|
|
Loading…
Reference in a new issue