ci: Merge pull request #6868 from ietf-tools/main

ci: merge main to release
This commit is contained in:
Robert Sparks 2024-01-03 16:36:32 -06:00 committed by GitHub
commit 9e0d93716f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 326 additions and 73 deletions

View file

@ -33,23 +33,16 @@
"oderwat.indent-rainbow",
"redhat.vscode-yaml",
"spmeesseman.vscode-taskexplorer",
"visualstudioexptteam.vscodeintellicode"
"visualstudioexptteam.vscodeintellicode",
"ms-python.pylint"
],
"settings": {
"terminal.integrated.defaultProfile.linux": "zsh",
"python.pythonPath": "/usr/local/bin/python",
"python.languageServer": "Default",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint",
"python.testing.pytestArgs": [
"ietf"
],

View file

@ -245,7 +245,7 @@ async function main () {
name: `dt-app-${branch}`,
Hostname: `dt-app-${branch}`,
Env: [
`LETSENCRYPT_HOST=${hostname}`,
// `LETSENCRYPT_HOST=${hostname}`,
`VIRTUAL_HOST=${hostname}`,
`VIRTUAL_PORT=8000`,
`PGHOST=dt-db-${branch}`

View file

@ -38,8 +38,5 @@ echo "Running Datatracker checks..."
echo "Running Datatracker migrations..."
/usr/local/bin/python ./ietf/manage.py migrate --settings=settings_local
echo "Syncing with the rfc-index"
./ietf/bin/rfc-editor-index-updates -d 1969-01-01
echo "Starting Datatracker..."
./ietf/manage.py runserver 0.0.0.0:8000 --settings=settings_local

View file

@ -634,6 +634,9 @@ class DocumentInfo(models.Model):
)
except AssertionError:
pdf = None
except Exception as e:
log.log('weasyprint failed:'+str(e))
raise
if pdf:
cache.set(cache_key, pdf, settings.PDFIZER_CACHE_TIME)
return pdf
@ -649,7 +652,7 @@ class DocumentInfo(models.Model):
source__states__slug="active",
)
| models.Q(source__type__slug="rfc")
)
).distinct()
def referenced_by_rfcs(self):
"""Get refs to this doc from RFCs"""

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 -*-
@ -31,6 +31,8 @@ from django.utils.text import slugify
from tastypie.test import ResourceTestCaseMixin
from weasyprint.urls import URLFetchingError
import debug # pyflakes:ignore
from ietf.doc.models import ( Document, DocRelationshipName, RelatedDocument, State,
@ -40,7 +42,7 @@ from ietf.doc.factories import ( DocumentFactory, DocEventFactory, CharterFactor
ConflictReviewFactory, WgDraftFactory, IndividualDraftFactory, WgRfcFactory,
IndividualRfcFactory, StateDocEventFactory, BallotPositionDocEventFactory,
BallotDocEventFactory, DocumentAuthorFactory, NewRevisionDocEventFactory,
StatusChangeFactory, DocExtResourceFactory, RgDraftFactory)
StatusChangeFactory, DocExtResourceFactory, RgDraftFactory, BcpFactory)
from ietf.doc.forms import NotifyForm
from ietf.doc.fields import SearchableDocumentsField
from ietf.doc.utils import create_ballot_if_not_open, uppercase_std_abbreviated_name
@ -156,6 +158,23 @@ class SearchTests(TestCase):
self.assertEqual(r.status_code, 200)
self.assertContains(r, draft.title)
def test_search_became_rfc(self):
draft = WgDraftFactory()
rfc = WgRfcFactory()
draft.set_state(State.objects.get(type="draft", slug="rfc"))
draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc)
base_url = urlreverse('ietf.doc.views_search.search')
# find by RFC
r = self.client.get(base_url + f"?rfcs=on&name={rfc.name}")
self.assertEqual(r.status_code, 200)
self.assertContains(r, rfc.title)
# find by draft
r = self.client.get(base_url + f"?activedrafts=on&rfcs=on&name={draft.name}")
self.assertEqual(r.status_code, 200)
self.assertContains(r, rfc.title)
def test_search_for_name(self):
draft = WgDraftFactory(name='draft-ietf-mars-test',group=GroupFactory(acronym='mars',parent=Group.objects.get(acronym='farfut')),authors=[PersonFactory()],ad=PersonFactory())
draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="pub-req"))
@ -1948,6 +1967,12 @@ class DocTestCase(TestCase):
@override_settings(RFC_EDITOR_INFO_BASE_URL='https://www.rfc-editor.ietf.org/info/')
def test_document_bibtex(self):
for factory in [CharterFactory, BcpFactory, StatusChangeFactory, ConflictReviewFactory]: # Should be extended to all other doc types
doc = factory()
url = urlreverse("ietf.doc.views_doc.document_bibtex", kwargs=dict(name=doc.name))
r = self.client.get(url)
self.assertEqual(r.status_code, 404)
rfc = WgRfcFactory.create(
time=datetime.datetime(2010, 10, 10, tzinfo=ZoneInfo(settings.TIME_ZONE))
)
@ -2844,6 +2869,12 @@ class PdfizedTests(TestCase):
self.should_succeed(dict(name=draft.name,rev=f'{r:02d}',ext=ext))
self.should_404(dict(name=draft.name,rev='02'))
with mock.patch('ietf.doc.models.DocumentInfo.pdfized', side_effect=URLFetchingError):
url = urlreverse(self.view, kwargs=dict(name=rfc.name))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertContains(r, "Error while rendering PDF")
class NotifyValidationTests(TestCase):
def test_notify_validation(self):
valid_values = [

View file

@ -1483,6 +1483,42 @@ class SubmitToIesgTests(TestCase):
self.assertTrue("aread@" in outbox[-1]['To'])
self.assertTrue("iesg-secretary@" in outbox[-1]['Cc'])
def test_confirm_submission_no_doc_ad(self):
url = urlreverse('ietf.doc.views_draft.to_iesg', kwargs=dict(name=self.docname))
self.client.login(username="marschairman", password="marschairman+password")
doc = Document.objects.get(name=self.docname)
RoleFactory(name_id='ad', group=doc.group, person=doc.ad)
e = DocEvent(type="changed_document", by=doc.ad, doc=doc, rev=doc.rev, desc="Remove doc AD")
e.save()
doc.ad = None
doc.save_with_history([e])
docevents_pre = set(doc.docevent_set.all())
mailbox_before = len(outbox)
r = self.client.post(url, dict(confirm="1"))
self.assertEqual(r.status_code, 302)
doc = Document.objects.get(name=self.docname)
self.assertTrue(doc.get_state('draft-iesg').slug=='pub-req')
self.assertTrue(doc.get_state('draft-stream-ietf').slug=='sub-pub')
self.assertCountEqual(doc.action_holders.all(), [doc.ad])
new_docevents = set(doc.docevent_set.all()) - docevents_pre
self.assertEqual(len(new_docevents), 5)
new_docevent_type_count = Counter([e.type for e in new_docevents])
self.assertEqual(new_docevent_type_count['changed_state'],2)
self.assertEqual(new_docevent_type_count['started_iesg_process'],1)
self.assertEqual(new_docevent_type_count['changed_action_holders'], 1)
self.assertEqual(new_docevent_type_count['changed_document'], 1)
self.assertEqual(len(outbox), mailbox_before + 1)
self.assertTrue("Publication has been requested" in outbox[-1]['Subject'])
self.assertTrue("aread@" in outbox[-1]['To'])
self.assertTrue("iesg-secretary@" in outbox[-1]['Cc'])
class RequestPublicationTests(TestCase):

View file

@ -380,6 +380,25 @@ class ReviewTests(TestCase):
reviewer_label = q("option[value=\"{}\"]".format(reviewer_email.address)).text().lower()
self.assertIn("rejected review of document before", reviewer_label)
def test_assign_reviewer_after_withdraw(self):
doc = WgDraftFactory()
review_team = ReviewTeamFactory()
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',name_id='secr')
review_req = ReviewRequestFactory(team=review_team,doc=doc)
reviewer = rev_role.person.email_set.first()
ReviewAssignmentFactory(review_request=review_req, state_id='withdrawn', reviewer=reviewer)
req_url = urlreverse('ietf.doc.views_review.review_request', kwargs={ "name": doc.name, "request_id": review_req.pk })
assign_url = urlreverse('ietf.doc.views_review.assign_reviewer', kwargs={ "name": doc.name, "request_id": review_req.pk })
login_testing_unauthorized(self, "reviewsecretary", assign_url)
r = self.client.post(assign_url, { "action": "assign", "reviewer": reviewer.pk })
self.assertRedirects(r, req_url)
review_req = reload_db_objects(review_req)
assignment = review_req.reviewassignment_set.last()
self.assertEqual(assignment.state, ReviewAssignmentStateName.objects.get(slug='assigned'))
self.assertEqual(review_req.state, ReviewRequestStateName.objects.get(slug='assigned'))
def test_previously_reviewed_replaced_doc(self):
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',person__name='Some Reviewer',name_id='reviewer')

View file

@ -51,7 +51,6 @@ from django.conf import settings
from django import forms
from django.contrib.staticfiles import finders
import debug # pyflakes:ignore
from ietf.doc.models import ( Document, DocHistory, DocEvent, BallotDocEvent, BallotType,
@ -1064,7 +1063,10 @@ def document_pdfized(request, name, rev=None, ext=None):
if not os.path.exists(doc.get_file_name()):
raise Http404("File not found: %s" % doc.get_file_name())
pdf = doc.pdfized()
try:
pdf = doc.pdfized()
except Exception:
return render(request, "doc/weasyprint_failed.html")
if pdf:
return HttpResponse(pdf,content_type='application/pdf')
else:
@ -1262,6 +1264,9 @@ def document_bibtex(request, name, rev=None):
doc = get_object_or_404(Document, name=name)
if doc.type_id not in ["rfc", "draft"]:
raise Http404()
doi = None
draft_became_rfc = None
replaced_by = None
@ -2185,13 +2190,31 @@ def idnits2_state(request, name, rev=None):
if doc.type_id == "rfc":
draft = doc.came_from_draft()
if draft:
zero_revision = NewRevisionDocEvent.objects.filter(doc=draft,rev='00').first()
zero_revision = NewRevisionDocEvent.objects.filter(
doc=draft, rev="00"
).first()
else:
zero_revision = NewRevisionDocEvent.objects.filter(doc=doc,rev='00').first()
zero_revision = NewRevisionDocEvent.objects.filter(doc=doc, rev="00").first()
if zero_revision:
doc.created = zero_revision.time
else:
doc.created = doc.docevent_set.order_by('-time').first().time
if doc.type_id == "draft":
if doc.became_rfc():
interesting_event = (
doc.became_rfc()
.docevent_set.filter(type="published_rfc")
.order_by("-time")
.first()
)
else:
interesting_event = doc.docevent_set.order_by(
"-time"
).first() # Is taking the most _recent_ instead of the oldest event correct?
else: # doc.type_id == "rfc"
interesting_event = (
doc.docevent_set.filter(type="published_rfc").order_by("-time").first()
)
doc.created = interesting_event.time
if doc.std_level:
doc.deststatus = doc.std_level.name
elif doc.intended_std_level:
@ -2199,8 +2222,16 @@ def idnits2_state(request, name, rev=None):
else:
text = doc.text()
if text:
parsed_draft = PlaintextDraft(text=doc.text(), source=name, name_from_source=False)
parsed_draft = PlaintextDraft(
text=doc.text(), source=name, name_from_source=False
)
doc.deststatus = parsed_draft.get_status()
else:
doc.deststatus="Unknown"
return render(request, 'doc/idnits2-state.txt', context={'doc':doc}, content_type='text/plain;charset=utf-8')
doc.deststatus = "Unknown"
return render(
request,
"doc/idnits2-state.txt",
context={"doc": doc},
content_type="text/plain;charset=utf-8",
)

View file

@ -560,22 +560,19 @@ def to_iesg(request,name):
if request.method == 'POST':
if request.POST.get("confirm", ""):
by = request.user.person
events = []
changes = []
def doc_event(type, by, doc, desc):
return DocEvent.objects.create(type=type, by=by, doc=doc, rev=doc.rev, desc=desc)
if doc.get_state_slug("draft-iesg") == "idexists":
e = DocEvent()
e.type = "started_iesg_process"
e.by = by
e.doc = doc
e.rev = doc.rev
e.desc = "Document is now in IESG state <b>%s</b>" % target_state['iesg'].name
e.save()
events.append(e)
events.append(doc_event("started_iesg_process", by, doc, f"Document is now in IESG state <b>{target_state['iesg'].name}</b>"))
# do this first, so AD becomes action holder
if not doc.ad == ad :
doc.ad = ad
events.append(doc_event("changed_document", by, doc, f"Responsible AD changed to {doc.ad}"))
for state_type in ['draft-iesg','draft-stream-ietf']:
prev_state=doc.get_state(state_type)
@ -587,25 +584,14 @@ def to_iesg(request,name):
events.append(e)
events.append(add_state_change_event(doc=doc,by=by,prev_state=prev_state,new_state=new_state))
if not doc.ad == ad :
doc.ad = ad
changes.append("Responsible AD changed to %s" % doc.ad)
if not doc.notify == notify :
doc.notify = notify
changes.append("State Change Notice email list changed to %s" % doc.notify)
events.append(doc_event("changed_document", by, doc, f"State Change Notice email list changed to {doc.notify}"))
# Get the last available writeup
previous_writeup = doc.latest_event(WriteupDocEvent,type="changed_protocol_writeup")
if previous_writeup != None:
changes.append(previous_writeup.text)
for c in changes:
e = DocEvent(doc=doc, rev=doc.rev, by=by)
e.desc = c
e.type = "changed_document"
e.save()
events.append(e)
events.append(doc_event("changed_document", by, doc, previous_writeup.text))
doc.save_with_history(events)

View file

@ -1,5 +1,7 @@
# Copyright The IETF Trust 2013-2023, All Rights Reserved
import debug # pyflakes: ignore
from django.shortcuts import render, get_object_or_404
from django.http import Http404
@ -18,6 +20,7 @@ def state_help(request, type=None):
"draft-stream-irtf": ("draft-stream-irtf", "IRTF Stream States for Internet-Drafts"),
"draft-stream-ise": ("draft-stream-ise", "ISE Stream States for Internet-Drafts"),
"draft-stream-iab": ("draft-stream-iab", "IAB Stream States for Internet-Drafts"),
"draft-stream-editorial": ("draft-stream-editorial", "Editorial Stream States for Internet-Drafts"),
"charter": ("charter", "Charter States"),
"conflict-review": ("conflrev", "Conflict Review States"),
"status-change": ("statchg", "RFC Status Change States"),

View file

@ -211,6 +211,9 @@ def retrieve_search_results(form, all_types=False):
Q(targets_related__source__title__icontains=singlespace, targets_related__relationship_id="contains"),
])
if query["rfcs"]:
queries.extend([Q(targets_related__source__name__icontains=look_for, targets_related__relationship_id="became_rfc")])
combined_query = reduce(operator.or_, queries)
docs = docs.filter(combined_query).distinct()
@ -468,11 +471,11 @@ def ad_workload(request):
state = doc_state(doc)
state_events = doc.docevent_set.filter(
Q(type="started_iesg_process")
| Q(type="changed_state")
| Q(type="published_rfc")
| Q(type="closed_ballot"),
).order_by("-time")
type__in=["started_iesg_process", "changed_state", "closed_ballot"]
)
if doc.became_rfc():
state_events = state_events | doc.became_rfc().docevent_set.filter(type="published_rfc")
state_events = state_events.order_by("-time")
# compute state history for drafts
last = now

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2009-2020, All Rights Reserved
# Copyright The IETF Trust 2009-2023, All Rights Reserved
# -*- coding: utf-8 -*-
import datetime
import io
@ -6106,21 +6106,21 @@ class MaterialsTests(TestCase):
test_file = BytesIO(b'this is some text for a test')
test_file.name = "not_really.json"
r = self.client.post(url,dict(file=test_file))
r = self.client.post(url,dict(submission_method="upload",file=test_file))
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(q('form .is-invalid'))
test_file = BytesIO(b'this is some text for a test'*1510000)
test_file.name = "not_really.pdf"
r = self.client.post(url,dict(file=test_file))
r = self.client.post(url,dict(submission_method="upload",file=test_file))
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(q('form .is-invalid'))
test_file = BytesIO(b'<html><frameset><frame src="foo.html"></frame><frame src="bar.html"></frame></frameset></html>')
test_file.name = "not_really.html"
r = self.client.post(url,dict(file=test_file))
r = self.client.post(url,dict(submission_method="upload",file=test_file))
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(q('form .is-invalid'))
@ -6128,7 +6128,7 @@ class MaterialsTests(TestCase):
# Test html sanitization
test_file = BytesIO(b'<html><head><title>Title</title></head><body><h1>Title</h1><section>Some text</section></body></html>')
test_file.name = "some.html"
r = self.client.post(url,dict(file=test_file))
r = self.client.post(url,dict(submission_method="upload",file=test_file))
self.assertEqual(r.status_code, 302)
doc = session.sessionpresentation_set.filter(document__type_id=doctype).first().document
self.assertEqual(doc.rev,'00')
@ -6140,7 +6140,7 @@ class MaterialsTests(TestCase):
# txt upload
test_file = BytesIO(b'This is some text for a test, with the word\nvirtual at the beginning of a line.')
test_file.name = "some.txt"
r = self.client.post(url,dict(file=test_file,apply_to_all=False))
r = self.client.post(url,dict(submission_method="upload",file=test_file,apply_to_all=False))
self.assertEqual(r.status_code, 302)
doc = session.sessionpresentation_set.filter(document__type_id=doctype).first().document
self.assertEqual(doc.rev,'01')
@ -6152,7 +6152,7 @@ class MaterialsTests(TestCase):
self.assertIn('Revise', str(q("Title")))
test_file = BytesIO(b'this is some different text for a test')
test_file.name = "also_some.txt"
r = self.client.post(url,dict(file=test_file,apply_to_all=True))
r = self.client.post(url,dict(submission_method="upload",file=test_file,apply_to_all=True))
self.assertEqual(r.status_code, 302)
doc = Document.objects.get(pk=doc.pk)
self.assertEqual(doc.rev,'02')
@ -6161,7 +6161,7 @@ class MaterialsTests(TestCase):
# Test bad encoding
test_file = BytesIO('<html><h1>Title</h1><section>Some\x93text</section></html>'.encode('latin1'))
test_file.name = "some.html"
r = self.client.post(url,dict(file=test_file))
r = self.client.post(url,dict(submission_method="upload",file=test_file))
self.assertContains(r, 'Could not identify the file encoding')
doc = Document.objects.get(pk=doc.pk)
self.assertEqual(doc.rev,'02')
@ -6191,7 +6191,7 @@ class MaterialsTests(TestCase):
test_file = BytesIO(b'this is some text for a test')
test_file.name = "not_really.txt"
r = self.client.post(url,dict(file=test_file,apply_to_all=False))
r = self.client.post(url,dict(submission_method="upload",file=test_file,apply_to_all=False))
self.assertEqual(r.status_code, 410)
@override_settings(MEETING_MATERIALS_SERVE_LOCALLY=True)
@ -6211,7 +6211,7 @@ class MaterialsTests(TestCase):
self.assertFalse(session.sessionpresentation_set.filter(document__type_id=doctype))
test_file = BytesIO(b'this is some text for a test')
test_file.name = "not_really.txt"
r = self.client.post(url,dict(file=test_file))
r = self.client.post(url,dict(submission_method="upload",file=test_file))
self.assertEqual(r.status_code, 302)
doc = session.sessionpresentation_set.filter(document__type_id=doctype).first().document
self.assertEqual(doc.rev,'00')
@ -6223,6 +6223,46 @@ class MaterialsTests(TestCase):
self.requests_mock.get(f'{session.notes_url()}/info', text=json.dumps({'title': 'title', 'updatetime': '2021-12-01T17:11:00z'}))
self.crawl_materials(url=url, top=top)
def test_enter_agenda(self):
session = SessionFactory(meeting__type_id='ietf')
url = urlreverse('ietf.meeting.views.upload_session_agenda',kwargs={'num':session.meeting.number,'session_id':session.id})
redirect_url = urlreverse('ietf.meeting.views.session_details', kwargs={'num':session.meeting.number,'acronym':session.group.acronym})
login_testing_unauthorized(self,"secretary",url)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertIn('Upload', str(q("Title")))
self.assertFalse(session.sessionpresentation_set.exists())
test_text = 'Enter agenda from scratch'
r = self.client.post(url,dict(submission_method="enter",content=test_text))
self.assertRedirects(r, redirect_url)
doc = session.sessionpresentation_set.filter(document__type_id='agenda').first().document
self.assertEqual(doc.rev,'00')
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertIn('Revise', str(q("Title")))
test_file = BytesIO(b'Upload after enter')
test_file.name = "some.txt"
r = self.client.post(url,dict(submission_method="upload",file=test_file))
self.assertRedirects(r, redirect_url)
doc = Document.objects.get(pk=doc.pk)
self.assertEqual(doc.rev,'01')
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertIn('Revise', str(q("Title")))
test_text = 'Enter after upload'
r = self.client.post(url,dict(submission_method="enter",content=test_text))
self.assertRedirects(r, redirect_url)
doc = Document.objects.get(pk=doc.pk)
self.assertEqual(doc.rev,'02')
def test_upload_slides(self):
session1 = SessionFactory(meeting__type_id='ietf')

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2007-2022, All Rights Reserved
# Copyright The IETF Trust 2007-2023, All Rights Reserved
# -*- coding: utf-8 -*-
@ -2662,6 +2662,40 @@ def upload_session_minutes(request, session_id, num):
})
class UploadOrEnterAgendaForm(UploadAgendaForm):
ACTIONS = [
("upload", "Upload agenda"),
("enter", "Enter agenda"),
]
submission_method = forms.ChoiceField(choices=ACTIONS, widget=forms.RadioSelect)
content = forms.CharField(widget=forms.Textarea, required=False, strip=False, label="Agenda text")
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["file"].required=False
self.order_fields(["submission_method", "file", "content"])
def clean_content(self):
return self.cleaned_data["content"].replace("\r", "")
def clean_file(self):
submission_method = self.cleaned_data.get("submission_method")
if submission_method == "upload":
return super().clean_file()
return None
def clean(self):
def require_field(f):
if not self.cleaned_data.get(f):
self.add_error(f, ValidationError("You must fill in this field."))
submission_method = self.cleaned_data.get("submission_method")
if submission_method == "upload":
require_field("file")
elif submission_method == "enter":
require_field("content")
def upload_session_agenda(request, session_id, num):
# num is redundant, but we're dragging it along an artifact of where we are in the current URL structure
session = get_object_or_404(Session,pk=session_id)
@ -2680,10 +2714,23 @@ def upload_session_agenda(request, session_id, num):
agenda_sp = session.sessionpresentation_set.filter(document__type='agenda').first()
if request.method == 'POST':
form = UploadAgendaForm(show_apply_to_all_checkbox,request.POST,request.FILES)
form = UploadOrEnterAgendaForm(show_apply_to_all_checkbox,request.POST,request.FILES)
if form.is_valid():
file = request.FILES['file']
_, ext = os.path.splitext(file.name)
submission_method = form.cleaned_data['submission_method']
if submission_method == "upload":
file = request.FILES['file']
_, ext = os.path.splitext(file.name)
else:
if agenda_sp:
doc = agenda_sp.document
_, ext = os.path.splitext(doc.uploaded_filename)
else:
ext = ".md"
fd, name = tempfile.mkstemp(suffix=ext, text=True)
os.close(fd)
with open(name, "w") as file:
file.write(form.cleaned_data['content'])
file = open(name, "rb")
apply_to_all = session.type.slug == 'regular'
if show_apply_to_all_checkbox:
apply_to_all = form.cleaned_data['apply_to_all']
@ -2738,7 +2785,11 @@ def upload_session_agenda(request, session_id, num):
doc.uploaded_filename = filename
e = NewRevisionDocEvent.objects.create(doc=doc,by=request.user.person,type='new_revision',desc='New revision available: %s'%doc.rev,rev=doc.rev)
# The way this function builds the filename it will never trigger the file delete in handle_file_upload.
save_error = handle_upload_file(file, filename, session.meeting, 'agenda', request=request, encoding=form.file_encoding[file.name])
try:
encoding=form.file_encoding[file.name]
except AttributeError:
encoding=None
save_error = handle_upload_file(file, filename, session.meeting, 'agenda', request=request, encoding=encoding)
if save_error:
form.add_error(None, save_error)
else:
@ -2746,7 +2797,11 @@ def upload_session_agenda(request, session_id, num):
messages.success(request, f'Successfully uploaded agenda as revision {doc.rev}.')
return redirect('ietf.meeting.views.session_details',num=num,acronym=session.group.acronym)
else:
form = UploadAgendaForm(show_apply_to_all_checkbox, initial={'apply_to_all':session.type_id=='regular'})
initial={'apply_to_all':session.type_id=='regular', 'submission_method':'upload'}
if agenda_sp:
doc = agenda_sp.document
initial['content'] = doc.text()
form = UploadOrEnterAgendaForm(show_apply_to_all_checkbox, initial=initial)
return render(request, "meeting/upload_session_agenda.html",
{'session': session,

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2016-2020, All Rights Reserved
# Copyright The IETF Trust 2016-2023, All Rights Reserved
# -*- coding: utf-8 -*-
@ -392,7 +392,9 @@ def assign_review_request_to_reviewer(request, review_req, reviewer, add_skip=Fa
# cannot reference reviewassignment_set relation until pk exists
if review_req.pk is not None:
reviewassignment_set = review_req.reviewassignment_set.filter(reviewer=reviewer)
if reviewassignment_set.exists() and not reviewassignment_set.filter(state_id='rejected').exists():
if (reviewassignment_set.exists() and not
(reviewassignment_set.filter(state_id='rejected').exists() or
reviewassignment_set.filter(state_id='withdrawn').exists())):
return
# Note that assigning a review no longer unassigns other reviews

View file

@ -0,0 +1,28 @@
$(document)
.ready(function () {
var form = $("form.my-3");
// review submission selection
form.find("[name=submission_method]")
.on("click change", function () {
var val = form.find("[name=submission_method]:checked")
.val();
var shouldBeVisible = {
upload: ['[name="file"]'],
enter: ['[name="content"]']
};
for (var v in shouldBeVisible) {
for (var i in shouldBeVisible[v]) {
var selector = shouldBeVisible[v][i];
var row = form.find(selector).parent();
if ($.inArray(selector, shouldBeVisible[val]) != -1)
row.show();
else
row.hide();
}
}
})
.trigger("change");
});

View file

@ -0,0 +1,17 @@
{# Copyright The IETF Trust 2023, All Rights Reserved #}
{% extends "base.html" %}
{% load origin %}
{% block title %}Error while rendering PDF{% endblock %}
{% block content %}
{% origin %}
<h1>Error while rendering PDF</h1>
<p>
An error was encountered while trying to render your document as PDF.
In case this was a temporary error, you may want to try again in a
little while.
</p>
<p>
A failure report with details about what happened has been sent to the
server administrators.
</p>
{% endblock %}

View file

@ -16,6 +16,11 @@
{% endwith %}
{% else %}
<div class="badge rounded-pill text-bg-secondary">{{ s.current_status_name }}</div>
{% if s.current_status == "canceled" %}
{% with timeslot=s.official_timeslotassignment.timeslot %}
<span class="text-decoration-line-through text-secondary session-time date me-3" data-start-utc="{{ timeslot.time|utc|date:'Y-m-d' }}" data-end-utc="{{ timeslot.end_time|utc|date:'Y-m-d' }}"></span>
{% endwith %}
{% endif %}
{% endif %}
{% if show_request and s.meeting.type_id == 'ietf' %}
{% if can_edit %}

View file

@ -1,5 +1,5 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{# Copyright The IETF Trust 2015-2023, All Rights Reserved #}
{% load origin static django_bootstrap5 tz %}
{% block title %}
{% if agenda_sp %}
@ -29,6 +29,9 @@
<form enctype="multipart/form-data" method="post" class="my-3">
{% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="btn btn-primary">Upload</button>
<button type="submit" class="btn btn-primary">Save</button>
</form>
{% endblock %}
{% block js %}
<script src="{% static 'ietf/js/upload-session-agenda.js' %}"></script>
{% endblock %}

View file

@ -152,6 +152,7 @@
"ietf/static/js/timezone.js",
"ietf/static/js/upcoming.js",
"ietf/static/js/upload-material.js",
"ietf/static/js/upload-session-agenda.js",
"ietf/static/js/upload_bofreq.js",
"ietf/static/js/upload_statement.js",
"ietf/static/js/zxcvbn.js"