From 8c6ceb370a4414d59ab7d0ef1ecc421e41de8e3c Mon Sep 17 00:00:00 2001
From: Jonathan Weth <git@jonathanweth.de>
Date: Tue, 5 Sep 2023 17:18:44 +0200
Subject: [PATCH] Add preview mode for card layouts

---
 aleksis/apps/kort/frontend/index.js           |   9 +
 ...gers_alter_cardlayout_managers_and_more.py | 161 ++++++++++++++++++
 aleksis/apps/kort/tables.py                   |   8 +-
 .../templates/kort/card/detail_content.html   |   4 +
 aleksis/apps/kort/urls.py                     |   1 +
 aleksis/apps/kort/views.py                    |  10 ++
 6 files changed, 186 insertions(+), 7 deletions(-)
 create mode 100644 aleksis/apps/kort/migrations/0017_alter_card_managers_alter_cardlayout_managers_and_more.py

diff --git a/aleksis/apps/kort/frontend/index.js b/aleksis/apps/kort/frontend/index.js
index 997acde..12baed9 100644
--- a/aleksis/apps/kort/frontend/index.js
+++ b/aleksis/apps/kort/frontend/index.js
@@ -52,6 +52,15 @@ export default {
         byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
       },
     },
+    {
+      path: "cards/:pk/preview/",
+      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+      name: "kort.previewCard",
+
+      props: {
+        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
+      },
+    },
     {
       path: "cards/:pk/print/",
       component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
diff --git a/aleksis/apps/kort/migrations/0017_alter_card_managers_alter_cardlayout_managers_and_more.py b/aleksis/apps/kort/migrations/0017_alter_card_managers_alter_cardlayout_managers_and_more.py
new file mode 100644
index 0000000..efed942
--- /dev/null
+++ b/aleksis/apps/kort/migrations/0017_alter_card_managers_alter_cardlayout_managers_and_more.py
@@ -0,0 +1,161 @@
+# Generated by Django 4.2.5 on 2023-09-05 14:43
+
+import aleksis.core.managers
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("sites", "0002_alter_domain_unique"),
+        ("kort", "0016_cardprinter_oauth2_client_secret"),
+    ]
+
+    operations = [
+        migrations.AlterModelManagers(
+            name="card",
+            managers=[
+                ("objects", aleksis.core.managers.AlekSISBaseManager()),
+            ],
+        ),
+        migrations.AlterModelManagers(
+            name="cardlayout",
+            managers=[
+                ("objects", aleksis.core.managers.AlekSISBaseManager()),
+            ],
+        ),
+        migrations.AlterModelManagers(
+            name="cardlayoutmediafile",
+            managers=[
+                ("objects", aleksis.core.managers.AlekSISBaseManager()),
+            ],
+        ),
+        migrations.AlterModelManagers(
+            name="cardprinter",
+            managers=[
+                ("objects", aleksis.core.managers.AlekSISBaseManager()),
+            ],
+        ),
+        migrations.AlterModelManagers(
+            name="cardprintjob",
+            managers=[
+                ("objects", aleksis.core.managers.AlekSISBaseManager()),
+            ],
+        ),
+        migrations.AddField(
+            model_name="card",
+            name="managed_by_app_label",
+            field=models.CharField(
+                blank=True,
+                editable=False,
+                max_length=255,
+                verbose_name="App label of app responsible for managing this instance",
+            ),
+        ),
+        migrations.AddField(
+            model_name="cardlayout",
+            name="managed_by_app_label",
+            field=models.CharField(
+                blank=True,
+                editable=False,
+                max_length=255,
+                verbose_name="App label of app responsible for managing this instance",
+            ),
+        ),
+        migrations.AddField(
+            model_name="cardlayoutmediafile",
+            name="managed_by_app_label",
+            field=models.CharField(
+                blank=True,
+                editable=False,
+                max_length=255,
+                verbose_name="App label of app responsible for managing this instance",
+            ),
+        ),
+        migrations.AddField(
+            model_name="cardprinter",
+            name="managed_by_app_label",
+            field=models.CharField(
+                blank=True,
+                editable=False,
+                max_length=255,
+                verbose_name="App label of app responsible for managing this instance",
+            ),
+        ),
+        migrations.AddField(
+            model_name="cardprintjob",
+            name="managed_by_app_label",
+            field=models.CharField(
+                blank=True,
+                editable=False,
+                max_length=255,
+                verbose_name="App label of app responsible for managing this instance",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="card",
+            name="site",
+            field=models.ForeignKey(
+                default=1,
+                editable=False,
+                on_delete=django.db.models.deletion.CASCADE,
+                related_name="+",
+                to="sites.site",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="cardlayout",
+            name="site",
+            field=models.ForeignKey(
+                default=1,
+                editable=False,
+                on_delete=django.db.models.deletion.CASCADE,
+                related_name="+",
+                to="sites.site",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="cardlayoutmediafile",
+            name="site",
+            field=models.ForeignKey(
+                default=1,
+                editable=False,
+                on_delete=django.db.models.deletion.CASCADE,
+                related_name="+",
+                to="sites.site",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="cardprinter",
+            name="cups_printer",
+            field=models.CharField(
+                blank=True,
+                help_text="Leave blank to deactivate CUPS printing",
+                max_length=255,
+                verbose_name="CUPS printer",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="cardprinter",
+            name="site",
+            field=models.ForeignKey(
+                default=1,
+                editable=False,
+                on_delete=django.db.models.deletion.CASCADE,
+                related_name="+",
+                to="sites.site",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="cardprintjob",
+            name="site",
+            field=models.ForeignKey(
+                default=1,
+                editable=False,
+                on_delete=django.db.models.deletion.CASCADE,
+                related_name="+",
+                to="sites.site",
+            ),
+        ),
+    ]
diff --git a/aleksis/apps/kort/tables.py b/aleksis/apps/kort/tables.py
index 6a67ac3..9cc9b6c 100644
--- a/aleksis/apps/kort/tables.py
+++ b/aleksis/apps/kort/tables.py
@@ -3,13 +3,7 @@ from django.template.loader import render_to_string
 from django.utils.safestring import SafeString, mark_safe
 from django.utils.translation import gettext as _
 
-from django_tables2 import (
-    BooleanColumn,
-    Column,
-    DateTimeColumn,
-    LinkColumn,
-    Table,
-)
+from django_tables2 import BooleanColumn, Column, DateTimeColumn, LinkColumn, Table
 from django_tables2.utils import A, AttributeDict, computed_values
 
 from aleksis.apps.kort.forms import PrinterSelectForm
diff --git a/aleksis/apps/kort/templates/kort/card/detail_content.html b/aleksis/apps/kort/templates/kort/card/detail_content.html
index 07e102e..dc1830e 100644
--- a/aleksis/apps/kort/templates/kort/card/detail_content.html
+++ b/aleksis/apps/kort/templates/kort/card/detail_content.html
@@ -63,5 +63,9 @@
         </div>
       {% endif %}
     {% endif %}
+    <a href="{% url 'preview_card' card.pk %}" class="btn waves-effect waves-light">
+      <i class="material-icons left iconify" data-icon="mdi:file-pdf-box"></i>
+      {% trans "Preview card layout" %}
+    </a>
   </div>
 </div>
\ No newline at end of file
diff --git a/aleksis/apps/kort/urls.py b/aleksis/apps/kort/urls.py
index 424fd51..06e9f01 100644
--- a/aleksis/apps/kort/urls.py
+++ b/aleksis/apps/kort/urls.py
@@ -12,6 +12,7 @@ urlpatterns = [
         name="generate_card_pdf",
     ),
     path("cards/<int:pk>/deactivate/", views.CardDeactivateView.as_view(), name="deactivate_card"),
+    path("cards/<int:pk>/preview/", views.CardPreviewView.as_view(), name="preview_card"),
     path("cards/<int:pk>/print/", views.CardPrintView.as_view(), name="print_card"),
     path("cards/<int:pk>/delete/", views.CardDeleteView.as_view(), name="delete_card"),
     path("printers/", views.CardPrinterListView.as_view(), name="card_printers"),
diff --git a/aleksis/apps/kort/views.py b/aleksis/apps/kort/views.py
index 53177c4..7e51033 100644
--- a/aleksis/apps/kort/views.py
+++ b/aleksis/apps/kort/views.py
@@ -209,6 +209,16 @@ class CardDetailView(PermissionRequiredMixin, RevisionMixin, PrinterSelectMixin,
     template_name = "kort/card/detail.html"
 
 
+class CardPreviewView(PermissionRequiredMixin, DetailView):
+    permission_required = "kort.view_card_rule"
+    model = Card
+
+    def get(self, *args, **kwargs):
+        self.object = self.get_object()
+        html = self.object.layout.render(self.object)
+        return HttpResponse(html)
+
+
 class CardGeneratePDFView(PermissionRequiredMixin, RevisionMixin, SingleObjectMixin, View):
     permission_required = "views.edit_card_rule"
     model = Card
-- 
GitLab