8000 ci: merge main to release by rjsparks · Pull Request #9029 · ietf-tools/datatracker · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

ci: merge main to release #9029

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 19, 2025
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
326 changes: 196 additions & 130 deletions dev/coverage-action/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dev/coverage-action/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"license": "BSD-3-Clause",
"dependencies": {
"@actions/core": "1.11.1",
"@actions/github": "6.0.0",
"@actions/github": "6.0.1",
"lodash": "4.17.21",
"luxon": "3.6.1"
}
Expand Down
2 changes: 1 addition & 1 deletion ietf/api/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1161,7 +1161,7 @@ def test_related_email_list(self):
self.assertEqual(r.headers["Content-Type"], "application/json")
result = json.loads(r.content)
self.assertCountEqual(result.keys(), ["addresses"])
self.assertCountEqual(result["addresses"], joe.person.email_set.exclude(address='joe@home.com').values_list("address", flat=True))
self.assertCountEqual(result["addresses"], joe.person.email_set.values_list("address", flat=True))
# non-ascii
non_ascii_url = urlreverse("ietf.api.views.related_email_list", kwargs={'email': 'jòe@spain.com'})
r = self.client.get(non_ascii_url, headers={"X-Api-Key": "valid-token"})
Expand Down
2 changes: 1 addition & 1 deletion ietf/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ def _http_err(code, text):
return JsonResponse({"addresses": []})
return JsonResponse(
{
"addresses": list(person.email_set.exclude(address=email).values_list("address", flat=True)),
"addresses": list(person.email_set.values_list("address", flat=True)),
}
)
return HttpResponse(status=405)
Expand Down
13 changes: 10 additions & 3 deletions ietf/doc/forms.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright The IETF Trust 2013-2020, All Rights Reserved
# Copyright The IETF Trust 2013-2025, All Rights Reserved
# -*- coding: utf-8 -*-


Expand All @@ -9,7 +9,7 @@
from django.core.validators import validate_email

from ietf.doc.fields import SearchableDocumentField, SearchableDocumentsField
from ietf.doc.models import RelatedDocument, DocExtResource
from ietf.doc.models import RelatedDocument, DocExtResource, State
from ietf.iesg.models import TelechatDate
from ietf.iesg.utils import telechat_page_count
from ietf.person.fields import SearchablePersonField, SearchablePersonsField
Expand Down Expand Up @@ -61,7 +61,7 @@ class DocAuthorChangeBasisForm(forms.Form):
basis = forms.CharField(max_length=255,
label='Reason for change',
help_text='What is the source or reasoning for the changes to the author list?')

class AdForm(forms.Form):
ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active", role__group__type='area').order_by 8000 ('name'),
label="Shepherding AD", empty_label="(None)", required=True)
Expand Down Expand Up @@ -288,3 +288,10 @@ def clean_name_fragment(self):
if any(c in name_fragment for c in disallowed_characters):
raise ValidationError(f"The following characters are disallowed: {', '.join(disallowed_characters)}")
return name_fragment


class ChangeStatementStateForm(forms.Form):
state = forms.ModelChoiceField(
State.objects.filter(used=True, type="statement"),
empty_label=None,
)
35 changes: 34 additions & 1 deletion ietf/doc/tests_statement.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright The IETF Trust 2023, All Rights Reserved
# Copyright The IETF Trust 2023-2025, All Rights Reserved

import debug # pyflakes:ignore

Expand Down Expand Up @@ -372,3 +372,36 @@ def test_submit_non_markdown_formats(self):
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue("Unexpected content" in q("#id_statement_file").next().text())

def test_change_statement_state(self):
statement = StatementFactory() # starts in "active" state
active_state = State.objects.get(type_id="statement", slug="active")
replaced_state = State.objects.get(type_id="statement", slug="replaced")
url = urlreverse(
"ietf.doc.views_statement.change_statement_state",
kwargs={"name": statement.name},
)

events_before = statement.docevent_set.count()
login_testing_unauthorized(self, "secretary", url)

r = self.client.get(url)
self.assertEqual(r.status_code,200)

r = self.client.post(url, {"state": active_state.pk}, follow=True)
self.assertContains(r, "State not changed", status_code=200)
statement = Document.objects.get(pk=statement.pk) # bust the state cache
self.assertEqual(statement.get_state(), active_state)

r = self.client.post(url, {"state": replaced_state.pk}, follow=True)
self.assertContains(r, "State changed to", status_code=200)
statement = Document.objects.get(pk=statement.pk) # bust the state cache
self.assertEqual(statement.get_state(), replaced_state)

