Skip to content

Commit

Permalink
Merge branch 'main' into feat/rpc-api
Browse files Browse the repository at this point in the history
  • Loading branch information
rjsparks committed Oct 11, 2024
2 parents ac66c85 + 83f5fc8 commit a7a7caf
Show file tree
Hide file tree
Showing 38 changed files with 807 additions and 763 deletions.
25 changes: 16 additions & 9 deletions client/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,24 @@ const appContainer = ref(null)
// Set user theme
// --------------------------------------------------------------------
const desiredTheme = window.localStorage?.getItem('theme')
if (desiredTheme === 'dark') {
siteStore.theme = 'dark'
} else if (desiredTheme === 'light') {
siteStore.theme = 'light'
} else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
siteStore.theme = 'dark'
} else {
siteStore.theme = 'light'
function updateTheme() {
const desiredTheme = window.localStorage?.getItem('theme')
if (desiredTheme === 'dark') {
siteStore.theme = 'dark'
} else if (desiredTheme === 'light') {
siteStore.theme = 'light'
} else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
siteStore.theme = 'dark'
} else {
siteStore.theme = 'light'
}
}
updateTheme()
// this change event fires for either light or dark changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateTheme)
// --------------------------------------------------------------------
// Handle browser resize
// --------------------------------------------------------------------
Expand Down
9 changes: 9 additions & 0 deletions docker/base.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@ ENV LC_ALL en_US.UTF-8
ADD https://raw.githubusercontent.com/ietf-tools/idnits-mirror/main/idnits /usr/local/bin/
RUN chmod +rx /usr/local/bin/idnits

# Install required fonts
RUN mkdir -p /tmp/fonts && \
wget -q -O /tmp/fonts.tar.gz https://github.com/ietf-tools/xml2rfc-fonts/archive/refs/tags/3.22.0.tar.gz && \
tar zxf /tmp/fonts.tar.gz -C /tmp/fonts && \
mv /tmp/fonts/*/noto/* /usr/local/share/fonts/ && \
mv /tmp/fonts/*/roboto_mono/* /usr/local/share/fonts/ && \
rm -rf /tmp/fonts.tar.gz /tmp/fonts/ && \
fc-cache -f

# Turn off rsyslog kernel logging (doesn't work in Docker)
RUN sed -i '/imklog/s/^/#/' /etc/rsyslog.conf

Expand Down
2 changes: 1 addition & 1 deletion ietf/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
# Email alias listing
url(r'^person/email/$', api_views.active_email_list),
# Draft submission API
url(r'^submit/?$', submit_views.api_submit),
url(r'^submit/?$', submit_views.api_submit_tombstone),
# Draft upload API
url(r'^submission/?$', submit_views.api_submission),
# Draft submission state API
Expand Down
4 changes: 2 additions & 2 deletions ietf/doc/expire.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ def expirable_drafts(queryset=None):

# Populate this first time through (but after django has been set up)
if nonexpirable_states is None:
# all IESG states except I-D Exists, AD Watching, and Dead block expiry
nonexpirable_states = list(State.objects.filter(used=True, type="draft-iesg").exclude(slug__in=("idexists","watching", "dead")))
# all IESG states except I-D Exists and Dead block expiry
nonexpirable_states = list(State.objects.filter(used=True, type="draft-iesg").exclude(slug__in=("idexists", "dead")))
# sent to RFC Editor and RFC Published block expiry (the latter
# shouldn't be possible for an active draft, though)
nonexpirable_states += list(State.objects.filter(used=True, type__in=("draft-stream-iab", "draft-stream-irtf", "draft-stream-ise"), slug__in=("rfc-edit", "pub")))
Expand Down
121 changes: 121 additions & 0 deletions ietf/doc/migrations/0024_remove_ad_is_watching_states.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Copyright The IETF Trust 2024, All Rights Reserved

from django.db import migrations


def get_helper(DocHistory, RelatedDocument, RelatedDocHistory, DocumentAuthor, DocHistoryAuthor):
"""Dependency injection wrapper"""

def save_document_in_history(doc):
"""Save a snapshot of document and related objects in the database.
Local copy of ietf.doc.utils.save_document_in_history() to avoid depending on the
code base in a migration.
"""

def get_model_fields_as_dict(obj):
return dict((field.name, getattr(obj, field.name))
for field in obj._meta.fields
if field is not obj._meta.pk)

# copy fields
fields = get_model_fields_as_dict(doc)
fields["doc"] = doc
fields["name"] = doc.name

dochist = DocHistory(**fields)
dochist.save()

# copy many to many
for field in doc._meta.many_to_many:
if field.remote_field.through and field.remote_field.through._meta.auto_created:
hist_field = getattr(dochist, field.name)
hist_field.clear()
hist_field.set(getattr(doc, field.name).all())

# copy remaining tricky many to many
def transfer_fields(obj, HistModel):
mfields = get_model_fields_as_dict(item)
# map doc -> dochist
for k, v in mfields.items():
if v == doc:
mfields[k] = dochist
HistModel.objects.create(**mfields)

for item in RelatedDocument.objects.filter(source=doc):
transfer_fields(item, RelatedDocHistory)

for item in DocumentAuthor.objects.filter(document=doc):
transfer_fields(item, DocHistoryAuthor)

return dochist

return save_document_in_history


def forward(apps, schema_editor):
"""Mark watching draft-iesg state unused after removing it from Documents"""
StateDocEvent = apps.get_model("doc", "StateDocEvent")
Document = apps.get_model("doc", "Document")
State = apps.get_model("doc", "State")
StateType = apps.get_model("doc", "StateType")
Person = apps.get_model("person", "Person")

save_document_in_history = get_helper(
DocHistory=apps.get_model("doc", "DocHistory"),
RelatedDocument=apps.get_model("doc", "RelatedDocument"),
RelatedDocHistory=apps.get_model("doc", "RelatedDocHistory"),
DocumentAuthor=apps.get_model("doc", "DocumentAuthor"),
DocHistoryAuthor=apps.get_model("doc", "DocHistoryAuthor"),
)

draft_iesg_state_type = StateType.objects.get(slug="draft-iesg")
idexists_state = State.objects.get(type=draft_iesg_state_type, slug="idexists")
watching_state = State.objects.get(type=draft_iesg_state_type, slug="watching")
system_person = Person.objects.get(name="(System)")

# Remove state from documents that currently have it
for doc in Document.objects.filter(states=watching_state):
assert doc.type_id == "draft"
doc.states.remove(watching_state)
doc.states.add(idexists_state)
e = StateDocEvent.objects.create(
type="changed_state",
by=system_person,
doc=doc,
rev=doc.rev,
desc=f"{draft_iesg_state_type.label} changed to <b>{idexists_state.name}</b> from {watching_state.name}",
state_type=draft_iesg_state_type,
state=idexists_state,
)
doc.time = e.time
doc.save()
save_document_in_history(doc)
assert not Document.objects.filter(states=watching_state).exists()

# Mark state as unused
watching_state.used = False
watching_state.save()


def reverse(apps, schema_editor):
"""Mark watching draft-iesg state as used
Does not try to re-apply the state to Documents modified by the forward migration. This
could be done in theory, but would either require dangerous history rewriting or add a
lot of history junk.
"""
State = apps.get_model("doc", "State")
StateType = apps.get_model("doc", "StateType")
State.objects.filter(
type=StateType.objects.get(slug="draft-iesg"), slug="watching"
).update(used=True)


class Migration(migrations.Migration):

dependencies = [
("doc", "0023_bofreqspamstate"),
]

operations = [migrations.RunPython(forward, reverse)]
2 changes: 1 addition & 1 deletion ietf/doc/templatetags/ballot_icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def state_age_colored(doc):
if not iesg_state:
return ""

if iesg_state in ["dead", "watching", "pub", "idexists"]:
if iesg_state in ["dead", "pub", "idexists"]:
return ""
try:
state_datetime = (
Expand Down
23 changes: 22 additions & 1 deletion ietf/doc/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
from ietf.meeting.factories import ( MeetingFactory, SessionFactory, SessionPresentationFactory,
ProceedingsMaterialFactory )

from ietf.name.models import SessionStatusName, BallotPositionName, DocTypeName
from ietf.name.models import SessionStatusName, BallotPositionName, DocTypeName, RoleName
from ietf.person.models import Person
from ietf.person.factories import PersonFactory, EmailFactory
from ietf.utils.mail import outbox, empty_outbox
Expand Down Expand Up @@ -1450,6 +1450,14 @@ def test_document_draft_action_holders_buttons(self, mock_method):
"""Buttons for action holders should be shown when AD or secretary"""
draft = WgDraftFactory()
draft.action_holders.set([PersonFactory()])
other_group = GroupFactory(type_id=draft.group.type_id)

# create a test RoleName and put it in the docman_roles for the document group
RoleName.objects.create(slug="wrangler", name="Wrangler", used=True)
draft.group.features.docman_roles.append("wrangler")
draft.group.features.save()
wrangler = RoleFactory(group=draft.group, name_id="wrangler").person
wrangler_of_other_group = RoleFactory(group=other_group, name_id="wrangler").person

url = urlreverse('ietf.doc.views_doc.document_main', kwargs=dict(name=draft.name))
edit_ah_url = urlreverse('ietf.doc.views_doc.edit_action_holders', kwargs=dict(name=draft.name))
Expand Down Expand Up @@ -1482,6 +1490,8 @@ def _run_test(username=None, expect_buttons=False):

_run_test(None, False)
_run_test('plain', False)
_run_test(wrangler_of_other_group.user.username, False)
_run_test(wrangler.user.username, True)
_run_test('ad', True)
_run_test('secretary', True)

Expand Down Expand Up @@ -1683,6 +1693,17 @@ def test_document_material(self):

r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=doc.name)))
self.assertEqual(r.status_code, 200)
self.assertNotContains(r, "The session for this document was cancelled.")

SchedulingEvent.objects.create(
session=session,
status_id='canceled',
by = Person.objects.get(user__username="marschairman"),
)

r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=doc.name)))
self.assertEqual(r.status_code, 200)
self.assertContains(r, "The session for this document was cancelled.")

def test_document_ballot(self):
doc = IndividualDraftFactory()
Expand Down
2 changes: 1 addition & 1 deletion ietf/doc/tests_ballot.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ def test_issue_ballot_warn_if_early(self):
q = PyQuery(r.content)
self.assertFalse(q('[class=text-danger]:contains("not completed IETF Last Call")'))

for state_slug in ["lc", "watching", "ad-eval"]:
for state_slug in ["lc", "ad-eval"]:
draft.set_state(State.objects.get(type="draft-iesg",slug=state_slug))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
Expand Down
Loading

0 comments on commit a7a7caf

Please sign in to comment.