Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ jobs:
token: ${{ secrets.GH_INFRA_K8S_TOKEN }}
inputs: '{ "environment":"${{ secrets.GHA_K8S_CLUSTER }}", "app":"datatracker", "manifest":"postgres", "forceRecreate":true, "waitClusterReady":true }'
wait-for-completion: true
wait-for-completion-timeout: 10m
wait-for-completion-timeout: 30m
wait-for-completion-interval: 20s
display-workflow-run-url: false

Expand Down
4 changes: 2 additions & 2 deletions ietf/doc/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ class StateTypeAdmin(admin.ModelAdmin):
admin.site.register(StateType, StateTypeAdmin)

class StateAdmin(admin.ModelAdmin):
list_display = ["slug", "type", 'name', 'order', 'desc']
list_filter = ["type", ]
list_display = ["slug", "type", 'name', 'order', 'desc', "used"]
list_filter = ["type", "used"]
search_fields = ["slug", "type__label", "type__slug", "name", "desc"]
filter_horizontal = ["next_states"]
admin.site.register(State, StateAdmin)
Expand Down
7 changes: 7 additions & 0 deletions ietf/group/templatetags/group_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,10 @@ def role_person_link(role, **kwargs):
plain_name = role.person.plain_name()
email = role.email.address
return {'name': name, 'plain_name': plain_name, 'email': email, 'title': title, 'class': cls}

