From d97a56f64b9bb8c66319edf28336872bcb3102b7 Mon Sep 17 00:00:00 2001 From: Jazzzny <75343012+Jazzzny@users.noreply.github.com> Date: Sat, 26 Oct 2024 13:55:56 -0400 Subject: [PATCH 1/8] Small changes for support for legacy browsers - Remove prefers-color-scheme from light stylesheet - Add fallback for addEventListener --- django_ace/static/django_ace/widget.js | 12 +++++++++--- templates/base.html | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/django_ace/static/django_ace/widget.js b/django_ace/static/django_ace/widget.js index 7da6f32436..de517577fa 100644 --- a/django_ace/static/django_ace/widget.js +++ b/django_ace/static/django_ace/widget.js @@ -111,9 +111,15 @@ } setEditorTheme(window.matchMedia('(prefers-color-scheme: dark)').matches); - window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(ev) { - setEditorTheme(ev.matches); - }) + try { + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(ev) { + setEditorTheme(ev.matches); + }) + } catch (err) { + window.matchMedia('(prefers-color-scheme: dark)').addListener(function(ev) { + setEditorTheme(ev.matches); + }) + } } } if (wordwrap == "true") { diff --git a/templates/base.html b/templates/base.html index c866ab3568..113868bd30 100644 --- a/templates/base.html +++ b/templates/base.html @@ -49,7 +49,7 @@ {% if PREFERRED_STYLE_CSS is not none %} {% else %} - + {% endif %} {% else %} From 743bf09e146dffe61cb762f58a439731d6064bfd Mon Sep 17 00:00:00 2001 From: kiritofeng Date: Mon, 16 Dec 2024 23:14:07 -0500 Subject: [PATCH 2/8] ci: Update Python to 3.8 --- .github/workflows/build.yml | 8 ++++---- .github/workflows/compilemessages.yml | 4 ++-- .github/workflows/makemessages.yml | 4 ++-- .github/workflows/updatemessages.yml | 4 ++-- judge/admin/submission.py | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 56333b47a6..40afce4232 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,10 +5,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up Python 3.7 + - name: Set up Python 3.8 uses: actions/setup-python@v5 with: - python-version: '3.7' + python-version: '3.8' - name: Install flake8 run: pip install flake8 flake8-import-order flake8-future-import flake8-commas flake8-logging-format flake8-quotes - name: Lint with flake8 @@ -19,10 +19,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up Python 3.7 + - name: Set up Python 3.8 uses: actions/setup-python@v5 with: - python-version: '3.7' + python-version: '3.8' - name: Cache pip uses: actions/cache@v4 with: diff --git a/.github/workflows/compilemessages.yml b/.github/workflows/compilemessages.yml index 106b353fbe..677208cfd9 100644 --- a/.github/workflows/compilemessages.yml +++ b/.github/workflows/compilemessages.yml @@ -11,10 +11,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up Python 3.7 + - name: Set up Python 3.8 uses: actions/setup-python@v5 with: - python-version: '3.7' + python-version: '3.8' - name: Checkout submodules run: | git submodule init diff --git a/.github/workflows/makemessages.yml b/.github/workflows/makemessages.yml index c5bd6aef41..f32a608ce1 100644 --- a/.github/workflows/makemessages.yml +++ b/.github/workflows/makemessages.yml @@ -8,10 +8,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up Python 3.7 + - name: Set up Python 3.8 uses: actions/setup-python@v5 with: - python-version: '3.7' + python-version: '3.8' - name: Checkout submodules run: | git submodule init diff --git a/.github/workflows/updatemessages.yml b/.github/workflows/updatemessages.yml index 1c56045a8c..9360835ea0 100644 --- a/.github/workflows/updatemessages.yml +++ b/.github/workflows/updatemessages.yml @@ -10,10 +10,10 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Set up Python 3.7 + - name: Set up Python 3.8 uses: actions/setup-python@v5 with: - python-version: '3.7' + python-version: '3.8' - name: Checkout submodules run: | git submodule init diff --git a/judge/admin/submission.py b/judge/admin/submission.py index 1fb5a27eb4..fc897a7f24 100644 --- a/judge/admin/submission.py +++ b/judge/admin/submission.py @@ -82,7 +82,7 @@ def formfield_for_dbfield(self, db_field, **kwargs): contest__problems=submission.problem) \ .only('id', 'contest__name', 'virtual') - def label(obj): + def label(obj): # noqa: F811 if obj.spectate: return gettext('%s (spectating)') % obj.contest.name if obj.virtual: @@ -92,7 +92,7 @@ def label(obj): kwargs['queryset'] = ContestProblem.objects.filter(problem=submission.problem) \ .only('id', 'problem__name', 'contest__name') - def label(obj): + def label(obj): # noqa: F811 return pgettext('contest problem', '%(problem)s in %(contest)s') % { 'problem': obj.problem.name, 'contest': obj.contest.name, } From 2f92af58926d84871104dfeaa755eb41bb570ec5 Mon Sep 17 00:00:00 2001 From: kiritofeng Date: Wed, 18 Dec 2024 02:12:08 +0000 Subject: [PATCH 3/8] Remove `contest_object` from submissions whose problem is removed from contest; fixes #2349 --- judge/signals.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/judge/signals.py b/judge/signals.py index ce4c5d4612..c54554b46b 100644 --- a/judge/signals.py +++ b/judge/signals.py @@ -9,8 +9,8 @@ from django.dispatch import receiver from .caching import finished_submission -from .models import BlogPost, Comment, Contest, ContestSubmission, EFFECTIVE_MATH_ENGINES, Judge, Language, License, \ - MiscConfig, Organization, Problem, Profile, Submission, WebAuthnCredential +from .models import BlogPost, Comment, Contest, ContestProblem, ContestSubmission, EFFECTIVE_MATH_ENGINES, Judge, \ + Language, License, MiscConfig, Organization, Problem, Profile, Submission, WebAuthnCredential def get_pdf_path(basename: str) -> Optional[str]: @@ -79,6 +79,13 @@ def contest_update(sender, instance, **kwargs): for engine in EFFECTIVE_MATH_ENGINES]) +@receiver(post_delete, sender=ContestProblem) +def contest_problem_delete(sender, instance, **kwargs): + # `contest_object` is the `Contest` object indirectly associated with the `Submission` object + # `contest` is the `ContestSubmission` object associated with the `Submission` object + Submission.objects.filter(contest_object=instance.contest, contest__isnull=True).update(contest_object=None) + + @receiver(post_save, sender=License) def license_update(sender, instance, **kwargs): cache.delete(make_template_fragment_key('license_html', (instance.id,))) From 80d7dcb5cfa07f599eb890e3c77564fd40a6142d Mon Sep 17 00:00:00 2001 From: Keenan Gugeler Date: Thu, 29 Aug 2024 10:08:16 -0400 Subject: [PATCH 4/8] models/contest: clarify rate_all behaviour Rate all is confusingly named, since it really means "rate even users who don't submit". In my opinion, this should have been the default, but instead we can simply fix the description. --- judge/migrations/0148_clarify_rate_all_desc.py | 16 ++++++++++++++++ judge/models/contest.py | 4 +++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 judge/migrations/0148_clarify_rate_all_desc.py diff --git a/judge/migrations/0148_clarify_rate_all_desc.py b/judge/migrations/0148_clarify_rate_all_desc.py new file mode 100644 index 0000000000..110bcfb0a9 --- /dev/null +++ b/judge/migrations/0148_clarify_rate_all_desc.py @@ -0,0 +1,16 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('judge', '0147_judge_add_tiers'), + ] + + operations = [ + migrations.AlterField( + model_name='contest', + name='rate_all', + field=models.BooleanField(default=False, help_text='Rate users even if they make no submissions.', verbose_name='rate all'), + ), + ] diff --git a/judge/models/contest.py b/judge/models/contest.py index 37f2010a47..3c752ef696 100644 --- a/judge/models/contest.py +++ b/judge/models/contest.py @@ -114,7 +114,9 @@ class Contest(models.Model): rating_ceiling = models.IntegerField(verbose_name=_('rating ceiling'), help_text=_('Do not rate users who have a higher rating.'), null=True, blank=True) - rate_all = models.BooleanField(verbose_name=_('rate all'), help_text=_('Rate all users who joined.'), default=False) + rate_all = models.BooleanField(verbose_name=_('rate all'), + help_text=_('Rate users even if they make no submissions.'), + default=False) rate_exclude = models.ManyToManyField(Profile, verbose_name=_('exclude from ratings'), blank=True, related_name='rate_exclude+') is_private = models.BooleanField(verbose_name=_('private to specific users'), default=False) From 4db9ef233be59f84cd8b807fd8dd8501cdd89ab0 Mon Sep 17 00:00:00 2001 From: kiritofeng Date: Wed, 18 Dec 2024 04:34:35 +0000 Subject: [PATCH 5/8] admin: make organization join request `state` read-only; closes #2354 --- judge/admin/organization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/judge/admin/organization.py b/judge/admin/organization.py index ff5fab5f0b..0224423b37 100644 --- a/judge/admin/organization.py +++ b/judge/admin/organization.py @@ -105,7 +105,7 @@ def has_change_permission(self, request, obj=None): class OrganizationRequestAdmin(admin.ModelAdmin): list_display = ('username', 'organization', 'state', 'time') - readonly_fields = ('user', 'organization', 'request_class') + readonly_fields = ('user', 'organization', 'state', 'request_class') @admin.display(description=_('username'), ordering='user__user__username') def username(self, obj): From dd8f5f06793a7f22e52414d043a6c94528c4e45c Mon Sep 17 00:00:00 2001 From: kiritofeng Date: Fri, 6 Dec 2024 00:51:58 -0500 Subject: [PATCH 6/8] admin: Add `create_private_problem` perm --- judge/admin/problem.py | 21 ++++++++++++++++--- ...rganization_private_problems_permission.py | 17 +++++++++++++++ judge/models/problem.py | 1 + 3 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 judge/migrations/0149_add_organization_private_problems_permission.py diff --git a/judge/admin/problem.py b/judge/admin/problem.py index c78c0981f6..1ca41ba5a0 100644 --- a/judge/admin/problem.py +++ b/judge/admin/problem.py @@ -2,6 +2,7 @@ from django import forms from django.contrib import admin +from django.core.exceptions import PermissionDenied from django.db import transaction from django.forms import ModelForm from django.urls import reverse, reverse_lazy @@ -150,7 +151,8 @@ class ProblemAdmin(NoBatchDeleteMixin, VersionAdmin): def get_actions(self, request): actions = super(ProblemAdmin, self).get_actions(request) - if request.user.has_perm('judge.change_public_visibility'): + if request.user.has_perm('judge.change_public_visibility') or \ + request.user.has_perm('judge.create_private_problem'): func, name, desc = self.get_action('make_public') actions[name] = (func, name, desc) @@ -164,8 +166,10 @@ def get_actions(self, request): def get_readonly_fields(self, request, obj=None): fields = self.readonly_fields - if not request.user.has_perm('judge.change_public_visibility'): - fields += ('is_public',) + if not request.user.has_perm('judge.create_private_problem'): + fields += ('organizations',) + if not request.user.has_perm('judge.change_public_visibility'): + fields += ('is_public',) if not request.user.has_perm('judge.change_manually_managed'): fields += ('is_manually_managed',) if not request.user.has_perm('judge.problem_full_markup'): @@ -195,6 +199,8 @@ def update_publish_date(self, request, queryset): @admin.display(description=_('Mark problems as public')) def make_public(self, request, queryset): + if not request.user.has_perm('judge.change_public_visibility'): + queryset = queryset.filter(is_organization_private=True) count = queryset.update(is_public=True) for problem_id in queryset.values_list('id', flat=True): self._rescore(request, problem_id) @@ -204,6 +210,8 @@ def make_public(self, request, queryset): @admin.display(description=_('Mark problems as private')) def make_private(self, request, queryset): + if not request.user.has_perm('judge.change_public_visibility'): + queryset = queryset.filter(is_organization_private=True) count = queryset.update(is_public=False) for problem_id in queryset.values_list('id', flat=True): self._rescore(request, problem_id) @@ -233,6 +241,13 @@ def save_model(self, request, obj, form, change): # `organizations` will not appear in `cleaned_data` if user cannot edit it if form.changed_data and 'organizations' in form.changed_data: obj.is_organization_private = bool(form.cleaned_data['organizations']) + + if form.cleaned_data.get('is_public') and not request.user.has_perm('judge.change_public_visibility'): + if not obj.is_organization_private: + raise PermissionDenied + if not request.user.has_perm('judge.create_private_problem'): + raise PermissionDenied + super(ProblemAdmin, self).save_model(request, obj, form, change) if ( form.changed_data and diff --git a/judge/migrations/0149_add_organization_private_problems_permission.py b/judge/migrations/0149_add_organization_private_problems_permission.py new file mode 100644 index 0000000000..226840f7af --- /dev/null +++ b/judge/migrations/0149_add_organization_private_problems_permission.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.25 on 2024-12-17 03:55 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('judge', '0148_clarify_rate_all_desc'), + ] + + operations = [ + migrations.AlterModelOptions( + name='problem', + options={'permissions': (('see_private_problem', 'See hidden problems'), ('edit_own_problem', 'Edit own problems'), ('edit_all_problem', 'Edit all problems'), ('edit_public_problem', 'Edit all public problems'), ('problem_full_markup', 'Edit problems with full markup'), ('clone_problem', 'Clone problem'), ('change_public_visibility', 'Change is_public field'), ('change_manually_managed', 'Change is_manually_managed field'), ('see_organization_problem', 'See organization-private problems'), ('create_private_problem', 'Create private problems')), 'verbose_name': 'problem', 'verbose_name_plural': 'problems'}, + ), + ] diff --git a/judge/models/problem.py b/judge/models/problem.py index aa436724fb..50f30f5cdb 100644 --- a/judge/models/problem.py +++ b/judge/models/problem.py @@ -501,6 +501,7 @@ class Meta: ('change_public_visibility', _('Change is_public field')), ('change_manually_managed', _('Change is_manually_managed field')), ('see_organization_problem', _('See organization-private problems')), + ('create_private_problem', _('Create private problems')), ) verbose_name = _('problem') verbose_name_plural = _('problems') From 8f90a83b5ce623f0fbcd21098887d8498c2ce117 Mon Sep 17 00:00:00 2001 From: Quantum Date: Wed, 18 Dec 2024 02:47:59 -0500 Subject: [PATCH 7/8] Use Python 3.11 on CI This is the version used in Debian bookworm, which we are running in production. --- .github/workflows/build.yml | 8 ++++---- .github/workflows/compilemessages.yml | 4 ++-- .github/workflows/makemessages.yml | 4 ++-- .github/workflows/updatemessages.yml | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 40afce4232..9c6286efaa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,10 +5,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up Python 3.8 + - name: Set up Python 3.11 uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.11' - name: Install flake8 run: pip install flake8 flake8-import-order flake8-future-import flake8-commas flake8-logging-format flake8-quotes - name: Lint with flake8 @@ -19,10 +19,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up Python 3.8 + - name: Set up Python 3.11 uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.11' - name: Cache pip uses: actions/cache@v4 with: diff --git a/.github/workflows/compilemessages.yml b/.github/workflows/compilemessages.yml index 677208cfd9..790d29bbfe 100644 --- a/.github/workflows/compilemessages.yml +++ b/.github/workflows/compilemessages.yml @@ -11,10 +11,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up Python 3.8 + - name: Set up Python 3.11 uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.11' - name: Checkout submodules run: | git submodule init diff --git a/.github/workflows/makemessages.yml b/.github/workflows/makemessages.yml index f32a608ce1..fd44a8bc3c 100644 --- a/.github/workflows/makemessages.yml +++ b/.github/workflows/makemessages.yml @@ -8,10 +8,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up Python 3.8 + - name: Set up Python 3.11 uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.11' - name: Checkout submodules run: | git submodule init diff --git a/.github/workflows/updatemessages.yml b/.github/workflows/updatemessages.yml index 9360835ea0..78ce42cb6f 100644 --- a/.github/workflows/updatemessages.yml +++ b/.github/workflows/updatemessages.yml @@ -10,10 +10,10 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Set up Python 3.8 + - name: Set up Python 3.11 uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.11' - name: Checkout submodules run: | git submodule init From 2d2bb65620e711ca8a16b2ba7e37e33a323431c3 Mon Sep 17 00:00:00 2001 From: int-y1 Date: Sun, 22 Dec 2024 23:16:47 -0500 Subject: [PATCH 8/8] Sort languages by name in /status/ --- judge/models/runtime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/judge/models/runtime.py b/judge/models/runtime.py index dda6a029d7..16046f149c 100644 --- a/judge/models/runtime.py +++ b/judge/models/runtime.py @@ -163,7 +163,7 @@ def toggle_disabled(self): def runtime_versions(cls): qs = (RuntimeVersion.objects.filter(judge__online=True) .values('judge__name', 'language__key', 'language__name', 'version', 'name') - .order_by('language__key', 'priority')) + .order_by('language__name', 'priority')) ret = defaultdict(OrderedDict)