diff --git a/api/urls_v2.py b/api/urls_v2.py index d5664db2c..70cdabe14 100644 --- a/api/urls_v2.py +++ b/api/urls_v2.py @@ -1,7 +1,7 @@ import copy from django.conf import settings -from django.conf.urls import include +from django.urls import include from django.urls import path, re_path from rest_framework_extensions.routers import ExtendedDefaultRouter @@ -110,7 +110,7 @@ def __init__(self): basename='submission-files') urlpatterns = [ - re_path(r'^', include((api.urls, 'api'), namespace='api')), + path('', include((api.urls, 'api'), namespace='api')), re_path(r"^get-token", authorization.api.views.RemoteAuthenticationView.as_view(), name="get-token"), re_path(r'^me', userprofile.api.views.MeDetail.as_view()), diff --git a/aplus/urls.py b/aplus/urls.py index b31392dbb..39379d761 100644 --- a/aplus/urls.py +++ b/aplus/urls.py @@ -1,7 +1,7 @@ from django.conf import settings -from django.conf.urls import include from django.contrib import admin from django.contrib.sitemaps.views import sitemap +from django.urls import include from django.urls import path, re_path from . import views @@ -37,22 +37,22 @@ # Pay attention to the order the URL patterns will be matched! urlpatterns = [ re_path(r'^admin/', admin.site.urls), - re_path(r'^', include(shibboleth_login.urls)), - re_path('', include(social_django.urls, namespace='social')), + path('', include(shibboleth_login.urls)), + path('', include(social_django.urls, namespace='social')), re_path(r'^api/v(?P(2))/', include(api.urls_v2)), # why version in url? doc/api_versioning.md - re_path(r'^accounts/', include(userprofile.urls)), - re_path(r'^diploma/', include(diploma.urls)), - re_path(r'^', include(redirect_old_urls.urls)), - re_path(r'^', include(apps.urls)), - re_path(r'^', include(news.urls)), - re_path(r'^', include(external_services.urls)), - re_path(r'^', include(course.long_urls)), - re_path(r'^', include(deviations.urls)), - re_path(r'^', include(edit_course.urls)), - re_path(r'^', include(notification.urls)), - re_path(r'^', include(lti_tool.urls)), - re_path(r'^', include(exercise.urls)), - re_path(r'^', include(course.urls)), + path('accounts/', include(userprofile.urls)), + path('diploma/', include(diploma.urls)), + path('', include(redirect_old_urls.urls)), + path('', include(apps.urls)), + path('', include(news.urls)), + path('', include(external_services.urls)), + path('', include(course.long_urls)), + path('', include(deviations.urls)), + path('', include(edit_course.urls)), + path('', include(notification.urls)), + path('', include(lti_tool.urls)), + path('', include(exercise.urls)), + path('', include(course.urls)), path('sitemap.xml', sitemap, { 'sitemaps': all_sitemaps }, name='django.contrib.sitemaps.views.sitemap'), ] diff --git a/apps/admin.py b/apps/admin.py index 8e045a3dd..f7e7a65b1 100644 --- a/apps/admin.py +++ b/apps/admin.py @@ -12,6 +12,7 @@ ) +@admin.register(BaseTab) class BaseTabAdmin(admin.ModelAdmin): search_fields = ( 'label', @@ -19,6 +20,7 @@ class BaseTabAdmin(admin.ModelAdmin): ) +@admin.register(HTMLTab) class HTMLTabAdmin(admin.ModelAdmin): search_fields = ( 'label', @@ -26,6 +28,7 @@ class HTMLTabAdmin(admin.ModelAdmin): ) +@admin.register(ExternalEmbeddedTab) class ExternalEmbeddedTabAdmin(admin.ModelAdmin): search_fields = ( 'label', @@ -34,6 +37,7 @@ class ExternalEmbeddedTabAdmin(admin.ModelAdmin): ) +@admin.register(ExternalIFrameTab) class ExternalIFrameTabAdmin(admin.ModelAdmin): search_fields = ( 'label', @@ -42,10 +46,12 @@ class ExternalIFrameTabAdmin(admin.ModelAdmin): ) +@admin.register(BasePlugin) class BasePluginAdmin(admin.ModelAdmin): search_fields = ('title',) +@admin.register(RSSPlugin) class RSSPluginAdmin(admin.ModelAdmin): search_fields = ( 'title', @@ -53,6 +59,7 @@ class RSSPluginAdmin(admin.ModelAdmin): ) +@admin.register(HTMLPlugin) class HTMLPluginAdmin(admin.ModelAdmin): search_fields = ('title',) list_display_links = ('title',) @@ -67,18 +74,9 @@ def course_instance_id(self, obj): return obj.container_pk +@admin.register(ExternalIFramePlugin) class ExternalIFramePluginAdmin(admin.ModelAdmin): search_fields = ( 'title', 'service_url', ) - - -admin.site.register(BaseTab, BaseTabAdmin) -admin.site.register(HTMLTab, HTMLTabAdmin) -admin.site.register(ExternalEmbeddedTab, ExternalEmbeddedTabAdmin) -admin.site.register(ExternalIFrameTab, ExternalIFrameTabAdmin) -admin.site.register(BasePlugin, BasePluginAdmin) -admin.site.register(RSSPlugin, RSSPluginAdmin) -admin.site.register(HTMLPlugin, HTMLPluginAdmin) -admin.site.register(ExternalIFramePlugin, ExternalIFramePluginAdmin) diff --git a/course/admin.py b/course/admin.py index ffdc8f773..5e6512dd6 100644 --- a/course/admin.py +++ b/course/admin.py @@ -26,6 +26,7 @@ def instance_url(instance): instance_url.short_description = _('URL') +@admin.register(Course) class CourseAdmin(admin.ModelAdmin): search_fields = ( 'name', @@ -44,6 +45,7 @@ class CourseAdmin(admin.ModelAdmin): ) +@admin.register(CourseInstance) class CourseInstanceAdmin(admin.ModelAdmin): search_fields = ( 'instance_name', @@ -74,6 +76,7 @@ def get_queryset(self, request): return CourseInstance.objects.get_teaching(request.user.userprofile) +@admin.register(Enrollment) class EnrollmentAdmin(admin.ModelAdmin): search_fields = ( 'course_instance__instance_name', @@ -104,6 +107,7 @@ class EnrollmentAdmin(admin.ModelAdmin): readonly_fields = ('timestamp',) +@admin.register(CourseModule) class CourseModuleAdmin(admin.ModelAdmin): search_fields = ( 'name', @@ -127,6 +131,7 @@ class CourseModuleAdmin(admin.ModelAdmin): raw_id_fields = ('course_instance',) +@admin.register(LearningObjectCategory) class LearningObjectCategoryAdmin(admin.ModelAdmin): search_fields = ( 'name', @@ -149,6 +154,7 @@ class LearningObjectCategoryAdmin(admin.ModelAdmin): raw_id_fields = ('course_instance',) +@admin.register(StudentGroup) class StudentGroupAdmin(admin.ModelAdmin): search_fields = ( 'course_instance__instance_name', @@ -182,6 +188,7 @@ def members_string(self, obj): ) +@admin.register(UserTag) class UserTagAdmin(admin.ModelAdmin): search_fields = ( 'name', @@ -207,6 +214,7 @@ class UserTagAdmin(admin.ModelAdmin): ) +@admin.register(UserTagging) class UserTaggingAdmin(admin.ModelAdmin): search_fields = ( 'tag__name', @@ -238,6 +246,7 @@ class UserTaggingAdmin(admin.ModelAdmin): ) +@admin.register(SubmissionTag) class SubmissionTagAdmin(admin.ModelAdmin): search_fields = ( 'name', @@ -264,6 +273,7 @@ class SubmissionTagAdmin(admin.ModelAdmin): ) +@admin.register(CourseHook) class CourseHookAdmin(admin.ModelAdmin): search_fields = ( 'hook_url', @@ -272,15 +282,3 @@ class CourseHookAdmin(admin.ModelAdmin): 'course_instance__course__code', ) raw_id_fields = ('course_instance',) - - -admin.site.register(Course, CourseAdmin) -admin.site.register(CourseInstance, CourseInstanceAdmin) -admin.site.register(Enrollment, EnrollmentAdmin) -admin.site.register(StudentGroup, StudentGroupAdmin) -admin.site.register(CourseHook, CourseHookAdmin) -admin.site.register(CourseModule, CourseModuleAdmin) -admin.site.register(LearningObjectCategory, LearningObjectCategoryAdmin) -admin.site.register(UserTag, UserTagAdmin) -admin.site.register(UserTagging, UserTaggingAdmin) -admin.site.register(SubmissionTag, SubmissionTagAdmin) diff --git a/course/urls.py b/course/urls.py index 98c4cb1d6..917bd4578 100644 --- a/course/urls.py +++ b/course/urls.py @@ -1,3 +1,4 @@ +from django.urls import path from django.urls import re_path from . import views @@ -10,10 +11,10 @@ MODULE_URL_PREFIX = INSTANCE_URL_PREFIX + r'(?P[\w\d\-\.]+)/' urlpatterns = [ - re_path(r'^$', + path('', views.HomeView.as_view(), name='home'), - re_path(r'^archive/$', + path('archive/', views.ArchiveView.as_view(), name="archive"), re_path(COURSE_URL_PREFIX + r'instances/$', diff --git a/course/views.py b/course/views.py index 5e7e10c98..246502fad 100644 --- a/course/views.py +++ b/course/views.py @@ -356,7 +356,7 @@ def post(self, request, *args, **kwargs): not url_has_allowed_host_and_scheme(url=next, allowed_hosts={request.get_host()}, require_https=request.is_secure())): - next = remove_query_param_from_url(request.META.get('HTTP_REFERER'), 'hl') + next = remove_query_param_from_url(request.headers.get('referer'), 'hl') next = next and unquote(next) # HTTP_REFERER may be encoded. if not url_has_allowed_host_and_scheme(url=next, allowed_hosts={request.get_host()}, diff --git a/deviations/admin.py b/deviations/admin.py index 72bc4cc47..eca44ccd1 100644 --- a/deviations/admin.py +++ b/deviations/admin.py @@ -8,6 +8,7 @@ class DeviationRecentCourseInstanceListFilter(RecentCourseInstanceListFilter): course_instance_query = 'exercise__course_module__course_instance' +@admin.register(DeadlineRuleDeviation) class DeadlineRuleDeviationAdmin(admin.ModelAdmin): search_fields = ( 'submitter__student_id', @@ -35,6 +36,7 @@ class DeadlineRuleDeviationAdmin(admin.ModelAdmin): readonly_fields = ('grant_time',) +@admin.register(MaxSubmissionsRuleDeviation) class MaxSubmissionsRuleDeviationAdmin(admin.ModelAdmin): search_fields = ( 'submitter__student_id', @@ -60,7 +62,3 @@ class MaxSubmissionsRuleDeviationAdmin(admin.ModelAdmin): 'granter', ) readonly_fields = ('grant_time',) - - -admin.site.register(DeadlineRuleDeviation, DeadlineRuleDeviationAdmin) -admin.site.register(MaxSubmissionsRuleDeviation, MaxSubmissionsRuleDeviationAdmin) diff --git a/diploma/admin.py b/diploma/admin.py index 07dc78af2..28888a6a9 100644 --- a/diploma/admin.py +++ b/diploma/admin.py @@ -3,6 +3,7 @@ from .models import CourseDiplomaDesign, StudentDiploma +@admin.register(CourseDiplomaDesign) class CourseDiplomaDesignAdmin(admin.ModelAdmin): search_fields = ( 'course__instance_name', @@ -30,6 +31,7 @@ class CourseDiplomaDesignAdmin(admin.ModelAdmin): ) +@admin.register(StudentDiploma) class StudentDiplomaAdmin(admin.ModelAdmin): search_fields = ( 'name', @@ -59,7 +61,3 @@ class StudentDiplomaAdmin(admin.ModelAdmin): 'profile', ) readonly_fields = ('created',) - - -admin.site.register(CourseDiplomaDesign, CourseDiplomaDesignAdmin) -admin.site.register(StudentDiploma, StudentDiplomaAdmin) diff --git a/diploma/urls.py b/diploma/urls.py index ede501c40..82275b160 100644 --- a/diploma/urls.py +++ b/diploma/urls.py @@ -1,13 +1,14 @@ +from django.urls import path from django.urls import re_path from . import views urlpatterns = [ - re_path(r'list/(?P\d+)/$', + path('list//', views.DiplomaListView.as_view(), name="diploma-list"), - re_path(r'create/(?P\d+)/(?P\d+)/$', + path('create///', views.DiplomaCreateView.as_view(), name="diploma-create"), re_path(r'(?P[a-f0-9]{32})/$', diff --git a/exercise/admin.py b/exercise/admin.py index ebb3e2939..72c22d3c7 100644 --- a/exercise/admin.py +++ b/exercise/admin.py @@ -98,6 +98,7 @@ class SubmittedFileRecentCourseInstanceListFilter(RecentCourseInstanceListFilter course_instance_query = 'submission__exercise__course_module__course_instance' +@admin.register(CourseChapter) class CourseChapterAdmin(admin.ModelAdmin): search_fields = ( 'name', @@ -126,6 +127,7 @@ class CourseChapterAdmin(admin.ModelAdmin): ) +@admin.register(BaseExercise) class BaseExerciseAdmin(admin.ModelAdmin): search_fields = ( 'name', @@ -157,6 +159,7 @@ class BaseExerciseAdmin(admin.ModelAdmin): ) +@admin.register(Submission) class SubmissionAdmin(admin.ModelAdmin): list_display_links = ('id',) list_display = ( @@ -197,6 +200,7 @@ def get_queryset(self, request): return super().get_queryset(request).prefetch_related('submitters') +@admin.register(SubmissionDraft) class SubmissionDraftAdmin(admin.ModelAdmin): search_fields = ( 'id', @@ -240,6 +244,7 @@ def get_queryset(self, request: HttpRequest) -> QuerySet[SubmissionDraft]: ) +@admin.register(SubmissionTagging) class SubmissionTaggingAdmin(admin.ModelAdmin): search_fields = ( 'tag__name', @@ -282,6 +287,7 @@ def get_submitters(self, obj): return ', '.join(str(s) for s in obj.submission.submitters.all()) +@admin.register(SubmittedFile) class SubmittedFileAdmin(admin.ModelAdmin): search_fields = ( 'submission__exercise__name', @@ -321,6 +327,7 @@ def get_submitters(self, obj): return ', '.join(str(s) for s in obj.submission.submitters.all()) +@admin.register(StaticExercise) class StaticExerciseAdmin(admin.ModelAdmin): search_fields = ( 'name', @@ -346,6 +353,7 @@ class StaticExerciseAdmin(admin.ModelAdmin): ) +@admin.register(ExerciseWithAttachment) class ExerciseWithAttachmentAdmin(admin.ModelAdmin): search_fields = ( 'name', @@ -371,6 +379,7 @@ class ExerciseWithAttachmentAdmin(admin.ModelAdmin): ) +@admin.register(LTIExercise) class LTIExerciseAdmin(admin.ModelAdmin): search_fields = ( 'name', @@ -401,6 +410,7 @@ class LTIExerciseAdmin(admin.ModelAdmin): ) +@admin.register(LTI1p3Exercise) class LTI1p3ExerciseAdmin(admin.ModelAdmin): search_fields = ( 'name', @@ -430,6 +440,7 @@ class LTI1p3ExerciseAdmin(admin.ModelAdmin): ) +@admin.register(ExerciseCollection) class ExerciseCollectionAdmin(admin.ModelAdmin): search_fields = ( 'name', @@ -457,6 +468,7 @@ class ExerciseCollectionExerciseAdmin(admin.ModelAdmin): ) +@admin.register(RevealRule) class RevealRuleAdmin(admin.ModelAdmin): search_fields = ( 'trigger', @@ -470,6 +482,7 @@ class RevealRuleAdmin(admin.ModelAdmin): 'currently_revealed', ) +@admin.register(ExerciseTask) class ExerciseTaskAdmin(admin.ModelAdmin): search_fields = ( 'task_id', @@ -494,6 +507,7 @@ def get_course(self, obj): return str(obj.exercise.course_module.course_instance) +@admin.register(LearningObjectDisplay) class LearningObjectDisplayAdmin(admin.ModelAdmin): search_fields = ( 'learning_object__name', @@ -515,6 +529,7 @@ class LearningObjectDisplayAdmin(admin.ModelAdmin): readonly_fields = ('timestamp',) +@admin.register(PendingSubmission) class PendingSubmissionAdmin(admin.ModelAdmin): list_display_links = ('id',) list_display = ( @@ -568,20 +583,3 @@ def exercise_link(self, pending_submission): @admin.display(description=_('LABEL_STATUS')) def submission_status(self, pending_submission): return Submission.STATUS[pending_submission.submission.status] - - -admin.site.register(CourseChapter, CourseChapterAdmin) -admin.site.register(BaseExercise, BaseExerciseAdmin) -admin.site.register(StaticExercise, StaticExerciseAdmin) -admin.site.register(ExerciseWithAttachment, ExerciseWithAttachmentAdmin) -admin.site.register(LTIExercise, LTIExerciseAdmin) -admin.site.register(LTI1p3Exercise, LTI1p3ExerciseAdmin) -admin.site.register(Submission, SubmissionAdmin) -admin.site.register(SubmissionDraft, SubmissionDraftAdmin) -admin.site.register(SubmissionTagging, SubmissionTaggingAdmin) -admin.site.register(SubmittedFile, SubmittedFileAdmin) -admin.site.register(ExerciseCollection, ExerciseCollectionAdmin) -admin.site.register(RevealRule, RevealRuleAdmin) -admin.site.register(ExerciseTask, ExerciseTaskAdmin) -admin.site.register(LearningObjectDisplay, LearningObjectDisplayAdmin) -admin.site.register(PendingSubmission, PendingSubmissionAdmin) diff --git a/exercise/api/tests.py b/exercise/api/tests.py index 925b9d441..e1576413f 100644 --- a/exercise/api/tests.py +++ b/exercise/api/tests.py @@ -1,5 +1,6 @@ from django.test import TestCase from rest_framework.test import APIClient +from rest_framework.exceptions import ErrorDetail from django.contrib.auth.models import User from course.models import Course, CourseInstance from exercise.models import LearningObjectCategory @@ -84,4 +85,4 @@ def test_get_submissiondetail(self): client = APIClient() client.force_authenticate(user=self.student) response = client.get('/api/v2/submissions/1/') - self.assertEqual(response.data, {'detail': 'Not found.'}) + self.assertEqual(response.data, {'detail': ErrorDetail(string='Submission not found', code='not_found')}) diff --git a/exercise/views.py b/exercise/views.py index 2ea529f41..8da11f39e 100644 --- a/exercise/views.py +++ b/exercise/views.py @@ -62,7 +62,7 @@ def post(self, request, *args, **kwargs): SubmissionTagging.objects.create(submission=submission, tag=subtag) # Redirect back to the previous page - return redirect(request.META.get('HTTP_REFERER', '/')) + return redirect(request.headers.get('referer', '/')) class SubmissionTaggingRemoveView(CourseInstanceBaseView): @@ -80,7 +80,7 @@ def post(self, request, *args, **kwargs): SubmissionTagging.objects.filter(submission=submission, tag=subtag).delete() # Redirect back to the previous page - return redirect(request.META.get('HTTP_REFERER', '/')) + return redirect(request.headers.get('referer', '/')) class ExerciseInfoView(ExerciseBaseView): diff --git a/external_services/admin.py b/external_services/admin.py index 722c343c8..f95324741 100644 --- a/external_services/admin.py +++ b/external_services/admin.py @@ -4,6 +4,7 @@ from lib.admin_helpers import RecentCourseInstanceListFilter +@admin.register(LinkService) class LinkServiceAdmin(admin.ModelAdmin): search_fields = ( 'url', @@ -28,16 +29,19 @@ class LinkServiceAdmin(admin.ModelAdmin): readonly_fields = ('content_type',) +@admin.register(LTIService) class LTIServiceAdmin(LinkServiceAdmin): search_fields = LinkServiceAdmin.search_fields + ('consumer_key',) list_display = LinkServiceAdmin.list_display + ('access_settings',) +@admin.register(LTI1p3Service) class LTI1p3ServiceAdmin(LinkServiceAdmin): search_fields = LinkServiceAdmin.search_fields + ('login_url',) list_display = LinkServiceAdmin.list_display + ('client_id',) +@admin.register(MenuItem) class MenuItemAdmin(admin.ModelAdmin): search_fields = ( 'course_instance__instance_name', @@ -70,9 +74,3 @@ class MenuItemAdmin(admin.ModelAdmin): 'course_instance', 'service', ) - - -admin.site.register(LTIService, LTIServiceAdmin) -admin.site.register(LTI1p3Service, LTI1p3ServiceAdmin) -admin.site.register(LinkService, LinkServiceAdmin) -admin.site.register(MenuItem, MenuItemAdmin) diff --git a/external_services/api/authentication.py b/external_services/api/authentication.py index 0fdd8918d..3724166dd 100644 --- a/external_services/api/authentication.py +++ b/external_services/api/authentication.py @@ -27,8 +27,8 @@ def verify_oauth_body_hash_and_signature(request, req_body_hash, lti_exercise=No ''' headers = { 'Content-Type': request.content_type, - 'Authorization': request.META.get('HTTP_AUTHORIZATION'), - 'Host': request.META.get('HTTP_HOST'), + 'Authorization': request.headers.get('authorization'), + 'Host': request.headers.get('host'), } # all OAuth parameters must be given in the HTTP Authorization header # (not in POST data or GET query parameters) when a body hash is used @@ -95,7 +95,7 @@ def verify_oauth_body_hash_and_signature(request, req_body_hash, lti_exercise=No class OAuthBodyHashAuthentication(BaseAuthentication): def authenticate(self, request): - if 'HTTP_AUTHORIZATION' not in request.META: + if 'authorization' not in request.headers: return None data = request.data # activates the request body parser if the body has not been parsed yet diff --git a/external_services/api/renderers.py b/external_services/api/renderers.py index cffbb5027..f81a73530 100644 --- a/external_services/api/renderers.py +++ b/external_services/api/renderers.py @@ -40,7 +40,7 @@ def render(self, data, media_type=None, renderer_context=None): response = renderer_context.get('response') error_detail = data.get('detail') # detail is usually a str or string-like rest_framework.exceptions.ErrorDetail, - # but for ValidationErros it is a dict mapping serializer field names to error strings. + # but for ValidationErrors it is a dict mapping serializer field names to error strings. # In addition, detail is a list if the exercise does not accept the student's submission. # data may also contain some of the normal LTI request data in addition to the error detail if isinstance(error_detail, dict): diff --git a/lib/helpers.py b/lib/helpers.py index 5d6852e82..b46869a71 100644 --- a/lib/helpers.py +++ b/lib/helpers.py @@ -195,10 +195,10 @@ def get_url_ip_address_list(url): def get_remote_addr(request): - real_ip = request.META.get('HTTP_X_REAL_IP') + real_ip = request.headers.get('x-real-ip') if real_ip: return real_ip - forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') + forwarded_for = request.headers.get('x-forwarded-for') if forwarded_for: return forwarded_for.split(',', 1)[0].strip() return request.META.get('REMOTE_ADDR') diff --git a/news/admin.py b/news/admin.py index 92432534b..e4747ea41 100644 --- a/news/admin.py +++ b/news/admin.py @@ -4,6 +4,7 @@ from .models import News +@admin.register(News) class NewsAdmin(admin.ModelAdmin): search_fields = ( "course_instance__instance_name", @@ -24,6 +25,3 @@ class NewsAdmin(admin.ModelAdmin): "publish", ) raw_id_fields = ("course_instance",) - - -admin.site.register(News, NewsAdmin) diff --git a/notification/admin.py b/notification/admin.py index c96474a63..358c47835 100644 --- a/notification/admin.py +++ b/notification/admin.py @@ -4,6 +4,7 @@ from notification.models import Notification +@admin.register(Notification) class NotificationAdmin(admin.ModelAdmin): search_fields = ( 'subject', @@ -42,6 +43,3 @@ class NotificationAdmin(admin.ModelAdmin): 'submission', ) readonly_fields = ('timestamp',) - - -admin.site.register(Notification, NotificationAdmin) diff --git a/redirect_old_urls/urls.py b/redirect_old_urls/urls.py index c371d149a..23ba2982b 100644 --- a/redirect_old_urls/urls.py +++ b/redirect_old_urls/urls.py @@ -1,3 +1,4 @@ +from django.urls import path from django.urls import re_path from . import views @@ -8,7 +9,7 @@ views.course), re_path(r'^course/(?P[\w\d\-\.]+)/(?P[\w\d\-\.]+)/$', views.instance), - re_path(r'^exercise/(?P\d+)/$', + path('exercise//', views.exercise), re_path(r'^(?P[\w\d\-\.]+)/(?P[\w\d\-\.]+)/exercises/(?P\d+)/$', views.instance_exercise), diff --git a/requirements.txt b/requirements.txt index e1937c065..32e14266e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,15 +1,15 @@ # Usage: pip install -r requirements.txt beautifulsoup4 ~= 4.9.3 -Django ~= 4.2.3 +Django ~= 5.2.13 django-bootstrap5 >= 24.2 django-html5-colorfield >= 2.0, < 3 django-settingsdict ~= 1.1.1 -djangorestframework ~= 3.14.0 +djangorestframework ~= 3.17.1 djangorestframework-csv ~= 2.1.0 -git+https://github.com/apluslms/drf-extensions.git@reimplement_nested_routes_v6#egg=drf_extensions -git+https://github.com/apluslms/django-essentials.git@1.6.0#egg=django-essentials +git+https://github.com/apluslms/drf-extensions.git@reimplement_nested_routes_v7#egg=drf_extensions +git+https://github.com/apluslms/django-essentials.git@1.7.0#egg=django-essentials git+https://github.com/apluslms/js-jquery-toggle.git@2.0.0#egg=js-jquery-toggle-django&subdirectory=django -git+https://github.com/apluslms/django-colortag.git@3.0.1#egg=django-colortag +git+https://github.com/apluslms/django-colortag.git@3.1.0#egg=django-colortag feedparser ~= 6.0.2 html5lib ~= 1.1 lxml >= 5.3.1 @@ -23,7 +23,7 @@ Pillow >= 9.0.0 python-dateutil ~= 2.8.1 social-auth-app-django >= 5.0.0, < 6 social-auth-core >= 4.1.0, < 5 -reportlab >= 3.5.0, < 4 +reportlab >= 4.4.10, < 5 requests >= 2.25.1, < 3 unittest-xml-reporting >= 3.0.4, < 4 markdown ~= 3.3.4 diff --git a/shibboleth_login/urls.py b/shibboleth_login/urls.py index 971056590..aa34156e6 100644 --- a/shibboleth_login/urls.py +++ b/shibboleth_login/urls.py @@ -1,13 +1,13 @@ from django.conf import settings -from django.urls import re_path +from django.urls import path from . import views urlpatterns = [ - re_path(r'^shibboleth/login/$', views.login, name="shibboleth-login"), - re_path(r'^Shibboleth.sso/haka_login$', views.login, name="haka-login"), + path('shibboleth/login/', views.login, name="shibboleth-login"), + path('Shibboleth.sso/haka_login', views.login, name="haka-login"), ] if settings.DEBUG: - urlpatterns.append(re_path(r'^debug/$', views.debug)) + urlpatterns.append(path('debug/', views.debug)) diff --git a/site_alert/admin.py b/site_alert/admin.py index 81d080ece..2175d3f99 100644 --- a/site_alert/admin.py +++ b/site_alert/admin.py @@ -4,6 +4,7 @@ from site_alert.models import SiteAlert +@admin.register(SiteAlert) class SiteAlertAdmin(admin.ModelAdmin): search_fields = ( 'alert', @@ -14,6 +15,3 @@ class SiteAlertAdmin(admin.ModelAdmin): 'alert', 'status', ) - - -admin.site.register(SiteAlert, SiteAlertAdmin) diff --git a/threshold/admin.py b/threshold/admin.py index d1706235a..cd1fb3ed4 100644 --- a/threshold/admin.py +++ b/threshold/admin.py @@ -17,6 +17,7 @@ class ThresholdPointsInline(admin.TabularInline): model = ThresholdPoints +@admin.register(Threshold) class ThresholdAdmin(admin.ModelAdmin): search_fields = ( 'name', @@ -47,6 +48,7 @@ class ThresholdAdmin(admin.ModelAdmin): ) +@admin.register(CourseModuleRequirement) class CourseModuleRequirementAdmin(admin.ModelAdmin): search_fields = ( 'module__name', @@ -79,7 +81,3 @@ class CourseModuleRequirementAdmin(admin.ModelAdmin): @admin.display(description=_('LABEL_COURSE_INSTANCE')) def get_course(self, obj): return str(obj.module.course_instance) - - -admin.site.register(Threshold, ThresholdAdmin) -admin.site.register(CourseModuleRequirement, CourseModuleRequirementAdmin) diff --git a/userprofile/admin.py b/userprofile/admin.py index 7af517fb7..d9c0e2a1e 100644 --- a/userprofile/admin.py +++ b/userprofile/admin.py @@ -5,6 +5,7 @@ from .models import UserProfile +@admin.register(UserProfile) class UserProfileAdmin(admin.ModelAdmin): search_fields = ( 'user__first_name', @@ -32,8 +33,6 @@ def get_user(self, obj): return f'{obj.user.get_username()} | user_id={obj.user.pk} | {obj.user.get_full_name()} | {obj.user.email}' -admin.site.register(UserProfile, UserProfileAdmin) - # Don't display a dropdown for selecting a user on the Token admin page. TokenAdmin.raw_id_fields = ('user',) TokenAdmin.search_fields = ( diff --git a/userprofile/urls.py b/userprofile/urls.py index fd87e2f40..c56272432 100644 --- a/userprofile/urls.py +++ b/userprofile/urls.py @@ -1,27 +1,28 @@ +from django.urls import path from django.urls import re_path from . import views urlpatterns = [ - re_path(r'^login/$', views.AplusLoginView.as_view(), name="login"), - re_path(r'^logout/$', views.AplusLogoutView.as_view(), name="logout"), - re_path(r'^privacy-notice/$', views.PrivacyNoticeView.as_view(), + path('login/', views.AplusLoginView.as_view(), name="login"), + path('logout/', views.AplusLogoutView.as_view(), name="logout"), + path('privacy-notice/', views.PrivacyNoticeView.as_view(), name="privacy_notice"), - re_path(r'^cookie-notice/$', views.CookieNoticeView.as_view(), + path('cookie-notice/', views.CookieNoticeView.as_view(), name="cookie_notice"), - re_path(r'^accessibility-statement/$', views.AccessibilityStatementView.as_view(), + path('accessibility-statement/', views.AccessibilityStatementView.as_view(), name="accessibility_statement"), - re_path(r'^support/$', views.SupportView.as_view(), + path('support/', views.SupportView.as_view(), name="support_channels"), - re_path(r'^accounts/$', views.ProfileView.as_view(), + path('accounts/', views.ProfileView.as_view(), name="profile"), re_path(r'^setlang', views.set_user_language, name="set-user-language"), - re_path(r'^regentoken/$', views.regenerate_access_token, + path('regentoken/', views.regenerate_access_token, name="regenerate-access-token"), - re_path(r'^teachers/$', views.TeacherListView.as_view(), + path('teachers/', views.TeacherListView.as_view(), name="teachers"), - re_path(r'^pseudonymize/$', views.PseudonymizeView.as_view(), + path('pseudonymize/', views.PseudonymizeView.as_view(), name="toggle-pseudonymization"), ] diff --git a/userprofile/views.py b/userprofile/views.py index b4148a13c..ffc3bf306 100644 --- a/userprofile/views.py +++ b/userprofile/views.py @@ -98,7 +98,7 @@ def set_user_language(request): not url_has_allowed_host_and_scheme(url=next, allowed_hosts={request.get_host()}, require_https=request.is_secure())): - next = remove_query_param_from_url(request.META.get('HTTP_REFERER'), 'hl') + next = remove_query_param_from_url(request.headers.get('referer'), 'hl') next = next and unquote(next) # HTTP_REFERER may be encoded. if not url_has_allowed_host_and_scheme(url=next, allowed_hosts={request.get_host()}, @@ -366,4 +366,4 @@ class PseudonymizeView(BaseView): def get(self, request: HttpRequest) -> HttpResponse: pseudonymize = request.session.get("pseudonymize", False) request.session["pseudonymize"] = not pseudonymize - return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) + return HttpResponseRedirect(request.headers.get("referer", "/"))