diff --git a/src/core/jinja2/core/artwork_detail.jinja2 b/src/core/jinja2/core/artwork_detail.jinja2 new file mode 100644 index 00000000..d4b27ebc --- /dev/null +++ b/src/core/jinja2/core/artwork_detail.jinja2 @@ -0,0 +1,14 @@ +{% extends '/core/base.jinja2' %} +{% block content %} +
+

{{ artwork.title }}

+

{{ _("Author") }}: {{ artwork.author }}

+ {% if artwork.marker %} +

{{ _("Marker") }}: {{ artwork.marker.title }}

+ {% endif %} + {% if artwork.augmented %} +

{{ _("Object") }}: {{ artwork.augmented.title }}

+ {% endif %} +

{{ _("Uploaded") }}: {{ artwork.uploaded_at }}

+
+{% endblock %} diff --git a/src/core/jinja2/core/marker_detail.jinja2 b/src/core/jinja2/core/marker_detail.jinja2 new file mode 100644 index 00000000..bc37cf51 --- /dev/null +++ b/src/core/jinja2/core/marker_detail.jinja2 @@ -0,0 +1,11 @@ +{% extends '/core/base.jinja2' %} +{% block content %} +
+

{{ marker.title }}

+

{{ _("Author") }}: {{ marker.author }}

+ {% if marker.source %} + {{ marker.title }} + {% endif %} +

{{ _("Uploaded") }}: {{ marker.uploaded_at }}

+
+{% endblock %} diff --git a/src/core/jinja2/core/object_detail.jinja2 b/src/core/jinja2/core/object_detail.jinja2 new file mode 100644 index 00000000..0abc83da --- /dev/null +++ b/src/core/jinja2/core/object_detail.jinja2 @@ -0,0 +1,14 @@ +{% extends '/core/base.jinja2' %} +{% block content %} +
+

{{ object.title }}

+

{{ _("Author") }}: {{ object.author }}

+ {% if object.source %} + + {% endif %} + {% if object.thumbnail %} + {{ object.title }} + {% endif %} +

{{ _("Uploaded") }}: {{ object.uploaded_at }}

