diff --git a/ietf/secr/groups/__init__.py b/ietf/secr/groups/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/ietf/secr/groups/forms.py b/ietf/secr/groups/forms.py deleted file mode 100644 index 7b9611515e..0000000000 --- a/ietf/secr/groups/forms.py +++ /dev/null @@ -1,117 +0,0 @@ -import re - -from django import forms -from django.db.models import Count - -from ietf.group.models import Group, Role -from ietf.name.models import GroupStateName, GroupTypeName, RoleName -from ietf.person.models import Person, Email - - -# --------------------------------------------- -# Select Choices -# --------------------------------------------- -SEARCH_MEETING_CHOICES = (('',''),('NO','NO'),('YES','YES')) - -# --------------------------------------------- -# Functions -# --------------------------------------------- -def get_person(name): - ''' - This function takes a string which is in the name autocomplete format "name - (id)" - and returns a person object - ''' - - match = re.search(r'\((\d+)\)', name) - if not match: - return None - id = match.group(1) - try: - person = Person.objects.get(id=id) - except (Person.ObjectDoesNoExist, Person.MultipleObjectsReturned): - return None - return person - -def get_parent_group_choices(): - area_choices = [(g.id, g.name) for g in Group.objects.filter(type='area',state='active')] - other_parents = Group.objects.annotate(children=Count('group')).filter(children__gt=0).order_by('name').exclude(type='area') - other_choices = [(g.id, g.name) for g in other_parents] - choices = (('Working Group Areas',area_choices),('Other',other_choices)) - return choices - -# --------------------------------------------- -# Forms -# --------------------------------------------- - -class DescriptionForm (forms.Form): - description = forms.CharField(widget=forms.Textarea(attrs={'rows':'20'}),required=True, strip=False) - - - -class RoleForm(forms.Form): - name = forms.ModelChoiceField(RoleName.objects.filter(slug__in=('chair','editor','secr','techadv')),empty_label=None) - person = forms.CharField(max_length=50,widget=forms.TextInput(attrs={'class':'name-autocomplete'}),help_text="To see a list of people type the first name, or last name, or both.") - email = forms.CharField(widget=forms.Select(),help_text="Select an email") - group_acronym = forms.CharField(widget=forms.HiddenInput(),required=False) - - def __init__(self, *args, **kwargs): - self.group = kwargs.pop('group') - super(RoleForm, self).__init__(*args,**kwargs) - # this form is re-used in roles app, use different roles in select - if self.group.features.custom_group_roles: - self.fields['name'].queryset = RoleName.objects.all() - - # check for id within parenthesis to ensure name was selected from the list - def clean_person(self): - person = self.cleaned_data.get('person', '') - m = re.search(r'(\d+)', person) - if person and not m: - raise forms.ValidationError("You must select an entry from the list!") - - # return person object - return get_person(person) - - # check that email exists and return the Email object - def clean_email(self): - email = self.cleaned_data['email'] - try: - obj = Email.objects.get(address=email) - except Email.ObjectDoesNoExist: - raise forms.ValidationError("Email address not found!") - - # return email object - return obj - - def clean(self): - # here we abort if there are any errors with individual fields - # One predictable problem is that the user types a name rather then - # selecting one from the list, as instructed to do. We need to abort - # so the error is displayed before trying to call get_person() - if any(self.errors): - # Don't bother validating the formset unless each form is valid on its own - return - super(RoleForm, self).clean() - cleaned_data = self.cleaned_data - person = cleaned_data['person'] - email = cleaned_data['email'] - name = cleaned_data['name'] - group_acronym = cleaned_data['group_acronym'] - - if email.person != person: - raise forms.ValidationError('ERROR: The person associated with the chosen email address is different from the chosen person') - - if Role.objects.filter(name=name,group=self.group,person=person,email=email): - raise forms.ValidationError('ERROR: This is a duplicate entry') - - if not group_acronym: - raise forms.ValidationError('You must select a group.') - - return cleaned_data - -class SearchForm(forms.Form): - group_acronym = forms.CharField(max_length=12,required=False) - group_name = forms.CharField(max_length=80,required=False) - primary_area = forms.ModelChoiceField(queryset=Group.objects.filter(type='area',state='active'),required=False) - type = forms.ModelChoiceField(queryset=GroupTypeName.objects.all(),required=False) - meeting_scheduled = forms.CharField(widget=forms.Select(choices=SEARCH_MEETING_CHOICES),required=False) - state = forms.ModelChoiceField(queryset=GroupStateName.objects.exclude(slug__in=('dormant','unknown')),required=False) diff --git a/ietf/secr/groups/tests.py b/ietf/secr/groups/tests.py deleted file mode 100644 index f4772abcfa..0000000000 --- a/ietf/secr/groups/tests.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright The IETF Trust 2013-2020, All Rights Reserved -# -*- coding: utf-8 -*- - - -from django.urls import reverse -from ietf.utils.test_utils import TestCase -from ietf.group.models import Group -from ietf.secr.groups.forms import get_parent_group_choices -from ietf.group.factories import GroupFactory, RoleFactory -from ietf.meeting.factories import MeetingFactory -from ietf.person.factories import PersonFactory -import debug # pyflakes:ignore - -class GroupsTest(TestCase): - def test_get_parent_group_choices(self): - GroupFactory(type_id='area') - choices = get_parent_group_choices() - area = Group.objects.filter(type='area',state='active').first() - # This is opaque. Can it be rewritten to be more self-documenting? - self.assertEqual(choices[0][1][0][0],area.id) - - # ------- Test Search -------- # - def test_search(self): - "Test Search" - MeetingFactory(type_id='ietf') - group = GroupFactory() - url = reverse('ietf.secr.groups.views.search') - post_data = {'group_acronym':group.acronym,'submit':'Search'} - self.client.login(username="secretary", password="secretary+password") - response = self.client.post(url,post_data,follow=True) - self.assertContains(response, group.acronym) - - # ------- Test View -------- # - def test_view(self): - MeetingFactory(type_id='ietf') - group = GroupFactory() - url = reverse('ietf.secr.groups.views.view', kwargs={'acronym':group.acronym}) - self.client.login(username="secretary", password="secretary+password") - response = self.client.get(url) - self.assertEqual(response.status_code, 200) - - - # ------- Test People -------- # - def test_people_delete(self): - role = RoleFactory(name_id='member') - group = role.group - id = role.id - url = reverse('ietf.secr.groups.views.delete_role', kwargs={'acronym':group.acronym,'id':role.id}) - target = reverse('ietf.secr.groups.views.people', kwargs={'acronym':group.acronym}) - self.client.login(username="secretary", password="secretary+password") - response = self.client.get(url) - self.assertEqual(response.status_code, 200) - response = self.client.post(url, {'post':'yes'}) - self.assertRedirects(response, target) - self.assertFalse(group.role_set.filter(id=id)) - - def test_people_add(self): - person = PersonFactory() - group = GroupFactory() - url = reverse('ietf.secr.groups.views.people', kwargs={'acronym':group.acronym}) - post_data = {'group_acronym':group.acronym, - 'name':'chair', - 'person':'Joe Smith - (%s)' % person.id, - 'email':person.email_set.all()[0].address, - 'submit':'Add'} - self.client.login(username="secretary", password="secretary+password") - response = self.client.post(url,post_data,follow=True) - self.assertRedirects(response, url) - self.assertContains(response, 'added successfully') diff --git a/ietf/secr/groups/urls.py b/ietf/secr/groups/urls.py deleted file mode 100644 index 60d3566cab..0000000000 --- a/ietf/secr/groups/urls.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.conf import settings - -from ietf.secr.groups import views -from ietf.utils.urls import url - -urlpatterns = [ - url(r'^$', views.search), - url(r'^blue-dot-report/$', views.blue_dot), - #(r'^ajax/get_ads/$', views.get_ads), - url(r'^%(acronym)s/$' % settings.URL_REGEXPS, views.view), - url(r'^%(acronym)s/delete/(?P\d{1,6})/$' % settings.URL_REGEXPS, views.delete_role), - url(r'^%(acronym)s/charter/$' % settings.URL_REGEXPS, views.charter), - url(r'^%(acronym)s/people/$' % settings.URL_REGEXPS, views.people), -] diff --git a/ietf/secr/groups/views.py b/ietf/secr/groups/views.py deleted file mode 100644 index 48541efe31..0000000000 --- a/ietf/secr/groups/views.py +++ /dev/null @@ -1,301 +0,0 @@ -from django.contrib import messages -from django.conf import settings -from django.shortcuts import render, get_object_or_404, redirect - -from ietf.group.models import Group, GroupEvent, Role -from ietf.group.utils import save_group_in_history, get_charter_text -from ietf.ietfauth.utils import role_required -from ietf.person.models import Person -from ietf.secr.groups.forms import RoleForm, SearchForm -from ietf.secr.utils.meeting import get_current_meeting -from ietf.liaisons.views import contacts_from_roles - -# ------------------------------------------------- -# Helper Functions -# ------------------------------------------------- - -def add_legacy_fields(group): - ''' - This function takes a Group object as input and adds legacy attributes: - start_date,proposed_date,concluded_date,meeting_scheduled - ''' - # it's possible there could be multiple records of a certain type in which case - # we just return the latest record - query = GroupEvent.objects.filter(group=group, type="changed_state").order_by('time') - proposed = query.filter(changestategroupevent__state="proposed") - meeting = get_current_meeting() - - if proposed: - group.proposed_date = proposed[0].time - active = query.filter(changestategroupevent__state="active") - if active: - group.start_date = active[0].time - concluded = query.filter(changestategroupevent__state="conclude") - if concluded: - group.concluded_date = concluded[0].time - - if group.session_set.filter(meeting__number=meeting.number): - group.meeting_scheduled = 'YES' - else: - group.meeting_scheduled = 'NO' - - group.chairs = group.role_set.filter(name="chair") - group.techadvisors = group.role_set.filter(name="techadv") - group.editors = group.role_set.filter(name="editor") - group.secretaries = group.role_set.filter(name="secr") - # Note: liaison_contacts is now a dict instead of a model instance with fields. In - # templates, the dict can still be accessed using '.contacts' and .cc_contacts', though. - group.liaison_contacts = dict( - contacts=contacts_from_roles(group.role_set.filter(name='liaison_contact')), - cc_contacts=contacts_from_roles(group.role_set.filter(name='liaison_cc_contact')), - ) - - #fill_in_charter_info(group) - -#-------------------------------------------------- -# AJAX Functions -# ------------------------------------------------- -''' -def get_ads(request): - """ AJAX function which takes a URL parameter, "area" and returns the area directors - in the form of a list of dictionaries with "id" and "value" keys(in json format). - Used to populate select options. - """ - - results=[] - area = request.GET.get('area','') - qs = AreaDirector.objects.filter(area=area) - for item in qs: - d = {'id': item.id, 'value': item.person.first_name + ' ' + item.person.last_name} - results.append(d) - - return HttpResponse(json.dumps(results), content_type='application/javascript') -''' -# ------------------------------------------------- -# Standard View Functions -# ------------------------------------------------- - - - -@role_required('Secretariat') -def blue_dot(request): - ''' - This is a report view. It returns a text/plain listing of working group chairs. - ''' - people = Person.objects.filter(role__name__slug='chair', - role__group__type='wg', - role__group__state__slug__in=('active','bof','proposed')).distinct() - chairs = [] - for person in people: - parts = person.name_parts() - groups = [ r.group.acronym for r in person.role_set.filter(name__slug='chair', - group__type='wg', - group__state__slug__in=('active','bof','proposed')) ] - entry = {'name':'%s, %s' % (parts[3], parts[1]), - 'groups': ', '.join(groups)} - chairs.append(entry) - - # sort the list - sorted_chairs = sorted(chairs, key = lambda a: a['name']) - - return render(request, 'groups/blue_dot_report.txt', { 'chairs':sorted_chairs }, - content_type="text/plain; charset=%s"%settings.DEFAULT_CHARSET, - ) - -@role_required('Secretariat') -def charter(request, acronym): - """ - View Group Charter - - **Templates:** - - * ``groups/charter.html`` - - **Template Variables:** - - * group, charter_text - - """ - - group = get_object_or_404(Group, acronym=acronym) - # TODO: get_charter_text() should be updated to return None - if group.charter: - charter_text = get_charter_text(group) - else: - charter_text = '' - - return render(request, 'groups/charter.html', { - 'group': group, - 'charter_text': charter_text}, - ) - -@role_required('Secretariat') -def delete_role(request, acronym, id): - """ - Handle deleting roles for groups (chair, editor, advisor, secretary) - - **Templates:** - - * none - - Redirects to people page on success. - - """ - group = get_object_or_404(Group, acronym=acronym) - role = get_object_or_404(Role, id=id) - - if request.method == 'POST' and request.POST['post'] == 'yes': - # save group - save_group_in_history(group) - - role.delete() - messages.success(request, 'The entry was deleted successfully') - return redirect('ietf.secr.groups.views.people', acronym=acronym) - - return render(request, 'confirm_delete.html', {'object': role}) - - -@role_required('Secretariat') -def people(request, acronym): - """ - Edit Group Roles (Chairs, Secretary, etc) - - **Templates:** - - * ``groups/people.html`` - - **Template Variables:** - - * form, group - - """ - - group = get_object_or_404(Group, acronym=acronym) - - if request.method == 'POST': - # we need to pass group for form validation - form = RoleForm(request.POST,group=group) - if form.is_valid(): - name = form.cleaned_data['name'] - person = form.cleaned_data['person'] - email = form.cleaned_data['email'] - - # save group - save_group_in_history(group) - - Role.objects.create(name=name, - person=person, - email=email, - group=group) - - if not email.origin or email.origin == person.user.username: - email.origin = "role: %s %s" % (group.acronym, name.slug) - email.save() - - messages.success(request, 'New %s added successfully!' % name) - return redirect('ietf.secr.groups.views.people', acronym=group.acronym) - else: - form = RoleForm(initial={'name':'chair', 'group_acronym':group.acronym}, group=group) - - return render(request, 'groups/people.html', { - 'form':form, - 'group':group}, - ) - -@role_required('Secretariat') -def search(request): - """ - Search IETF Groups - - **Templates:** - - * ``groups/search.html`` - - **Template Variables:** - - * form, results - - """ - results = [] - if request.method == 'POST': - form = SearchForm(request.POST) - - if form.is_valid(): - kwargs = {} - group_acronym = form.cleaned_data['group_acronym'] - group_name = form.cleaned_data['group_name'] - primary_area = form.cleaned_data['primary_area'] - meeting_scheduled = form.cleaned_data['meeting_scheduled'] - state = form.cleaned_data['state'] - type = form.cleaned_data['type'] - meeting = get_current_meeting() - - # construct search query - if group_acronym: - kwargs['acronym__istartswith'] = group_acronym - if group_name: - kwargs['name__istartswith'] = group_name - if primary_area: - kwargs['parent'] = primary_area - if state: - kwargs['state'] = state - if type: - kwargs['type'] = type - #else: - # kwargs['type__in'] = ('wg','rg','ietf','ag','sdo','team') - - if meeting_scheduled == 'YES': - kwargs['session__meeting__number'] = meeting.number - # perform query - if kwargs: - if meeting_scheduled == 'NO': - qs = Group.objects.filter(**kwargs).exclude(session__meeting__number=meeting.number).distinct() - else: - qs = Group.objects.filter(**kwargs).distinct() - else: - qs = Group.objects.all() - results = qs.order_by('acronym') - - # if there's just one result go straight to view - if len(results) == 1: - return redirect('ietf.secr.groups.views.view', acronym=results[0].acronym) - - # process GET argument to support link from area app - elif 'primary_area' in request.GET: - area = request.GET.get('primary_area','') - results = Group.objects.filter(parent__id=area,type='wg',state__in=('bof','active','proposed')).order_by('name') - form = SearchForm({'primary_area':area,'state':'','type':'wg'}) - else: - form = SearchForm(initial={'state':'active'}) - - # loop through results and tack on meeting_scheduled because it is no longer an - # attribute of the meeting model - for result in results: - add_legacy_fields(result) - - return render(request, 'groups/search.html', { - 'results': results, - 'form': form}, - ) - -@role_required('Secretariat') -def view(request, acronym): - """ - View IETF Group details - - **Templates:** - - * ``groups/view.html`` - - **Template Variables:** - - * group - - """ - - group = get_object_or_404(Group, acronym=acronym) - - add_legacy_fields(group) - - return render(request, 'groups/view.html', { 'group': group } ) - diff --git a/ietf/secr/templates/areas/view.html b/ietf/secr/templates/areas/view.html index 3b95988682..e3ecac70a1 100644 --- a/ietf/secr/templates/areas/view.html +++ b/ietf/secr/templates/areas/view.html @@ -43,7 +43,6 @@

