ci: merge main to release

This commit is contained in:
Robert Sparks 2023-10-12 06:36:25 -05:00 committed by GitHub
commit dfe06e8074
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 534 additions and 401 deletions

View file

@ -8,7 +8,7 @@ const octokit = new Octokit({
const oldestDate = DateTime.utc().minus({ days: 7 })
for (const pkgName of ['datatracker-db', 'datatracker-db-pg']) {
for (const pkgName of ['datatracker-db']) {
let hasMore = true
let currentPage = 1

View file

@ -828,7 +828,7 @@ Man Expires September 22, 2015 [Page 3]
stream_id=draft.stream_id, group_id=draft.group_id, abstract=draft.abstract,stream=draft.stream, rev=draft.rev,
pages=draft.pages, intended_std_level_id=draft.intended_std_level_id,
shepherd_id=draft.shepherd_id, ad_id=draft.ad_id, expires=draft.expires,
notify=draft.notify, note=draft.note)
notify=draft.notify)
rel = RelatedDocument.objects.create(source=replacement,
target=draft.docalias.get(name__startswith="draft"),
relationship_id="replaces")

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright The IETF Trust 2011-2020, All Rights Reserved
# Copyright The IETF Trust 2011-2023, All Rights Reserved
import datetime
@ -817,6 +817,19 @@ class EditCharterTests(TestCase):
self.assertEqual(group.groupmilestone_set.filter(state="active", desc=m1.desc).count(), 1)
self.assertEqual(group.groupmilestone_set.filter(state="active", desc=m4.desc).count(), 1)
def test_approve_irtf(self):
charter = CharterFactory(group__type_id='rg')
url = urlreverse('ietf.doc.views_charter.approve', kwargs=dict(name=charter.name))
login_testing_unauthorized(self, "secretary", url)
empty_outbox()
r = self.client.post(url, dict())
self.assertEqual(r.status_code, 302)
self.assertEqual(len(outbox), 2)
self.assertTrue("IRTF" in outbox[1]['From'])
self.assertTrue("irtf-announce" in outbox[1]['To'])
self.assertTrue(charter.group.acronym in outbox[1]['Cc'])
self.assertTrue("RG Action" in outbox[1]['Subject'])
def test_charter_with_milestones(self):
charter = CharterFactory()

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2012-2020, All Rights Reserved
# Copyright The IETF Trust 2012-2023, All Rights Reserved
# -*- coding: utf-8 -*-
@ -13,8 +13,8 @@ from django.urls import reverse as urlreverse
import debug # pyflakes:ignore
from ietf.doc.factories import IndividualDraftFactory, ConflictReviewFactory
from ietf.doc.models import Document, DocEvent, NewRevisionDocEvent, BallotPositionDocEvent, TelechatDocEvent, State
from ietf.doc.factories import IndividualDraftFactory, ConflictReviewFactory, RgDraftFactory
from ietf.doc.models import Document, DocEvent, NewRevisionDocEvent, BallotPositionDocEvent, TelechatDocEvent, State, DocTagName
from ietf.doc.utils import create_ballot_if_not_open
from ietf.doc.views_conflict_review import default_approval_text
from ietf.group.models import Person
@ -168,6 +168,21 @@ class ConflictReviewTests(TestCase):
self.assertTrue(review_doc.active_ballot())
self.assertEqual(review_doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position").pos_id,'yes')
# try to change to an AD-forbidden state
appr_noprob_sent_pk = str(State.objects.get(used=True, slug='appr-noprob-sent',type__slug='conflrev').pk)
r = self.client.post(url,dict(review_state=appr_noprob_sent_pk,comment='xyzzy'))
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(q('form .invalid-feedback'))
# try again as secretariat
self.client.logout()
login_testing_unauthorized(self, 'secretary', url)
r = self.client.post(url,dict(review_state=appr_noprob_sent_pk,comment='xyzzy'))
self.assertEqual(r.status_code, 302)
review_doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
self.assertEqual(review_doc.get_state('conflrev').slug, 'appr-noprob-sent')
def test_edit_notices(self):
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
@ -450,3 +465,65 @@ class ConflictReviewSubmitTests(TestCase):
def setUp(self):
super().setUp()
ConflictReviewFactory(name='conflict-review-imaginary-irtf-submission',review_of=IndividualDraftFactory(name='draft-imaginary-irtf-submission',stream_id='irtf'),notify='notifyme@example.net')
class ConflictReviewIrtfStateTests(TestCase):
def start_review(self, role, kwargs=None):
doc = RgDraftFactory()
url = urlreverse('ietf.doc.views_conflict_review.start_review', kwargs=dict(name=doc.name))
login_testing_unauthorized(self, role, url)
r = self.client.post(url, kwargs)
self.assertEqual(r.status_code, 302)
self.assertEqual(doc.get_state('draft-stream-irtf').slug, 'iesg-rev')
def test_start_review_as_secretary(self):
ad_strpk = str(Person.objects.get(name='Areað Irector').pk)
state_strpk = str(State.objects.get(used=True, slug='needshep', type__slug='conflrev').pk)
self.start_review('secretary', kwargs=dict(ad=ad_strpk, create_in_state=state_strpk))
def test_start_review_as_stream_owner(self):
self.start_review('irtf-chair')
def close_review(self, close_type, role):
doc = RgDraftFactory()
review = ConflictReviewFactory(review_of=doc)
url = urlreverse('ietf.doc.views_conflict_review.change_state', kwargs=dict(name=review.name))
login_testing_unauthorized(self, role, url)
strpk = str(State.objects.get(used=True, slug=close_type, type__slug='conflrev').pk)
r = self.client.post(url, dict(review_state=strpk))
self.assertEqual(r.status_code, 302)
self.assertEqual(doc.get_state('draft-stream-irtf').slug, 'chair-w')
self.assertIn(DocTagName.objects.get(pk='iesg-com'), doc.tags.all())
def test_close_review_reqnopub_as_secretary(self):
self.close_review('appr-reqnopub-sent', 'secretary')
def test_close_review_noprob_as_secretary(self):
self.close_review('appr-noprob-sent', 'secretary')
def test_close_review_withdraw_as_secretary(self):
self.close_review('withdraw', 'secretary')
def test_close_review_dead_as_secretary(self):
self.close_review('dead', 'secretary')
def test_close_review_withdraw_as_ad(self):
self.close_review('withdraw', 'ad')
def test_close_review_dead_as_ad(self):
self.close_review('dead', 'ad')
def test_approve_review(self):
doc = RgDraftFactory()
review = ConflictReviewFactory(review_of=doc)
review.set_state(State.objects.get(used=True, slug='appr-noprob-pend', type='conflrev'))
url = urlreverse('ietf.doc.views_conflict_review.approve_conflict_review', kwargs=dict(name=review.name))
login_testing_unauthorized(self, 'secretary', url)
r = self.client.post(url, dict(announcement_text=default_approval_text(review)))
self.assertEqual(r.status_code, 302)
self.assertEqual(doc.get_state('draft-stream-irtf').slug, 'chair-w')
self.assertIn(DocTagName.objects.get(pk='iesg-com'), doc.tags.all())

View file

@ -98,7 +98,7 @@ class ChangeStateTests(TestCase):
draft.action_holders.add(ad)
url = urlreverse('ietf.doc.views_draft.change_state', kwargs=dict(name=draft.name))
login_testing_unauthorized(self, "secretary", url)
login_testing_unauthorized(self, "ad", url)
first_state = draft.get_state("draft-iesg")
next_states = first_state.next_states.all()
@ -154,6 +154,20 @@ class ChangeStateTests(TestCase):
q = PyQuery(r.content)
self.assertEqual(len(q('form [type=submit]:contains("%s")' % first_state.name)), 1)
# try to change to an AD-forbidden state
r = self.client.post(url, dict(state=State.objects.get(used=True, type='draft-iesg', slug='ann').pk, comment='Test comment'))
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(q('form .invalid-feedback'))
# try again as secretariat
self.client.logout()
login_testing_unauthorized(self, 'secretary', url)
r = self.client.post(url, dict(state=State.objects.get(used=True, type='draft-iesg', slug='ann').pk, comment='Test comment'))
self.assertEqual(r.status_code, 302)
draft = Document.objects.get(name=draft.name)
self.assertEqual(draft.get_state_slug('draft-iesg'), 'ann')
def test_pull_from_rfc_queue(self):
ad = Person.objects.get(user__username="ad")
draft = WgDraftFactory(
@ -362,16 +376,14 @@ class EditInfoTests(TestCase):
stream=draft.stream_id,
ad=str(new_ad.pk),
notify="test@example.com",
note="New note",
telechat_date="",
))
self.assertEqual(r.status_code, 302)
draft = Document.objects.get(name=draft.name)
self.assertEqual(draft.ad, new_ad)
self.assertEqual(draft.note, "New note")
self.assertTrue(not draft.latest_event(TelechatDocEvent, type="scheduled_for_telechat"))
self.assertEqual(draft.docevent_set.count(), events_before + 3)
self.assertEqual(draft.docevent_set.count(), events_before + 2)
self.assertEqual(len(outbox), mailbox_before + 1)
self.assertTrue(draft.name in outbox[-1]['Subject'])
@ -386,7 +398,6 @@ class EditInfoTests(TestCase):
stream=draft.stream_id,
ad=str(draft.ad_id),
notify=draft.notify,
note="",
)
# get
@ -489,7 +500,6 @@ class EditInfoTests(TestCase):
ad=ad.pk,
create_in_state=State.objects.get(used=True, type="draft-iesg", slug="watching").pk,
notify="test@example.com",
note="This is a note",
telechat_date="",
))
self.assertEqual(r.status_code, 302)
@ -497,12 +507,11 @@ class EditInfoTests(TestCase):
draft = Document.objects.get(name=draft.name)
self.assertEqual(draft.get_state_slug("draft-iesg"), "watching")
self.assertEqual(draft.ad, ad)
self.assertEqual(draft.note, "This is a note")
self.assertTrue(not draft.latest_event(TelechatDocEvent, type="scheduled_for_telechat"))
self.assertEqual(draft.docevent_set.count(), events_before + 5)
self.assertEqual(draft.docevent_set.count(), events_before + 4)
self.assertCountEqual(draft.action_holders.all(), [draft.ad])
events = list(draft.docevent_set.order_by('time', 'id'))
self.assertEqual(events[-5].type, "started_iesg_process")
self.assertEqual(events[-4].type, "started_iesg_process")
self.assertEqual(len(outbox), mailbox_before+1)
self.assertTrue('IESG processing' in outbox[-1]['Subject'])
self.assertTrue('draft-ietf-mars-test2@' in outbox[-1]['To'])
@ -518,7 +527,6 @@ class EditInfoTests(TestCase):
ad=ad.pk,
create_in_state=State.objects.get(used=True, type="draft-iesg", slug="pub-req").pk,
notify="test@example.com",
note="This is a note",
telechat_date="",
))
self.assertEqual(r.status_code, 302)
@ -1043,23 +1051,6 @@ class IndividualInfoFormsTests(TestCase):
doc = Document.objects.get(name=self.docname)
self.assertEqual(doc.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date,None)
def test_doc_change_iesg_note(self):
url = urlreverse('ietf.doc.views_draft.edit_iesg_note', kwargs=dict(name=self.docname))
login_testing_unauthorized(self, "secretary", url)
# get
r = self.client.get(url)
self.assertEqual(r.status_code,200)
q = PyQuery(r.content)
self.assertEqual(len(q('[type=submit]:contains("Save")')),1)
# post
r = self.client.post(url,dict(note='ZpyQFGmA\r\nZpyQFGmA'))
self.assertEqual(r.status_code,302)
doc = Document.objects.get(name=self.docname)
self.assertEqual(doc.note,'ZpyQFGmA\nZpyQFGmA')
self.assertTrue('ZpyQFGmA' in doc.latest_event(DocEvent,type='added_comment').desc)
def test_doc_change_ad(self):
url = urlreverse('ietf.doc.views_draft.edit_ad', kwargs=dict(name=self.docname))
login_testing_unauthorized(self, "secretary", url)

