diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index afb5951ac..8f88c7c22 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -47,6 +47,10 @@ on:
required: true
type: boolean
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
jobs:
# -----------------------------------------------------------------
# PREPARE
diff --git a/README.md b/README.md
index af059f972..133d08f5e 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
[](#prerequisites)
[](#prerequisites)
[](#prerequisites)
-[](#prerequisites)
+[](#prerequisites)
##### The day-to-day front-end to the IETF database for people who work on IETF standards.
diff --git a/ietf/api/views.py b/ietf/api/views.py
index 6aaed4b6a..62857bff5 100644
--- a/ietf/api/views.py
+++ b/ietf/api/views.py
@@ -429,6 +429,7 @@ def directauth(request):
data = None
if raw_data is None or data is None:
+ log.log("Request body is either missing or invalid")
return HttpResponse(json.dumps(dict(result="failure",reason="invalid post")), content_type='application/json')
authtoken = data.get('authtoken', None)
@@ -436,9 +437,11 @@ def directauth(request):
password = data.get('password', None)
if any([item is None for item in (authtoken, username, password)]):
+ log.log("One or more mandatory fields are missing: authtoken, username, password")
return HttpResponse(json.dumps(dict(result="failure",reason="invalid post")), content_type='application/json')
if not is_valid_token("ietf.api.views.directauth", authtoken):
+ log.log("Auth token provided is invalid")
return HttpResponse(json.dumps(dict(result="failure",reason="invalid authtoken")), content_type='application/json')
user_query = User.objects.filter(username__iexact=username)
@@ -449,18 +452,20 @@ def directauth(request):
# Note well that we are using user.username, not what was passed to the API.
- if user_query.count() == 1 and authenticate(username = user_query.first().username, password = password):
+ user_count = user_query.count()
+ if user_count == 1 and authenticate(username = user_query.first().username, password = password):
user = user_query.get()
if user_query.filter(person__isnull=True).count() == 1: # Can't inspect user.person direclty here
- log.log(f"Direct auth of personless user {user.pk}:{user.username}")
+ log.log(f"Direct auth success (personless user): {user.pk}:{user.username}")
else:
- log.log(f"Direct auth: {user.pk}:{user.person.plain_name()}")
+ log.log(f"Direct auth success: {user.pk}:{user.person.plain_name()}")
return HttpResponse(json.dumps(dict(result="success")), content_type='application/json')
- log.log(f"Direct auth failure: {username}")
+ log.log(f"Direct auth failure: {username} ({user_count} user(s) found)")
return HttpResponse(json.dumps(dict(result="failure", reason="authentication failed")), content_type='application/json')
else:
+ log.log(f"Request must be POST: {request.method} received")
return HttpResponse(status=405)
diff --git a/ietf/doc/admin.py b/ietf/doc/admin.py
index 3ad4bee2a..301d32d7c 100644
--- a/ietf/doc/admin.py
+++ b/ietf/doc/admin.py
@@ -142,6 +142,13 @@ admin.site.register(DocumentActionHolder, DocumentActionHolderAdmin)
# events
+class DeletedEventAdmin(admin.ModelAdmin):
+ list_display = ['id', 'content_type', 'json', 'by', 'time']
+ list_filter = ['time']
+ raw_id_fields = ['content_type', 'by']
+admin.site.register(DeletedEvent, DeletedEventAdmin)
+
+
class DocEventAdmin(admin.ModelAdmin):
def event_type(self, obj):
return str(obj.type)
@@ -159,39 +166,42 @@ admin.site.register(NewRevisionDocEvent, DocEventAdmin)
admin.site.register(StateDocEvent, DocEventAdmin)
admin.site.register(ConsensusDocEvent, DocEventAdmin)
admin.site.register(BallotDocEvent, DocEventAdmin)
+admin.site.register(IRSGBallotDocEvent, DocEventAdmin)
admin.site.register(WriteupDocEvent, DocEventAdmin)
admin.site.register(LastCallDocEvent, DocEventAdmin)
admin.site.register(TelechatDocEvent, DocEventAdmin)
-admin.site.register(ReviewRequestDocEvent, DocEventAdmin)
-admin.site.register(ReviewAssignmentDocEvent, DocEventAdmin)
admin.site.register(InitialReviewDocEvent, DocEventAdmin)
-admin.site.register(AddedMessageEvent, DocEventAdmin)
-admin.site.register(SubmissionDocEvent, DocEventAdmin)
admin.site.register(EditedAuthorsDocEvent, DocEventAdmin)
admin.site.register(IanaExpertDocEvent, DocEventAdmin)
-class DeletedEventAdmin(admin.ModelAdmin):
- list_display = ['id', 'content_type', 'json', 'by', 'time']
- list_filter = ['time']
- raw_id_fields = ['content_type', 'by']
-admin.site.register(DeletedEvent, DeletedEventAdmin)
-
class BallotPositionDocEventAdmin(DocEventAdmin):
- raw_id_fields = ["doc", "by", "balloter", "ballot"]
+ raw_id_fields = DocEventAdmin.raw_id_fields + ["balloter", "ballot"]
admin.site.register(BallotPositionDocEvent, BallotPositionDocEventAdmin)
-
-class IRSGBallotDocEventAdmin(DocEventAdmin):
- raw_id_fields = ["doc", "by"]
-admin.site.register(IRSGBallotDocEvent, IRSGBallotDocEventAdmin)
class BofreqEditorDocEventAdmin(DocEventAdmin):
- raw_id_fields = ["doc", "by", "editors" ]
+ raw_id_fields = DocEventAdmin.raw_id_fields + ["editors"]
admin.site.register(BofreqEditorDocEvent, BofreqEditorDocEventAdmin)
class BofreqResponsibleDocEventAdmin(DocEventAdmin):
- raw_id_fields = ["doc", "by", "responsible" ]
+ raw_id_fields = DocEventAdmin.raw_id_fields + ["responsible"]
admin.site.register(BofreqResponsibleDocEvent, BofreqResponsibleDocEventAdmin)
+class ReviewRequestDocEventAdmin(DocEventAdmin):
+ raw_id_fields = DocEventAdmin.raw_id_fields + ["review_request"]
+admin.site.register(ReviewRequestDocEvent, ReviewRequestDocEventAdmin)
+
+class ReviewAssignmentDocEventAdmin(DocEventAdmin):
+ raw_id_fields = DocEventAdmin.raw_id_fields + ["review_assignment"]
+admin.site.register(ReviewAssignmentDocEvent, ReviewAssignmentDocEventAdmin)
+
+class AddedMessageEventAdmin(DocEventAdmin):
+ raw_id_fields = DocEventAdmin.raw_id_fields + ["message"]
+admin.site.register(AddedMessageEvent, AddedMessageEventAdmin)
+
+class SubmissionDocEventAdmin(DocEventAdmin):
+ raw_id_fields = DocEventAdmin.raw_id_fields + ["submission"]
+admin.site.register(SubmissionDocEvent, SubmissionDocEventAdmin)
+
class DocumentUrlAdmin(admin.ModelAdmin):
list_display = ['id', 'doc', 'tag', 'url', 'desc', ]
search_fields = ['doc__name', 'url', ]
diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py
index 42898d209..dfef40e55 100644
--- a/ietf/doc/views_doc.py
+++ b/ietf/doc/views_doc.py
@@ -265,6 +265,8 @@ def document_main(request, name, rev=None, document_html=False):
can_change_stream = bool(can_edit or roles)
file_urls, found_types = build_file_urls(doc)
+ if not request.user.is_authenticated:
+ file_urls = [fu for fu in file_urls if fu[0] != "pdfized"]
content = doc.text_or_error() # pyflakes:ignore
content = markup_txt.markup(maybe_split(content, split=split_content))
@@ -406,6 +408,8 @@ def document_main(request, name, rev=None, document_html=False):
latest_revision = None
file_urls, found_types = build_file_urls(doc)
+ if not request.user.is_authenticated:
+ file_urls = [fu for fu in file_urls if fu[0] != "pdfized"]
content = doc.text_or_error() # pyflakes:ignore
content = markup_txt.markup(maybe_split(content, split=split_content))
@@ -1039,6 +1043,8 @@ def document_html(request, name, rev=None):
document_html=True,
)
+
+@login_required
def document_pdfized(request, name, rev=None, ext=None):
found = fuzzy_find_documents(name, rev)
diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py
index 7211a6bc0..4579316f2 100644
--- a/ietf/iesg/tests.py
+++ b/ietf/iesg/tests.py
@@ -52,6 +52,15 @@ class IESGTests(TestCase):
self.assertContains(r, draft.name)
self.assertContains(r, escape(pos.balloter.plain_name()))
+ # Mark draft as replaced
+ draft.set_state(State.objects.get(type="draft", slug="repl"))
+
+ r = self.client.get(urlreverse("ietf.iesg.views.discusses"))
+ self.assertEqual(r.status_code, 200)
+
+ self.assertNotContains(r, draft.name)
+ self.assertNotContains(r, escape(pos.balloter.plain_name()))
+
def test_milestones_needing_review(self):
draft = WgDraftFactory()
RoleFactory(name_id='ad',group=draft.group,person=Person.objects.get(user__username='ad'))
diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py
index a219a6b5d..b67ef04a0 100644
--- a/ietf/iesg/views.py
+++ b/ietf/iesg/views.py
@@ -483,6 +483,7 @@ def discusses(request):
models.Q(states__type__in=("statchg", "conflrev"),
states__slug__in=("iesgeval", "defer")),
docevent__ballotpositiondocevent__pos__blocking=True)
+ possible_docs = possible_docs.exclude(states__in=State.objects.filter(type="draft", slug="repl"))
possible_docs = possible_docs.select_related("stream", "group", "ad").distinct()
docs = []
diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py
index d783ed9c7..db62fe620 100644
--- a/ietf/meeting/tests_views.py
+++ b/ietf/meeting/tests_views.py
@@ -6246,6 +6246,12 @@ class MaterialsTests(TestCase):
q = PyQuery(r.content)
self.assertTrue(q('form input[type="checkbox"]'))
+ # test not submitting a file
+ r = self.client.post(url, dict(submission_method="upload"))
+ 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')
test_file.name = "not_really.json"
r = self.client.post(url,dict(submission_method="upload",file=test_file))
diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py
index 2948a2e71..253f2852f 100644
--- a/ietf/meeting/views.py
+++ b/ietf/meeting/views.py
@@ -2790,7 +2790,8 @@ class UploadOrEnterAgendaForm(UploadAgendaForm):
def clean_file(self):
submission_method = self.cleaned_data.get("submission_method")
if submission_method == "upload":
- return super().clean_file()
+ if self.cleaned_data.get("file", None) is not None:
+ return super().clean_file()
return None
def clean(self):
diff --git a/ietf/name/fixtures/names.json b/ietf/name/fixtures/names.json
index 913c6c987..3eb2c38d6 100644
--- a/ietf/name/fixtures/names.json
+++ b/ietf/name/fixtures/names.json
@@ -3464,7 +3464,7 @@
"parent_types": [],
"req_subm_approval": true,
"role_order": "[\n \"chair\",\n \"delegate\"\n]",
- "session_purposes": "[\n \"officehours\"\n]",
+ "session_purposes": "[\n \"officehours\",\n \"regular\"\n]",
"show_on_agenda": true
},
"model": "group.groupfeatures",
diff --git a/ietf/person/urls.py b/ietf/person/urls.py
index f37d8b46c..867646fe3 100644
--- a/ietf/person/urls.py
+++ b/ietf/person/urls.py
@@ -2,7 +2,7 @@ from ietf.person import views, ajax
from ietf.utils.urls import url
urlpatterns = [
- url(r'^merge/$', views.merge),
+ url(r'^merge/?$', views.merge),
url(r'^search/(?P
If you enter the review below, the review will be sent - to {% for addr in to %}{{ addr|linkify }}{% if not forloop.last %}, {% endif %}{% endfor %}{% if review_cc %}, with a CC to {% for addr in cc %}{{ addr|linkify }}{% if not forloop.last %}, {% endif %}{% endfor %}{% endif %}. + to {% for addr in review_to %}{{ addr|linkify }}{% if not forloop.last %}, {% endif %}{% endfor %}{% if review_cc %}, with a CC to {% for addr in review_cc %}{{ addr|linkify }}{% if not forloop.last %}, {% endif %}{% endfor %}{% endif %}.
{% elif assignment %}diff --git a/ietf/templates/group/concluded_groups.html b/ietf/templates/group/concluded_groups.html index c748c2061..725e8bd3c 100644 --- a/ietf/templates/group/concluded_groups.html +++ b/ietf/templates/group/concluded_groups.html @@ -40,8 +40,8 @@