Directors

  • -
  • diff --git a/ietf/secr/templates/groups/blue_dot_report.txt b/ietf/secr/templates/groups/blue_dot_report.txt deleted file mode 100644 index 7516b01dad..0000000000 --- a/ietf/secr/templates/groups/blue_dot_report.txt +++ /dev/null @@ -1,6 +0,0 @@ -BLUE DOT REPORT - -NAMES ROSTER BADGE --------------------------------------------------------------------------- -{% for chair in chairs %}{{ chair.name|safe|stringformat:"-33s" }}{{ chair.groups|stringformat:"-36s" }}BLUE -{% endfor %} \ No newline at end of file diff --git a/ietf/secr/templates/groups/charter.html b/ietf/secr/templates/groups/charter.html deleted file mode 100644 index 84649f4fde..0000000000 --- a/ietf/secr/templates/groups/charter.html +++ /dev/null @@ -1,25 +0,0 @@ -{% extends "base_site.html" %} -{% load staticfiles %} - -{% block title %}Groups - Charter{% endblock %} - -{% block extrahead %}{{ block.super }} - -{% endblock %} - -{% block breadcrumbs %}{{ block.super }} - » Groups - » {{ group.acronym }} - » Charter -{% endblock %} - -{% block content %} - -
    -

    Groups - Charter

    -
    -    {% if charter_txt %}{{ charter_text }}{% else %}Charter not found.{% endif %}
    -    
    -
    - -{% endblock %} diff --git a/ietf/secr/templates/groups/people.html b/ietf/secr/templates/groups/people.html deleted file mode 100644 index 3c207d3dbf..0000000000 --- a/ietf/secr/templates/groups/people.html +++ /dev/null @@ -1,72 +0,0 @@ -{% extends "base_site.html" %} -{% load staticfiles widget_tweaks %} - -{% block title %}Groups - People{% endblock %} - -{% block extrahead %}{{ block.super }} - - - -{% endblock %} - -{% block breadcrumbs %}{{ block.super }} - » Groups - » {{ group.acronym }} - » People -{% endblock %} - -{% block content %} - -
    -

    People

    - - - - - - - - - - {% if group.role_set.all %} - - {% for role in group.role_set.all %} - - - - - - - {% endfor %} - - {% endif %} -
    RoleNameEmailAction
    {{ role.name }}{{ role.person }}{{ role.email }}Delete
    - - - -
    -
      -
    • -
    -
    - -
    - -{% endblock %} \ No newline at end of file diff --git a/ietf/secr/templates/groups/search.html b/ietf/secr/templates/groups/search.html deleted file mode 100644 index fa7d3d2154..0000000000 --- a/ietf/secr/templates/groups/search.html +++ /dev/null @@ -1,39 +0,0 @@ -{% extends "base_site.html" %} -{% load staticfiles %} - -{% block title %}Groups - Search{% endblock %} - -{% block extrahead %}{{ block.super }} - - - -{% endblock %} - -{% block breadcrumbs %}{{ block.super }} - » Groups -{% endblock %} - -{% block content %} - -
    -

    Groups - Search

    -
    {% csrf_token %} - - - - {{ form.as_table }} - -
    - - {% include "includes/buttons_search.html" %} - -
    - - -
    - -{% endblock %} \ No newline at end of file diff --git a/ietf/secr/templates/groups/view.html b/ietf/secr/templates/groups/view.html deleted file mode 100644 index a0509b0630..0000000000 --- a/ietf/secr/templates/groups/view.html +++ /dev/null @@ -1,123 +0,0 @@ -{% extends "base_site.html" %} -{% load staticfiles %} - -{% block title %}Groups - View{% endblock %} - -{% block extrahead %}{{ block.super }} - -{% endblock %} - -{% block breadcrumbs %}{{ block.super }} - » Groups - » {{ group.acronym }} -{% endblock %} - -{% block content %} - -
    -
    -

    Groups - View

    - - - - - - - - - - {% comment %} - Here we need to check that group.area_director and group.area_director.area are defined before referencing. - Otherwise the template would raise errors if the group area director record didn't exist or - in the case of Area Director = TBD, the area field is NULL - {% endcomment %} - - - - - - - - - - {% if group.liaison_contacts %} - - {% endif %} - {% if group.features.has_chartering_process %} - - {% else %} - - {% endif %} - - - -
    Group Acronym:{{ group.acronym }}
    Group Name:{{ group.name }}
    Status:{{ group.state }}
    Type:{{ group.type }}
    Proposed Date:{{ group.proposed_date|date:"Y-m-d" }}
    Start Date:{{ group.start_date|date:"Y-m-d" }}
    Concluded Date:{{ group.concluded_date|date:"Y-m-d" }}
    Primary Area:{% if not group.parent %}(No Data){% else %} - {{ group.parent }} - {% endif %} -
    Primary Area Director:{% if group.ad_role %} - {{ group.ad_role.person }} - {% endif %} -
    Meeting Scheduled:{{ group.meeting_scheduled}}
    Email Address:{{ group.list_email }}
    Email Subscription:{{ group.list_subscribe }}
    Email Archive:{{ group.list_archive }}
    Default Liaison Contacts:{{ group.liaison_contacts.contacts }}
    Charter:View Charter
    Description:{{ group.description }}
    Comments:{{ group.comments }}
    Last Modified Date:{{ group.time }}
    - - -
    -
    - - - - - - - - -
    - -
    -
      -
    • -
    • - {% comment %} -
    • -
    • - {% endcomment %} -
    -
    -
    - -{% endblock %} \ No newline at end of file diff --git a/ietf/secr/templates/includes/group_search_results.html b/ietf/secr/templates/includes/group_search_results.html deleted file mode 100644 index fb8b2b06e3..0000000000 --- a/ietf/secr/templates/includes/group_search_results.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - {% for item in results %} - - - - - - - - {% empty %} - - {% endfor %} - -
    Group NameGroup AcronymStatusTypeMeeting Scheduled
    {{item.name}}{{item.acronym}}{{item.state}}{{item.type}}{{item.meeting_scheduled}}
    No Results
    diff --git a/ietf/secr/templates/main.html b/ietf/secr/templates/main.html index 64cd063b83..455895c76d 100644 --- a/ietf/secr/templates/main.html +++ b/ietf/secr/templates/main.html @@ -21,7 +21,6 @@

    IESG

    IDs and WGs Process

    diff --git a/ietf/secr/templates/rolodex/view.html b/ietf/secr/templates/rolodex/view.html index 738ab3361a..73d1c3f58b 100644 --- a/ietf/secr/templates/rolodex/view.html +++ b/ietf/secr/templates/rolodex/view.html @@ -46,7 +46,7 @@

    Roles

    {% if role.group.type.slug == "area" %} {{ role.group.acronym }}{% if role.group.state.slug == "conclude" %} (concluded){% endif %} {% else %} - {{ role.group.acronym }}{% if role.group.state.slug == "conclude" %} (concluded){% endif %} + {{ role.group.acronym }}{% if role.group.state.slug == "conclude" %} (concluded){% endif %} {% endif %} {{ role.email }} diff --git a/ietf/secr/urls.py b/ietf/secr/urls.py index 21c575cf62..5a3df23d0b 100644 --- a/ietf/secr/urls.py +++ b/ietf/secr/urls.py @@ -6,7 +6,6 @@ url(r'^announcement/', include('ietf.secr.announcement.urls')), url(r'^areas/', include('ietf.secr.areas.urls')), url(r'^console/', include('ietf.secr.console.urls')), - url(r'^groups/', include('ietf.secr.groups.urls')), url(r'^meetings/', include('ietf.secr.meetings.urls')), url(r'^rolodex/', include('ietf.secr.rolodex.urls')), url(r'^sreq/', include('ietf.secr.sreq.urls')), diff --git a/ietf/settings.py b/ietf/settings.py index a91eb8bc1c..4fae3ec468 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -476,7 +476,6 @@ def skip_unreadable_post(record): # IETF Secretariat apps 'ietf.secr.announcement', 'ietf.secr.areas', - 'ietf.secr.groups', 'ietf.secr.meetings', 'ietf.secr.rolodex', 'ietf.secr.sreq',