+
+{% endblock %} diff --git a/src/core/migrations/0028_add_slug_to_marker_object_artwork.py b/src/core/migrations/0028_add_slug_to_marker_object_artwork.py new file mode 100644 index 00000000..e4218e4a --- /dev/null +++ b/src/core/migrations/0028_add_slug_to_marker_object_artwork.py @@ -0,0 +1,28 @@ +# Generated manually to add slug fields to Marker, Object, and Artwork models + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0027_sound_soundevent_remove_artwork_insert_insert_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='marker', + name='slug', + field=models.SlugField(blank=True, max_length=80, unique=True), + ), + migrations.AddField( + model_name='object', + name='slug', + field=models.SlugField(blank=True, max_length=80, unique=True), + ), + migrations.AddField( + model_name='artwork', + name='slug', + field=models.SlugField(blank=True, max_length=80, unique=True), + ), + ] diff --git a/src/core/models.py b/src/core/models.py index 702dcede..87ec90ac 100644 --- a/src/core/models.py +++ b/src/core/models.py @@ -6,6 +6,7 @@ from django.db.models.signals import post_delete from django.dispatch import receiver from django.urls import reverse +from django.utils.text import slugify from django.utils.translation import gettext_lazy as _ from django_extensions.db.models import TimeStampedModel from fast_html import a, audio, b, div, h1, img, p, render, span, video @@ -219,10 +220,22 @@ class Marker(TimeStampedModel, ContentMixin): # Save the file size of the Marker, so we avoid making requests to S3 / MinIO to check for it. file_size = models.IntegerField(default=0, blank=True, null=True) + slug = models.SlugField(unique=True, blank=True, max_length=80) def save(self, *args, **kwargs): + if not self.slug: + base_slug = slugify(self.title) or "marker" + slug = base_slug + counter = 1 + while Marker.objects.filter(slug=slug).exclude(pk=self.pk).exists(): + slug = f"{base_slug}-{counter}" + counter += 1 + self.slug = slug super().save(*args, **kwargs) + def get_absolute_url(self): + return reverse("marker-detail", kwargs={"slug": self.slug}) + def __str__(self): return self.source.name @@ -321,6 +334,21 @@ class Object(TimeStampedModel, ContentMixin): blank=True, null=True, ) + slug = models.SlugField(unique=True, blank=True, max_length=80) + + def save(self, *args, **kwargs): + if not self.slug: + base_slug = slugify(self.title) or "object" + slug = base_slug + counter = 1 + while Object.objects.filter(slug=slug).exclude(pk=self.pk).exists(): + slug = f"{base_slug}-{counter}" + counter += 1 + self.slug = slug + super().save(*args, **kwargs) + + def get_absolute_url(self): + return reverse("object-detail", kwargs={"slug": self.slug}) def __str__(self): return self.source.name @@ -433,6 +461,21 @@ class Artwork(TimeStampedModel, ContentMixin): scale_y = models.FloatField(default=1.0) position_x = models.FloatField(default=0.0) position_y = models.FloatField(default=0.0) + slug = models.SlugField(unique=True, blank=True, max_length=80) + + def save(self, *args, **kwargs): + if not self.slug: + base_slug = slugify(self.title) or "artwork" + slug = base_slug + counter = 1 + while Artwork.objects.filter(slug=slug).exclude(pk=self.pk).exists(): + slug = f"{base_slug}-{counter}" + counter += 1 + self.slug = slug + super().save(*args, **kwargs) + + def get_absolute_url(self): + return reverse("artwork-detail", kwargs={"slug": self.slug}) @property def exhibits_count(self): @@ -515,6 +558,9 @@ class Exhibit(TimeStampedModel, ContentMixin, models.Model): def __str__(self): return self.name + def get_absolute_url(self): + return reverse("exhibit", kwargs={"slug": self.slug}) + @property def artworks_count(self): return self.artworks.count() diff --git a/src/core/urls.py b/src/core/urls.py index abc40c6d..112b98da 100644 --- a/src/core/urls.py +++ b/src/core/urls.py @@ -15,6 +15,7 @@ service_worker, ) from core.views.views import ( + artwork_detail, artwork_preview, collection, create_artwork, @@ -29,8 +30,10 @@ exhibit_detail, exhibit_select, get_element, + marker_detail, marker_preview, marker_upload, + object_detail, object_upload, related_content, see_all, @@ -41,6 +44,7 @@ path("", home_new, name="home"), path("arviewer/", home_old, name="home-old"), path("artwork/", artwork_preview, name="artwork-preview"), + path("artworks//", artwork_detail, name="artwork-detail"), path("artworks/create/", create_artwork, name="create-artwork"), path("artworks/edit/", edit_artwork, name="edit-artwork"), path("collection/", collection, name="collection"), @@ -55,8 +59,10 @@ path("exhibits/edit-mr/", create_or_edit_mr_exhibit, name="edit-mr-exhibit"), path("generator/", marker_generator, name="marker-generator"), path("marker/", marker_preview, name="marker-preview"), + path("markers//", marker_detail, name="marker-detail"), path("markers/edit/", edit_marker, name="edit-marker"), path("markers/upload/", marker_upload, name="marker-upload"), + path("objects//", object_detail, name="object-detail"), path("objects/edit/", edit_object, name="edit-object"), path("objects/upload/", object_upload, name="object-upload"), path("sounds/edit/", edit_sound, name="edit-sound"), diff --git a/src/core/views/views.py b/src/core/views/views.py index 208c552a..3079c172 100644 --- a/src/core/views/views.py +++ b/src/core/views/views.py @@ -404,6 +404,27 @@ def artwork_preview(request): return render(request, "core/exhibit.jinja2", ctx) +@require_http_methods(["GET"]) +def marker_detail(request, slug): + marker = get_object_or_404(Marker, slug=slug) + ctx = {"marker": marker} + return render(request, "core/marker_detail.jinja2", ctx) + + +@require_http_methods(["GET"]) +def object_detail(request, slug): + obj = get_object_or_404(Object, slug=slug) + ctx = {"object": obj} + return render(request, "core/object_detail.jinja2", ctx) + + +@require_http_methods(["GET"]) +def artwork_detail(request, slug): + artwork = get_object_or_404(Artwork, slug=slug) + ctx = {"artwork": artwork} + return render(request, "core/artwork_detail.jinja2", ctx) + + @login_required def get_element(request): if request.htmx: