From 51e9e47d195e766eab5324132194d4096247fd49 Mon Sep 17 00:00:00 2001
From: Hangzhi Yu <hangzhi@protonmail.com>
Date: Fri, 3 Feb 2023 18:34:43 +0100
Subject: [PATCH] Add poster groups dynamically as routes and menu entries

---
 aleksis/apps/resint/frontend/index.js         |  1 +
 aleksis/apps/resint/frontend/messages/en.json |  3 ++
 .../0008_postergroup_slug_unique.py           | 24 ++++++++++
 aleksis/apps/resint/models.py                 | 47 +++++++++++++++++--
 aleksis/apps/resint/rules.py                  |  5 +-
 5 files changed, 76 insertions(+), 4 deletions(-)
 create mode 100644 aleksis/apps/resint/frontend/index.js
 create mode 100644 aleksis/apps/resint/frontend/messages/en.json
 create mode 100644 aleksis/apps/resint/migrations/0008_postergroup_slug_unique.py

diff --git a/aleksis/apps/resint/frontend/index.js b/aleksis/apps/resint/frontend/index.js
new file mode 100644
index 0000000..ff8b4c5
--- /dev/null
+++ b/aleksis/apps/resint/frontend/index.js
@@ -0,0 +1 @@
+export default {};
diff --git a/aleksis/apps/resint/frontend/messages/en.json b/aleksis/apps/resint/frontend/messages/en.json
new file mode 100644
index 0000000..0db3279
--- /dev/null
+++ b/aleksis/apps/resint/frontend/messages/en.json
@@ -0,0 +1,3 @@
+{
+
+}
diff --git a/aleksis/apps/resint/migrations/0008_postergroup_slug_unique.py b/aleksis/apps/resint/migrations/0008_postergroup_slug_unique.py
new file mode 100644
index 0000000..e95a488
--- /dev/null
+++ b/aleksis/apps/resint/migrations/0008_postergroup_slug_unique.py
@@ -0,0 +1,24 @@
+# Generated by Django 4.1.5 on 2023-02-03 16:42
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("contenttypes", "0002_remove_content_type_name"),
+        ("resint", "0007_current_file_not_null"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="postergroup",
+            name="slug",
+            field=models.SlugField(
+                help_text="If you use 'example', the filename will be 'example.pdf'.",
+                unique=True,
+                verbose_name="Slug used in URL name",
+            ),
+        ),
+    ]
diff --git a/aleksis/apps/resint/models.py b/aleksis/apps/resint/models.py
index eab9cc4..e453376 100644
--- a/aleksis/apps/resint/models.py
+++ b/aleksis/apps/resint/models.py
@@ -4,6 +4,7 @@ from typing import Any, Optional
 from django.core.files import File
 from django.core.validators import FileExtensionValidator, MaxValueValidator, MinValueValidator
 from django.db import models
+from django.urls import reverse
 from django.utils import timezone
 from django.utils.translation import gettext_lazy as _
 
@@ -15,17 +16,56 @@ from celery.states import SUCCESS
 from reversion.models import Revision, Version
 
 from aleksis.core.mixins import ExtensibleModel, ExtensiblePolymorphicModel
+from aleksis.core.models import DynamicRoute
 from aleksis.core.util.pdf import generate_pdf_from_template
 
 
+class PosterGroupDynamicRoute(DynamicRoute):
+    @classmethod
+    def get_dynamic_routes(cls):
+        poster_groups = PosterGroup.objects.all()
+
+        dynamic_routes = []
+
+        for poster_group in poster_groups:
+          dynamic_routes.append(cls.get_route_data(poster_group))
+
+        return dynamic_routes
+
+    @classmethod
+    def get_route_data(cls, instance):
+      dynamic_route = {}
+
+      dynamic_route["parent_route_name"] = ""
+
+      dynamic_route["route_path"] = reverse("poster_show_current", args=[instance.slug])
+      dynamic_route["route_name"] = f"resint.posterGroup.{instance.slug}"
+
+      dynamic_route["display_account_menu"] = False
+      dynamic_route["display_sidenav_menu"] = instance.show_in_menu
+      dynamic_route["sidenav_menu_root"] = True
+      dynamic_route["menu_new_tab"] = True
+
+      dynamic_route["menu_title"] = instance.name
+      dynamic_route["menu_icon"] = "mdi-file-pdf-box"
+
+      dynamic_route["menu_permission"] = "resint.view_poster_pdf_menu"
+      dynamic_route["route_permission"] = "resint.view_poster_pdf_menu"
+
+      # TODO Handle case of posters that are supposed to be displayed even when user is not logged in
+
+      return dynamic_route
+
+
 class PosterGroup(ExtensibleModel):
     """Group for time-based documents, called posters."""
 
     slug = models.SlugField(
         verbose_name=_("Slug used in URL name"),
         help_text=_("If you use 'example', the filename will be 'example.pdf'."),
+        unique=True
     )
-    name = models.CharField(max_length=255, verbose_name=_("Name"))
+    name = models.CharField(max_length=255, verbose_name=_("Name"), unique=True)
     publishing_day = models.PositiveSmallIntegerField(
         verbose_name=_("Publishing weekday"), choices=i18n_day_name_choices_lazy()
     )
@@ -44,8 +84,8 @@ class PosterGroup(ExtensibleModel):
         verbose_name = _("Poster group")
         verbose_name_plural = _("Poster groups")
         constraints = [
-            models.UniqueConstraint(fields=["site_id", "name"], name="unique_site_name"),
-            models.UniqueConstraint(fields=["site_id", "slug"], name="unique_site_slug"),
+          models.UniqueConstraint(fields=["site_id", "name"], name="unique_site_name"),
+          models.UniqueConstraint(fields=["site_id", "slug"], name="unique_site_slug"),
         ]
         permissions = [
             ("view_poster_of_group", _("Can view all posters of this group")),
@@ -57,6 +97,7 @@ class PosterGroup(ExtensibleModel):
     def __str__(self) -> str:
         return f"{self.name} ({self.publishing_day_name}, {self.publishing_time})"
 
+
     @property
     def publishing_day_name(self) -> str:
         """Return the full name of the publishing day (e. g. Monday)."""
diff --git a/aleksis/apps/resint/rules.py b/aleksis/apps/resint/rules.py
index f1d9f72..1db1cfb 100644
--- a/aleksis/apps/resint/rules.py
+++ b/aleksis/apps/resint/rules.py
@@ -103,7 +103,10 @@ view_poster_pdf_predicate = is_public_poster_group | (
 add_perm("resint.view_poster_pdf", view_poster_pdf_predicate)
 
 # View menu entry for single posters
-view_poster_pdf_menu_predicate = show_poster_group_in_menu & view_poster_pdf_predicate
+view_poster_pdf_menu_predicate = has_person & (
+	has_global_perm("resint.view_postergroup")
+	| has_global_perm("resint.view_poster")
+)
 add_perm("resint.view_poster_pdf_menu", view_poster_pdf_menu_predicate)
 
 # Show the poster manage menu
-- 
GitLab