diff --git a/assets/css/userarea.css b/assets/css/userarea.css index 68b79d5..83be2c8 100644 --- a/assets/css/userarea.css +++ b/assets/css/userarea.css @@ -227,3 +227,7 @@ table.dataTable { .error-message { color: red; } + +.admin-header a { + color: #9cdcfe; +} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index e38f349..461d9ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,8 @@ boto3 = "*" argon2-cffi = "*" django-csp = "*" django-rest-polymorphic = "*" +django-crispy-forms = "*" +crispy-bootstrap5 = "*" [tool.poetry.group.mysql.dependencies] mysqlclient = "*" diff --git a/quackscape/settings.py b/quackscape/settings.py index 2519556..bf1fa22 100644 --- a/quackscape/settings.py +++ b/quackscape/settings.py @@ -39,6 +39,8 @@ INSTALLED_APPS = [ "django_celery_results", "drf_spectacular", "drf_spectacular_sidecar", + "crispy_forms", + "crispy_bootstrap5", "quackscape", "quackscape.users", "quackscape.tours", @@ -218,7 +220,7 @@ QUACKSCAPE_CONTENT_RESOLUTIONS = [ (65536, 32768), ] -MAX_IMAGE_PIXELS = 34359738368 # 262144x131072 - should be enough, no? +MAX_IMAGE_PIXELS = 34359738368 # 262144x131072 - should be enough, no? # ffmpeg settings @@ -248,4 +250,9 @@ FFMPEG_DEFAULT_OPTION = ASK.config.get("ffmpeg", "DefaultOption", fallback="defa LOGIN_URL = reverse_lazy("quackscape.users:login") LOGIN_REDIRECT_URL = reverse_lazy("quackscape.users:categories") -LOGOUT_REDIRECT_URL = reverse_lazy("quackscape.users:login") \ No newline at end of file +LOGOUT_REDIRECT_URL = reverse_lazy("quackscape.users:login") + +# Crispy forms settings + +CRISPY_ALLOWED_TEMPLATE_PACKS = {"bootstrap5"} +CRISPY_TEMPLATE_PACK = "bootstrap5" \ No newline at end of file diff --git a/quackscape/tours/forms.py b/quackscape/tours/forms.py new file mode 100644 index 0000000..4dcc03c --- /dev/null +++ b/quackscape/tours/forms.py @@ -0,0 +1,21 @@ +from django.forms import ModelForm + +from .models import Scene, Element, Category, OriginalMedia + + +class SceneForm(ModelForm): + class Meta: + model = Scene + fields = ("title", "description") + + +class CategoryForm(ModelForm): + class Meta: + model = Category + fields = ("title", "description") + + +class OriginalMediaForm(ModelForm): + class Meta: + model = OriginalMedia + fields = ("title", "description") diff --git a/quackscape/tours/migrations/0018_category_description.py b/quackscape/tours/migrations/0018_category_description.py new file mode 100644 index 0000000..3369996 --- /dev/null +++ b/quackscape/tours/migrations/0018_category_description.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.3 on 2024-03-27 07:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tours', '0017_originalvideo_thumbnail'), + ] + + operations = [ + migrations.AddField( + model_name='category', + name='description', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/quackscape/tours/models.py b/quackscape/tours/models.py index 42f7e76..b8cc5a3 100644 --- a/quackscape/tours/models.py +++ b/quackscape/tours/models.py @@ -22,6 +22,7 @@ def upload_to(instance, filename): class Category(models.Model): id = models.UUIDField(default=uuid.uuid4, primary_key=True) title = models.CharField(max_length=100) + description = models.TextField(null=True, blank=True) owner = models.ForeignKey( get_user_model(), related_name="owned_categories", diff --git a/quackscape/users/templates/users/base.html b/quackscape/users/templates/users/base.html index efa6be2..b7a98c5 100644 --- a/quackscape/users/templates/users/base.html +++ b/quackscape/users/templates/users/base.html @@ -11,7 +11,8 @@

Quackscape

{% if user.is_authenticated %} -

Logged in as {{ user.email }}

+

Logged in as {{ user.email }} – + Logout

{% else %}

Not logged in

{% endif %} diff --git a/quackscape/users/templates/users/categories.html b/quackscape/users/templates/users/categories.html index aad002c..b64d0b6 100644 --- a/quackscape/users/templates/users/categories.html +++ b/quackscape/users/templates/users/categories.html @@ -15,7 +15,7 @@

Available categories

- + Create category
@@ -142,7 +149,9 @@
User permissions
- +
diff --git a/quackscape/users/templates/users/generic_form.html b/quackscape/users/templates/users/generic_form.html new file mode 100644 index 0000000..fff161d --- /dev/null +++ b/quackscape/users/templates/users/generic_form.html @@ -0,0 +1,27 @@ +{% extends "users/base.html" %} +{% load crispy_forms_tags %} + +{% block content %} +
+
+
+
+
+

{{ title }}

+
+
+
+ {% csrf_token %} + {{ form|crispy }} {% if form.non_field_errors %} +
+ {% for error in form.non_field_errors %} {{ error }} {% endfor %} +
+ {% endif %} + + +
+
+
+
+
+{% endblock %} diff --git a/quackscape/users/templates/users/login.html b/quackscape/users/templates/users/login.html index 42cf1d7..5da7641 100644 --- a/quackscape/users/templates/users/login.html +++ b/quackscape/users/templates/users/login.html @@ -6,7 +6,7 @@
-

Login

+

{{ title }}

diff --git a/quackscape/users/urls.py b/quackscape/users/urls.py index a0a0690..0fdfe45 100644 --- a/quackscape/users/urls.py +++ b/quackscape/users/urls.py @@ -1,4 +1,4 @@ -from .views import UserAreaMainView, CategoriesView, CategoryView, FileUploadView, Login, Logout +from .views import UserAreaMainView, CategoriesView, CategoryView, FileUploadView, Login, Logout, CategoryCreateView from django.urls import path @@ -7,6 +7,7 @@ app_name = 'quackscape.users' urlpatterns = [ path('', UserAreaMainView.as_view(), name='user-area-main'), path('categories/', CategoriesView.as_view(), name='categories'), + path('category/create/', CategoryCreateView.as_view(), name='category-create'), path('category//', CategoryView.as_view(), name='category'), path('category//upload/', FileUploadView.as_view(), name='media-upload'), path('login/', Login.as_view(), name='login'), diff --git a/quackscape/users/views.py b/quackscape/users/views.py index 2f34c7f..f1b5419 100644 --- a/quackscape/users/views.py +++ b/quackscape/users/views.py @@ -1,7 +1,15 @@ -from django.views.generic import TemplateView, ListView, DetailView +from django.views.generic import ( + TemplateView, + ListView, + DetailView, + CreateView, + UpdateView, + DeleteView, +) from django.http import Http404 from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.views import LoginView, LogoutView +from django.urls import reverse_lazy from rest_framework.parsers import MultiPartParser, FormParser from rest_framework.response import Response @@ -14,6 +22,7 @@ from quackscape.tours.serializers import ( OriginalImageSerializer, OriginalVideoSerializer, ) +from quackscape.tours.forms import CategoryForm class TitleMixin: @@ -63,9 +72,20 @@ class MediaUploadView(LoginRequiredMixin, TitleMixin, TemplateView): title = "Upload Media" -class CategoryCreateView(LoginRequiredMixin, TitleMixin, TemplateView): - template_name = "users/category_create.html" +class CategoryCreateView(LoginRequiredMixin, TitleMixin, CreateView): + template_name = "users/generic_form.html" title = "Create Category" + form_class = CategoryForm + model = Category + + def form_valid(self, form): + form.instance.owner = self.request.user + return super().form_valid(form) + + def get_success_url(self): + return reverse_lazy( + "quackscape.users:category", kwargs={"category": self.object.id} + ) class FileUploadView(LoginRequiredMixin, GenericAPIView):