diff --git a/lectures/models.py b/lectures/models.py index 02314de4689025f2756db8abf900e15b25d2df29..66a2d65919e28f9b0dc2c0e01894b766b18c1813 100644 --- a/lectures/models.py +++ b/lectures/models.py @@ -1,9 +1,15 @@ -from datetime import timedelta +import mimetypes +import uuid + +from datetime import datetime, timedelta from django.contrib.auth.models import Group from django.core.exceptions import ValidationError from django.db import models +from django.db.models.fields.files import FieldFile +from django.urls import reverse from django.utils import timezone +from django.utils.safestring import mark_safe from markdownx.models import MarkdownxField from shared.models import NameStrMixin @@ -35,7 +41,7 @@ class LectureGroup(NameStrMixin, models.Model): Group, blank=True, verbose_name="Uživatelské skupiny", - help_text="Pokud žádné nedefinuješ, školení ve skupině jsou dostupné všem.", + help_text="Pokud žádné nedefinuješ, školení ve skupině jsou dostupná všem.", ) class Meta: @@ -112,6 +118,7 @@ class LectureLector(NameStrMixin, models.Model): blank=True, null=True, verbose_name="Odkaz", + help_text=mark_safe("Běžně na <a href=\"https://lide.pirati.cz\">aplikaci Lidé</a>.") ) username = models.CharField( @@ -148,6 +155,7 @@ class LectureRecording(NameStrMixin, models.Model): blank=True, null=True, verbose_name="Odkaz", + help_text=mark_safe("Běžně na <a href=\"https://tv.pirati.cz\">Pirátskou TV</a>.") ) class Meta: @@ -155,6 +163,46 @@ class LectureRecording(NameStrMixin, models.Model): verbose_name_plural = "Nahrávky" +def get_lecture_material_file_location(instance, filename): + mimetypes_instance = mimetypes.MimeTypes() + + current_time = datetime.today() + guessed_type = mimetypes_instance.guess_type(filename, strict=False)[0] + + extension = "" + + if guessed_type is not None: + for mapper in mimetypes_instance.types_map_inv: + if guessed_type not in mapper: + continue + + extension = mapper[guessed_type] + + if isinstance(extension, list): + extension = extension[0] + + break + + return ( + "_private/" + f"{current_time.year}/{current_time.month}/{current_time.day}/" + f"{uuid.uuid4()}{extension}" + ) + + +class LectureMaterialFileProxy(FieldFile): + @property + def url(self) -> str: + return reverse( + "lectures:download_material_file", + args=(str(self.instance.id),) + ) + + +class LectureMaterialFileField(models.FileField): + attr_class = LectureMaterialFileProxy + + class LectureMaterial(NameStrMixin, models.Model): lecture = models.ForeignKey( "Lecture", @@ -176,13 +224,21 @@ class LectureMaterial(NameStrMixin, models.Model): help_text="Pokud máš zadaný odkaz, nemůžeš definovat soubor.", ) - file = models.FileField( + file = LectureMaterialFileField( blank=True, null=True, + upload_to=get_lecture_material_file_location, verbose_name="Soubor", help_text="Pokud máš vložený soubor, nemůžeš definovat odkaz.", ) + @property + def protected_file_url(self) -> str: + return reverse( + "lectures:download_material_file", + args=(self.id,), + ) + def clean(self) -> None: BOTH_FILE_AND_LINK_DEFINED = ( "Definuj prosím pouze odkaz, nebo soubor. Nemůžeš mít oboje najednou." diff --git a/lectures/urls.py b/lectures/urls.py index 27da58fcf3087d4b144f1ff2e0a54baff5a9bc04..5b082c4b1f4c12faa943f4d4f04830777548d81f 100644 --- a/lectures/urls.py +++ b/lectures/urls.py @@ -9,4 +9,9 @@ urlpatterns = [ "groups/<int:group_id>", views.view_group_lectures, name="view_group_lectures" ), path("lectures/<int:lecture_id>", views.view_lecture, name="view_lecture"), + path( + "lectures/materials/<str:pk>/file", + views.LectureMaterialFileDownloadView.as_view(), + name="download_material_file", + ) ] diff --git a/lectures/views.py b/lectures/views.py index 673ccba0a0513dc4a6ef40e275150b7cac4dadca..722b735c37059ec003305d47f16ebe8718c75b91 100644 --- a/lectures/views.py +++ b/lectures/views.py @@ -1,4 +1,5 @@ # import calendar +# import locale import json from datetime import datetime from itertools import chain @@ -8,12 +9,50 @@ from django.db import models from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.utils import timezone +from django_downloadview import ObjectDownloadView from django_http_exceptions import HTTPExceptions from guardian.shortcuts import get_objects_for_user -from .models import Lecture, LectureGroup +from .models import Lecture, LectureGroup, LectureMaterial + + +class LectureMaterialFileDownloadView(ObjectDownloadView): + model = LectureMaterial + file_field = "file" + attachment = False + + def get_queryset(self, *args, **kwargs): + queryset = ( + super() + .get_queryset(*args, **kwargs) + .filter( + lecture__groups__in=( + get_objects_for_user( + self.current_user, + "lectures.view_lecturegroup" + ). + filter( + models.Q(user_groups__in=self.current_user.groups.all()) + | models.Q(user_groups=None) + ) + ) + ) + ) -# import locale + print( + queryset, + get_objects_for_user( + self.current_user, + "lectures.view_lecturegroup" + ) + ) + + return queryset + + def get(self, request, *args, **kwargs): + self.current_user = request.user + + return super().get(request, *args, **kwargs) def get_base_context(request) -> dict: diff --git a/media_server/__init__.py b/media_server/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/media_server/admin.py b/media_server/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..8c38f3f3dad51e4585f3984282c2a4bec5349c1e --- /dev/null +++ b/media_server/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/media_server/apps.py b/media_server/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..4aff1ea9e92371e7e70bb043a67dd347b60b3dce --- /dev/null +++ b/media_server/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class MediaServerConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "media_server" diff --git a/media_server/models.py b/media_server/models.py new file mode 100644 index 0000000000000000000000000000000000000000..71a836239075aa6e6e4ecb700e9c42c95c022d91 --- /dev/null +++ b/media_server/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/media_server/tests.py b/media_server/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6 --- /dev/null +++ b/media_server/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/media_server/urls.py b/media_server/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..23bb64ae39d8b5b07f98b14e44c3cff7b6532382 --- /dev/null +++ b/media_server/urls.py @@ -0,0 +1,12 @@ +from django.urls import path + +from . import views + +app_name = "media_server" +urlpatterns = [ + path( + "<path:path>", + views.view_media, + name="view_media", + ), +] diff --git a/media_server/views.py b/media_server/views.py new file mode 100644 index 0000000000000000000000000000000000000000..370803dd04a396d2ee3c1946d80d853a84796d9e --- /dev/null +++ b/media_server/views.py @@ -0,0 +1,27 @@ +import os + +from django.core.files.storage import FileSystemStorage +from django_downloadview import StorageDownloadView +from django_http_exceptions import HTTPExceptions + +# Create your views here. + +storage = FileSystemStorage() + + +class MediaView(StorageDownloadView): + attachment = False + + def get_path(self, *args, **kwargs) -> str: + path = super().get_path(*args, **kwargs) + + # Make sure path is clean + path = os.path.normpath(path) + + if path.startswith("_"): # Private path + raise HTTPExceptions.NOT_FOUND + + return path + + +view_media = MediaView.as_view(storage=storage) diff --git a/requirements/base.txt b/requirements/base.txt index 8fb7229b52aa0c083f8da7352ec6e68e425c18d6..97e5804765d5d634eba2d20228d011108b56ebd1 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -3,6 +3,7 @@ django-admin-index==2.0.2 django-admin-interface==0.24.2 django-database-url==1.0.3 django-dbsettings==1.3.0 +django-downloadview==2.3.0 django-markdownx==4.0.0b1 django-ordered-model==3.7.1 psycopg2-binary==2.9.5 diff --git a/ucebnice/settings/base.py b/ucebnice/settings/base.py index 0e22973ec2664874dc53e992bb7ed417f7f2dd4f..adf529f7a87b27f1f353290151b9a4ae9da91151 100644 --- a/ucebnice/settings/base.py +++ b/ucebnice/settings/base.py @@ -57,6 +57,7 @@ INSTALLED_APPS = [ "pirates", "webpack_loader", "shared", + "media_server", "oidc", "users", "lectures", diff --git a/ucebnice/urls.py b/ucebnice/urls.py index 4f40d785e77ebd4f1deb0c8724a729a583da5ca3..8ed67ab7b598a2ab8dce8e9b275226f6183700ff 100644 --- a/ucebnice/urls.py +++ b/ucebnice/urls.py @@ -27,5 +27,6 @@ urlpatterns = [ path("", include("lectures.urls")), path("markdownx/", include("markdownx.urls")), path("settings/", include("dbsettings.urls")), + path("media/", include("media_server.urls")), path("admin/", admin.site.urls), ] + pirates_urlpatterns