View file

@ -255,6 +255,7 @@ This test section has some text.
statement.latest_event(NewRevisionDocEvent).rev, "00"
)
self.assertIsNotNone(statement.latest_event(type="published_statement"))
self.assertIsNotNone(statement.history_set.last().latest_event(type="published_statement"))
if postdict["statement_submission"] == "enter":
self.assertEqual(statement.text_or_error(), "some stuff")
else:

View file

@ -147,6 +147,21 @@ class StatusChangeTests(TestCase):
self.assertTrue(doc.active_ballot())
self.assertEqual(doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position").pos_id,'yes')
# try to change to an AD-forbidden state
appr_sent_pk = str(State.objects.get(used=True, slug='appr-sent',type__slug='statchg').pk)
r = self.client.post(url, dict(new_state=appr_sent_pk, comment='xyzzy'))
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(q('form .invalid-feedback'))
# try again as secretariat
self.client.logout()
login_testing_unauthorized(self, 'secretary', url)
r = self.client.post(url, dict(new_state=appr_sent_pk, comment='xyzzy'))
self.assertEqual(r.status_code, 302)
doc = Document.objects.get(name='status-change-imaginary-mid-review')
self.assertEqual(doc.get_state('statchg').slug, 'appr-sent')
def test_edit_notices(self):
doc = Document.objects.get(name='status-change-imaginary-mid-review')
url = urlreverse('ietf.doc.views_doc.edit_notify;status-change',kwargs=dict(name=doc.name))

View file

@ -134,7 +134,6 @@ urlpatterns = [
url(r'^%(name)s/edit/suggested-replaces/$' % settings.URL_REGEXPS, views_draft.review_possibly_replaces),
url(r'^%(name)s/edit/status/$' % settings.URL_REGEXPS, views_draft.change_intention),
url(r'^%(name)s/edit/telechat/$' % settings.URL_REGEXPS, views_doc.telechat_date),
url(r'^%(name)s/edit/iesgnote/$' % settings.URL_REGEXPS, views_draft.edit_iesg_note),
url(r'^%(name)s/edit/ad/$' % settings.URL_REGEXPS, views_draft.edit_ad),
url(r'^%(name)s/edit/authors/$' % settings.URL_REGEXPS, views_doc.edit_authors),
url(r'^%(name)s/edit/consensus/$' % settings.URL_REGEXPS, views_draft.edit_consensus),

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2012-2020, All Rights Reserved
# Copyright The IETF Trust 2012-2023, All Rights Reserved
# -*- coding: utf-8 -*-
@ -25,6 +25,7 @@ from ietf.doc.forms import AdForm
from ietf.group.models import Role, Group
from ietf.iesg.models import TelechatDate
from ietf.ietfauth.utils import has_role, role_required, is_authorized_in_doc_stream
from ietf.name.models import DocTagName
from ietf.person.models import Person
from ietf.utils import log
from ietf.utils.mail import send_mail_preformatted
@ -35,6 +36,12 @@ class ChangeStateForm(forms.Form):
review_state = forms.ModelChoiceField(State.objects.filter(used=True, type="conflrev"), label="Conflict review state", empty_label=None, required=True)
comment = forms.CharField(widget=forms.Textarea, help_text="Optional comment for the review history.", required=False, strip=False)
def __init__(self, *args, **kwargs):
user = kwargs.pop("user")
super(ChangeStateForm, self).__init__(*args, **kwargs)
if not has_role(user, "Secretariat"):
self.fields["review_state"].queryset = self.fields["review_state"].queryset.exclude(slug__in=("appr-reqnopub-sent","appr-noprob-sent"))
@role_required("Area Director", "Secretariat")
def change_state(request, name, option=None):
"""Change state of an IESG review for IETF conflicts in other stream's documents, notifying parties as necessary
@ -44,7 +51,7 @@ def change_state(request, name, option=None):
login = request.user.person
if request.method == 'POST':
form = ChangeStateForm(request.POST)
form = ChangeStateForm(request.POST, user=request.user)
if form.is_valid():
clean = form.cleaned_data
new_state = clean['review_state']
@ -90,12 +97,16 @@ def change_state(request, name, option=None):
review,
ok_to_publish)
if new_state.slug in ["appr-reqnopub-sent", "appr-noprob-sent", "withdraw", "dead"]:
doc = review.related_that_doc("conflrev")[0].document
if doc.stream_id == "irtf":
close_review_irtf_state(doc, login)
return redirect('ietf.doc.views_doc.document_main', name=review.name)
else:
s = review.get_state()
init = dict(review_state=s.pk if s else None)
form = ChangeStateForm(initial=init)
form = ChangeStateForm(initial=init, user=request.user)
return render(request, 'doc/change_state.html',
dict(form=form,
@ -355,6 +366,10 @@ def approve_conflict_review(request, name):
c.desc = "The following approval message was sent\n"+form.cleaned_data['announcement_text']
c.save()
doc = review.related_that_doc("conflrev")[0].document
if doc.stream_id == "irtf":
close_review_irtf_state(doc, login)
return HttpResponseRedirect(review.get_absolute_url())
else:
@ -488,6 +503,9 @@ def start_review_as_secretariat(request, name):
send_conflict_review_started_email(request, conflict_review)
if doc_to_review.stream_id == 'irtf':
start_review_irtf_state(doc_to_review, login)
return HttpResponseRedirect(conflict_review.get_absolute_url())
else:
notify_addresses = build_notify_addresses(doc_to_review)
@ -522,6 +540,9 @@ def start_review_as_stream_owner(request, name):
send_conflict_review_started_email(request, conflict_review)
if doc_to_review.stream_id == 'irtf':
start_review_irtf_state(doc_to_review, login)
return HttpResponseRedirect(conflict_review.get_absolute_url())
else:
notify_addresses = build_notify_addresses(doc_to_review)
@ -536,3 +557,26 @@ def start_review_as_stream_owner(request, name):
'doc_to_review': doc_to_review,
},
)
def start_review_irtf_state(doc, by):
prev_state = doc.get_state('draft-stream-irtf')
new_state = State.objects.get(type_id='draft-stream-irtf', slug='iesg-rev')
if new_state != prev_state:
doc.set_state(new_state)
events = []
events.append(add_state_change_event(doc, by, prev_state, new_state))
doc.save_with_history(events)
def close_review_irtf_state(doc, by):
prev_state = doc.get_state("draft-stream-irtf")
new_state = State.objects.get(type_id="draft-stream-irtf", slug="chair-w")
prev_tags = set(doc.tags.all())
new_tags = set(DocTagName.objects.filter(pk="iesg-com"))
if new_state != prev_state:
doc.set_state(new_state)
doc.tags.clear()
doc.tags.set(new_tags)
events = [add_state_change_event(doc, by, prev_state, new_state, prev_tags, new_tags)]
doc.save_with_history(events)

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2010-2020, All Rights Reserved
# Copyright The IETF Trust 2010-2023, All Rights Reserved
# -*- coding: utf-8 -*-
@ -61,6 +61,12 @@ class ChangeStateForm(forms.Form):
substate = forms.ModelChoiceField(DocTagName.objects.filter(slug__in=IESG_SUBSTATE_TAGS), required=False)
comment = forms.CharField(widget=forms.Textarea, required=False, strip=False)
def __init__(self, *args, **kwargs):
user = kwargs.pop("user")
super(ChangeStateForm, self).__init__(*args, **kwargs)
if not has_role(user, "Secretariat"):
self.fields["state"].queryset = self.fields["state"].queryset.exclude(slug="ann")
def clean(self):
retclean = self.cleaned_data
state = self.cleaned_data.get('state', '(None)')
@ -94,7 +100,7 @@ def change_state(request, name):
login = request.user.person
if request.method == 'POST':
form = ChangeStateForm(request.POST)
form = ChangeStateForm(request.POST, user=request.user)
form.docname=name
if form.is_valid():
@ -175,7 +181,8 @@ def change_state(request, name):
state = doc.get_state("draft-iesg")
t = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS)
form = ChangeStateForm(initial=dict(state=state.pk if state else None,
substate=t[0].pk if t else None))
substate=t[0].pk if t else None),
user=request.user)
form.docname=name
state = doc.get_state("draft-iesg")
@ -491,7 +498,6 @@ class EditInfoForm(forms.Form):
help_text="Separate email addresses with commas.",
required=False,
)
note = forms.CharField(widget=forms.Textarea, label="IESG note", required=False, strip=False)
telechat_date = forms.TypedChoiceField(coerce=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d').date(), empty_value=None, required=False, widget=forms.Select(attrs={'onchange':'make_bold()'}))
returning_item = forms.BooleanField(required=False)
@ -515,9 +521,6 @@ class EditInfoForm(forms.Form):
# returning item is rendered non-standard
self.standard_fields = [x for x in self.visible_fields() if x.name not in ('returning_item',)]
def clean_note(self):
return self.cleaned_data['note'].replace('\r', '').strip()
def to_iesg(request,name):
""" Submit an IETF stream document to the IESG for publication """
doc = get_object_or_404(Document, docalias__name=name, stream='ietf')
@ -715,18 +718,6 @@ def edit_info(request, name):
diff('ad', "Responsible AD")
diff('notify', "State Change Notice email list")
if r['note'] != doc.note:
if not r['note']:
if doc.note:
changes.append("Note field has been cleared")
else:
if doc.note:
changes.append("Note changed to '%s'" % r['note'])
else:
changes.append("Note added '%s'" % r['note'])
doc.note = r['note']
if doc.group.type_id in ("individ", "area"):
if not r["area"]:
r["area"] = Group.objects.get(type="individ")
@ -769,7 +760,6 @@ def edit_info(request, name):
area=doc.group_id,
ad=doc.ad_id,
notify=doc.notify,
note=doc.note,
telechat_date=initial_telechat_date,
returning_item=initial_returning_item,
)
@ -862,52 +852,6 @@ def restore_draft_file(request, draft):
log.log(" Exception %s when attempting to move %s" % (ex, file))
class IESGNoteForm(forms.Form):
note = forms.CharField(widget=forms.Textarea, label="IESG note", required=False, strip=False)
def clean_note(self):
# not munging the database content to use html line breaks --
# that has caused a lot of pain in the past.
return self.cleaned_data['note'].replace('\r', '').strip()
@role_required("Area Director", "Secretariat")
def edit_iesg_note(request, name):
doc = get_object_or_404(Document, type="draft", name=name)
login = request.user.person
initial = dict(note=doc.note)
if request.method == "POST":
form = IESGNoteForm(request.POST, initial=initial)
if form.is_valid():
new_note = form.cleaned_data['note']
if new_note != doc.note:
if not new_note:
if doc.note:
log_message = "Note field has been cleared"
else:
if doc.note:
log_message = "Note changed to '%s'" % new_note
else:
log_message = "Note added '%s'" % new_note
c = DocEvent(type="added_comment", doc=doc, rev=doc.rev, by=login)
c.desc = log_message
c.save()
doc.note = new_note
doc.save_with_history([c])
return redirect('ietf.doc.views_doc.document_main', name=doc.name)
else:
form = IESGNoteForm(initial=initial)
return render(request, 'doc/draft/edit_iesg_note.html',
dict(doc=doc,
form=form,
))
class ShepherdWriteupUploadForm(forms.Form):
content = forms.CharField(widget=forms.Textarea, label="Shepherd writeup", help_text="Edit the shepherd writeup.", required=False, strip=False)
txt = forms.FileField(label=".txt format", help_text="Or upload a .txt file.", required=False)

View file

@ -239,6 +239,7 @@ def new_statement(request):
rev=statement.rev,
by=request.user.person,
desc="Statement published",
time=statement.time,
)
statement.save_with_history([e1, e2])
alias = DocAlias.objects.create(name=name)

View file

@ -42,6 +42,12 @@ class ChangeStateForm(forms.Form):
new_state = forms.ModelChoiceField(State.objects.filter(type="statchg", used=True), label="Status Change Evaluation State", empty_label=None, required=True)
comment = forms.CharField(widget=forms.Textarea, help_text="Optional comment for the review history.", required=False, strip=False)
def __init__(self, *args, **kwargs):
user = kwargs.pop("user")
super(ChangeStateForm, self).__init__(*args, **kwargs)
if not has_role(user, "Secretariat"):
self.fields["new_state"].queryset = self.fields["new_state"].queryset.exclude(slug="appr-sent")
@role_required("Area Director", "Secretariat")
def change_state(request, name, option=None):
@ -52,7 +58,7 @@ def change_state(request, name, option=None):
login = request.user.person
if request.method == 'POST':
form = ChangeStateForm(request.POST)
form = ChangeStateForm(request.POST, user=request.user)
if form.is_valid():
clean = form.cleaned_data
new_state = clean['new_state']
@ -116,7 +122,7 @@ def change_state(request, name, option=None):
type='statchg',
label='Status Change Evaluation State',
)
form = ChangeStateForm(initial=init)
form = ChangeStateForm(initial=init, user=request.user)
return render(request, 'doc/change_state.html',
dict(form=form,

View file

@ -27,7 +27,7 @@ class GroupFactory(factory.django.DjangoModelFactory):
django_get_or_create = ('acronym',)
skip_postgeneration_save = True
name = factory.Faker('sentence',nb_words=6)
name = factory.Faker('text', max_nb_chars=80)
acronym = factory.Sequence(lambda n: 'acronym%d' %n)
state_id = 'active'
type_id = 'wg'

View file

@ -142,8 +142,6 @@ def agenda_json(request, date=None):
'ad':doc.ad.name if doc.ad else None,
}
if doc.note:
docinfo['note'] = doc.note
defer = doc.active_defer_event()
if defer:
docinfo['defer-by'] = defer.by.name

View file

@ -109,7 +109,9 @@ class DraftForm(forms.ModelForm):
cleaned_data = super().clean()
revisions = cleaned_data.get("revisions")
document = cleaned_data.get("document")
if not document.name.startswith("rfc"):
if not document:
self.add_error("document", "Identifying the Internet-Draft or RFC for this disclosure is required.")
elif not document.name.startswith("rfc"):
if revisions.strip() == "":
self.add_error("revisions", "Revisions of this Internet-Draft for which this disclosure is relevant must be specified.")
return cleaned_data

View file

@ -0,0 +1,26 @@
# Copyright The IETF Trust 2023, All Rights Reserved
from django.db import migrations
def forward(apps, schema_editor):
MailTrigger = apps.get_model("mailtrigger", "MailTrigger")
Recipient = apps.get_model("mailtrigger", "Recipient")
mt = MailTrigger.objects.get(pk="ballot_approved_charter")
mt.to.remove(mt.to.first())
mt.to.add(Recipient.objects.get(slug="group_stream_announce"))
def reverse(apps, schema_editor):
MailTrigger = apps.get_model("mailtrigger", "MailTrigger")
Recipient = apps.get_model("mailtrigger", "Recipient")
mt = MailTrigger.objects.get(pk="ballot_approved_charter")
mt.to.remove(mt.to.first())
mt.to.add(Recipient.objects.get(slug="ietf_announce"))
class Migration(migrations.Migration):
dependencies = [
("mailtrigger", "0002_slidesubmitter"),
]
operations = [
migrations.RunPython(forward, reverse)
]

View file

@ -3770,7 +3770,7 @@
],
"desc": "Recipients when a charter is approved",
"to": [
"ietf_announce"
"group_stream_announce"
]
},
"model": "mailtrigger.mailtrigger",

View file

@ -2419,6 +2419,7 @@ class rfc8713EligibilityTests(TestCase):
self.eligible_people = list()
self.ineligible_people = list()
# Section 4.14 qualification criteria
for combo_len in range(0,6):
for combo in combinations(meetings,combo_len):
p = PersonFactory()
@ -2429,6 +2430,18 @@ class rfc8713EligibilityTests(TestCase):
else:
self.eligible_people.append(p)
# Section 4.15 disqualification criteria
def ineligible_person_with_role(**kwargs):
p = RoleFactory(**kwargs).person
for m in meetings:
MeetingRegistrationFactory(person=p, meeting=m, attended=True)
self.ineligible_people.append(p)
for group in ['isocbot', 'ietf-trust', 'llc-board', 'iab']:
for role in ['member', 'chair']:
ineligible_person_with_role(group__acronym=group, name_id=role)
ineligible_person_with_role(group__type_id='area', group__state_id='active',name_id='ad')
ineligible_person_with_role(group=self.nomcom.group, name_id='chair')
# No-one is eligible for the other_nomcom
self.other_nomcom = NomComFactory(group__acronym='nomcom2018',first_call_for_volunteers=datetime.date(2018,5,1))

View file

@ -22,8 +22,8 @@ urlpatterns = [
url(r'^(?P<year>\d{4})/private/view-feedback/nominee/(?P<nominee_id>\d+)$', views.view_feedback_nominee),
url(r'^(?P<year>\d{4})/private/view-feedback/topic/(?P<topic_id>\d+)$', views.view_feedback_topic),
url(r'^(?P<year>\d{4})/private/edit/nominee/(?P<nominee_id>\d+)$', views.edit_nominee),
url(r'^(?P<year>\d{4})/private/merge-nominee/?$', views.private_merge_nominee),
url(r'^(?P<year>\d{4})/private/merge-person/?$', views.private_merge_person),
url(r'^(?P<year>\d{4})/private/merge-nominee/$', views.private_merge_nominee),
url(r'^(?P<year>\d{4})/private/merge-person/$', views.private_merge_person),
url(r'^(?P<year>\d{4})/private/send-reminder-mail/(?P<type>\w+)/$', views.send_reminder_mail),
url(r'^(?P<year>\d{4})/private/extract-email-lists/$', views.extract_email_lists),
url(r'^(?P<year>\d{4})/private/edit-members/$', views.edit_members),

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2012-2022, All Rights Reserved
# Copyright The IETF Trust 2012-2023, All Rights Reserved
# -*- coding: utf-8 -*-
@ -66,8 +66,11 @@ DEFAULT_NOMCOM_TEMPLATES = [HOME_TEMPLATE,
]
# See RFC8713 section 4.15
# This potentially over-disqualifies past nomcom chairs if some
# nomcom 2+ nomcoms ago is still in the active state
DISQUALIFYING_ROLE_QUERY_EXPRESSION = ( Q(group__acronym__in=['isocbot', 'ietf-trust', 'llc-board', 'iab'], name_id__in=['member', 'chair'])
| Q(group__type_id='area', group__state='active',name_id='ad')
| Q(group__type_id='nomcom', group__state='active', name_id='chair')
)

View file

@ -77,7 +77,6 @@ def year_index(request, year):
return render(request, 'nomcom/year_index.html',
{'nomcom': nomcom,
'year': year,
'selected': 'index',
'template': template})
def announcements(request):
@ -182,8 +181,7 @@ def private_key(request, year):
{'nomcom': nomcom,
'year': year,
'back_url': back_url,
'form': form,
'selected': 'private_key'})
'form': form})
@role_required("Nomcom")
@ -283,7 +281,6 @@ def private_index(request, year):
'positions': positions,
'selected_state': selected_state,
'selected_position': selected_position and int(selected_position) or None,
'selected': 'index',
'is_chair': is_chair,
'mailto': mailto,
})
@ -308,13 +305,11 @@ def send_reminder_mail(request, year, type):
interesting_state = 'pending'
mail_path = nomcom_template_path + NOMINEE_ACCEPT_REMINDER_TEMPLATE
reminder_description = 'accept (or decline) a nomination'
selected_tab = 'send_accept_reminder'
state_description = NomineePositionStateName.objects.get(slug=interesting_state).name
elif type=='questionnaire':
interesting_state = 'accepted'
mail_path = nomcom_template_path + NOMINEE_QUESTIONNAIRE_REMINDER_TEMPLATE
reminder_description = 'complete the questionnaire for a nominated position'
selected_tab = 'send_questionnaire_reminder'
state_description = NomineePositionStateName.objects.get(slug=interesting_state).name+' but no questionnaire has been received'
else:
raise Http404
@ -349,7 +344,6 @@ def send_reminder_mail(request, year, type):
'year': year,
'nominees': annotated_nominees,
'mail_template': mail_template,
'selected': selected_tab,
'reminder_description': reminder_description,
'state_description': state_description,
'is_chair_task' : True,
@ -376,7 +370,6 @@ def private_merge_person(request, year):
{'nomcom': nomcom,
'year': year,
'form': form,
'selected': 'merge_person',
'is_chair_task' : True,
})
@ -401,7 +394,6 @@ def private_merge_nominee(request, year):
{'nomcom': nomcom,
'year': year,
'form': form,
'selected': 'merge_nominee',
'is_chair_task' : True,
})
@ -411,8 +403,7 @@ def requirements(request, year):
return render(request, 'nomcom/requirements.html',
{'nomcom': nomcom,
'positions': positions,
'year': year,
'selected': 'requirements'})
'year': year})
def questionnaires(request, year):
@ -421,8 +412,7 @@ def questionnaires(request, year):
return render(request, 'nomcom/questionnaires.html',
{'nomcom': nomcom,
'positions': positions,
'year': year,
'selected': 'questionnaires'})
'year': year})
@login_required
@ -456,15 +446,13 @@ def nominate(request, year, public, newperson):
messages.warning(request, "This Nomcom is not yet accepting nominations")
return render(request, template,
{'nomcom': nomcom,
'year': year,
'selected': 'nominate'})
'year': year})
if nomcom.group.state_id == 'conclude':
messages.warning(request, "Nominations to this Nomcom are closed.")
return render(request, template,
{'nomcom': nomcom,
'year': year,
'selected': 'nominate'})
'year': year})
if request.method == 'POST':
if newperson:
@ -488,8 +476,7 @@ def nominate(request, year, public, newperson):
{'form': form,
'nomcom': nomcom,
'year': year,
'positions': nomcom.position_set.filter(is_open=True),
'selected': 'nominate'})
'positions': nomcom.position_set.filter(is_open=True)})
@login_required
def public_feedback(request, year):
@ -553,7 +540,6 @@ def feedback(request, year, public):
return render(request, 'nomcom/feedback.html', {
'nomcom': nomcom,
'year': year,
'selected': 'feedback',
'counts' : counts,
'base_template': base_template
})
@ -564,7 +550,6 @@ def feedback(request, year, public):
'form': None,
'nomcom': nomcom,
'year': year,
'selected': 'feedback',
'positions': positions,
'topics': topics,
'counts' : counts,
@ -578,7 +563,6 @@ def feedback(request, year, public):
'form': None,
'nomcom': nomcom,
'year': year,
'selected': 'feedback',
'positions': positions,
'topics': topics,
'counts' : counts,
@ -625,7 +609,6 @@ def feedback(request, year, public):
'year': year,
'positions': positions,
'topics': topics,
'selected': 'feedback',
'counts': counts,
'topic_counts': topic_counts,
'base_template': base_template
@ -651,7 +634,6 @@ def private_feedback_email(request, year):
return render(request, template,
{'nomcom': nomcom,
'year': year,
'selected': 'feedback_email',
'is_chair_task' : True,
})
@ -671,8 +653,7 @@ def private_feedback_email(request, year):
return render(request, template,
{'form': form,
'nomcom': nomcom,
'year': year,
'selected': 'feedback_email'})
'year': year})
@role_required("Nomcom Chair", "Nomcom Advisor")
def private_questionnaire(request, year):
@ -694,7 +675,6 @@ def private_questionnaire(request, year):
return render(request, template,
{'nomcom': nomcom,
'year': year,
'selected': 'questionnaire',
'is_chair_task' : True,
})
@ -713,8 +693,7 @@ def private_questionnaire(request, year):
{'form': form,
'questionnaire_response': questionnaire_response,
'nomcom': nomcom,
'year': year,
'selected': 'questionnaire'})
'year': year})
def process_nomination_status(request, year, nominee_position_id, state, date, hash):
@ -768,7 +747,6 @@ def process_nomination_status(request, year, nominee_position_id, state, date, h
'nominee_position': nominee_position,
'state': state,
'need_confirmation': need_confirmation,
'selected': 'feedback',
'form': form })
@role_required("Nomcom")
@ -833,7 +811,6 @@ def view_feedback(request, year):
return render(request, 'nomcom/view_feedback.html',
{'year': year,
'selected': 'view_feedback',
'nominees': nominees,
'nominee_feedback_types': nominee_feedback_types,
'independent_feedback_types': independent_feedback_types,
@ -930,7 +907,6 @@ def view_feedback_pending(request, year):
form.set_nomcom(nomcom, request.user)
return render(request, 'nomcom/view_feedback_pending.html',
{'year': year,
'selected': 'feedback_pending',
'formset': formset,
'extra_step': extra_step,
'extra_ids': extra_ids,
@ -1093,7 +1069,6 @@ def edit_nominee(request, year, nominee_id):
return render(request, 'nomcom/edit_nominee.html',
{'year': year,
'selected': 'index',
'nominee': nominee,
'form': form,
'nomcom': nomcom,
@ -1131,7 +1106,6 @@ def edit_nomcom(request, year):
'formset': formset,
'nomcom': nomcom,
'year': year,
'selected': 'edit_nomcom',
'is_chair_task' : True,
})
@ -1145,7 +1119,6 @@ def list_templates(request, year):
return render(request, 'nomcom/list_templates.html',
{'template_list': template_list,
'year': year,
'selected': 'edit_templates',
'nomcom': nomcom,
'is_chair_task' : True,
})
@ -1218,7 +1191,6 @@ def list_positions(request, year):
return render(request, 'nomcom/list_positions.html',
{'positions': positions,
'year': year,
'selected': 'edit_positions',
'nomcom': nomcom,
'is_chair_task' : True,
})
@ -1285,7 +1257,6 @@ def list_topics(request, year):
return render(request, 'nomcom/list_topics.html',
{'topics': topics,
'year': year,
'selected': 'edit_topics',
'nomcom': nomcom,
'is_chair_task' : True,
})
@ -1367,8 +1338,7 @@ def edit_members(request, year):
return render(request, 'nomcom/new_edit_members.html',
{'nomcom' : nomcom,
'year' : year,
'form': form,
})
'form': form})
@role_required("Nomcom Chair", "Nomcom Advisor")
def extract_email_lists(request, year):
@ -1388,8 +1358,7 @@ def extract_email_lists(request, year):
'pending': pending,
'accepted': accepted,
'noresp': noresp,
'bypos': bypos,
})
'bypos': bypos})
@login_required
def volunteer(request):

View file

@ -165,7 +165,8 @@ class SessionRequestTestCase(TestCase):
self.assertContains(r, 'Schedule the sessions on subsequent days')
self.assertContains(r, 'Thursday early afternoon, Thursday late afternoon')
self.assertContains(r, group2.acronym)
self.assertContains(r, 'Second session with: {} {}'.format(group3.acronym, group4.acronym))
# The sessions can be in any order in the HTML, deal with that
self.assertRegex(r.content.decode(), r'Second session with: ({} {}|{} {})'.format(group3.acronym, group4.acronym, group4.acronym, group3.acronym))
# check that a notification was sent
self.assertEqual(len(outbox), 1)

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(/([@._+])/g, "$1<wbr>");
if (newtext === text) {
return;
}

View file

@ -1,16 +1,21 @@
import * as List from "list.js";
var dummy = new List();
import {
default as List
} from "list.js";
function text_sort(a, b, options) {
function prep(e, options) {
return $($.parseHTML(e.values()[options.valueName]))
.text()
.trim()
.replaceAll(/\s+/g, ' ');
}
// sort by text content
return dummy.utils.naturalSort.caseInsensitive($($.parseHTML(a.values()[options.valueName]))
.text()
.trim()
.replaceAll(/\s+/g, ' '), $($.parseHTML(b.values()[options.valueName]))
.text()
.trim()
.replaceAll(/\s+/g, ' '));
return prep(a, options).localeCompare(prep(b, options), "en", {
sensitivity: "base",
ignorePunctuation: true
});
}
function replace_with_internal(table, internal_table, i) {
@ -204,12 +209,12 @@ $(document)
}
let newlist = new List(hook, pagination ? {
valueNames: fields,
pagination: pagination,
page: items_per_page
} : {
valueNames: fields
});
valueNames: fields,
pagination: pagination,
page: items_per_page
} : {
valueNames: fields
});
// override search module with a patched version
// see https://github.com/javve/list.js/issues/699
// TODO: check if this is still needed if list.js ever sees an update
@ -220,7 +225,7 @@ $(document)
if (enable_search) {
reset_search.on("click", function () {
search_field.val("");
$.each(list_instance, (i, e) => {
$.each(list_instance, (_, e) => {
e.search();
});
});
@ -229,7 +234,7 @@ $(document)
if (event.key == "Escape") {
reset_search.trigger("click");
} else {
$.each(list_instance, (i, e) => {
$.each(list_instance, (_, e) => {
e.search($(this)
.val());
});
@ -242,16 +247,19 @@ $(document)
.on("click", function () {
var order = $(this)
.hasClass("asc") ? "desc" : "asc";
$.each(list_instance, (i, e) => {
$.each(list_instance, (_, e) => {
e.sort($(this)
.attr("data-sort"), { order: order, sortFunction: text_sort });
.attr("data-sort"), {
order: order,
sortFunction: text_sort
});
});
});
$.each(list_instance, (i, e) => {
e.on("sortComplete", function () {
replace_with_internal(table, internal_table, i);
$(table).find("[data-bs-original-title]").tooltip();
$(table).find("[data-bs-original-title]").tooltip();
if (i == list_instance.length - 1) {
$(table)
.find("thead:first tr")
@ -287,8 +295,11 @@ $(document)
if (presort_col) {
const order = presort_col.attr("data-default-sort");
if (order === "asc" || order === "desc") {
$.each(list_instance, (i, e) => {
e.sort(presort_col.attr("data-sort"), { order: order, sortFunction: text_sort });
$.each(list_instance, (_, e) => {
e.sort(presort_col.attr("data-sort"), {
order: order,
sortFunction: text_sort
});
});
}
}

View file

@ -34,7 +34,7 @@ function filter_calendar_events(filter_params, event_list) {
}
// format a moment in a tz
var moment_formats = { time: 'HH:mm', date: 'YYYY-MM-DD', datetime: 'YYYY-MM-DD HH:mm' };
var moment_formats = { time: 'HH:mm', date: 'YYYY-MM-DD', datetime: 'YYYY-MM-DD HH:mm' , timezone: 'z'};
function format_moment(t_moment, tz, fmt_type) {
return t_moment.tz(tz)
@ -43,6 +43,9 @@ function format_moment(t_moment, tz, fmt_type) {
function make_display_events(event_data, tz) {
var calendarEl = document.getElementById('calendar');
if (!calendarEl) {
return;
}
var glue = calendarEl.clientWidth > 720 ? ' ' : '\n';
return $.map(event_data, function (src_event) {
var title;
@ -83,6 +86,9 @@ function update_calendar(tz, filter_params) {
* filtered events.
*/
var calendarEl = document.getElementById('calendar');
if (!calendarEl) {
return;
}
event_calendar = new FullCalendar(calendarEl, {
plugins: [dayGridPlugin, bootstrap5Plugin],
initialView: 'dayGridMonth',
@ -136,7 +142,7 @@ function format_session_time(session_elt, tz) {
.attr('data-start-utc'));
var end = moment.utc($(session_elt)
.attr('data-end-utc'));
return format_moment(start, tz, 'datetime') + '-' + format_moment(end, tz, 'time');
return format_moment(start, tz, 'datetime') + '-' + format_moment(end, tz, 'time') + ' ' + format_moment(start, tz, 'timezone');
}
function format_meeting_time(meeting_elt, tz) {

View file

@ -355,7 +355,6 @@ class SubmitTests(BaseSubmitTestCase):
ad=draft.ad,
expires=timezone.now() + datetime.timedelta(days=settings.INTERNET_DRAFT_DAYS_TO_EXPIRE),
notify="aliens@example.mars",
note="",
)
sug_replaced_draft.set_state(State.objects.get(used=True, type="draft", slug="active"))
sug_replaced_alias = DocAlias.objects.create(name=sug_replaced_draft.name)
@ -1413,7 +1412,6 @@ class SubmitTests(BaseSubmitTestCase):
"submitter-name": "Some Random Test Person",
"submitter-email": "random@example.com",
"replaces": [str(draft.docalias.first().pk)],
"edit-note": "no comments",
"authors-0-name": "Person 1",
"authors-0-email": "person1@example.com",
"authors-1-name": "Person 2",
@ -1429,7 +1427,6 @@ class SubmitTests(BaseSubmitTestCase):
self.assertEqual(submission.document_date, document_date)
self.assertEqual(submission.abstract, "some abstract")
self.assertEqual(submission.pages, 123)
self.assertEqual(submission.note, "no comments")
self.assertEqual(submission.submitter, "Some Random Test Person <random@example.com>")
self.assertEqual(submission.replaces, draft.docalias.first().name)
self.assertEqual(submission.state_id, "manual")

View file

@ -14,7 +14,7 @@
{% csrf_token %}
{% bootstrap_form ballot_writeup_form %}
<div class="form-text my-3">
Technical summary, Working Group summary, document quality, personnel, IRTF note, IESG note, IANA note. This text will be appended to all announcements and messages to the IRTF or RFC Editor.
Technical summary, Working Group summary, document quality, personnel, IANA note. This text will be appended to all announcements and messages to the IRTF or RFC Editor.
{% if ballot_issue_danger %}
<p class="text-danger">
This document has not completed IETF Last Call. Please do not issue the ballot early without good reason.
@ -36,4 +36,4 @@
Back
</a>
</form>
{% endblock %}
{% endblock %}

View file

@ -1,4 +1,4 @@
{% load ietf_filters %}{% autoescape off %}From: The IESG <iesg-secretary@ietf.org>
{% load ietf_filters %}{% autoescape off %}From: {% if group.type_id == "rg" %}The IRTF <irtf-chair@irtf.org>{% else %}The IESG <iesg-secretary@ietf.org>{% endif %}
To: {{ to }}{% if cc %}
Cc: {{ cc }} {% endif %}
Subject: {{ group.type.name }} Action: {{ action_type }} {{ group.name }} ({{ group.acronym }})

View file

@ -36,14 +36,6 @@ RFC Editor Note
(Insert RFC Editor Note here or remove section)
IRTF Note
(Insert IRTF Note here or remove section)
IESG Note
(Insert IESG Note here or remove section)
IANA Note
(Insert IANA Note here or remove section)

View file

@ -13,7 +13,7 @@
<form class="mt-3" method="post">
{% csrf_token %}
{% bootstrap_form ballot_writeup_form %}
<div class="form-text mb-3">Working group summary, personnel, IAB note, IESG note, IANA note.</div>
<div class="form-text mb-3">Working group summary, personnel, IANA note.</div>
<button type="submit"
class="btn btn-primary"
name="save_ballot_writeup"

View file

@ -408,32 +408,19 @@
{% endif %}
</td>
</tr>
{% if iesg_state.slug != 'idexists' %}
{% if doc.note or can_edit %}
<tr>
<td></td>
<th scope="row">
IESG note
</th>
<td class="edit">
{% if can_edit and not snapshot %}
<a class="btn btn-primary btn-sm"
href="{% url 'ietf.doc.views_draft.edit_iesg_note' name=doc.name %}">
Edit
</a>
{% endif %}
</td>
<td>
{% if doc.note %}
{{ doc.note|linebreaksbr }}
{% else %}
<span class="text-body-secondary">
(None)
</span>
{% endif %}
</td>
</tr>
{% endif %}
{% if iesg_state.slug != 'idexists' and doc.note %}
<tr>
<td></td>
<th scope="row">
IESG note
</th>
<td class="edit">
{# IESG Notes are historic and read-only now #}
</td>
<td>
{{ doc.notedoc.note|urlize_ietf_docs|linkify|linebreaksbr }}
</td>
</tr>
{% endif %}
<tr>
<td></td>

View file

@ -2,7 +2,7 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load static %}
{% load ietf_filters textfilters %}
{% load ietf_filters textfilters tz %}
{% block title %}{{ doc.title|default:"Untitled" }}{% endblock %}
{% block content %}
{% origin %}
@ -29,6 +29,24 @@
{% if snapshot %}<span class="badge rounded-pill text-bg-warning">Snapshot</span>{% endif %}
</td>
</tr>
{% if doc.meeting_related %}
<tr>
<th scope="row">
Date and time
</th>
<td></td>
<td>
{% with session=doc.get_related_session %}
{% with timeslot=session.official_timeslotassignment.timeslot %}
{% if session.meeting %}
<span class="session-time date me-3" data-start-utc="{{ timeslot.time|utc|date:'Y-m-d H:i' }}" data-end-utc="{{ timeslot.end_time|utc|date:'Y-m-d H:i' }}"></span>
{% include "meeting/tz-display.html" with meeting_timezone=session.meeting.time_zone id_suffix="" minimal=True only %}
{% endif %}
{% endwith %}
{% endwith %}
</td>
</tr>
{% endif %}
<tr>
<th scope="row">Title</th>
<td class="edit">
@ -97,7 +115,21 @@
{% if presentations %}
{% for pres in presentations %}
{{ pres.session.short_name }} at {{ pres.session.meeting }}
{% if pres.rev != doc.rev %}(version -{{ pres.rev }}){% endif %}{% if not forloop.last %},{% endif %}
{% if pres.rev != doc.rev %}(version -{{ pres.rev }}){% endif %}
<br>
<b>Remote instructions:</b>
{% if pres.session.agenda_note|first_url|conference_url %}
<a href="{{ pres.session.agenda_note|first_url }}" title="Online conference">
<i class="bi bi-beople"></i>
</a>
{% elif pres.session.remote_instructions|first_url|conference_url %}
<a href="{{ pres.session.remote_instructions|first_url }}"
title="Online conference">
<i class="bi bi-people"></i>
</a>
{% endif %}
{{ pres.session.remote_instructions|linkify }}
{% if not forloop.last %}<br>{% endif %}
{% endfor %}
{% else %}
<span class="text-body-secondary">(None)</span>
@ -142,4 +174,16 @@
{% block js %}
<script src="{% static 'ietf/js/d3.js' %}"></script>
<script src="{% static 'ietf/js/document_timeline.js' %}"></script>
{% if doc.meeting_related %}
<script src="{% static 'ietf/js/moment.js' %}"></script>
<script src="{% static 'ietf/js/upcoming.js' %}"></script>
<script src="{% static 'ietf/js/timezone.js' %}"></script>
<script>
$(function () {
// Init with best guess at local timezone.
ietf_timezone.set_tz_change_callback(timezone_changed);
ietf_timezone.initialize('local');
});
</script>
{% endif %}
{% endblock %}

View file

@ -1,19 +0,0 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load django_bootstrap5 %}
{% block title %}Edit IESG note for {{ doc.name }}{% endblock %}
{% block content %}
{% origin %}
<h1>
Edit IESG note
<br>
<small class="text-body-secondary">{{ doc.name }}</small>
</h1>
<form class="mt-3" method="post">
{% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="btn btn-primary">Save</button>
<a class="btn btn-secondary float-end" href="{{ doc.get_absolute_url }}">Back</a>
</form>
{% endblock %}

View file

@ -33,13 +33,6 @@ Personnel
Who is the Document Shepherd for this document? Who is the
Responsible Area Director?
{% endif %}
{% if doc.stream.slug == "irtf" %}IRTF Note
(Insert IRTF Note here or remove section)
{% elif doc.stream.slug == "ietf" %}IESG Note
(Insert IESG Note here or remove section)
{% endif %}
IANA Note
{% if iana %}
{% filter wordwrap:"76"|indent:2 %}{{ iana }}{% endfilter %}

View file

@ -8,6 +8,7 @@
<h1>{{ group_type | upper }} {{ role }} photos</h1>
{% regroup roles|dictsort:"last_initial" by last_initial as alphabet_blocks %}
{% for letter in alphabet_blocks %}
<!-- [html-validate-disable-next valid-id -- Unicode IDs are OK in HTML5] -->
<h2 class="mt-4" {% if letter.grouper|slugify %}id="{{ letter.grouper }}"{% endif %}>{{ letter.grouper }}</h2>
{% regroup letter.list by person as person_groups %}
{# keep in sync with group_photos.html #}

View file

@ -1,7 +1,6 @@
{% load origin tz %}
{% origin %}
{% for s in sessions %}
{% timezone s.meeting.time_zone %}
<tr>
<td>
{% if s.meeting.type.slug == 'ietf' %}
@ -12,9 +11,11 @@
</td>
<td>
{% if s.current_status == "sched" %}
{{ s.time|date:"Y-m-d" }}
{% with timeslot=s.official_timeslotassignment.timeslot %}
<span class="session-time date me-3" data-start-utc="{{ timeslot.time|utc|date:'Y-m-d H:i' }}" data-end-utc="{{ timeslot.end_time|utc|date:'Y-m-d H:i' }}"></span>
{% endwith %}
{% else %}
<i>{{ s.current_status_name }}</i>
<div class="badge rounded-pill text-bg-secondary">{{ s.current_status_name }}</div>
{% endif %}
{% if show_request and s.meeting.type_id == 'ietf' %}
{% if can_edit %}
@ -28,9 +29,6 @@
<td>
{% if s.name %}{{ s.name }}{% endif %}
</td>
<td>
{% if s.current_status == "sched" %}{{ s.time|date:"D" }}{% endif %}
</td>
<td>
{% if show_ical and s.current_status == "sched" %}
{% if s.meeting.type_id == 'ietf' %}
@ -78,5 +76,4 @@
{% endif %}
</td>
</tr>
{% endtimezone %}
{% endfor %}

View file

@ -1,6 +1,5 @@
{% extends "group/group_base.html" %}
{# Copyright The IETF Trust 2015-2022, All Rights Reserved #}
{% load origin %}
{% load origin static %}
{% block title %}
Meetings
{% if group %}for {{ group.acronym }}{% endif %}
@ -19,15 +18,15 @@
{% endblock %}
{% block group_content %}
{% origin %}
{% include "meeting/tz-display.html" with meeting_timezone=None id_suffix="" minimal=False only %}
{% if in_progress %}
<h2 class="mt-3" id="inprogressmeets">Meetings in progress</h2>
<h2 class="mt-5" id="inprogressmeets">Meetings in progress</h2>
{% with sessions=in_progress show_request=True show_ical=True can_edit_materials=can_edit %}
<table class="table table-sm table-striped tablesorter">
<thead>
<tr>
<th scope="col" data-sort="meeting">Meeting</th>
<th scope="col" data-sort="date">Date</th>
<th scope="col"></th>
<th class="col-3" scope="col" data-sort="meeting">Meeting</th>
<th class="col-3" scope="col" data-sort="date">Date</th>
<th scope="col"></th>
<th scope="col">Materials</th>
</tr>
@ -39,7 +38,7 @@
{% endwith %}
{% endif %}
{% if future %}
<h2 class="mt-3" id="futuremeets">
<h2 class="mt-5" id="futuremeets">
Future Meetings
<a class="float-end"
aria-label="icalendar entry for all scheduled future {{ group.acronym }} meetings"
@ -51,9 +50,8 @@
<table class="table table-sm table-striped tablesorter">
<thead>
<tr>
<th scope="col" data-sort="meeting">Meeting</th>
<th scope="col" data-sort="date">Date</th>
<th scope="col"></th>
<th class="col-3" scope="col" data-sort="meeting">Meeting</th>
<th class="col-3" scope="col" data-sort="date">Date</th>
<th scope="col"></th>
<th scope="col">Materials</th>
</tr>
@ -66,13 +64,12 @@
</table>
{% endif %}
{% if past or recent %}
<h2 class="mt-3" id="pastmeets">Past Meetings (within the last four years)</h2>
<h2 class="mt-5" id="pastmeets">Past Meetings (within the last four years)</h2>
<table class="table table-sm table-striped tablesorter">
<thead>
<tr>
<th scope="col" data-sort="meeting">Meeting</th>
<th scope="col" data-sort="date">Date</th>
<th scope="col"></th>
<th class="col-3" scope="col" data-sort="meeting">Meeting</th>
<th class="col-3" scope="col" data-sort="date">Date</th>
<th scope="col"></th>
<th scope="col">Materials</th>
</tr>
@ -100,13 +97,12 @@
older_sessions value, this block will need to be adjusted.
{% endcomment %}
{% if far_past %}
<h2 class="mt-3" id="farpastmeets">Meetings more than four years ago</h2>
<h2 class="mt-5" id="farpastmeets">Meetings more than four years ago</h2>
<table class="table table-sm table-striped tablesorter">
<thead>
<tr>
<th scope="col" data-sort="meeting">Meeting</th>
<th scope="col" data-sort="date">Date</th>
<th scope="col"></th>
<th class="col-3" scope="col" data-sort="meeting">Meeting</th>
<th class="col-3" scope="col" data-sort="date">Date</th>
<th scope="col"></th>
<th scope="col">Materials</th>
</tr>
@ -160,3 +156,16 @@
{% endif %}
{% endif %}
{% endblock %}
{% block js %}
<script src="{% static 'ietf/js/list.js' %}"></script>
<script src="{% static 'ietf/js/moment.js' %}"></script>
<script src="{% static 'ietf/js/upcoming.js' %}"></script>
<script src="{% static 'ietf/js/timezone.js' %}"></script>
<script>
$(function () {
// Init with best guess at local timezone.
ietf_timezone.set_tz_change_callback(timezone_changed);
ietf_timezone.initialize('local');
});
</script>
{% endblock %}

View file

@ -1,7 +1,7 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% origin %}
{% load ietf_filters ballot_icon person_filters %}
{% load ietf_filters ballot_icon person_filters textfilters %}
<div class="card mb-3">
<div class="position-absolute top-0 end-0 m-3">{% ballot_icon doc %}</div>
<div class="card-body">
@ -27,7 +27,7 @@
{% if conflictdoc.note %}
<div class="row">
<div class="col-3 text-end fw-bold">Note</div>
<div class="col">{{ conflictdoc.note|linebreaksbr }}</div>
<div class="col">{{ conflictdoc.note|urlize_ietf_docs|linkify|linebreaksbr }}</div>
</div>
{% endif %}
<div class="row">

View file

@ -3,6 +3,5 @@
{% filter wordwrap:"68"|indent|indent %}{{ doc.title }}{% endfilter %}
{{ conflictdoc.canonical_name }}-{{ conflictdoc.rev }}
{% filter wordwrap:"66"|indent:"4" %}{{ conflictdoc.title }} ({{ conflictdoc.stream }}: {{ conflictdoc.intended_std_level }}){% endfilter %}
{% if conflictdoc.note %}{# note: note is not escaped #} {% filter wordwrap:"64"|indent:"6" %}Note: {{ conflictdoc.note|striptags }}{% endfilter %}
{% endif %} Token: {{ doc.ad }}
Token: {{ doc.ad }}
{% with doc.active_defer_event as defer %}{% if defer %} Was deferred by {{defer.by}} on {{defer.time|date:"Y-m-d"}}{% endif %}{% endwith %}{% endwith %}

View file

@ -1,8 +1,7 @@
{% load ietf_filters %}{% with doc.rfc_number as rfc_number %}
o {{doc.canonical_name}}{% if not rfc_number %}-{{doc.rev}}{% endif %}{% endwith %}{%if doc.has_rfc_editor_note %} (Has RFC Editor Note){% endif %}{% if doc.stream %} - {{ doc.stream }} stream{% endif %}
{% filter wordwrap:"68"|indent|indent %}{{ doc.title }} ({{ doc.intended_std_level }}){% endfilter %}
{% if doc.note %}{# note: note is not escaped #} {% filter wordwrap:"68"|indent|indent %}Note: {{ doc.note|striptags }}{% endfilter %}
{% endif %} Token: {{ doc.ad }}{% if doc.iana_review_state %}
Token: {{ doc.ad }}{% if doc.iana_review_state %}
IANA Review: {{ doc.iana_review_state }}{% endif %}{% if doc.consensus %}
Consensus: {{ doc.consensus }}{% endif %}{% if doc.lastcall_expires %}
Last call expires: {{ doc.lastcall_expires|date:"Y-m-d" }}{% endif %}{% if doc.review_assignments %}

View file

@ -93,7 +93,7 @@ Parts Copyright (c) 2009 The IETF Trust, all rights reserved.
<p>If APPROVED with caveats - The Secretariat will send a working
group submission, Protocol Action Announcement that includes the
[RFC Editor Note, IESG Note, etc.] to be drafted by [Name that
[RFC Editor Note, etc.] to be drafted by [Name that
AD].</p>
{% endif %}
@ -103,7 +103,7 @@ Parts Copyright (c) 2009 The IETF Trust, all rights reserved.
<p>If APPROVED with caveats - The Secretariat will send an
individual submission, Protocol Action Announcement that includes
the [RFC Editor Note, IESG Note, etc.] to be drafted by [Name that
the [RFC Editor Note, etc.] to be drafted by [Name that
AD].</p>
{% endif %}
@ -113,7 +113,7 @@ Parts Copyright (c) 2009 The IETF Trust, all rights reserved.
<p>If APPROVED with caveats - The Secretariat will send the
associated status change Protocol Action Announcements that includes the
[RFC Editor Note, IESG Note, etc.] to be drafted by [Name that
[RFC Editor Note, etc.] to be drafted by [Name that
AD].</p>
{% endif %}
@ -123,7 +123,7 @@ Parts Copyright (c) 2009 The IETF Trust, all rights reserved.
<p>If APPROVED with caveats - The Secretariat will send a working
group submission Document Action announcement that includes the [RFC
Ed. Note, IESG, note, etc.] from [Name that AD].</p>
Ed. Note, etc.] from [Name that AD].</p>
{% endif %}
{% if num|startswith:"3.2.1" or num|startswith:"3.2.2" %}
@ -132,7 +132,7 @@ Parts Copyright (c) 2009 The IETF Trust, all rights reserved.
<p>If APPROVED with caveats - The Secretariat will send an
individual submission Document Action announcement that includes the
[RFC Ed. Note, IESG, note, etc.] from [Name that AD].</p>
[RFC Ed. Note, etc.] from [Name that AD].</p>
{% endif %}
{% if num|startswith:"3.3.1" or num|startswith:"3.3.2" %}
@ -141,13 +141,12 @@ Parts Copyright (c) 2009 The IETF Trust, all rights reserved.
<p>If APPROVED with caveats - The Secretariat will send the associated
status change Document Action announcements that includes the [RFC
Ed. Note, IESG, note, etc.] from [Name that AD].</p>
Ed. Note, etc.] from [Name that AD].</p>
{% endif %}
{% if num|startswith:"3.4.1" or num|startswith:"3.4.2" %}
<p>If APPROVED - The Secretariat will send a standard no problem
message to the RFC Editor. [Name of AD] will you supply the text for
the IESG Note?</p>
message to the RFC Editor.</p>
<p>If APPROVED with caveats - The Secretariat will send a standard
no problem message to the RFC Editor that includes the note drafted

View file

@ -57,6 +57,17 @@
{% endif %}
{% endblock %}
{% block js %}
<script src="{% static 'ietf/js/list.js' %}"></script>
<script src="{% static 'ietf/js/moment.js' %}"></script>
<script src="{% static 'ietf/js/upcoming.js' %}"></script>
<script src="{% static 'ietf/js/timezone.js' %}"></script>
<script>
$(function () {
// Init with best guess at local timezone.
ietf_timezone.set_tz_change_callback(timezone_changed);
ietf_timezone.initialize('local');
});
</script>
{% if can_manage_materials %}
<script src="{% static 'ietf/js/sortable.js' %}"></script>
<script>

View file

@ -17,8 +17,8 @@
{% if sessions|length > 1 %}Session {{ forloop.counter }} :{% endif %}
{% for time in session.times %}
{% if not forloop.first %},{% endif %}
{{ time|timezone:session.meeting.time_zone|dateformat:"l Y-m-d H:i T" }}
{% if time.tzinfo.zone != "UTC" %}<span class="small">({{ time|utc|dateformat:"H:i T" }})</span>{% endif %}
<span class="session-time date me-3" data-start-utc="{{ timeslot.time|utc|date:'Y-m-d H:i' }}" data-end-utc="{{ timeslot.end_time|utc|date:'Y-m-d H:i' }}"></span>
{% include "meeting/tz-display.html" with meeting_timezone=session.meeting.time_zone id_suffix=session.pk minimal=True only %}
{% endfor %}
{% if session.cancelled %}
<small class="badge rounded-pill text-bg-warning">Cancelled</small>

View file

@ -12,8 +12,8 @@ As long as id_suffix is different, should allow for as many copies of the widget
{% load origin %}
{% origin %}
{% firstof id_suffix "" as suffix %}
<div class="tz-display flex-fill {% if minimal %}btn-group btn-group-sm my-2{% else %}input-group my-3{% endif %} flex-wrap">
{% if not minimal %}<label class="input-group-text border-primary bg-white fw-bold">Time zone:</label>{% endif %}
<span class="tz-display flex-fill {% if minimal %}btn-group btn-group-sm my-2{% else %}input-group my-3{% endif %} flex-wrap">
{% if not minimal %}<label class="input-group-text border-primary bg-transparent fw-bold">Time zone:</label>{% endif %}
{% if meeting_timezone is not None %}
<input type="radio"
name="tzradio{{ suffix }}"
@ -46,4 +46,4 @@ As long as id_suffix is different, should allow for as many copies of the widget
</option>
</select>
{% endif %}
</div>
</span>

View file

@ -4,9 +4,6 @@
{% load django_bootstrap5 textfilters person_filters %}
{% load static %}
{% block subtitle %}- Eligible People{% endblock %}
{% block pagehead %}
<link rel="stylesheet" href="{% static "ietf/css/list.css" %}">
{% endblock %}
{% block nomcom_content %}
{% origin %}
<h2>Eligible People for {{ nomcom.group }}</h2>
@ -41,7 +38,4 @@
{% endif %}
</table>
}
{% endblock %}
{% block js %}
<script src="{% static "ietf/js/list.js" %}"></script>
{% endblock %}

View file

@ -24,23 +24,24 @@
<thead>
<tr>
{% if nomcom.group.state_id == 'active' %}
<th scope="colgroup" colspan="3">
<i class="bi bi-check"></i>
<th scope="col">
</th>
<th scope="col"></th>
{% endif %}
<th scope="col" data-sort="position">
Position
</th>
<th scope="col" data-sort="iesg">
<th class="text-center" scope="col" data-sort="iesg">
IESG
</th>
<th scope="col" data-sort="open">
<th class="text-center" scope="col" data-sort="open">
Open
</th>
<th scope="col" data-sort="accept_nom">
<th class="text-center" scope="col" data-sort="accept_nom">
Accepting Nominations
</th>
<th scope="col" data-sort="accept_fb">
<th class="text-center" scope="col" data-sort="accept_fb">
Accepting Feedback
</th>
</tr>
@ -58,13 +59,11 @@
aria-label="position.name"
name="selected">
</td>
<td class="edit">
<td class="text-nowrap">
<a class="btn btn-primary btn-sm"
href="{% url 'ietf.nomcom.views.edit_position' year position.id %}">
Edit
</a>
</td>
<td class="remove">
<a class="btn btn-danger btn-sm"
href="{% url 'ietf.nomcom.views.remove_position' year position.id %}">
Remove
@ -74,16 +73,16 @@
<td>
{{ position.name }}
</td>
<td>
<td class="text-center">
{{ position.is_iesg_position|yesno:"✓," }}
</td>
<td>
<td class="text-center">
{{ position.is_open|yesno:"✓," }}
</td>
<td>
<td class="text-center">
{{ position.accepting_nominations|yesno:"✓," }}
</td>
<td>
<td class="text-center">
{{ position.accepting_feedback|yesno:"✓," }}
</td>
</tr>
@ -117,7 +116,7 @@
<option value="unset_accept_nom">
Is Accepting Nominations: No
</option>
<option value="set_accept_bf">
<option value="set_accept_fb">
Is Accepting Feedback: Yes
</option>
<option value="unset_accept_fb">
@ -135,7 +134,4 @@
There are no positions defined.
</p>
{% endif %}
{% endblock %}
{% block js %}
<script src="{% static "ietf/js/list.js" %}"></script>
{% endblock %}
{% endblock %}

View file

@ -1,14 +1,18 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load origin static %}
{% load nomcom_tags %}
{% load ietf_filters %}
{% block pagehead %}
<link rel="stylesheet" href="{% static "ietf/css/list.css" %}">
{% endblock %}
{% block title %}
NomCom {{ year }} Private
{% block subtitle %}{% endblock %}
{% endblock %}
{% block content %}
{% origin %}
{% with selected=request.path|split:'/'|slice:'4:-1'|join:'-' %}
<h1>
NomCom {{ year }}
{% if nomcom.group.state_id == 'conclude' %}<span class="badge rounded-pill text-bg-info">Concluded</span>{% endif %}
@ -19,129 +23,140 @@
</h1>
<ul class="nav nav-tabs my-3">
<li class="nav-item">
<a class="nav-link {% if selected == "index" %}active{% endif %}"
<a class="nav-link {% if selected == '' %}active{% endif %}"
href="{% url "ietf.nomcom.views.private_index" year %}">
Nominees
</a>
</li>
{% if nomcom|has_publickey %}
<li class="nav-item">
<a class="nav-link {% if selected == "nominate" %}active{% endif %}"
<a class="nav-link {% if selected == 'nominate' %}active{% endif %}"
href="{% url 'ietf.nomcom.views.private_nominate' year %}">
Nominate
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if selected == "feedback" %}active{% endif %}"
{% endif %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle {% if selected in 'feedback,view-feedback'|split:',' %}active{% endif %}" data-bs-toggle="dropdown" href="#">Feedback</a>
<ul class="dropdown-menu mt-n1" role="menu">
{% if nomcom|has_publickey %}
<li>
<a class="dropdown-item {% if selected == 'feedback' %}active{% endif %}"
href="{% url 'ietf.nomcom.views.private_feedback' year %}">
Enter feedback
</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link {% if selected == "view_feedback" %}active{% endif %}"
href="{% url 'ietf.nomcom.views.view_feedback' year %}">
View feedback
</a>
{% endif %}
<li>
<a class="dropdown-item {% if selected == 'view-feedback' %}active{% endif %}"
href="{% url 'ietf.nomcom.views.view_feedback' year %}">
View feedback
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link {% if selected == "private_key" %}active{% endif %}"
<a class="nav-link {% if selected == 'key' %}active{% endif %}"
href="{% url 'ietf.nomcom.views.private_key' year %}">
Private key
</a>
</li>
{% if user|is_chair_or_advisor:year %}
<li class="nav-item">
<a class="nav-link {% if selected == "feedback_pending" %}active{% endif %}"
<a class="nav-link {% if selected == 'view-feedback-pending' %}active{% endif %}"
href="{% url 'ietf.nomcom.views.view_feedback_pending' year %}">
Pending emails
{% if nomcom.pending_email_count %}<span class="badge rounded-pill text-bg-primary">{{ nomcom.pending_email_count }}</span>{% endif %}
</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#">Chair/Advisor Tasks</a>
<a class="nav-link dropdown-toggle {% if selected in 'feedback-email,questionnaire-response,extract-email-lists,send-reminder-mail-accept,send-reminder-mail-questionnaire,merge-person,merge-nominee,edit-nomcom,chair-templates,chair-position,chair-topic,edit-members,help'|split:',' %}active{% endif %}" data-bs-toggle="dropdown" href="#">Chair/advisor tasks</a>
<ul class="dropdown-menu mt-n1" role="menu">
{% if nomcom.group.state_id == 'active' %}
<li role="presentation" class="dropdown-header">Feedback Management</li>
<li class="dropdown-item">
<a href="{% url 'ietf.nomcom.views.private_feedback_email' year %}">Enter email feedback</a>
<li>
<a class="dropdown-item {% if selected == 'feedback-email' %}active{% endif %}" href="{% url 'ietf.nomcom.views.private_feedback_email' year %}">Enter email feedback</a>
</li>
<li class="dropdown-item">
<a href="{% url 'ietf.nomcom.views.private_questionnaire' year %}">Enter questionnaire response</a>
<li>
<a class="dropdown-item {% if selected == 'questionnaire-response' %}active{% endif %}" href="{% url 'ietf.nomcom.views.private_questionnaire' year %}">Enter questionnaire response</a>
</li>
<li class="dropdown-item">
<a href="{% url 'ietf.nomcom.views.extract_email_lists' year %}">Extract email lists</a>
<li>
<a class="dropdown-item {% if selected == 'extract-email-lists' %}active{% endif %}" href="{% url 'ietf.nomcom.views.extract_email_lists' year %}">Extract email lists</a>
</li>
<li class="dropdown-item">
<a href="{% url 'ietf.nomcom.views.send_reminder_mail' year "accept" %}">Send accept reminder</a>
<li>
<a class="dropdown-item {% if selected == 'send-reminder-mail-accept' %}active{% endif %}" href="{% url 'ietf.nomcom.views.send_reminder_mail' year "accept" %}">Send accept reminder</a>
</li>
<li class="dropdown-item">
<a href="{% url 'ietf.nomcom.views.send_reminder_mail' year "questionnaire" %}">Send questionnaire reminder</a>
<li>
<a class="dropdown-item {% if selected == 'send-reminder-mail-questionnaire' %}active{% endif %}" href="{% url 'ietf.nomcom.views.send_reminder_mail' year "questionnaire" %}">Send questionnaire reminder</a>
</li>
<li class="dropdown-item">
<a href="{% url "ietf.nomcom.views.private_merge_person" year %}">Request person record merge</a>
<li>
<a class="dropdown-item {% if selected == 'merge-person' %}active{% endif %}" href="{% url "ietf.nomcom.views.private_merge_person" year %}">Request person record merge</a>
</li>
<li class="dropdown-item">
<a href="{% url "ietf.nomcom.views.private_merge_nominee" year %}">Merge nominee records</a>
<li>
<a class="dropdown-item {% if selected == 'merge-nominee' %}active{% endif %}" href="{% url "ietf.nomcom.views.private_merge_nominee" year %}">Merge nominee records</a>
</li>
<li><hr class="dropdown-divider"></li>
{% endif %}
<li role="presentation" class="dropdown-header">NomCom configuration</li>
<li class="dropdown-item">
<a href="{% url 'ietf.nomcom.views.edit_nomcom' year %}">Edit settings</a>
<li>
<a class="dropdown-item {% if selected == 'edit-nomcom' %}active{% endif %}" href="{% url 'ietf.nomcom.views.edit_nomcom' year %}">Edit settings</a>
</li>
<li class="dropdown-item">
<a href="{% url 'ietf.nomcom.views.list_templates' year %}">Edit pages</a>
<li>
<a class="dropdown-item {% if selected == 'chair-templates' %}active{% endif %}" href="{% url 'ietf.nomcom.views.list_templates' year %}">Edit pages</a>
</li>
<li class="dropdown-item">
<a href="{% url 'ietf.nomcom.views.list_positions' year %}">Edit positions</a>
<li>
<a class="dropdown-item {% if selected == 'chair-position' %}active{% endif %}" href="{% url 'ietf.nomcom.views.list_positions' year %}">Edit positions</a>
</li>
<li class="dropdown-item">
<a href="{% url 'ietf.nomcom.views.list_topics' year %}">Edit topics</a>
<li>
<a class="dropdown-item {% if selected == 'chair-topic' %}active{% endif %}" href="{% url 'ietf.nomcom.views.list_topics' year %}">Edit topics</a>
</li>
{% if nomcom.group.state_id == 'active' %}
<li class="dropdown-item">
<a href="{% url 'ietf.nomcom.views.edit_members' year %}">Edit members</a>
<li>
<a class="dropdown-item {% if selected == 'edit-members' %}active{% endif %}" href="{% url 'ietf.nomcom.views.edit_members' year %}">Edit members</a>
</li>
{% endif %}
<li class="dropdown-item">
<a href="{% url 'ietf.nomcom.views.configuration_help' year %}">Configuration help</a>
<li>
<a class="dropdown-item {% if selected == 'help' %}active{% endif %}" href="{% url 'ietf.nomcom.views.configuration_help' year %}">Configuration help</a>
</li>
<li><hr class="dropdown-divider"></li>
<li role="presentation" class="dropdown-header">Other tools</li>
<li class="dropdown-item">
<a href="{% url 'ietf.secr.announcement.views.main' %}">Announcement tool</a>
</li>
<li class="dropdown-item">
<a href="https://www.ietf.org/registration/nomcom_chair/nomcomstaff.py">Secretariat private eligibility checker</a>
</li>
<li class="dropdown-item">
<a href="https://www.ietf.org/registration/nomcom.py">Secretariat public eligibility checker</a>
<li>
<a class="dropdown-item" href="{% url 'ietf.secr.announcement.views.main' %}">Announcement tool</a>
</li>
</ul>
</li>
{% endif %}
{% if user|is_chair_or_advisor:year or user|has_role:"Secretariat" %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#">
<a class="nav-link dropdown-toggle {% if selected in 'chair-eligible,chair-volunteers'|split:',' %}active{% endif %}" data-bs-toggle="dropdown" href="#">
Volunteers
</a>
<ul class="dropdown-menu mt-n1" role="menu">
<li class="dropdown-item">
<a href="{% url 'ietf.nomcom.views.private_eligible' year %}">
<li>
<a class="dropdown-item {% if selected == 'chair-eligible' %}active{% endif %}" href="{% url 'ietf.nomcom.views.private_eligible' year %}">
View eligible
</a>
</li>
<li class="dropdown-item">
<a href="{% url 'ietf.nomcom.views.private_volunteers' year %}">
<li>
<a class="dropdown-item {% if selected == 'chair-volunteers' %}active{% endif %}" href="{% url 'ietf.nomcom.views.private_volunteers' year %}">
View volunteers
</a>
</li>
</ul>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link"
href="/nomcom/{{ nomcom.year }}">
Public pages &raquo;
</a>
</li>
</ul>
{% block nomcom_content %}{% endblock %}
{% endwith %}
{% endblock %}
{% block js %}
<script src="{% static "ietf/js/list.js" %}"></script>
<script>
// Javascript to enable link to tab
var url=document.location.toString();

View file

@ -1,21 +1,25 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load origin static %}
{% load nomcom_tags %}
{% load ietf_filters %}
{% block pagehead %}
<link rel="stylesheet" href="{% static "ietf/css/list.css" %}">
{% endblock %}
{% block title %}
NomCom {{ year }}
{% block subtitle %}{% endblock %}
{% endblock %}
{% block content %}
{% origin %}
{% with selected=request.path|split:'/'|slice:'3:-1'|join:'-' %}
<h1>
NomCom {{ year }}
{% if nomcom.group.state_id == 'conclude' %}<span class="badge rounded-pill text-bg-info">Concluded</span>{% endif %}
</h1>
<ul class="nav nav-tabs my-3">
<li class="nav-item">
<a class="nav-link {% if selected == "index" %}active{% endif %}"
<a class="nav-link {% if selected == '' %}active{% endif %}"
href="{% url 'ietf.nomcom.views.year_index' year %}">
Home
</a>
@ -35,7 +39,7 @@
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link {% if selected == "requirements" %}active{% endif %}"
<a class="nav-link {% if selected == "expertise" %}active{% endif %}"
href="{% url 'ietf.nomcom.views.requirements' year %}">
Desired expertise
</a>
@ -48,14 +52,14 @@
</li>
{% if user|is_chair_or_advisor:year or user|has_role:"Secretariat" %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#">Volunteers</a>
<a class="nav-link dropdown-toggle {% if selected in 'eligible,volunteers'|split:',' %}active{% endif %}" data-bs-toggle="dropdown" href="#">Volunteers</a>
<ul class="dropdown-menu mt-n1" role="menu">
<li class="nav-item">
<a class="dropdown-item"
<a class="dropdown-item {% if selected == 'eligible' %}active{% endif %}"
href="{% url 'ietf.nomcom.views.public_eligible' year %}">View eligible</a>
</li>
<li class="nav-item">
<a class="dropdown-item"
<a class="dropdown-item {% if selected == 'volunteers' %}active{% endif %}"
href="{% url 'ietf.nomcom.views.public_volunteers' year %}">View volunteers</a>
</li>
</ul>
@ -63,8 +67,10 @@
{% endif %}
</ul>
{% block nomcom_content %}{% endblock %}
{% endwith %}
{% endblock %}
{% block js %}
<script src="{% static "ietf/js/list.js" %}"></script>
<script>
$(document)
.ready(function () {

View file

@ -2,9 +2,6 @@
{# Copyright The IETF Trust 2015-2020, All Rights Reserved #}
{% load origin static %}
{% block subtitle %}- Administration{% endblock %}
{% block pagehead %}
<link rel="stylesheet" href="{% static "ietf/css/list.css" %}">
{% endblock %}
{% block nomcom_content %}
{% origin %}
<h2 class="mt-3">Nomination status</h2>
@ -251,7 +248,4 @@
{% endif %}
{% endif %}
{% endif %}
{% endblock %}
{% block js %}
<script src="{% static "ietf/js/list.js" %}"></script>
{% endblock %}

View file

@ -3,9 +3,6 @@
{% load origin static %}
{% load nomcom_tags %}
{% block subtitle %}- View feedback{% endblock %}
{% block pagehead %}
<link rel="stylesheet" href="{% static "ietf/css/list.css" %}">
{% endblock %}
{% block nomcom_content %}
{% origin %}
<h2 class="mt-3">Feedback related to nominees</h2>
@ -113,7 +110,4 @@
</tbody>
</table>
{% endif %}
{% endblock %}
{% block js %}
<script src="{% static "ietf/js/list.js" %}"></script>
{% endblock %}
{% endblock %}

View file

@ -1,12 +1,9 @@
{% extends public|yesno:"nomcom/nomcom_public_base.html,nomcom/nomcom_private_base.html" %}
{# Copyright The IETF Trust 2021, All Rights Reserved #}
{% load origin %}
{% load django_bootstrap5 textfilters person_filters %}
{% load django_bootstrap5 textfilters person_filters ietf_filters%}
{% load static %}
{% block subtitle %}- Volunteers{% endblock %}
{% block pagehead %}
<link rel="stylesheet" href="{% static "ietf/css/list.css" %}">
{% endblock %}
{% block nomcom_content %}
{% origin %}
<h2>Volunteers for {{ nomcom.group }}</h2>
@ -34,7 +31,7 @@
{% for v in eligibility_group.list %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ v.person.last_name }}</td>
<td>{{ v.person.last_name|split:'+'|join:'+<wbr>' }}</td>
<td>{{ v.person.first_name }}</td>
<td>{{ v.person.ascii_name }}</td>
<td>{% person_link v.person %}</td>
@ -46,7 +43,4 @@
</tbody>
</table>
{% endfor %}
{% endblock %}
{% block js %}
<script src="{% static "ietf/js/list.js" %}"></script>
{% endblock %}

View file

@ -21,7 +21,6 @@
</p>
<ul>
<li>Only XML-only uploads are supported, not text or combined.</li>
<li>Document replacement information cannot be supplied.</li>
<li>
The server expects <code>multipart/form-data</code>, supported by <code>curl</code> but <b>not</b> by <code>wget</code>.
</li>

View file

@ -306,7 +306,6 @@ def make_test_data():
ad=ad,
expires=timezone.now() + datetime.timedelta(days=settings.INTERNET_DRAFT_DAYS_TO_EXPIRE),
notify="aliens@example.mars",
note="",
)
draft.set_state(State.objects.get(used=True, type="draft", slug="active"))

View file

@ -719,8 +719,11 @@ class IetfTestRunner(DiscoverRunner):
parser.add_argument('--validate-html-harder',
action='store_true', dest="validate_html_harder", default=False,
help='Validate all generated HTML with additional validators (slow)')
parser.add_argument('--rerun-until-failure',
action='store_true', dest='rerun', default=False,
help='Run the indicated tests in a loop until a failure occurs. ' )
def __init__(self, ignore_lower_coverage=False, skip_coverage=False, save_version_coverage=None, html_report=None, permit_mixed_migrations=None, show_logging=None, validate_html=None, validate_html_harder=None, **kwargs):
def __init__(self, ignore_lower_coverage=False, skip_coverage=False, save_version_coverage=None, html_report=None, permit_mixed_migrations=None, show_logging=None, validate_html=None, validate_html_harder=None, rerun=None, **kwargs):
#
self.ignore_lower_coverage = ignore_lower_coverage
self.check_coverage = not skip_coverage
@ -728,6 +731,8 @@ class IetfTestRunner(DiscoverRunner):
self.html_report = html_report
self.permit_mixed_migrations = permit_mixed_migrations
self.show_logging = show_logging
self.rerun = rerun
self.test_labels = None
global validation_settings
validation_settings["validate_html"] = self if validate_html else None
validation_settings["validate_html_harder"] = self if validate_html and validate_html_harder else None
@ -1108,6 +1113,13 @@ class IetfTestRunner(DiscoverRunner):
]
return tests
def run_suite(self, suite, **kwargs):
failures = super(IetfTestRunner, self).run_suite(suite, **kwargs)
while self.rerun and not failures.errors and not failures.failures:
suite = self.build_suite(self.test_labels)
failures = super(IetfTestRunner, self).run_suite(suite, **kwargs)
return failures
def run_tests(self, test_labels, extra_tests=None, **kwargs):
# Tests that involve switching back and forth between the real
# database and the test database are way too dangerous to run
@ -1129,6 +1141,7 @@ class IetfTestRunner(DiscoverRunner):
self.test_apps, self.test_paths = self.get_test_paths(test_labels)
self.test_labels = test_labels # these are used in our run_suite() and not available to it otherwise
failures = super(IetfTestRunner, self).run_tests(test_labels, extra_tests=extra_tests, **kwargs)
if self.check_coverage: