ci: merge main to release (#7460)

ci: merge main to release
This commit is contained in:
Robert Sparks 2024-05-23 12:22:37 -05:00 committed by GitHub
commit d9df3f2a65
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 186 additions and 13 deletions

View file

@ -247,6 +247,8 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
tags: ghcr.io/ietf-tools/datatracker:${{ env.PKG_VERSION }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Update CHANGELOG
id: changelog

View file

@ -933,3 +933,28 @@ def url_for_path(path):
return f"{settings.IETF_ID_ARCHIVE_URL}{path.name}"
else:
return "#"
@register.filter
def is_in_stream(doc):
"""
Check if the doc is in one of the states in it stream that
indicate that is actually adopted, i.e., part of the stream.
(There are various "candidate" states that necessitate this
filter.)
"""
if not doc.stream:
return False
stream = doc.stream.slug
state = doc.get_state_slug(f"draft-stream-{doc.stream.slug}")
if not state:
return True
if stream == "ietf":
return state not in ["wg-cand", "c-adopt"]
elif stream == "irtf":
return state != "candidat"
elif stream == "iab":
return state not in ["candidat", "diff-org"]
elif stream == "editorial":
return True
return False

View file

@ -7,9 +7,20 @@ from ietf.doc.factories import (
IndividualDraftFactory,
CharterFactory,
NewRevisionDocEventFactory,
StatusChangeFactory,
RgDraftFactory,
EditorialDraftFactory,
WgDraftFactory,
ConflictReviewFactory,
BofreqFactory,
StatementFactory,
)
from ietf.doc.models import DocEvent
from ietf.doc.templatetags.ietf_filters import urlize_ietf_docs, is_valid_url
from ietf.doc.templatetags.ietf_filters import (
urlize_ietf_docs,
is_valid_url,
is_in_stream,
)
from ietf.person.models import Person
from ietf.utils.test_utils import TestCase
@ -19,13 +30,28 @@ import debug # pyflakes: ignore
class IetfFiltersTests(TestCase):
def test_is_in_stream(self):
for draft in [
IndividualDraftFactory(),
CharterFactory(),
StatusChangeFactory(),
ConflictReviewFactory(),
StatementFactory(),
BofreqFactory(),
]:
self.assertFalse(is_in_stream(draft))
for draft in [RgDraftFactory(), WgDraftFactory(), EditorialDraftFactory()]:
self.assertTrue(is_in_stream(draft))
for stream in ["iab", "ietf", "irtf", "ise", "editorial"]:
self.assertTrue(is_in_stream(IndividualDraftFactory(stream_id=stream)))
def test_is_valid_url(self):
cases = [(settings.IDTRACKER_BASE_URL, True), ("not valid", False)]
for url, result in cases:
self.assertEqual(is_valid_url(url), result)
def test_urlize_ietf_docs(self):
rfc = WgRfcFactory(rfc_number=123456,std_level_id="bcp")
rfc = WgRfcFactory(rfc_number=123456, std_level_id="bcp")
rfc.save_with_history(
[
DocEvent.objects.create(
@ -57,7 +83,6 @@ class IetfFiltersTests(TestCase):
cases = [
("no change", "no change"),
# TODO: rework subseries when we add them
# ("bCp123456", '<a href="/doc/bcp123456/">bCp123456</a>'),
# ("Std 00123456", '<a href="/doc/std123456/">Std 00123456</a>'),

View file

@ -0,0 +1,82 @@
# Generated by Django 4.2.13 on 2024-05-22 18:50
from django.db import migrations, models
import ietf.person.models
class Migration(migrations.Migration):
dependencies = [
("person", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="historicalperson",
name="ascii",
field=models.CharField(
help_text="Name as rendered in ASCII (Latin, unaccented) characters.",
max_length=255,
validators=[ietf.person.models.name_character_validator],
verbose_name="Full Name (ASCII)",
),
),
migrations.AlterField(
model_name="historicalperson",
name="ascii_short",
field=models.CharField(
blank=True,
help_text="Example: A. Nonymous. Fill in this with initials and surname only if taking the initials and surname of the ASCII name above produces an incorrect initials-only form. (Blank is OK).",
max_length=32,
null=True,
validators=[ietf.person.models.name_character_validator],
verbose_name="Abbreviated Name (ASCII)",
),
),
migrations.AlterField(
model_name="historicalperson",
name="plain",
field=models.CharField(
blank=True,
default="",
help_text="Use this if you have a Spanish double surname. Don't use this for nicknames, and don't use it unless you've actually observed that the datatracker shows your name incorrectly.",
max_length=64,
validators=[ietf.person.models.name_character_validator],
verbose_name="Plain Name correction (Unicode)",
),
),
migrations.AlterField(
model_name="person",
name="ascii",
field=models.CharField(
help_text="Name as rendered in ASCII (Latin, unaccented) characters.",
max_length=255,
validators=[ietf.person.models.name_character_validator],
verbose_name="Full Name (ASCII)",
),
),
migrations.AlterField(
model_name="person",
name="ascii_short",
field=models.CharField(
blank=True,
help_text="Example: A. Nonymous. Fill in this with initials and surname only if taking the initials and surname of the ASCII name above produces an incorrect initials-only form. (Blank is OK).",
max_length=32,
null=True,
validators=[ietf.person.models.name_character_validator],
verbose_name="Abbreviated Name (ASCII)",
),
),
migrations.AlterField(
model_name="person",
name="plain",
field=models.CharField(
blank=True,
default="",
help_text="Use this if you have a Spanish double surname. Don't use this for nicknames, and don't use it unless you've actually observed that the datatracker shows your name incorrectly.",
max_length=64,
validators=[ietf.person.models.name_character_validator],
verbose_name="Plain Name correction (Unicode)",
),
),
]

View file

@ -37,8 +37,12 @@ from ietf.utils.models import ForeignKey, OneToOneField
def name_character_validator(value):
if '/' in value:
raise ValidationError('Name cannot contain "/" character.')
disallowed = "@:/"
found = set(disallowed).intersection(value)
if len(found) > 0:
raise ValidationError(
f"This name cannot contain the characters {', '.join(disallowed)}"
)
class Person(models.Model):
@ -48,11 +52,11 @@ class Person(models.Model):
# The normal unicode form of the name. This must be
# set to the same value as the ascii-form if equal.
name = models.CharField("Full Name (Unicode)", max_length=255, db_index=True, help_text="Preferred long form of name.", validators=[name_character_validator])
plain = models.CharField("Plain Name correction (Unicode)", max_length=64, default='', blank=True, help_text="Use this if you have a Spanish double surname. Don't use this for nicknames, and don't use it unless you've actually observed that the datatracker shows your name incorrectly.")
plain = models.CharField("Plain Name correction (Unicode)", max_length=64, default='', blank=True, help_text="Use this if you have a Spanish double surname. Don't use this for nicknames, and don't use it unless you've actually observed that the datatracker shows your name incorrectly.", validators=[name_character_validator])
# The normal ascii-form of the name.
ascii = models.CharField("Full Name (ASCII)", max_length=255, help_text="Name as rendered in ASCII (Latin, unaccented) characters.")
ascii = models.CharField("Full Name (ASCII)", max_length=255, help_text="Name as rendered in ASCII (Latin, unaccented) characters.", validators=[name_character_validator])
# The short ascii-form of the name. Also in alias table if non-null
ascii_short = models.CharField("Abbreviated Name (ASCII)", max_length=32, null=True, blank=True, help_text="Example: A. Nonymous. Fill in this with initials and surname only if taking the initials and surname of the ASCII name above produces an incorrect initials-only form. (Blank is OK).")
ascii_short = models.CharField("Abbreviated Name (ASCII)", max_length=32, null=True, blank=True, help_text="Example: A. Nonymous. Fill in this with initials and surname only if taking the initials and surname of the ASCII name above produces an incorrect initials-only form. (Blank is OK).", validators=[name_character_validator])
pronouns_selectable = jsonfield.JSONCharField("Pronouns", max_length=120, blank=True, null=True, default=list )
pronouns_freetext = models.CharField(" ", max_length=30, null=True, blank=True, help_text="Optionally provide your personal pronouns. These will be displayed on your public profile page and alongside your name in Meetecho and, in future, other systems. Select any number of the checkboxes OR provide a custom string up to 30 characters.")
biography = models.TextField(blank=True, help_text="Short biography for use on leadership pages. Use plain text or reStructuredText markup.")

View file

@ -246,9 +246,11 @@ class PersonTests(TestCase):
self.assertNotIn('cdn-cgi/photo',p.cdn_photo_url())
def test_invalid_name_characters_rejected(self):
slash_person = PersonFactory.build(name='I have a /', user=None) # build() does not save the new object
with self.assertRaises(ValidationError):
slash_person.full_clean() # calls validators (save() does *not*)
for disallowed in "/:@":
# build() does not save the new object
person_with_bad_name = PersonFactory.build(name=f"I have a {disallowed}", user=None)
with self.assertRaises(ValidationError, msg=f"Name with a {disallowed} char should be rejected"):
person_with_bad_name.full_clean() # calls validators (save() does *not*)
class PersonUtilsTests(TestCase):

View file

@ -57,7 +57,7 @@ $(document)
var text = $(this)
.text();
// insert some <wbr> at strategic places
var newtext = text.replace(/([@._+])/g, "$1<wbr>");
var newtext = text.replace(/(\S)([@._+])(\S)/g, "$1$2<wbr>$3");
if (newtext === text) {
return;
}

View file

@ -0,0 +1,28 @@
{# Copyright The IETF Trust 2016-2023, All Rights Reserved #}
{% load origin %}
{% load ietf_filters %}
{% origin %}
{% if doc.type_id == "rfc" %}
{% if doc.stream.slug != "ietf" and doc.std_level.slug|default:"unk" not in "bcp,ds,ps,std"|split:"," %}
<div class="alert alert-warning {% if document_html %}small p-2 mt-2{% endif %}" role="alert">
This RFC was published on the {{ doc.stream.desc }} stream.
This RFC is <strong>not endorsed by the IETF</strong> and has <strong>no formal standing</strong> in the
<a href="{% url 'ietf.doc.views_doc.document_main' name='rfc2026' %}">IETF standards process</a>.
</div>
{% endif %}
{% elif doc|is_in_stream %}
{% if doc.stream.slug != "ietf" and doc.std_level.slug|default:"unk" not in "bcp,ds,ps,std"|split:"," %}
<div class="alert alert-warning {% if document_html %}small p-2 mt-2{% endif %}" role="alert">
This document is an Internet-Draft (I-D) that has been submitted to the {{ doc.stream.desc }} stream.
This I-D is <strong>not endorsed by the IETF</strong> and has <strong>no formal standing</strong> in the
<a href="{% url 'ietf.doc.views_doc.document_main' name='rfc2026' %}">IETF standards process</a>.
</div>
{% endif %}
{% else %}
<div class="alert alert-warning {% if document_html %}small p-2 mt-2{% endif %}" role="alert">
This document is an Internet-Draft (I-D).
Anyone may submit an I-D to the IETF.
This I-D is <strong>not endorsed by the IETF</strong> and has <strong>no formal standing</strong> in the
<a href="{% url 'ietf.doc.views_doc.document_main' name='rfc2026' %}">IETF standards process</a>.
</div>
{% endif %}

View file

@ -27,6 +27,7 @@
{% origin %}
{{ top|safe }}
{% include "doc/revisions_list.html" with document_html=document_html %}
{% include "doc/disclaimer.html" with document_html=document_html %}
<div id="doc-timeline"></div>
{% if doc.rev != latest_rev %}
<div class="alert alert-warning my-3">The information below is for an old version of the document.</div>

View file

@ -63,7 +63,7 @@
{% if doc.became_rfc %}
<div{% if document_html %} class="alert alert-warning small"{% endif %}>This is an older version of an Internet-Draft that was ultimately published as <a href="{% if document_html %}{% url 'ietf.doc.views_doc.document_html' name=doc.became_rfc.name %}{% else %}{% url 'ietf.doc.views_doc.document_main' name=doc.became_rfc.name %}{% endif %}">{{doc.became_rfc.name|prettystdname}}</a>.</div>
{% elif snapshot and doc.rev != latest_rev %}
<div{% if document_html %} class="alert alert-warning small"{% endif %}>This is an older version of an Internet-Draft whose latest revision state is "{{ doc.doc.get_state }}".</div>
<div{% if document_html %} class="alert alert-warning small p-2 mt-2"{% endif %}>This is an older version of an Internet-Draft whose latest revision state is "{{ doc.doc.get_state }}".</div>
{% else %}
<span class="{% if doc.get_state_slug == 'active' %}text-success{% elif doc.get_state_slug == 'expired' or doc.get_state_slug == 'repl' %}text-danger{% endif %}">{% if snapshot and doc.rev == latest_rev %}{{ doc.doc.get_state }}{% else %}{{ doc.get_state }}{% endif %} Internet-Draft</span>
{% if submission %}({{ submission|safe }}){% endif %}
@ -75,6 +75,9 @@
Expired &amp; archived
</div>
{% endif %}
{% if document_html %}
{% include "doc/disclaimer.html" with document_html=document_html %}
{% endif %}
</td>
</tr>
{% if document_html %}

View file

@ -22,6 +22,7 @@
{% block content %}
{% origin %}
{{ top|safe }}
{% include "doc/disclaimer.html" with document_html=document_html %}
<div id="doc-timeline"></div>
<table class="table table-sm table-borderless">
{% include "doc/document_info.html" %}