events_after = statement.docevent_set.count()
self.assertEqual(events_after, events_before + 1)
event = statement.docevent_set.first()
self.assertEqual(event.type, "changed_state")
self.assertEqual(
event.desc, "Statement State changed to <b>Replaced</b> from Active"
)
3 changes: 2 additions & 1 deletion ietf/doc/urls.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright The IETF Trust 2009-2023, All Rights Reserved
# Copyright The IETF Trust 2009-2025, All Rights Reserved
# -*- coding: utf-8 -*-
# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
Expand Down Expand Up @@ -145,6 +145,7 @@
url(r'^%(name)s/edit/adopt/$' % settings.URL_REGEXPS, views_draft.adopt_draft),
url(r'^%(name)s/edit/release/$' % settings.URL_REGEXPS, views_draft.release_draft),
url(r'^%(name)s/edit/state/(?P<state_type>draft-stream-[a-z]+)/$' % settings.URL_REGEXPS, views_draft.change_stream_state),
url(r'^%(name)s/edit/state/statement/$' % settings.URL_REGEXPS, views_statement.change_statement_state),

url(r'^%(name)s/edit/clearballot/(?P<ballot_type_slug>[\w-]+)/$' % settings.URL_REGEXPS, views_ballot.clear_ballot),
url(r'^%(name)s/edit/deferballot/$' % settings.URL_REGEXPS, views_ballot.defer_ballot),
Expand Down
45 changes: 43 additions & 2 deletions ietf/doc/views_statement.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
# Copyright The IETF Trust 2023, All Rights Reserved
# Copyright The IETF Trust 2023-2025, All Rights Reserved
from django.contrib import messages

import debug # pyflakes: ignore

from pathlib import Path

from django import forms
from django.conf import settings
from django.http import FileResponse, Http404
from django.http import FileResponse, Http404, HttpResponseRedirect
from django.views.decorators.cache import cache_control
from django.shortcuts import get_object_or_404, render, redirect
from django.template.loader import render_to_string

from ietf.doc.forms import ChangeStatementStateForm
from ietf.doc.utils import add_state_change_event
from ietf.utils import markdown
from django.utils.html import escape

Expand Down Expand Up @@ -278,3 +282,40 @@ def new_statement(request):
}
form = NewStatementForm(initial=init)
return render(request, "doc/statement/new_statement.html", {"form": form})


@role_required("Secretariat")
def change_statement_state(request, name):
"""Change state of a statement Document"""
statement = get_object_or_404(
Document.objects.filter(type_id="statement"),
name=name,
)
if request.method == "POST":
form = ChangeStatementStateForm(request.POST)
if form.is_valid():
new_state = form.cleaned_data["state"]
prev_state = statement.get_state()
if new_state == prev_state:
messages.info(request, f"State not changed, remains {prev_state}.")
else:
statement.set_state(new_state)
e = add_state_change_event(
statement,
request.user.person,
prev_state,
new_state,
)
statement.save_with_history([e])
messages.success(request, f"State changed to {new_state}.")
return HttpResponseRedirect(statement.get_absolute_url())
else:
form = ChangeStatementStateForm(initial={"state": statement.get_state()})
return render(
request,
"doc/statement/change_statement_state.html",
{
"form": form,
"statement": statement,
},
)
2 changes: 1 addition & 1 deletion ietf/iesg/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,7 @@ def test_telechat_agenda_content_view(self):
urlreverse("ietf.iesg.views.telechat_agenda_content_view", kwargs={"section": section})
)
self.assertContains(r, content, status_code=200)
self.assertEqual(r.get("Content-Type", None), "text/plain")
self.assertEqual(r.get("Content-Type", None), "text/plain; charset=utf-8")