@register.filter
def name_with_conditional_acronym(group):
if group.type_id in ("sdo", "isoc", "individ", "nomcom", "ietf", "irtf", ):
return group.name
else:
return f"{group.name} ({group.acronym})"
3 changes: 1 addition & 2 deletions ietf/liaisons/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ def internal_groups_for_person(person: Optional[Person]):
"Secretariat",
"IETF Chair",
"IAB Chair",
"IAB Executive Director",
"Liaison Manager",
"Liaison Coordinator",
"Authorized Individual",
Expand All @@ -115,7 +114,7 @@ def internal_groups_for_person(person: Optional[Person]):
# Interesting roles, as Group queries
queries = [
Q(role__person=person, role__name="chair", acronym="ietf"),
Q(role__person=person, role__name__in=("chair", "execdir"), acronym="iab"),
Q(role__person=person, role__name="chair", acronym="iab"),
Q(role__person=person, role__name="ad", type="area", state="active"),
Q(
role__person=person,
Expand Down
12 changes: 0 additions & 12 deletions ietf/liaisons/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ def test_get_cc(self):
cc = get_cc(Group.objects.get(acronym='iab'))
self.assertTrue(EMAIL_ALIASES['IAB'] in cc)
self.assertTrue(EMAIL_ALIASES['IABCHAIR'] in cc)
self.assertTrue(EMAIL_ALIASES['IABEXECUTIVEDIRECTOR'] in cc)
# test an Area
area = Group.objects.filter(type='area').first()
cc = get_cc(area)
Expand Down Expand Up @@ -166,7 +165,6 @@ def test_get_contacts_for_group(self):
# test iab
contacts = get_contacts_for_group(Group.objects.get(acronym='iab'))
self.assertTrue(EMAIL_ALIASES['IABCHAIR'] in contacts)
self.assertTrue(EMAIL_ALIASES['IABEXECUTIVEDIRECTOR'] in contacts)
# test iesg
contacts = get_contacts_for_group(Group.objects.get(acronym='iesg'))
self.assertTrue(EMAIL_ALIASES['IESG'] in contacts)
Expand Down Expand Up @@ -534,7 +532,6 @@ def test_outgoing_access(self):
RoleFactory(name_id='liaison_coordinator', group__acronym='iab', person__user__username='liaison-coordinator')
mars = RoleFactory(name_id='chair',person__user__username='marschairman',group__acronym='mars').group
RoleFactory(name_id='secr',group=mars,person__user__username='mars-secr')
RoleFactory(name_id='execdir',group=Group.objects.get(acronym='iab'),person__user__username='iab-execdir')

url = urlreverse('ietf.liaisons.views.liaison_list')
addurl = urlreverse('ietf.liaisons.views.liaison_add', kwargs={'type':'outgoing'})
Expand Down Expand Up @@ -592,15 +589,6 @@ def test_outgoing_access(self):
r = self.client.get(addurl)
self.assertEqual(r.status_code, 200)

# IAB Executive Director
self.assertTrue(self.client.login(username="iab-execdir", password="iab-execdir+password"))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEqual(len(q("a.btn:contains('New outgoing liaison')")), 1)
r = self.client.get(addurl)
self.assertEqual(r.status_code, 200)

# Liaison Manager has access
self.assertTrue(self.client.login(username="ulm-liaiman", password="ulm-liaiman+password"))
r = self.client.get(url)
Expand Down
12 changes: 0 additions & 12 deletions ietf/liaisons/tests_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,6 @@ def test_all_internal_groups(self):
def test_internal_groups_for_person(self):
# test relies on the data created in ietf.utils.test_data.make_immutable_test_data()
# todo add liaison coordinator when modeled
RoleFactory(
name_id="execdir",
group=Group.objects.get(acronym="iab"),
person__user__username="iab-execdir",
)
RoleFactory(
name_id="auth",
group__type_id="sdo",
Expand All @@ -121,7 +116,6 @@ def test_internal_groups_for_person(self):
"secretary",
"ietf-chair",
"iab-chair",
"iab-execdir",
"sdo-authperson",
):
returned_queryset = internal_groups_for_person(
Expand Down Expand Up @@ -151,11 +145,6 @@ def test_internal_groups_for_person(self):
)

def test_external_groups_for_person(self):
RoleFactory(
name_id="execdir",
group=Group.objects.get(acronym="iab"),
person__user__username="iab-execdir",
)
RoleFactory(name_id="liaison_coordinator", group__acronym="iab", person__user__username="liaison-coordinator")
the_sdo = GroupFactory(type_id="sdo", acronym="the-sdo")
liaison_manager = RoleFactory(name_id="liaiman", group=the_sdo).person
Expand All @@ -166,7 +155,6 @@ def test_external_groups_for_person(self):
"secretary",
"ietf-chair",
"iab-chair",
"iab-execdir",
"liaison-coordinator",
"ad",
"sopschairman",
Expand Down
1 change: 0 additions & 1 deletion ietf/liaisons/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
OUTGOING_LIAISON_ROLES = [
"Area Director",
"IAB Chair",
"IAB Executive Director",
"IETF Chair",
"Liaison Manager",
"Liaison Coordinator",
Expand Down
17 changes: 7 additions & 10 deletions ietf/liaisons/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@
from ietf.utils.response import permission_denied

EMAIL_ALIASES = {
'IETFCHAIR':'The IETF Chair <chair@ietf.org>',
'IESG':'The IESG <iesg@ietf.org>',
'IAB':'The IAB <iab@iab.org>',
'IABCHAIR':'The IAB Chair <iab-chair@iab.org>',
'IABEXECUTIVEDIRECTOR':'The IAB Executive Director <execd@iab.org>'}
"IETFCHAIR": "The IETF Chair <chair@ietf.org>",
"IESG": "The IESG <iesg@ietf.org>",
"IAB": "The IAB <iab@iab.org>",
"IABCHAIR": "The IAB Chair <iab-chair@iab.org>",
}


# -------------------------------------------------
# Helper Functions
Expand Down Expand Up @@ -84,8 +85,6 @@ def _find_person_in_emails(liaison, person):
return True
elif addr in ('iab@iab.org', 'iab-chair@iab.org') and has_role(person.user, "IAB Chair"):
return True
elif addr in ('execd@iab.org', ) and has_role(person.user, "IAB Executive Director"):
return True

return False

Expand All @@ -110,7 +109,6 @@ def get_cc(group):
elif group.acronym in ('iab'):
emails.append(EMAIL_ALIASES['IAB'])
emails.append(EMAIL_ALIASES['IABCHAIR'])
emails.append(EMAIL_ALIASES['IABEXECUTIVEDIRECTOR'])
elif group.type_id == 'area':
emails.append(EMAIL_ALIASES['IETFCHAIR'])
ad_roles = group.role_set.filter(name='ad')
Expand Down Expand Up @@ -151,7 +149,6 @@ def get_contacts_for_group(group):
contacts.append(EMAIL_ALIASES['IETFCHAIR'])
elif group.acronym == 'iab':
contacts.append(EMAIL_ALIASES['IABCHAIR'])
contacts.append(EMAIL_ALIASES['IABEXECUTIVEDIRECTOR'])
elif group.acronym == 'iesg':
contacts.append(EMAIL_ALIASES['IESG'])

Expand All @@ -171,7 +168,7 @@ def needs_approval(group,person):
user = person.user
if group.acronym in ('ietf','iesg') and has_role(user, 'IETF Chair'):
return False
if group.acronym == 'iab' and (has_role(user,'IAB Chair') or has_role(user,'IAB Executive Director')):
if group.acronym == 'iab' and has_role(user,'IAB Chair'):
return False
if group.type_id == 'area' and group.role_set.filter(name='ad',person=person):
return False
Expand Down
7 changes: 4 additions & 3 deletions ietf/mailtrigger/admin.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
# Copyright The IETF Trust 2015-2019, All Rights Reserved
# Copyright The IETF Trust 2015-2025, All Rights Reserved
from django.contrib import admin
from simple_history.admin import SimpleHistoryAdmin

from ietf.mailtrigger.models import MailTrigger, Recipient

class RecipientAdmin(admin.ModelAdmin):
class RecipientAdmin(SimpleHistoryAdmin):
list_display = [ 'slug', 'desc', 'template', 'has_code', ]
def has_code(self, obj):
return hasattr(obj,'gather_%s'%obj.slug)
has_code.boolean = True # type: ignore # https://github.com/python/mypy/issues/2087
admin.site.register(Recipient, RecipientAdmin)


class MailTriggerAdmin(admin.ModelAdmin):
class MailTriggerAdmin(SimpleHistoryAdmin):
list_display = [ 'slug', 'desc', ]
filter_horizontal = [ 'to', 'cc', ]
admin.site.register(MailTrigger, MailTriggerAdmin)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Copyright The IETF Trust 2025, All Rights Reserved
from io import StringIO

from django.conf import settings
from django.core import management
from django.db import migrations, models
import django.db.models.deletion
import simple_history.models

from ietf.utils.log import log


def forward(apps, schema_editor):
# Fill in history for existing data using the populate_history management command
captured_stdout = StringIO()
captured_stderr = StringIO()
try:
management.call_command(
"populate_history",
"mailtrigger.MailTrigger",
"mailtrigger.Recipient",
stdout=captured_stdout,
stderr=captured_stderr,
)
except management.CommandError as err:
log(
"Failed to populate history for mailtrigger models.\n"
"\n"
f"stdout:\n{captured_stdout.getvalue() or '<none>'}\n"
"\n"
f"stderr:\n{captured_stderr.getvalue() or '<none>'}\n"
)
raise RuntimeError("Failed to populate history for mailtrigger models") from err
log(
"Populated history for mailtrigger models.\n"
"\n"
f"stdout:\n{captured_stdout.getvalue() or '<none>'}\n"
"\n"
f"stderr:\n{captured_stderr.getvalue() or '<none>'}\n"
)


def reverse(apps, schema_editor):
pass # nothing to do


class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("mailtrigger", "0006_call_for_adoption_and_last_call_issued"),
]

operations = [
migrations.CreateModel(
name="HistoricalRecipient",
fields=[
("slug", models.CharField(db_index=True, max_length=32)),
("desc", models.TextField(blank=True)),
("template", models.TextField(blank=True, null=True)),
("history_id", models.AutoField(primary_key=True, serialize=False)),
("history_date", models.DateTimeField(db_index=True)),
("history_change_reason", models.CharField(max_length=100, null=True)),
(
"history_type",
models.CharField(
choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")],
max_length=1,
),
),
(
"history_user",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="+",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"verbose_name": "historical recipient",
"verbose_name_plural": "historical recipients",
"ordering": ("-history_date", "-history_id"),
"get_latest_by": ("history_date", "history_id"),
},
bases=(simple_history.models.HistoricalChanges, models.Model),
),
migrations.CreateModel(
name="HistoricalMailTrigger",
fields=[
("slug", models.CharField(db_index=True, max_length=64)),
("desc", models.TextField(blank=True)),
("history_id", models.AutoField(primary_key=True, serialize=False)),
("history_date", models.DateTimeField(db_index=True)),
("history_change_reason", models.CharField(max_length=100, null=True)),
(
"history_type",
models.CharField(
choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")],
max_length=1,
),
),
(
"history_user",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="+",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"verbose_name": "historical mail trigger",
"verbose_name_plural": "historical mail triggers",
"ordering": ("-history_date", "-history_id"),
"get_latest_by": ("history_date", "history_id"),
},
bases=(simple_history.models.HistoricalChanges, models.Model),
),
migrations.RunPython(forward, reverse),
]
6 changes: 5 additions & 1 deletion ietf/mailtrigger/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright The IETF Trust 2015-2020, All Rights Reserved
# Copyright The IETF Trust 2015-2025, All Rights Reserved
# -*- coding: utf-8 -*-


Expand All @@ -7,6 +7,8 @@

from email.utils import parseaddr

from simple_history.models import HistoricalRecords

from ietf.doc.utils_bofreq import bofreq_editors, bofreq_responsible
from ietf.utils.mail import formataddr, get_email_addresses_from_text
from ietf.group.models import Group, Role
Expand Down Expand Up @@ -38,6 +40,7 @@ class MailTrigger(models.Model):
desc = models.TextField(blank=True)
to = models.ManyToManyField('mailtrigger.Recipient', blank=True, related_name='used_in_to')
cc = models.ManyToManyField('mailtrigger.Recipient', blank=True, related_name='used_in_cc')
history = HistoricalRecords()

class Meta:
ordering = ["slug"]
Expand All @@ -49,6 +52,7 @@ class Recipient(models.Model):
slug = models.CharField(max_length=32, primary_key=True)
desc = models.TextField(blank=True)
template = models.TextField(null=True, blank=True)
history = HistoricalRecords()

class Meta:
ordering = ["slug"]
Expand Down
Loading