def test_telechat_agenda_content_view_permissions(self):
for section in TelechatAgendaSectionName.objects.filter(used=True).values_list("slug", flat=True):
Expand Down
2 changes: 1 addition & 1 deletion ietf/iesg/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,4 +610,4 @@ def telechat_agenda_content_manage(request):
@role_required("Secretariat", "IAB Chair", "Area Director")
def telechat_agenda_content_view(request, section):
content = get_object_or_404(TelechatAgendaContent, section__slug=section, section__used=True)
return HttpResponse(content=content.text, content_type="text/plain")
return HttpResponse(content=content.text, content_type="text/plain; charset=utf-8")
14 changes: 13 additions & 1 deletion ietf/static/js/edit-meeting-schedule.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ $(function () {
let sessionPurposeInputs = schedEditor.find('.session-purpose-toggles input');
let timeSlotGroupInputs = schedEditor.find("#timeslot-group-toggles-modal .modal-body .individual-timeslots input");
let sessionParentInputs = schedEditor.find(".session-parent-toggles input");
let sessionParentToggleAll = schedEditor.find(".session-parent-toggles .session-parent-toggle-all")
const classes_to_hide = '.hidden-timeslot-group,.hidden-timeslot-type';

// hack to work around lack of position sticky support in old browsers, see https://caniuse.com/#feat=css-sticky
Expand Down Expand Up @@ -769,6 +770,17 @@ $(function () {
sessionParentInputs.on("click", updateSessionParentToggling);
updateSessionParentToggling();

// Toggle _all_ session parents
function toggleAllSessionParents() {
if (sessionParentInputs.filter(":checked").length < sessionParentInputs.length) {
sessionParentInputs.prop("checked", true);
} else {
sessionParentInputs.prop("checked", false);
}
updateSessionParentToggling();
}
sessionParentToggleAll.on("click", toggleAllSessionParents);

// Toggling timeslot types
function updateTimeSlotTypeToggling() {
const checkedTypes = jQuery.map(timeSlotTypeInputs.filter(":checked"), elt => elt.value);
Expand Down Expand Up @@ -1020,4 +1032,4 @@ $(function () {
.on("mouseleave", ".other-session", function () {
sessions.filter("#session" + this.dataset.othersessionid).removeClass("highlight");
});
});
});
20 changes: 10 additions & 10 deletions ietf/submit/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2981,7 +2981,7 @@ def test_process_and_accept_uploaded_submission_invalid(self):
xml_path = Path(settings.IDSUBMIT_STAGING_PATH) / 'draft-somebody-test-00.xml'
with xml_path.open('w') as f:
f.write(xml_data)
store_str("staging", "draft-somebody-test-00.xml", xml_data)
store_str("staging", "draft-somebody-test-00.xml", xml_data, allow_overwrite=True)
with mock.patch(
'ietf.submit.utils.apply_checkers',
side_effect = lambda _, __: submission.checks.create(
Expand Down Expand Up @@ -3047,25 +3047,25 @@ def test_process_submission_xml(self):
# Should behave on missing or partial <date> elements
TestBlobstoreManager().emptyTestBlobstores()
xml_path.write_text(re.sub(r"<date.+>", "", xml_contents)) # strip <date...> entirely
store_str("staging", "draft-somebody-test-00.xml", re.sub(r"<date.+>", "", xml_contents))
store_str("staging", "draft-somebody-test-00.xml", re.sub(r"<date.+>", "", xml_contents), allow_overwrite=True)
output = process_submission_xml("draft-somebody-test", "00")
self.assertEqual(output["document_date"], None)

TestBlobstoreManager().emptyTestBlobstores()
xml_path.write_text(re.sub(r"<date year=.+ month", "<date month", xml_contents)) # remove year
store_str("staging", "draft-somebody-test-00.xml", re.sub(r"<date year=.+ month", "<date month", xml_contents))
store_str("staging", "draft-somebody-test-00.xml", re.sub(r"<date year=.+ month", "<date month", xml_contents), allow_overwrite=True)
output = process_submission_xml("draft-somebody-test", "00")
self.assertEqual(output["document_date"], date_today())

TestBlobstoreManager().emptyTestBlobstores()
xml_path.write_text(re.sub(r"(<date.+) month=.+day=(.+>)", r"\1 day=\2", xml_contents)) # remove month
store_str("staging", "draft-somebody-test-00.xml", re.sub(r"(<date.+) month=.+day=(.+>)", r"\1 day=\2", xml_contents))
store_str("staging", "draft-somebody-test-00.xml", re.sub(r"(<date.+) month=.+day=(.+>)", r"\1 day=\2", xml_contents), allow_overwrite=True)
output = process_submission_xml("draft-somebody-test", "00")
self.assertEqual(output["document_date"], date_today())

TestBlobstoreManager().emptyTestBlobstores()
xml_path.write_text(re.sub(r"<date(.+) day=.+>", r"<date\1>", xml_contents)) # remove day
store_str("staging", "draft-somebody-test-00.xml", re.sub(r"<date(.+) day=.+>", r"<date\1>", xml_contents))
store_str("staging", "draft-somebody-test-00.xml", re.sub(r"<date(.+) day=.+>", r"<date\1>", xml_contents), allow_overwrite=True)
output = process_submission_xml("draft-somebody-test", "00")
self.assertEqual(output["document_date"], date_today())

Expand All @@ -3080,7 +3080,7 @@ def test_process_submission_xml(self):
)
xml_path.write_text(xml.read())
xml.seek(0)
store_str("staging", "draft-somebody-test-00.xml", xml.read())
store_str("staging", "draft-somebody-test-00.xml", xml.read(), allow_overwrite=True)
with self.assertRaisesMessage(SubmissionError, "disagrees with submission filename"):
process_submission_xml("draft-somebody-test", "00")

Expand All @@ -3095,7 +3095,7 @@ def test_process_submission_xml(self):
)
xml_path.write_text(xml.read())
xml.seek(0)
store_str("staging", "draft-somebody-test-00.xml", xml.read())
store_str("staging", "draft-somebody-test-00.xml", xml.read(), allow_overwrite=True)
with self.assertRaisesMessage(SubmissionError, "disagrees with submission revision"):
process_submission_xml("draft-somebody-test", "00")

Expand All @@ -3110,7 +3110,7 @@ def test_process_submission_xml(self):
)
xml_path.write_text(xml.read())
xml.seek(0)
store_str("staging", "draft-somebody-test-00.xml", xml.read())
store_str("staging", "draft-somebody-test-00.xml", xml.read(), allow_overwrite=True)
with self.assertRaisesMessage(SubmissionError, "Could not extract a valid title"):
process_submission_xml("draft-somebody-test", "00")

Expand Down Expand Up @@ -3153,7 +3153,7 @@ def test_process_submission_text(self):
with txt_path.open('w') as fd:
fd.write(txt.read())
txt.seek(0)
store_str("staging", "draft-somebody-test-00.txt", txt.read())
store_str("staging", "draft-somebody-test-00.txt", txt.read(), allow_overwrite=True)
txt.close()
with self.assertRaisesMessage(SubmissionError, 'disagrees with submission filename'):
process_submission_text("draft-somebody-test", "00")
Expand All @@ -3170,7 +3170,7 @@ def test_process_submission_text(self):
with txt_path.open('w') as fd:
fd.write(txt.read())
txt.seek(0)
store_str("staging", "draft-somebody-test-00.txt", txt.read())
store_str("staging", "draft-somebody-test-00.txt", txt.read(), allow_overwrite=True)
txt.close()
with self.assertRaisesMessage(SubmissionError, 'disagrees with submission revision'):
process_submission_text("draft-somebody-test", "00")
Expand Down
9 changes: 7 additions & 2 deletions ietf/templates/doc/document_statement.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2023, All Rights Reserved #}
{# Copyright The IETF Trust 2023-2025, All Rights Reserved #}
{% load origin %}
{% load static %}
{% load ietf_filters %}
Expand Down Expand Up @@ -49,7 +49,12 @@
<th scope="row">
<a href="{% url 'ietf.doc.views_help.state_help' type='statement' %}">State</a>
</th>
<td class="edit"></td>
<td class="edit">{% if can_manage %}
<a class="btn btn-primary btn-sm"
href="{% url 'ietf.doc.views_statement.change_statement_state' name=doc.name %}">
Edit
</a>
{% endif %}</td>
<td id="statement-state">
{% if doc.get_state %}
<span title="{{ doc.get_state.desc }}" class="badge rounded-pill {% if doc.get_state.name|slugify == 'active' %}text-bg-success{% else %}text-bg-warning{% endif %}">{{ doc.get_state.name }}</span>
Expand Down
22 changes: 22 additions & 0 deletions ietf/templates/doc/statement/change_statement_state.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{# Copyright The IETF Trust 2025, All Rights Reserved #}
{% extends "base.html" %}
{% load origin %}
{% load django_bootstrap5 %}
{% block title %}Change state for {{ statement }}{% endblock %}
{% block content %}
{% origin %}
<h1>
Change state
<br>
<small class="text-body-secondary">{{ statement }}</small>
</h1>
<form class="mt-3" method="post">
{% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="btn btn-primary">Submit</button>
<a class="btn btn-secondary float-end"
href="{{ statement.get_absolute_url }}">
Back
</a>
</form>
{% endblock %}
5 changes: 5 additions & 0 deletions ietf/templates/meeting/edit_meeting_schedule.html
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ <h1>
{{ p.acronym }}
</label>
{% endfor %}
<button type="button"
class="btn btn-outline-primary btn-sm session-parent-toggle-all">
Toggle All
</button>

</div>
<div class="my-3">
{% if session_purposes|length > 1 %}
Expand Down
Loading
0