From 39de9689c25025db961ff9146083ee099c4d18e2 Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Wed, 27 Nov 2024 15:09:08 +0000 Subject: [PATCH 01/52] Merge page_tree_archived into page_tree --- .../cms/templates/pages/page_tree.html | 376 ++++++++++-------- .../cms/views/pages/page_tree_view.py | 3 +- integreat_cms/locale/de/LC_MESSAGES/django.po | 18 +- 3 files changed, 223 insertions(+), 174 deletions(-) diff --git a/integreat_cms/cms/templates/pages/page_tree.html b/integreat_cms/cms/templates/pages/page_tree.html index fd39690ae5..60b05b42b0 100644 --- a/integreat_cms/cms/templates/pages/page_tree.html +++ b/integreat_cms/cms/templates/pages/page_tree.html @@ -9,21 +9,36 @@ {% with filter_form.filters_visible as filters_visible %}
-

- {% translate "Page Tree" %} - -

- - {% translate "Archived pages" %} - + {% if is_archive %} +

+ {% translate "Archived Pages" %} +

+ + {% translate "Back to pages" %} + + {% else %} +

+ {% translate "Page Tree" %} + +

+ + {% translate "Archived pages" %} + + {% endif %}
- {% include "generic_language_switcher.html" with target="pages" %} - {% include "_search_input.html" with object_type="page" related_form="page-filter-form" search_query=filter_form.cleaned_data.query %} + {% if is_archive %} + {% include "generic_language_switcher.html" with target="archived_pages" %} + {% include "_search_input.html" with object_type="page" object_archived=True related_form="page-filter-form" %} + {% else %} + {% include "generic_language_switcher.html" with target="pages" %} + {% include "_search_input.html" with object_type="page" related_form="page-filter-form" search_query=filter_form.cleaned_data.query %} + {% endif %}
{% if request.user.expert_mode %} @@ -32,19 +47,21 @@

{% translate "Hide filters" %} {% endif %} - {% if can_edit_pages %} - {% if request.region.default_language == language %} - - {% translate "Create page" %} - - {% else %} - {% blocktranslate trimmed asvar disabled_button_title with request.region.default_language.translated_name as default_language %} - You can only create pages in the default language ({{ default_language }}). - {% endblocktranslate %} - + {% if not is_archive %} + {% if can_edit_pages %} + {% if request.region.default_language == language %} + + {% translate "Create page" %} + + {% else %} + {% blocktranslate trimmed asvar disabled_button_title with request.region.default_language.translated_name as default_language %} + You can only create pages in the default language ({{ default_language }}). + {% endblocktranslate %} + + {% endif %} {% endif %} {% endif %}

@@ -80,9 +97,11 @@

{% translate "Title in" %} {{ backend_language.translated_name }} {% endif %} - - {% translate "Tags" %} - + {% if not is_archive %} + + {% translate "Tags" %} + + {% endif %}
{% spaceless %} @@ -109,35 +128,52 @@

- {% for page in pages %} - {% include "pages/page_tree_node.html" %} - {% if forloop.last %} - - -
- {# djlint:off H020 #} - - {# djlint:on #} -
+ {% if is_archive %} + {% for page in pages %} + {% get_translation page language.slug as page_translation %} + {% include "pages/page_tree_archived_node.html" with page_translation=page_translation %} + {% empty %} + + + {% if filter_form.is_enabled %} + {% translate "No archived pages found with these filters." %} + {% else %} + {% translate "No pages archived yet." %} + {% endif %} - {% endif %} - {% empty %} - - - - - {% if filter_form.is_enabled %} - {% translate "No pages found with these filters." %} - {% else %} - {% translate "No pages available yet." %} - {% endif %} - - - {% endfor %} + {% endfor %} + {% else %} + {% for page in pages %} + {% include "pages/page_tree_node.html" %} + {% if forloop.last %} + + +
+ {# djlint:off H020 #} + + {# djlint:on #} +
+ + + {% endif %} + {% empty %} + + + + + {% if filter_form.is_enabled %} + {% translate "No pages found with these filters." %} + {% else %} + {% translate "No pages available yet." %} + {% endif %} + + + {% endfor %} + {% endif %}

@@ -170,94 +206,102 @@

- {% if can_edit_pages %} - - {% endif %} - {% if MT_PERMITTED %} - {% translate "pages" as content_type %} - {% if MT_PROVIDER %} - + {% endif %} + {% if MT_PERMITTED %} + {% translate "pages" as content_type %} + {% if MT_PROVIDER %} + + {% else %} + + {% endif %} + {% endif %} + {% if language.can_be_pdf_exported %} + {% else %} + {% endif %} + {% if request.user.expert_mode %} + {% with language_translated_name=language.translated_name %} + {% blocktranslate trimmed asvar xliff_export_public %} + Export published pages for {{ language_translated_name }} translation + {% endblocktranslate %} + {% blocktranslate trimmed asvar xliff_export_all %} + Export unpublished (⚠️) and published pages for {{ language_translated_name }} translation + {% endblocktranslate %} + {% if language == request.region.default_language %} + {% blocktranslate trimmed asvar xliff_export_disabled %} + You cannot export XLIFF files for the default language + {% endblocktranslate %} + + + {% else %} + + + {% endif %} + {% endwith %} + {% endif %} + {% if request.user.is_superuser or request.user.is_staff %} + + {% translate "pages" as content_type %} + + {% endif %} - {% endif %} - {% if language.can_be_pdf_exported %} - + {% if has_pages_in_translation %} + + {% endif %} {% else %} - - {% endif %} - {% if request.user.expert_mode %} - {% with language_translated_name=language.translated_name %} - {% blocktranslate trimmed asvar xliff_export_public %} - Export published pages for {{ language_translated_name }} translation - {% endblocktranslate %} - {% blocktranslate trimmed asvar xliff_export_all %} - Export unpublished (⚠️) and published pages for {{ language_translated_name }} translation - {% endblocktranslate %} - {% if language == request.region.default_language %} - {% blocktranslate trimmed asvar xliff_export_disabled %} - You cannot export XLIFF files for the default language - {% endblocktranslate %} - - - {% else %} - - - {% endif %} - {% endwith %} - {% endif %} - {% if request.user.is_superuser or request.user.is_staff %} - - {% translate "pages" as content_type %} - - - {% endif %} - {% if has_pages_in_translation %} - + {% if can_edit_pages %} + + {% endif %} {% endif %}

- {% if request.user.expert_mode and can_edit_pages %} -
-

- {% translate "Import XLIFF files" %} -

-
- {% translate "Supported file extensions" %}: .zip, .xlf, .xliff + {% if not is_archive %} + {% if request.user.expert_mode and can_edit_pages %} +
+

+ {% translate "Import XLIFF files" %} +

+
+ {% translate "Supported file extensions" %}: .zip, .xlf, .xliff +
+
+ + + + {% csrf_token %} + +
-
- - - - {% csrf_token %} - -
-
+ {% endif %} {% endif %} {% include "../generic_confirmation_dialog.html" %} {% include "../tutorials/page_tree.html" with tutorial_id="page-tree" hidden=request.user.page_tree_tutorial_seen %} diff --git a/integreat_cms/cms/views/pages/page_tree_view.py b/integreat_cms/cms/views/pages/page_tree_view.py index 19430164dc..1e601b1ac8 100644 --- a/integreat_cms/cms/views/pages/page_tree_view.py +++ b/integreat_cms/cms/views/pages/page_tree_view.py @@ -136,7 +136,7 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: return render( request, - self.template_name, + self.template, { **self.get_context_data(**kwargs), "pages": pages, @@ -154,5 +154,6 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: currently_in_translation=True, ).count() > 0, + "is_archive": self.archived, }, ) diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index cba16a1cd0..b8141ce20e 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -7354,6 +7354,16 @@ msgstr "" " Dieses Feld freilassen, um einen eindeutigen Permalink aus dem Titel zu " "generieren" +#: cms/templates/pages/page_tree.html +#: cms/templates/pages/page_tree_archived.html +msgid "Archived Pages" +msgstr "Archivierte Seiten" + +#: cms/templates/pages/page_tree.html +#: cms/templates/pages/page_tree_archived.html +msgid "Back to pages" +msgstr "Zurück zu Seiten" + #: cms/templates/pages/page_tree.html msgid "Page Tree" msgstr "Seiten-Baum" @@ -7449,14 +7459,6 @@ msgstr "und {} weitere Dateien" msgid "Import" msgstr "Importieren" -#: cms/templates/pages/page_tree_archived.html -msgid "Archived Pages" -msgstr "Archivierte Seiten" - -#: cms/templates/pages/page_tree_archived.html -msgid "Back to pages" -msgstr "Zurück zu Seiten" - #: cms/templates/pages/page_tree_archived.html msgid "No archived pages found with these filters." msgstr "Keine archivierten Seiten mit diesen Filtern gefunden." From 63219f472dea5bcaad72f418f413b5ca0e24559d Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Wed, 27 Nov 2024 18:02:04 +0100 Subject: [PATCH 02/52] Fix translation format --- integreat_cms/locale/de/LC_MESSAGES/django.po | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index b8141ce20e..49a3b9f2eb 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -7387,6 +7387,16 @@ msgstr "" msgid "Tags" msgstr "Tags" +#: cms/templates/pages/page_tree.html +#: cms/templates/pages/page_tree_archived.html +msgid "No archived pages found with these filters." +msgstr "Keine archivierten Seiten mit diesen Filtern gefunden." + +#: cms/templates/pages/page_tree.html +#: cms/templates/pages/page_tree_archived.html +msgid "No pages archived yet." +msgstr "Noch keine Seiten archiviert." + #: cms/templates/pages/page_tree.html msgid "No pages found with these filters." msgstr "Keine Seiten mit diesen Filtern gefunden." @@ -7439,6 +7449,11 @@ msgstr "" msgid "Export selected pages for multilingual translation" msgstr "Ausgewählte Seiten für die mehrsprachige Übersetzung exportieren" +#: cms/templates/pages/page_tree.html +#: cms/templates/pages/page_tree_archived.html +msgid "Restore pages" +msgstr "Seiten wiederherstellen" + #: cms/templates/pages/page_tree.html msgid "Import XLIFF files" msgstr "XLIFF-Dateien importieren" @@ -7459,18 +7474,6 @@ msgstr "und {} weitere Dateien" msgid "Import" msgstr "Importieren" -#: cms/templates/pages/page_tree_archived.html -msgid "No archived pages found with these filters." -msgstr "Keine archivierten Seiten mit diesen Filtern gefunden." - -#: cms/templates/pages/page_tree_archived.html -msgid "No pages archived yet." -msgstr "Noch keine Seiten archiviert." - -#: cms/templates/pages/page_tree_archived.html -msgid "Restore pages" -msgstr "Seiten wiederherstellen" - #: cms/templates/pages/page_tree_archived_node.html msgid "Drag & drop is disabled for archived pages." msgstr "Drag & drop ist für archivierte Seiten nicht möglich." From 8eb72797b7ae3f937dc6c9c6a1392f3a5772b10e Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Wed, 27 Nov 2024 18:22:26 +0100 Subject: [PATCH 03/52] Add template for statistics about viewed pages --- .../templates/statistics/statistics_overview.html | 1 + .../statistics/statistics_viewed_pages.html | 12 ++++++++++++ integreat_cms/locale/de/LC_MESSAGES/django.po | 4 ++++ 3 files changed, 17 insertions(+) create mode 100644 integreat_cms/cms/templates/statistics/statistics_viewed_pages.html diff --git a/integreat_cms/cms/templates/statistics/statistics_overview.html b/integreat_cms/cms/templates/statistics/statistics_overview.html index 2a7c754b34..25ed45faba 100644 --- a/integreat_cms/cms/templates/statistics/statistics_overview.html +++ b/integreat_cms/cms/templates/statistics/statistics_overview.html @@ -11,6 +11,7 @@

{% include "statistics/statistics_chart.html" with box_id="statistics_chart" %} + {% include "statistics/statistics_viewed_pages.html" with box_id="statistics_viewed_pages" %}
{% include "statistics/statistics_sidebar.html" with box_id="statistics_sidebar" %} diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html new file mode 100644 index 0000000000..a86b09a10b --- /dev/null +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -0,0 +1,12 @@ +{% extends "_collapsible_box.html" %} +{% load i18n %} +{% load static %} +{% load widget_tweaks %} +{% block collapsible_box_icon %} + chart-column-increasing +{% endblock collapsible_box_icon %} +{% block collapsible_box_title %} + {% translate "Page views" %} +{% endblock collapsible_box_title %} +{% block collapsible_box_content %} +{% endblock collapsible_box_content %} diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index 49a3b9f2eb..6021a82052 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -8481,6 +8481,10 @@ msgstr "Bild/PNG" msgid "Table Document/CSV" msgstr "Tabellendokument/CSV" +#: cms/templates/statistics/statistics_viewed_pages.html +msgid "Page views" +msgstr "Seitenzugriffe" + #: cms/templates/translations/translations_management.html msgid "Manage machine translations" msgstr "Maschinelle Übersetzungen verwalten" From 94fcc30f2d8b6a586a9bc01d625a42b6466e96af Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Wed, 27 Nov 2024 18:57:48 +0100 Subject: [PATCH 04/52] Add _generic_page_tree.html --- .../templates/pages/_generic_page_tree.html | 25 +++++++++++++++++++ .../cms/templates/pages/page_tree.html | 19 +------------- .../statistics/statistics_viewed_pages.html | 12 +++++++++ integreat_cms/locale/de/LC_MESSAGES/django.po | 5 ++-- 4 files changed, 41 insertions(+), 20 deletions(-) create mode 100644 integreat_cms/cms/templates/pages/_generic_page_tree.html diff --git a/integreat_cms/cms/templates/pages/_generic_page_tree.html b/integreat_cms/cms/templates/pages/_generic_page_tree.html new file mode 100644 index 0000000000..5316316b87 --- /dev/null +++ b/integreat_cms/cms/templates/pages/_generic_page_tree.html @@ -0,0 +1,25 @@ +{% load i18n %} +{% load static %} +{% load content_filters %} +{% load page_filters %} +{% load rules %} +{% block content %} + + + + {% if not filter_form.is_enabled %} + + {% translate "Hierarchy" %} + + {% endif %} + + {% translate "Title in" %} {{ language.translated_name }} + + {% get_current_language as LANGUAGE_CODE %} + {% get_language LANGUAGE_CODE as backend_language %} + {% if backend_language and backend_language != language %} + + {% translate "Title in" %} {{ backend_language.translated_name }} + + {% endif %} +{% endblock content %} diff --git a/integreat_cms/cms/templates/pages/page_tree.html b/integreat_cms/cms/templates/pages/page_tree.html index 60b05b42b0..c49b522423 100644 --- a/integreat_cms/cms/templates/pages/page_tree.html +++ b/integreat_cms/cms/templates/pages/page_tree.html @@ -79,24 +79,7 @@

class="w-full mt-4 rounded border-2 border-solid border-gray-200 shadow bg-white table-auto"> - - - - {% if not filter_form.is_enabled %} - - {% translate "Hierarchy" %} - - {% endif %} - - {% translate "Title in" %} {{ language.translated_name }} - - {% get_current_language as LANGUAGE_CODE %} - {% get_language LANGUAGE_CODE as backend_language %} - {% if backend_language and backend_language != language %} - - {% translate "Title in" %} {{ backend_language.translated_name }} - - {% endif %} + {% include "pages/_generic_page_tree.html" %} {% if not is_archive %} {% translate "Tags" %} diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html index a86b09a10b..dd0daef926 100644 --- a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -9,4 +9,16 @@ {% translate "Page views" %} {% endblock collapsible_box_title %} {% block collapsible_box_content %} +
+ {% csrf_token %} +
+ + + + {% include "pages/_generic_page_tree.html" %} + + +
+
+
{% endblock collapsible_box_content %} diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index 6021a82052..e6ee410d73 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -3485,7 +3485,8 @@ msgstr "Ob die Seite explizit archiviert ist oder nicht" #: cms/models/pages/abstract_base_page_translation.py #: cms/templates/events/event_form.html cms/templates/events/event_list.html -#: cms/templates/pages/page_form.html cms/templates/pages/page_tree.html +#: cms/templates/pages/_generic_page_tree.html +#: cms/templates/pages/page_form.html #: cms/templates/pages/page_tree_archived.html cms/templates/pois/poi_form.html #: cms/templates/pois/poi_list.html cms/templates/pois/poi_list_archived.html msgid "Title in" @@ -6722,7 +6723,7 @@ msgid "Create language tree node" msgstr "Sprach-Knoten hinzufügen" #: cms/templates/languagetreenodes/languagetreenode_list.html -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/_generic_page_tree.html #: cms/templates/pages/page_tree_archived.html msgid "Hierarchy" msgstr "Hierarchie" From 3c12cc7bde32c84dc18cb40628cfbf321862b15b Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Wed, 27 Nov 2024 19:15:20 +0100 Subject: [PATCH 05/52] Add additional column to page tree on statistics --- .../cms/templates/statistics/statistics_viewed_pages.html | 3 +++ integreat_cms/locale/de/LC_MESSAGES/django.po | 1 + 2 files changed, 4 insertions(+) diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html index dd0daef926..4b571d2675 100644 --- a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -16,6 +16,9 @@ {% include "pages/_generic_page_tree.html" %} + + {% translate "Accesses" %} ({{ form.start_date.value|date:"d.m.Y" }} - {{ form.end_date.value|date:"d.m.Y" }}) + diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index e6ee410d73..85d5c5dc4b 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -8434,6 +8434,7 @@ msgid "Shown languages" msgstr "Angezeigte Sprachen" #: cms/templates/statistics/_statistics_legend.html +#: cms/templates/statistics/statistics_viewed_pages.html msgid "Accesses" msgstr "Zugriffe" From b1545cc9692272b8826f46567689f2673bec61af Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Fri, 29 Nov 2024 18:04:32 +0000 Subject: [PATCH 06/52] Fix missing backend titles for root pages in page tree and deleting tags for archive nodes --- .../cms/templates/pages/page_tree.html | 71 +++++++------------ .../cms/templates/pages/page_tree_node.html | 24 ++++--- 2 files changed, 42 insertions(+), 53 deletions(-) diff --git a/integreat_cms/cms/templates/pages/page_tree.html b/integreat_cms/cms/templates/pages/page_tree.html index c49b522423..b595b9fa62 100644 --- a/integreat_cms/cms/templates/pages/page_tree.html +++ b/integreat_cms/cms/templates/pages/page_tree.html @@ -111,52 +111,35 @@

- {% if is_archive %} - {% for page in pages %} - {% get_translation page language.slug as page_translation %} - {% include "pages/page_tree_archived_node.html" with page_translation=page_translation %} - {% empty %} - - - {% if filter_form.is_enabled %} - {% translate "No archived pages found with these filters." %} - {% else %} - {% translate "No pages archived yet." %} - {% endif %} - - - {% endfor %} - {% else %} - {% for page in pages %} - {% include "pages/page_tree_node.html" %} - {% if forloop.last %} - - -
- {# djlint:off H020 #} - - {# djlint:on #} -
- - - {% endif %} - {% empty %} - - - - - {% if filter_form.is_enabled %} - {% translate "No pages found with these filters." %} - {% else %} - {% translate "No pages available yet." %} - {% endif %} + {% for page in pages %} + {% include "pages/page_tree_node.html" with is_archive=is_archive %} + {% if forloop.last %} + + +
+ {# djlint:off H020 #} + + {# djlint:on #} +
- {% endfor %} - {% endif %} + {% endif %} + {% empty %} + + + + + {% if filter_form.is_enabled %} + {% translate "No pages found with these filters." %} + {% else %} + {% translate "No pages available yet." %} + {% endif %} + + + {% endfor %}

diff --git a/integreat_cms/cms/templates/pages/page_tree_node.html b/integreat_cms/cms/templates/pages/page_tree_node.html index dfce0bb660..53bdc59cb3 100644 --- a/integreat_cms/cms/templates/pages/page_tree_node.html +++ b/integreat_cms/cms/templates/pages/page_tree_node.html @@ -61,11 +61,15 @@ {% endif %} - {% if backend_language and backend_language != language %} + {% if not backend_language %} + {% get_current_language as LANGUAGE_CODE %} + {% get_language LANGUAGE_CODE as backend_language %} + {% endif %} + {% if backend_language != language %} + class="block py-1.5 px-2 overflow-hidden max-w-xs whitespace-nowrap text-ellipsis text-gray-800" + title="{% if page.backend_translation %} {{ page.backend_translation.title }}{% endif %}">
{% if page.backend_translation %} {{ page.backend_translation.title }} @@ -76,12 +80,14 @@ {% endif %} - -
- {% for tag in page_translation.tags %} - {{ tag }} - {% endfor %} -
+ {% if not is_archive %} + +
+ {% for tag in page_translation.tags %} + {{ tag }} + {% endfor %} +
+ {% endif %}
From 9edae892068513c4f179b8f089bb688d7c91bca3 Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Mon, 2 Dec 2024 12:51:43 +0000 Subject: [PATCH 07/52] Merging page_tree_node and page_tree_archived_node --- .../cms/templates/pages/page_tree.html | 2 +- .../cms/templates/pages/page_tree_node.html | 117 ++++++++++++------ integreat_cms/locale/de/LC_MESSAGES/django.po | 18 ++- 3 files changed, 86 insertions(+), 51 deletions(-) diff --git a/integreat_cms/cms/templates/pages/page_tree.html b/integreat_cms/cms/templates/pages/page_tree.html index b595b9fa62..a949941db9 100644 --- a/integreat_cms/cms/templates/pages/page_tree.html +++ b/integreat_cms/cms/templates/pages/page_tree.html @@ -112,7 +112,7 @@

{% for page in pages %} - {% include "pages/page_tree_node.html" with is_archive=is_archive %} + {% include "pages/page_tree_node.html" with is_archive=is_archive %} {% if forloop.last %} + class="block py-1.5 px-2 overflow-hidden max-w-xs whitespace-nowrap text-ellipsis text-gray-800" + title="{% if page.backend_translation %} {{ page.backend_translation.title }}{% endif %}">
{% if page.backend_translation %} {{ page.backend_translation.title }} @@ -87,7 +87,7 @@ {{ tag }} {% endfor %}
- {% endif %} + {% endif %}
@@ -139,9 +139,25 @@
-
- {{ page_translation.get_status_display }} -
+ {% if is_archive %} + {% if page.explicitly_archived %} +
+ {% translate "Archived" %} +
+ {% else %} +
+ {% translate "Archived, because a parent page is archived" %} +
+ {% endif %} + {% else %} +
+ {{ page_translation.get_status_display }} +
+ {% endif %}
@@ -164,43 +180,64 @@ {% endif %} - {% if page_translation.status == PUBLIC %} - - - - {% else %} - - {% endif %} - - - - {% if can_edit_pages %} - {% if page.mirroring_pages.exists %} - - {% else %} - {% endif %} + + + + {% if can_edit_pages %} + {% if page.mirroring_pages.exists %} + + {% else %} + + {% endif %} + {% endif %} + {% else %} + {% if can_edit_pages %} + {% if page.explicitly_archived %} + + {% else %} + + + + {% endif %} + {% endif %} {% endif %} {% if perms.cms.delete_page %} {# djlint:off H023 #} @@ -228,7 +265,7 @@ {% endif %} {# djlint:on #} {% endif %} - {% if request.region.short_urls_enabled and request.user.expert_mode %} + {% if not is_archive and request.region.short_urls_enabled and request.user.expert_mode %} {% if page_translation %} Date: Tue, 3 Dec 2024 09:27:44 +0000 Subject: [PATCH 08/52] Disable dragable tree node for archived tree nodes --- .../cms/templates/pages/page_tree_node.html | 18 +++++++++--------- integreat_cms/locale/de/LC_MESSAGES/django.po | 8 +++++++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/integreat_cms/cms/templates/pages/page_tree_node.html b/integreat_cms/cms/templates/pages/page_tree_node.html index 5a237aaf60..ce46cf6c59 100644 --- a/integreat_cms/cms/templates/pages/page_tree_node.html +++ b/integreat_cms/cms/templates/pages/page_tree_node.html @@ -30,21 +30,21 @@ {% has_perm 'cms.change_page_object' request.user as can_edit_pages %} + data-node-descendants="{{ page|get_descendant_ids }}" + class="drag cursor-move text-gray-800 inline-block pl-4 align-middle" + draggable="{% if can_edit_pages and not is_archive %}true{% else %}false{% endif %}" + title="{% if not is_archive %}{% translate "Change the order and position of the pages with drag & drop." %}{% else %}{% translate "Drag & drop is disabled for archived pages." %}{% endif %}"> {% if can_edit_pages %} {% endif %} {% if not page.parent_id and not page.is_leaf or page.parent_id and page.cached_children|length > 0 %} + data-expand-title="{% translate "Expand all subpages" %}" + data-collapse-title="{% translate "Collapse all subpages" %}" + data-page-id="{{ page.id }}" + data-page-children="{{ page|get_children_ids }}" + data-page-tree-id="{{ page.tree_id }}"> {% endif %} diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index ac505fd06b..d6b3b1eeff 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -1684,7 +1684,7 @@ msgstr "Versteckt" #: cms/models/regions/region.py cms/templates/events/event_form.html #: cms/templates/pages/page_form.html #: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pois/poi_form.html +#: cms/templates/pages/page_tree_node.html cms/templates/pois/poi_form.html msgid "Archived" msgstr "Archiviert" @@ -5344,6 +5344,7 @@ msgstr "Kontakt archivieren" #: cms/templates/contacts/contact_form.html #: cms/templates/pages/page_form_sidebar/actions_box.html #: cms/templates/pages/page_tree_archived_node.html +#: cms/templates/pages/page_tree_node.html msgid "Restore page" msgstr "Seite wiederherstellen" @@ -7172,6 +7173,7 @@ msgstr "Übersetzungsprozess abbrechen" #: cms/templates/pages/page_form.html #: cms/templates/pages/page_tree_archived_node.html +#: cms/templates/pages/page_tree_node.html msgid "Archived, because a parent page is archived" msgstr "Archiviert, weil eine übergeordnete Seite archiviert ist" @@ -7474,6 +7476,7 @@ msgid "No pages archived yet." msgstr "Noch keine Seiten archiviert." #: cms/templates/pages/page_tree_archived_node.html +#: cms/templates/pages/page_tree_node.html msgid "Drag & drop is disabled for archived pages." msgstr "Drag & drop ist für archivierte Seiten nicht möglich." @@ -7488,10 +7491,12 @@ msgid "Collapse all subpages" msgstr "Alle Unterseiten einklappen" #: cms/templates/pages/page_tree_archived_node.html +#: cms/templates/pages/page_tree_node.html msgid "This page is archived." msgstr "Diese Seite ist archiviert." #: cms/templates/pages/page_tree_archived_node.html +#: cms/templates/pages/page_tree_node.html msgid "" "This page is archived, because at least one of its parent pages is archived." msgstr "" @@ -7509,6 +7514,7 @@ msgid "You cannot preview an empty page." msgstr "Eine leere Seite hat keine Vorschau" #: cms/templates/pages/page_tree_archived_node.html +#: cms/templates/pages/page_tree_node.html msgid "To restore this page, you have to restore its archived parent page." msgstr "" "Um diese Seite wiederherzustellen, müssen Sie ihre archivierte übergeordnete " From 8466b660c4730f450fb0495cdc9be6f3dae4cfb7 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Fri, 6 Dec 2024 13:59:04 +0100 Subject: [PATCH 09/52] [WIP] Page based access --- .../views/statistics/statistics_actions.py | 7 ++ integreat_cms/matomo_api/matomo_api_client.py | 96 ++++++++++++++++++- integreat_cms/matomo_api/utils.py | 23 +++++ 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 integreat_cms/matomo_api/utils.py diff --git a/integreat_cms/cms/views/statistics/statistics_actions.py b/integreat_cms/cms/views/statistics/statistics_actions.py index a0ecd82f84..f696778583 100644 --- a/integreat_cms/cms/views/statistics/statistics_actions.py +++ b/integreat_cms/cms/views/statistics/statistics_actions.py @@ -88,6 +88,13 @@ def get_visits_per_language_ajax( ) try: + region.statistics.get_page_based_statistics( + start_date=statistics_form.cleaned_data["start_date"], + end_date=statistics_form.cleaned_data["end_date"], + period=statistics_form.cleaned_data["period"], + region=region, + ) + result = region.statistics.get_visits_per_language( start_date=statistics_form.cleaned_data["start_date"], end_date=statistics_form.cleaned_data["end_date"], diff --git a/integreat_cms/matomo_api/matomo_api_client.py b/integreat_cms/matomo_api/matomo_api_client.py index e0af86421f..055e73ef8e 100644 --- a/integreat_cms/matomo_api/matomo_api_client.py +++ b/integreat_cms/matomo_api/matomo_api_client.py @@ -13,6 +13,7 @@ from django.utils.translation import gettext_lazy as _ from ..cms.constants import language_color, matomo_periods +from .utils import create_translation_slugs if TYPE_CHECKING: import sys @@ -23,7 +24,7 @@ from aiohttp import ClientSession from django.utils.functional import Promise - from ..cms.models import Language, Region + from ..cms.models import Language, Page, Region logger = logging.getLogger(__name__) @@ -527,3 +528,96 @@ def get_access_data( ), ] return data_entries, legend_entries + + def get_page_based_statistics( + self, + start_date: date, + end_date: date, + period: str, + region: Region, + ) -> list[dict[str, Any]]: + """ + Returns the statistics for each page of a region + + :param start_date: Start date + :param end_date: End date + :param period: The period (one of :attr:`~integreat_cms.cms.constants.matomo_periods.CHOICES`) + :param region: The region + :return: ? + :raises ~integreat_cms.matomo_api.matomo_api_client.MatomoException: When a :class:`~aiohttp.ClientError` was raised during a + """ + query_params = { + "date": f"{start_date},{end_date}", + "expanded": "1", + "filter_limit": "-1", + "format_metrics": "1", + "idSite": self.matomo_id, + "method": "VisitsSummary.getActions", + "period": period, + } + logger.debug( + "Query params: %r", + query_params, + ) + pages = region.get_pages() + languages = list(self.languages) + + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + # Execute async request to Matomo API + logger.debug("Fetching visits for languages %r asynchronously.", languages) + datasets = loop.run_until_complete( + self.get_page_based_statistics_async(loop, query_params, languages, pages) + ) + return datasets + + async def get_page_based_statistics_async( + self, + loop: AbstractEventLoop, + query_params: dict[str, Any], + languages: list[Language], + pages: list[Page], + ) -> list[dict[str, Any]]: + """ + Async wrapper to fetch the total visits with :mod:`aiohttp`. + Opens a :class:`~aiohttp.ClientSession`, creates a :class:`~asyncio.Task` for each language to call + :func:`~integreat_cms.matomo_api.matomo_api_client.MatomoApiClient.fetch` and waits for all tasks to finish with + :func:`~asyncio.gather`. + The returned list of gathered results has the correct order in which the tasks were created (at first the + ordered list of languages and the last element is the task for the total visits). + Called from :func:`~integreat_cms.matomo_api.matomo_api_client.MatomoApiClient.get_visits_per_language`. + + :param loop: The asyncio event loop + :param query_params: The parameters which are passed to the Matomo API + :param languages: The list of languages which should be retrieved + :param pages: The list of pages for which the data should be retrieved + :raises ~integreat_cms.matomo_api.matomo_api_client.MatomoException: When a :class:`~aiohttp.ClientError` was raised during a + Matomo API request + + :return: The list of gathered results + """ + create_translation_slugs(pages, languages) + async with aiohttp.ClientSession() as session: + # Create tasks for visits by language + tasks = [ + loop.create_task( + self.fetch( + session, + **query_params, + segment=f"pageUrl=@/{language.slug}/wp-json/extensions/v3/,pageUrl=@/api/v3/{self.region_slug}/{language.slug}/", + ) + ) + for language in languages + ] + + result = await asyncio.gather(*tasks) + # We're not retrieving the matomo id as part of the tasks, thus we know that the result is a list of dicts, not a list of list of ints. + if TYPE_CHECKING: + + def is_dict_list( + lst: list[dict[str, Any] | list[int]] + ) -> TypeGuard[list[dict[str, Any]]]: + return all(isinstance(d, dict) for d in lst) + + assert is_dict_list(result) + return result diff --git a/integreat_cms/matomo_api/utils.py b/integreat_cms/matomo_api/utils.py new file mode 100644 index 0000000000..0bfa220c26 --- /dev/null +++ b/integreat_cms/matomo_api/utils.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from typing import Dict, TYPE_CHECKING + +if TYPE_CHECKING: + from django.db.models.query import QuerySet + from ..cms.models import Language, Page + + +def create_translation_slugs( + pages: QuerySet[Page], languages: list[Language] +) -> dict[Page, dict[str, str]]: + """ + This function creates the translation slug for the matomo call. It returns then language_slug and the slug in the target translation. + """ + translation_slugs: Dict[Page, Dict[str, str]] = {} + for page in pages: + translation_slugs[page] = {} + for language in languages: + if page_translation := page.get_translation(language.slug): + # print(page_translation.ancestor_path) + translation_slugs[page][language.slug] = page_translation.slug + return translation_slugs From 813b751d472172b056eb597c8447f42fff43a95b Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Sat, 7 Dec 2024 12:51:29 +0100 Subject: [PATCH 10/52] [WIP] Restored commit --- .../views/statistics/statistics_actions.py | 2 +- integreat_cms/matomo_api/matomo_api_client.py | 154 +++++++++++++++++- integreat_cms/matomo_api/utils.py | 18 +- 3 files changed, 163 insertions(+), 11 deletions(-) diff --git a/integreat_cms/cms/views/statistics/statistics_actions.py b/integreat_cms/cms/views/statistics/statistics_actions.py index f696778583..9b5cd7f197 100644 --- a/integreat_cms/cms/views/statistics/statistics_actions.py +++ b/integreat_cms/cms/views/statistics/statistics_actions.py @@ -88,7 +88,7 @@ def get_visits_per_language_ajax( ) try: - region.statistics.get_page_based_statistics( + region.statistics.get_page_based_accesses( start_date=statistics_form.cleaned_data["start_date"], end_date=statistics_form.cleaned_data["end_date"], period=statistics_form.cleaned_data["period"], diff --git a/integreat_cms/matomo_api/matomo_api_client.py b/integreat_cms/matomo_api/matomo_api_client.py index 055e73ef8e..3c2437566b 100644 --- a/integreat_cms/matomo_api/matomo_api_client.py +++ b/integreat_cms/matomo_api/matomo_api_client.py @@ -4,6 +4,7 @@ import logging import re from datetime import date, datetime +from collections.abc import Mapping from typing import TYPE_CHECKING from urllib.parse import urlencode @@ -13,7 +14,7 @@ from django.utils.translation import gettext_lazy as _ from ..cms.constants import language_color, matomo_periods -from .utils import create_translation_slugs +from .utils import get_translation_slug if TYPE_CHECKING: import sys @@ -528,7 +529,7 @@ def get_access_data( ), ] return data_entries, legend_entries - + """ def get_page_based_statistics( self, start_date: date, @@ -536,7 +537,6 @@ def get_page_based_statistics( period: str, region: Region, ) -> list[dict[str, Any]]: - """ Returns the statistics for each page of a region :param start_date: Start date @@ -545,7 +545,6 @@ def get_page_based_statistics( :param region: The region :return: ? :raises ~integreat_cms.matomo_api.matomo_api_client.MatomoException: When a :class:`~aiohttp.ClientError` was raised during a - """ query_params = { "date": f"{start_date},{end_date}", "expanded": "1", @@ -578,7 +577,6 @@ async def get_page_based_statistics_async( languages: list[Language], pages: list[Page], ) -> list[dict[str, Any]]: - """ Async wrapper to fetch the total visits with :mod:`aiohttp`. Opens a :class:`~aiohttp.ClientSession`, creates a :class:`~asyncio.Task` for each language to call :func:`~integreat_cms.matomo_api.matomo_api_client.MatomoApiClient.fetch` and waits for all tasks to finish with @@ -595,7 +593,6 @@ async def get_page_based_statistics_async( Matomo API request :return: The list of gathered results - """ create_translation_slugs(pages, languages) async with aiohttp.ClientSession() as session: # Create tasks for visits by language @@ -621,3 +618,148 @@ def is_dict_list( assert is_dict_list(result) return result + """ + + async def get_visits_per_page_and_language_async( + self, + loop: AbstractEventLoop, + query_params: dict[str, Any], + languages: list[Language], + pages: list[Page], + region_slug: str, + ) -> list[dict[str, Any]]: + translation_slugs = get_translation_slug(pages, languages) + print(translation_slugs) + async with aiohttp.ClientSession() as session: + # Create tasks for visits by language + tasks = [ + loop.create_task( + self.nice_function(session, query_params, region_slug=region_slug, page_id=page_id, lang_slug=lang_slug, full_slug=full_slug) + ) + for page_id, langs in translation_slugs.items() + for lang_slug, full_slug in langs.items() + ] + # Wait for all tasks to finish and collect the results + # (the results are sorted in the order the tasks were created) + result = await asyncio.gather(*tasks) + # We're not retrieving the matomo id as part of the tasks, thus we know that the result is a list of dicts, not a list of list of ints. + if TYPE_CHECKING: + + def is_dict_list( + lst: list[dict[str, Any] | list[int]] + ) -> TypeGuard[list[dict[str, Any]]]: + return all(isinstance(d, dict) for d in lst) + + assert is_dict_list(result) + return result + + async def nice_function(self, session: aiohttp.ClientSession, query_params: dict[str, Any], region_slug: str, page_id: int, lang_slug: str, full_slug: str) -> dict: + print(f"full slug: {full_slug}") + return { + page_id: { + lang_slug: await self.fetch( session, **query_params, segment=f"pageUrl=@/children/?depth=2&url=/{region_slug}/{full_slug}" ) + } + } + + def get_page_based_accesses(self, start_date: date, end_date: date, period: str, region: Region): + from ..cms.models import Page, Language + + language1 = Language.objects.get(id=1) + language2 = Language.objects.get(id=2) + language3 = Language.objects.get(id=3) + print("get_page_based_accesses") + + query_params = { + "date": f"{start_date},{end_date}", + "expanded": "1", + "filter_limit": "-1", + "format_metrics": "1", + "idSite": self.matomo_id, + "method": "VisitsSummary.getActions", + "period": period, + } + languages = [language1, language2, language3] + pages = region.get_pages() + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + # Execute async request to Matomo API + logger.debug("Fetching visits for languages %r asynchronously.", languages) + datasets = loop.run_until_complete( + self.get_visits_per_page_and_language_async(loop, query_params, languages, pages, region.slug) + ) + + def deep_merge(*dicts): + """ + Recursively merges dictionaries. Values in later dictionaries override earlier ones + for non-dict values, while dictionaries are merged recursively. + """ + merged = {} + for d in dicts: + for key, value in d.items(): + if key in merged and isinstance(merged[key], Mapping) and isinstance(value, Mapping): + # Recursively merge dictionaries + merged[key] = deep_merge(merged[key], value) + else: + # Otherwise, override or add the value + merged[key] = value + return merged + + datasets = deep_merge(*datasets) + + """datasets = { + key: value + for result in datasets + for key, value in result.items() + }""" + print(f"Datasets: {datasets}") + + """ + # alle Daten zu holen + raw_data = [ + { + "slug": "/sprachkurse/integrationskurse", + "language_slug": "de", + "accesses": 8, + }, + { + "slug": "/language_courses/integration_courses", + "language_slug": "de", + "accesses": 8, + }, + ] + + # strukturieren + # page id raus finden + + translations = PageTranslations.objects.all() + # nicht aktuelle Versionen rausfiltern + for value in data: + # page id raus finden + + translations = PageTranslations.objects.all() + # nicht aktuelle Versionen rausfiltern + for value in data: + gesuchte_translation = translations.get(slug=value.slug) + # erstelle aus der gesuchter translation ein dict + { + "id": gesuchte_translation.id, #654 + "access": value.accesses, #88 + "language_slug": value.language_slug, # "en" + "page_id": gesuchte_translation.page.id, #14 + } + # rausfinden welche page translations dazu gehören + # gibt es mit meiner page_id schon ein dict? + # wenn nein, erstelle neues dict und fülle mit informationen aus, die du hast + + # wenn ja, dann + # hänge nur meinen language slug in accesses an + # end_data.access[language_slug] = current_data.access + + # schicke ich strukturiert an AJAX + end_data = [ + { + "page_id": 14, + "accesses": {"de": 123, "en":23, "fr":312, "ar": 76543} + } + {"page_id": 1, "de": 12, "ru": 1}, + """ diff --git a/integreat_cms/matomo_api/utils.py b/integreat_cms/matomo_api/utils.py index 0bfa220c26..55271d798e 100644 --- a/integreat_cms/matomo_api/utils.py +++ b/integreat_cms/matomo_api/utils.py @@ -7,17 +7,27 @@ from ..cms.models import Language, Page -def create_translation_slugs( +"""def create_translation_slugs( pages: QuerySet[Page], languages: list[Language] ) -> dict[Page, dict[str, str]]: - """ This function creates the translation slug for the matomo call. It returns then language_slug and the slug in the target translation. - """ translation_slugs: Dict[Page, Dict[str, str]] = {} for page in pages: translation_slugs[page] = {} for language in languages: if page_translation := page.get_translation(language.slug): - # print(page_translation.ancestor_path) + # print(page_translation.ancestor_path)Sollen wir uns Sol translation_slugs[page][language.slug] = page_translation.slug return translation_slugs + """ + +def get_translation_slug(pages: list[Page], languages: list[Language]) -> dict[int, dict[str, str]]: + translation_slugs = {} + for page in pages: + for language in languages: + if page_translation := page.get_translation(language.slug): + if page.id not in translation_slugs: + translation_slugs[page.id] = {} + translation_slugs[page.id][language.slug] = f"{language.slug}/{page_translation.slug}" # nach richtigen Slug noch mal recherchieren + return translation_slugs + From 04ce650ef89a523769bd3061d66261ab3ff89a3f Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Mon, 9 Dec 2024 14:37:09 +0000 Subject: [PATCH 11/52] Fix loading of childnodes --- .../cms/templates/pages/page_tree.html | 2 +- integreat_cms/cms/urls/protected.py | 13 ++++++++++--- integreat_cms/cms/views/pages/page_tree_view.py | 17 +++++++++-------- .../cms/views/pages/partial_page_tree_view.py | 15 ++++++--------- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/integreat_cms/cms/templates/pages/page_tree.html b/integreat_cms/cms/templates/pages/page_tree.html index a949941db9..d367c56015 100644 --- a/integreat_cms/cms/templates/pages/page_tree.html +++ b/integreat_cms/cms/templates/pages/page_tree.html @@ -75,7 +75,7 @@

{% csrf_token %}
- diff --git a/integreat_cms/cms/urls/protected.py b/integreat_cms/cms/urls/protected.py index 4e3ae8c9b8..53b73b5524 100644 --- a/integreat_cms/cms/urls/protected.py +++ b/integreat_cms/cms/urls/protected.py @@ -647,9 +647,16 @@ name="render_mirrored_page_field", ), path( - "partial-page-tree/", - pages.render_partial_page_tree_views, - name="get_page_tree_ajax", + "/", + include( + [ + path( + "partial-page-tree/", + pages.render_partial_page_tree_views, + name="get_page_tree_ajax", + ), + ] + ), ), ] ), diff --git a/integreat_cms/cms/views/pages/page_tree_view.py b/integreat_cms/cms/views/pages/page_tree_view.py index 1e601b1ac8..428df28ab3 100644 --- a/integreat_cms/cms/views/pages/page_tree_view.py +++ b/integreat_cms/cms/views/pages/page_tree_view.py @@ -109,9 +109,7 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ), ) else: - messages.warning( - request, _("You don't have the permission to edit or create pages.") - ) + messages.warning(request, _("You don't have the permission to edit or create pages.")) # Initialize page filter form filter_form = PageFilterForm(data=request.GET) @@ -125,11 +123,14 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ) # Cache tree structure to reduce database queries - pages = ( - page_queryset.prefetch_major_translations() - .prefetch_related("mirroring_pages") - .cache_tree(archived=self.archived) - ) + pages = page_queryset.prefetch_major_translations().prefetch_related("mirroring_pages").cache_tree(archived=self.archived) + + # When only archived pages are requested, remove implicitly archived pages since they are handeld by loading the partial page tree + if self.archived: + archived_pages = region.archived_pages + for page in archived_pages: + if page.implicitly_archived: + pages.remove(page) # Filter pages according to given filters, if any pages = filter_form.apply(pages, language_slug) diff --git a/integreat_cms/cms/views/pages/partial_page_tree_view.py b/integreat_cms/cms/views/pages/partial_page_tree_view.py index 4f8ac2a997..aff2fc2afd 100644 --- a/integreat_cms/cms/views/pages/partial_page_tree_view.py +++ b/integreat_cms/cms/views/pages/partial_page_tree_view.py @@ -16,9 +16,7 @@ @permission_required("cms.view_page") @require_POST # pylint: disable=unused-argument -def render_partial_page_tree_views( - request: HttpRequest, region_slug: str, language_slug: str -) -> JsonResponse: +def render_partial_page_tree_views(request: HttpRequest, region_slug: str, language_slug: str, is_archive: bool) -> JsonResponse: r""" Retrieve the rendered subtree of a given root page @@ -32,14 +30,12 @@ def render_partial_page_tree_views( region = request.region language = region.get_language_or_404(language_slug, only_active=True) + # Convert is_archive from String to Boolean + is_archive = eval(is_archive) + backend_language = Language.objects.filter(slug=get_language()).first() - all_pages = ( - region.pages.filter(tree_id__in=requested_tree_ids) - .prefetch_major_translations() - .prefetch_related("mirroring_pages") - .cache_tree(archived=False) - ) + all_pages = region.pages.filter(tree_id__in=requested_tree_ids).prefetch_major_translations().prefetch_related("mirroring_pages").cache_tree(archived=is_archive) pages_by_id = defaultdict(list) for page in all_pages: @@ -66,6 +62,7 @@ def render_partial_page_tree_views( "language": language, "languages": region.active_languages, "parent_id": parent.id, + "is_archive": is_archive, }, request, ) From aed75ed3bd6b1812eeb037a26524bad2b9f53462 Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Mon, 9 Dec 2024 14:46:47 +0000 Subject: [PATCH 12/52] Fix codestyle --- .../cms/templates/pages/page_tree_node.html | 18 +++++++++--------- .../cms/views/pages/page_tree_view.py | 10 ++++++++-- .../cms/views/pages/partial_page_tree_view.py | 11 +++++++++-- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/integreat_cms/cms/templates/pages/page_tree_node.html b/integreat_cms/cms/templates/pages/page_tree_node.html index ce46cf6c59..8f18250a43 100644 --- a/integreat_cms/cms/templates/pages/page_tree_node.html +++ b/integreat_cms/cms/templates/pages/page_tree_node.html @@ -30,21 +30,21 @@ {% has_perm 'cms.change_page_object' request.user as can_edit_pages %} + {% if not filter_form.is_enabled %} + {% has_perm 'cms.change_page_object' request.user as can_edit_pages %} + + {% endif %} + + {% if not backend_language %} + {% get_current_language as LANGUAGE_CODE %} + {% get_language LANGUAGE_CODE as backend_language %} + {% endif %} + {% if backend_language != language %} + + {% endif %} +{% endblock content %} \ No newline at end of file diff --git a/integreat_cms/cms/templates/pages/page_tree_node.html b/integreat_cms/cms/templates/pages/page_tree_node.html index 8f18250a43..22192e3075 100644 --- a/integreat_cms/cms/templates/pages/page_tree_node.html +++ b/integreat_cms/cms/templates/pages/page_tree_node.html @@ -20,66 +20,7 @@ data-drop-id="{{ page.id }}" data-drop-position="last-child" class="page-row drop drop-on border-t border-b border-solid border-gray-200 hover:bg-gray-100 level-{{ page.relative_depth }} {% if parent_id %}hidden parent-page-{{ parent_id }}{% endif %}"> - - {% if not filter_form.is_enabled %} - {% has_perm 'cms.change_page_object' request.user as can_edit_pages %} - - {% endif %} - - {% if not backend_language %} - {% get_current_language as LANGUAGE_CODE %} - {% get_language LANGUAGE_CODE as backend_language %} - {% endif %} - {% if backend_language != language %} - - {% endif %} + {% include "pages/_generic_page_tree_node.html" %} {% if not is_archive %} - - {% if not filter_form.is_enabled %} - - {% endif %} - - {% if backend_language and backend_language != language %} - - {% endif %} - - - - - diff --git a/integreat_cms/cms/views/pages/page_tree_view.py b/integreat_cms/cms/views/pages/page_tree_view.py index 0668ed0529..ce22617a6c 100644 --- a/integreat_cms/cms/views/pages/page_tree_view.py +++ b/integreat_cms/cms/views/pages/page_tree_view.py @@ -33,8 +33,6 @@ class PageTreeView(TemplateView, PageContextMixin, MachineTranslationContextMixi #: Template for list of non-archived pages template = "pages/page_tree.html" - #: Template for list of archived pages - template_archived = "pages/page_tree_archived.html" #: Whether or not to show archived pages archived = False #: The translation model of this list view (used to determine whether machine translations are permitted) diff --git a/integreat_cms/cms/views/pages/partial_page_tree_view.py b/integreat_cms/cms/views/pages/partial_page_tree_view.py index 0a02adf4b6..901ff3eb85 100644 --- a/integreat_cms/cms/views/pages/partial_page_tree_view.py +++ b/integreat_cms/cms/views/pages/partial_page_tree_view.py @@ -16,7 +16,9 @@ @permission_required("cms.view_page") @require_POST # pylint: disable=unused-argument -def render_partial_page_tree_views(request: HttpRequest, region_slug: str, language_slug: str, is_archive: str) -> JsonResponse: +def render_partial_page_tree_views( + request: HttpRequest, region_slug: str, language_slug: str, is_archive: str +) -> JsonResponse: r""" Retrieve the rendered subtree of a given root page @@ -36,7 +38,12 @@ def render_partial_page_tree_views(request: HttpRequest, region_slug: str, langu backend_language = Language.objects.filter(slug=get_language()).first() - all_pages = region.pages.filter(tree_id__in=requested_tree_ids).prefetch_major_translations().prefetch_related("mirroring_pages").cache_tree(archived=is_archive) + all_pages = ( + region.pages.filter(tree_id__in=requested_tree_ids) + .prefetch_major_translations() + .prefetch_related("mirroring_pages") + .cache_tree(archived=is_archive) + ) pages_by_id = defaultdict(list) for page in all_pages: diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index 5d52a5ba15..ce512e4db4 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -1682,9 +1682,8 @@ msgstr "Versteckt" #: cms/constants/region_status.py cms/models/pages/page_translation.py #: cms/models/regions/region.py cms/templates/events/event_form.html -#: cms/templates/pages/page_form.html -#: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pages/page_tree_node.html cms/templates/pois/poi_form.html +#: cms/templates/pages/page_form.html cms/templates/pages/page_tree_node.html +#: cms/templates/pois/poi_form.html msgid "Archived" msgstr "Archiviert" @@ -1757,7 +1756,6 @@ msgstr "Rechts nach Links" #: cms/constants/translation_status.py cms/templates/_form_language_tabs.html #: cms/templates/events/event_list_row.html #: cms/templates/imprint/imprint_sbs.html cms/templates/pages/page_sbs.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html cms/templates/pois/poi_list_row.html msgid "Translation up-to-date" msgstr "Übersetzung ist aktuell" @@ -1765,7 +1763,6 @@ msgstr "Übersetzung ist aktuell" #: cms/constants/translation_status.py cms/templates/_form_language_tabs.html #: cms/templates/events/event_list_row.html #: cms/templates/imprint/imprint_sbs.html cms/templates/pages/page_sbs.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html cms/templates/pois/poi_list_row.html msgid "Currently in translation" msgstr "Wird derzeit übersetzt" @@ -1773,14 +1770,12 @@ msgstr "Wird derzeit übersetzt" #: cms/constants/translation_status.py cms/templates/_form_language_tabs.html #: cms/templates/events/event_list_row.html #: cms/templates/imprint/imprint_sbs.html cms/templates/pages/page_sbs.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html cms/templates/pois/poi_list_row.html msgid "Translation outdated" msgstr "Übersetzung ist veraltet" #: cms/constants/translation_status.py cms/templates/_form_language_tabs.html #: cms/templates/events/event_list_row.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html #: cms/templates/poicategories/poicategory_list_row.html #: cms/templates/pois/poi_list_row.html @@ -2025,9 +2020,7 @@ msgstr "Kategorie" #: cms/templates/imprint/imprint_form.html #: cms/templates/imprint/imprint_sbs.html #: cms/templates/linkcheck/links_by_filter.html -#: cms/templates/pages/page_sbs.html -#: cms/templates/pages/page_tree_archived.html -#: cms/templates/pois/poi_list_archived.html +#: cms/templates/pages/page_sbs.html cms/templates/pois/poi_list_archived.html #: cms/templates/push_notifications/push_notification_form.html #: cms/templates/regions/region_list.html msgid "Status" @@ -3486,8 +3479,7 @@ msgstr "Ob die Seite explizit archiviert ist oder nicht" #: cms/models/pages/abstract_base_page_translation.py #: cms/templates/events/event_form.html cms/templates/events/event_list.html #: cms/templates/pages/_generic_page_tree.html -#: cms/templates/pages/page_form.html -#: cms/templates/pages/page_tree_archived.html cms/templates/pois/poi_form.html +#: cms/templates/pages/page_form.html cms/templates/pois/poi_form.html #: cms/templates/pois/poi_list.html cms/templates/pois/poi_list_archived.html msgid "Title in" msgstr "Titel auf" @@ -4774,7 +4766,6 @@ msgstr "Inhalt ansehen" #: cms/templates/_related_contents_table.html #: cms/templates/events/event_list_row.html #: cms/templates/pages/_generic_page_tree_node.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pois/poi_list_archived_row.html #: cms/templates/pois/poi_list_row.html msgid "Translation not available" @@ -5343,7 +5334,6 @@ msgstr "Kontakt archivieren" #: cms/templates/contacts/contact_form.html #: cms/templates/pages/page_form_sidebar/actions_box.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html msgid "Restore page" msgstr "Seite wiederherstellen" @@ -5410,8 +5400,7 @@ msgstr "Webseite" #: cms/templates/linkcheck/links_by_filter.html #: cms/templates/offertemplates/offertemplate_list.html #: cms/templates/organizations/organization_list.html -#: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html cms/templates/pois/poi_list.html +#: cms/templates/pages/page_tree.html cms/templates/pois/poi_list.html #: cms/templates/pois/poi_list_archived.html cms/templates/roles/role_list.html msgid "Options" msgstr "Optionen" @@ -5437,8 +5426,7 @@ msgstr "Kontakte ausgewählt" #: cms/templates/languagetreenodes/languagetreenode_list.html #: cms/templates/linkcheck/links_by_filter.html #: cms/templates/organizations/organization_list.html -#: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html cms/templates/pois/poi_list.html +#: cms/templates/pages/page_tree.html cms/templates/pois/poi_list.html #: cms/templates/pois/poi_list_archived.html msgid "Select bulk action" msgstr "Mehrfachaktion auswählen" @@ -5465,8 +5453,7 @@ msgstr "Kontakte löschen" #: cms/templates/linkcheck/links_by_filter.html #: cms/templates/organizations/organization_list.html #: cms/templates/pages/_page_xliff_export_overlay.html -#: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html cms/templates/pois/poi_list.html +#: cms/templates/pages/page_tree.html cms/templates/pois/poi_list.html #: cms/templates/pois/poi_list_archived.html msgid "Execute" msgstr "Ausführen" @@ -6010,9 +5997,7 @@ msgstr "Zurück zu Veranstaltungen" #: cms/templates/feedback/admin_feedback_list_archived.html #: cms/templates/feedback/region_feedback_list.html #: cms/templates/feedback/region_feedback_list_archived.html -#: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html -#: cms/templates/users/user_list.html +#: cms/templates/pages/page_tree.html cms/templates/users/user_list.html msgid "Show filters" msgstr "Filter einblenden" @@ -6021,9 +6006,7 @@ msgstr "Filter einblenden" #: cms/templates/feedback/admin_feedback_list_archived.html #: cms/templates/feedback/region_feedback_list.html #: cms/templates/feedback/region_feedback_list_archived.html -#: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html -#: cms/templates/users/user_list.html +#: cms/templates/pages/page_tree.html cms/templates/users/user_list.html msgid "Hide filters" msgstr "Filter ausblenden" @@ -6205,7 +6188,6 @@ msgstr "Zuletzt geändert von" #: cms/templates/pages/_page_filter_form.html #: cms/templates/pages/_page_xliff_import_diff.html #: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html #: cms/templates/push_notifications/push_notification_list.html #: cms/templates/push_notifications/push_notification_template_list.html #: cms/templates/regions/region_list.html @@ -6725,7 +6707,6 @@ msgstr "Sprach-Knoten hinzufügen" #: cms/templates/languagetreenodes/languagetreenode_list.html #: cms/templates/pages/_generic_page_tree.html -#: cms/templates/pages/page_tree_archived.html msgid "Hierarchy" msgstr "Hierarchie" @@ -7084,17 +7065,14 @@ msgid "Change the order and position of the pages with drag & drop." msgstr "Ändern Sie die Reihenfolge und Position der Seiten mit Drag & Drop." #: cms/templates/pages/_generic_page_tree_node.html -#: cms/templates/pages/page_tree_archived_node.html msgid "Drag & drop is disabled for archived pages." msgstr "Drag & drop ist für archivierte Seiten nicht möglich." #: cms/templates/pages/_generic_page_tree_node.html -#: cms/templates/pages/page_tree_archived_node.html msgid "Expand all subpages" msgstr "Alle Unterseiten ausklappen" #: cms/templates/pages/_generic_page_tree_node.html -#: cms/templates/pages/page_tree_archived_node.html msgid "Collapse all subpages" msgstr "Alle Unterseiten einklappen" @@ -7190,9 +7168,7 @@ msgstr "Übersetzung wird durchgeführt" msgid "Cancel translation process" msgstr "Übersetzungsprozess abbrechen" -#: cms/templates/pages/page_form.html -#: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/page_form.html cms/templates/pages/page_tree_node.html msgid "Archived, because a parent page is archived" msgstr "Archiviert, weil eine übergeordnete Seite archiviert ist" @@ -7247,13 +7223,11 @@ msgstr[1] "" "diesen Seiten entfernen:" #: cms/templates/pages/page_form_sidebar/actions_box.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html msgid "Delete page" msgstr "Seite löschen" #: cms/templates/pages/page_form_sidebar/actions_box.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html cms/views/pages/page_actions.py msgid "You cannot delete a page which has subpages." msgstr "Sie können keine Seite löschen, die Unterseiten besitzt." @@ -7270,7 +7244,6 @@ msgstr[1] "" "verschieben:" #: cms/templates/pages/page_form_sidebar/actions_box.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html cms/views/pages/page_actions.py msgid "" "This page cannot be deleted because it was embedded as live content from " @@ -7377,12 +7350,10 @@ msgstr "" "generieren" #: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html msgid "Archived Pages" msgstr "Archivierte Seiten" #: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html msgid "Back to pages" msgstr "Zurück zu Seiten" @@ -7462,7 +7433,6 @@ msgid "Export selected pages for multilingual translation" msgstr "Ausgewählte Seiten für die mehrsprachige Übersetzung exportieren" #: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html msgid "Restore pages" msgstr "Seiten wiederherstellen" @@ -7486,20 +7456,10 @@ msgstr "und {} weitere Dateien" msgid "Import" msgstr "Importieren" -#: cms/templates/pages/page_tree_archived.html -msgid "No archived pages found with these filters." -msgstr "Keine archivierten Seiten mit diesen Filtern gefunden." - -#: cms/templates/pages/page_tree_archived.html -msgid "No pages archived yet." -msgstr "Noch keine Seiten archiviert." - -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html msgid "This page is archived." msgstr "Diese Seite ist archiviert." -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html msgid "" "This page is archived, because at least one of its parent pages is archived." @@ -7507,27 +7467,14 @@ msgstr "" "Diese Seite ist archiviert, weil eine ihrer übergeordneten Seiten archiviert " "ist." -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html msgid "View page as preview" msgstr "Seite als Vorschau ansehen" -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html msgid "You cannot preview an empty page." msgstr "Eine leere Seite hat keine Vorschau" -#: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pages/page_tree_node.html -msgid "To restore this page, you have to restore its archived parent page." -msgstr "" -"Um diese Seite wiederherzustellen, müssen Sie ihre archivierte übergeordnete " -"Seite wiederherstellen." - -#: cms/templates/pages/page_tree_archived_node.html -msgid "This also involves non-archived subpages." -msgstr "Dies betrifft auch nicht-archivierte Unterseiten." - #: cms/templates/pages/page_tree_node.html msgid "Open page in web app" msgstr "Seite in der Web-App öffnen" @@ -7550,6 +7497,12 @@ msgstr "" "Diese Seite kann nicht archiviert werden, da sie von einer anderen Seite als " "Live-Inhalt eingebunden wurde." +#: cms/templates/pages/page_tree_node.html +msgid "To restore this page, you have to restore its archived parent page." +msgstr "" +"Um diese Seite wiederherzustellen, müssen Sie ihre archivierte übergeordnete " +"Seite wiederherstellen." + #: cms/templates/pages/page_tree_node.html msgid "This also involves archived subpages." msgstr "Dies betrifft auch archivierte Unterseiten." @@ -11162,6 +11115,15 @@ msgstr "" "Diese Seite konnte nicht importiert werden, da sie zu einer anderen Region " "gehört ({})." +#~ msgid "No archived pages found with these filters." +#~ msgstr "Keine archivierten Seiten mit diesen Filtern gefunden." + +#~ msgid "No pages archived yet." +#~ msgstr "Noch keine Seiten archiviert." + +#~ msgid "This also involves non-archived subpages." +#~ msgstr "Dies betrifft auch nicht-archivierte Unterseiten." + #~ msgid "Individual languages can be hidden by clicking on the labels." #~ msgstr "" #~ "Einzelne Sprachen können durch Anklicken der Beschriftungen ausgeblendet " From 4fd9720e65316c79caf49bbc719853842d6c5649 Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Mon, 9 Dec 2024 16:59:21 +0000 Subject: [PATCH 16/52] Rename page_tree to pages_page_tree --- .../templates/pages/_page_tree_children.html | 2 +- .../{page_tree.html => pages_page_tree.html} | 2 +- ...ee_node.html => pages_page_tree_node.html} | 0 .../cms/views/pages/page_tree_view.py | 2 +- integreat_cms/locale/de/LC_MESSAGES/django.po | 176 +++++++++--------- .../static/src/js/tree-drag-and-drop.ts | 2 +- 6 files changed, 96 insertions(+), 88 deletions(-) rename integreat_cms/cms/templates/pages/{page_tree.html => pages_page_tree.html} (99%) rename integreat_cms/cms/templates/pages/{page_tree_node.html => pages_page_tree_node.html} (100%) diff --git a/integreat_cms/cms/templates/pages/_page_tree_children.html b/integreat_cms/cms/templates/pages/_page_tree_children.html index cfd8b73c28..b084ca457e 100644 --- a/integreat_cms/cms/templates/pages/_page_tree_children.html +++ b/integreat_cms/cms/templates/pages/_page_tree_children.html @@ -3,7 +3,7 @@
+ data-node-descendants="{{ page|get_descendant_ids }}" + class="drag cursor-move text-gray-800 inline-block pl-4 align-middle" + draggable="{% if can_edit_pages and not is_archive %}true{% else %}false{% endif %}" + title="{% if not is_archive %}{% translate "Change the order and position of the pages with drag & drop." %}{% else %}{% translate "Drag & drop is disabled for archived pages." %}{% endif %}"> {% if can_edit_pages %} {% endif %} {% if not page.parent_id and not page.is_leaf or page.parent_id and page.cached_children|length > 0 %} + data-expand-title="{% translate "Expand all subpages" %}" + data-collapse-title="{% translate "Collapse all subpages" %}" + data-page-id="{{ page.id }}" + data-page-children="{{ page|get_children_ids }}" + data-page-tree-id="{{ page.tree_id }}"> {% endif %} diff --git a/integreat_cms/cms/views/pages/page_tree_view.py b/integreat_cms/cms/views/pages/page_tree_view.py index 428df28ab3..0668ed0529 100644 --- a/integreat_cms/cms/views/pages/page_tree_view.py +++ b/integreat_cms/cms/views/pages/page_tree_view.py @@ -109,7 +109,9 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ), ) else: - messages.warning(request, _("You don't have the permission to edit or create pages.")) + messages.warning( + request, _("You don't have the permission to edit or create pages.") + ) # Initialize page filter form filter_form = PageFilterForm(data=request.GET) @@ -123,7 +125,11 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ) # Cache tree structure to reduce database queries - pages = page_queryset.prefetch_major_translations().prefetch_related("mirroring_pages").cache_tree(archived=self.archived) + pages = ( + page_queryset.prefetch_major_translations() + .prefetch_related("mirroring_pages") + .cache_tree(archived=self.archived) + ) # When only archived pages are requested, remove implicitly archived pages since they are handeld by loading the partial page tree if self.archived: diff --git a/integreat_cms/cms/views/pages/partial_page_tree_view.py b/integreat_cms/cms/views/pages/partial_page_tree_view.py index aff2fc2afd..21c3c3a2be 100644 --- a/integreat_cms/cms/views/pages/partial_page_tree_view.py +++ b/integreat_cms/cms/views/pages/partial_page_tree_view.py @@ -16,7 +16,9 @@ @permission_required("cms.view_page") @require_POST # pylint: disable=unused-argument -def render_partial_page_tree_views(request: HttpRequest, region_slug: str, language_slug: str, is_archive: bool) -> JsonResponse: +def render_partial_page_tree_views( + request: HttpRequest, region_slug: str, language_slug: str, is_archive: bool +) -> JsonResponse: r""" Retrieve the rendered subtree of a given root page @@ -35,7 +37,12 @@ def render_partial_page_tree_views(request: HttpRequest, region_slug: str, langu backend_language = Language.objects.filter(slug=get_language()).first() - all_pages = region.pages.filter(tree_id__in=requested_tree_ids).prefetch_major_translations().prefetch_related("mirroring_pages").cache_tree(archived=is_archive) + all_pages = ( + region.pages.filter(tree_id__in=requested_tree_ids) + .prefetch_major_translations() + .prefetch_related("mirroring_pages") + .cache_tree(archived=is_archive) + ) pages_by_id = defaultdict(list) for page in all_pages: From 05c2ad10ca7854073df9303a38c6799ba7b119b6 Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Mon, 9 Dec 2024 16:08:40 +0000 Subject: [PATCH 13/52] Add _generic_page_tree_node.hmtl --- .../pages/_generic_page_tree_node.html | 68 +++++++++++++++++++ .../cms/templates/pages/page_tree_node.html | 61 +---------------- .../cms/views/pages/partial_page_tree_view.py | 12 +--- 3 files changed, 72 insertions(+), 69 deletions(-) create mode 100644 integreat_cms/cms/templates/pages/_generic_page_tree_node.html diff --git a/integreat_cms/cms/templates/pages/_generic_page_tree_node.html b/integreat_cms/cms/templates/pages/_generic_page_tree_node.html new file mode 100644 index 0000000000..6aaa4d95a9 --- /dev/null +++ b/integreat_cms/cms/templates/pages/_generic_page_tree_node.html @@ -0,0 +1,68 @@ +{% load i18n %} +{% load content_filters %} +{% load page_filters %} +{% load tree_filters %} +{% load rules %} +{% get_translation page language.slug as page_translation %} +{% block content %} + + + + + {% if can_edit_pages %} + + {% endif %} + + {% if not page.parent_id and not page.is_leaf or page.parent_id and page.cached_children|length > 0 %} + + + + {% endif %} + + + {% if page_translation %} + {{ page_translation.title }} + {% else %} + {% translate "Translation not available" %} + {% endif %} + + + +
+ {% if page.backend_translation %} + {{ page.backend_translation.title }} + {% else %} + {% translate "Translation not available" %} + {% endif %} +
+
+
- - - - {% if can_edit_pages %} - - {% endif %} - - {% if not page.parent_id and not page.is_leaf or page.parent_id and page.cached_children|length > 0 %} - - - - {% endif %} - - - {% if page_translation %} - {{ page_translation.title }} - {% else %} - {% translate "Translation not available" %} - {% endif %} - - - -
- {% if page.backend_translation %} - {{ page.backend_translation.title }} - {% else %} - {% translate "Translation not available" %} - {% endif %} -
-
-
diff --git a/integreat_cms/cms/views/pages/partial_page_tree_view.py b/integreat_cms/cms/views/pages/partial_page_tree_view.py index 21c3c3a2be..0a02adf4b6 100644 --- a/integreat_cms/cms/views/pages/partial_page_tree_view.py +++ b/integreat_cms/cms/views/pages/partial_page_tree_view.py @@ -16,15 +16,14 @@ @permission_required("cms.view_page") @require_POST # pylint: disable=unused-argument -def render_partial_page_tree_views( - request: HttpRequest, region_slug: str, language_slug: str, is_archive: bool -) -> JsonResponse: +def render_partial_page_tree_views(request: HttpRequest, region_slug: str, language_slug: str, is_archive: str) -> JsonResponse: r""" Retrieve the rendered subtree of a given root page :param request: The current request :param region_slug: The slug of the current region :param language_slug: The slug of the current language + :param is_archive: True when only archive pages are requested, False otherwise :return: The rendered template responses """ requested_tree_ids = [int(i) for i in json.loads(request.body.decode("utf-8"))] @@ -37,12 +36,7 @@ def render_partial_page_tree_views( backend_language = Language.objects.filter(slug=get_language()).first() - all_pages = ( - region.pages.filter(tree_id__in=requested_tree_ids) - .prefetch_major_translations() - .prefetch_related("mirroring_pages") - .cache_tree(archived=is_archive) - ) + all_pages = region.pages.filter(tree_id__in=requested_tree_ids).prefetch_major_translations().prefetch_related("mirroring_pages").cache_tree(archived=is_archive) pages_by_id = defaultdict(list) for page in all_pages: From 71289040496426014b421c3815aa0e4967d34ad6 Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Mon, 9 Dec 2024 16:11:24 +0000 Subject: [PATCH 14/52] update translations --- integreat_cms/locale/de/LC_MESSAGES/django.po | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index d6b3b1eeff..5d52a5ba15 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -4773,8 +4773,8 @@ msgstr "Inhalt ansehen" #: cms/templates/_related_contents_table.html #: cms/templates/events/event_list_row.html +#: cms/templates/pages/_generic_page_tree_node.html #: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pages/page_tree_node.html #: cms/templates/pois/poi_list_archived_row.html #: cms/templates/pois/poi_list_row.html msgid "Translation not available" @@ -7079,6 +7079,25 @@ msgstr "Organisation wiederherstellen" msgid "Archive organization" msgstr "Organisationen archivieren" +#: cms/templates/pages/_generic_page_tree_node.html +msgid "Change the order and position of the pages with drag & drop." +msgstr "Ändern Sie die Reihenfolge und Position der Seiten mit Drag & Drop." + +#: cms/templates/pages/_generic_page_tree_node.html +#: cms/templates/pages/page_tree_archived_node.html +msgid "Drag & drop is disabled for archived pages." +msgstr "Drag & drop ist für archivierte Seiten nicht möglich." + +#: cms/templates/pages/_generic_page_tree_node.html +#: cms/templates/pages/page_tree_archived_node.html +msgid "Expand all subpages" +msgstr "Alle Unterseiten ausklappen" + +#: cms/templates/pages/_generic_page_tree_node.html +#: cms/templates/pages/page_tree_archived_node.html +msgid "Collapse all subpages" +msgstr "Alle Unterseiten einklappen" + #: cms/templates/pages/_page_order_table.html msgid "Change the position of the page with drag & drop." msgstr "Ändern Sie die Position der Seite mit Drag & Drop." @@ -7475,21 +7494,6 @@ msgstr "Keine archivierten Seiten mit diesen Filtern gefunden." msgid "No pages archived yet." msgstr "Noch keine Seiten archiviert." -#: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pages/page_tree_node.html -msgid "Drag & drop is disabled for archived pages." -msgstr "Drag & drop ist für archivierte Seiten nicht möglich." - -#: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pages/page_tree_node.html -msgid "Expand all subpages" -msgstr "Alle Unterseiten ausklappen" - -#: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pages/page_tree_node.html -msgid "Collapse all subpages" -msgstr "Alle Unterseiten einklappen" - #: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html msgid "This page is archived." @@ -7524,10 +7528,6 @@ msgstr "" msgid "This also involves non-archived subpages." msgstr "Dies betrifft auch nicht-archivierte Unterseiten." -#: cms/templates/pages/page_tree_node.html -msgid "Change the order and position of the pages with drag & drop." -msgstr "Ändern Sie die Reihenfolge und Position der Seiten mit Drag & Drop." - #: cms/templates/pages/page_tree_node.html msgid "Open page in web app" msgstr "Seite in der Web-App öffnen" From 718358e82cad65c1c0507d84147354430e6ce811 Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Mon, 9 Dec 2024 16:37:31 +0000 Subject: [PATCH 15/52] Cleaning up and deleting page_tree_archived files --- .../templates/pages/page_tree_archived.html | 126 ------------- .../pages/page_tree_archived_node.html | 170 ------------------ .../cms/views/pages/page_tree_view.py | 2 - .../cms/views/pages/partial_page_tree_view.py | 11 +- integreat_cms/locale/de/LC_MESSAGES/django.po | 88 +++------ 5 files changed, 34 insertions(+), 363 deletions(-) delete mode 100644 integreat_cms/cms/templates/pages/page_tree_archived.html delete mode 100644 integreat_cms/cms/templates/pages/page_tree_archived_node.html diff --git a/integreat_cms/cms/templates/pages/page_tree_archived.html b/integreat_cms/cms/templates/pages/page_tree_archived.html deleted file mode 100644 index 92050a645e..0000000000 --- a/integreat_cms/cms/templates/pages/page_tree_archived.html +++ /dev/null @@ -1,126 +0,0 @@ -{% extends "_base.html" %} -{% load i18n %} -{% load static %} -{% load content_filters %} -{% load page_filters %} -{% load rules %} -{% block content %} - {% has_perm 'cms.change_page_object' request.user as can_edit_pages %} - {% with filter_form.filters_visible as filters_visible %} -
-
-

- {% translate "Archived Pages" %} -

- - {% translate "Back to pages" %} - -
-
-
- {% include "generic_language_switcher.html" with target="archived_pages" %} - {% include "_search_input.html" with object_type="page" object_archived=True related_form="page-filter-form" %} -
- {% if request.user.expert_mode %} - - {% endif %} -
-
-
- {% include "pages/_page_filter_form.html" %} -
- {% endwith %} -
-
- - {% csrf_token %} - - - - - - {% if not filter_form.is_enabled %} - - {% endif %} - - {% get_current_language as LANGUAGE_CODE %} - {% get_language LANGUAGE_CODE as backend_language %} - {% if backend_language and backend_language != language %} - - {% endif %} - - - - - - - - {% for page in pages %} - {% get_translation page language.slug as page_translation %} - {% include "pages/page_tree_archived_node.html" with page_translation=page_translation %} - {% empty %} - - - - {% endfor %} - -
- - - {% translate "Hierarchy" %} - - {% translate "Title in" %} {{ language.translated_name }} - - {% translate "Title in" %} {{ backend_language.translated_name }} - -
- {% spaceless %} - {% for lang in languages %} - {% if lang != request.region.default_language %} - - - - {% endif %} - {% endfor %} - {% endspaceless %} -
-
- {% translate "Status" %} - - {% translate "Last updated" %} - - {% translate "Options" %} -
- {% if filter_form.is_enabled %} - {% translate "No archived pages found with these filters." %} - {% else %} - {% translate "No pages archived yet." %} - {% endif %} -
- {% if can_edit_pages %} -
- - -
- {% endif %} -
-
- {% include "../generic_confirmation_dialog.html" %} - {% include "pages/_page_preview_overlay.html" %} -{% endblock content %} diff --git a/integreat_cms/cms/templates/pages/page_tree_archived_node.html b/integreat_cms/cms/templates/pages/page_tree_archived_node.html deleted file mode 100644 index 1d6e6d72b8..0000000000 --- a/integreat_cms/cms/templates/pages/page_tree_archived_node.html +++ /dev/null @@ -1,170 +0,0 @@ -{% load i18n %} -{% load content_filters %} -{% load page_filters %} -{% load tree_filters %} -
- - - - - - {% if page.cached_children|length > 0 %} - - - - {% endif %} - - - {% if page_translation %} - {{ page_translation.title }} - {% else %} - {% translate "Translation not available" %} - {% endif %} - - - - {% if page.backend_translation %} - {{ page.backend_translation.title }} - {% else %} - {% translate "Translation not available" %} - {% endif %} - - - - - {% if page.explicitly_archived %} -
- {% translate "Archived" %} -
- {% else %} -
- {% translate "Archived, because a parent page is archived" %} -
- {% endif %} -
-
- {{ page_translation.last_updated|date:"SHORT_DATE_FORMAT" }} -
-
- {% if page_translation.content or page.mirrored_page %} - - {% else %} - - {% endif %} - {% if can_edit_pages %} - {% if page.explicitly_archived %} - - {% else %} - - - - {% endif %} - {% endif %} - {% if perms.cms.delete_page %} - {% if not page.is_leaf %} - {# djlint:off H023 #} - - - - {# djlint:on #} - {% elif page.mirroring_pages.exists %} - - {% else %} - - {% endif %} - {% endif %} -
{% for page in pages %} - {% include "pages/page_tree_node.html" %} + {% include "pages/pages_page_tree_node.html" %} {% endfor %}
diff --git a/integreat_cms/cms/templates/pages/page_tree.html b/integreat_cms/cms/templates/pages/pages_page_tree.html similarity index 99% rename from integreat_cms/cms/templates/pages/page_tree.html rename to integreat_cms/cms/templates/pages/pages_page_tree.html index d367c56015..9d9e6916c0 100644 --- a/integreat_cms/cms/templates/pages/page_tree.html +++ b/integreat_cms/cms/templates/pages/pages_page_tree.html @@ -112,7 +112,7 @@

{% for page in pages %} - {% include "pages/page_tree_node.html" with is_archive=is_archive %} + {% include "pages/pages_page_tree_node.html" with is_archive=is_archive %} {% if forloop.last %} Date: Mon, 9 Dec 2024 22:27:47 +0100 Subject: [PATCH 17/52] Add page accesses calls --- integreat_cms/cms/urls/protected.py | 5 + .../cms/views/statistics/__init__.py | 6 +- .../views/statistics/statistics_actions.py | 43 +++- integreat_cms/matomo_api/matomo_api_client.py | 235 +++++------------- integreat_cms/matomo_api/utils.py | 35 +-- 5 files changed, 128 insertions(+), 196 deletions(-) diff --git a/integreat_cms/cms/urls/protected.py b/integreat_cms/cms/urls/protected.py index 4e3ae8c9b8..42937c5479 100644 --- a/integreat_cms/cms/urls/protected.py +++ b/integreat_cms/cms/urls/protected.py @@ -712,6 +712,11 @@ statistics.get_visits_per_language_ajax, name="statistics_visits_per_language", ), + path( + "page-based-accesses/", + statistics.get_page_accesses_ajax, + name="statistics_page_based_accesses", + ), ] ), ), diff --git a/integreat_cms/cms/views/statistics/__init__.py b/integreat_cms/cms/views/statistics/__init__.py index c5427853da..cd438b9b60 100644 --- a/integreat_cms/cms/views/statistics/__init__.py +++ b/integreat_cms/cms/views/statistics/__init__.py @@ -4,5 +4,9 @@ from __future__ import annotations -from .statistics_actions import get_total_visits_ajax, get_visits_per_language_ajax +from .statistics_actions import ( + get_page_accesses_ajax, + get_total_visits_ajax, + get_visits_per_language_ajax, +) from .statistics_view import AnalyticsView diff --git a/integreat_cms/cms/views/statistics/statistics_actions.py b/integreat_cms/cms/views/statistics/statistics_actions.py index 9b5cd7f197..ce681ab590 100644 --- a/integreat_cms/cms/views/statistics/statistics_actions.py +++ b/integreat_cms/cms/views/statistics/statistics_actions.py @@ -88,17 +88,54 @@ def get_visits_per_language_ajax( ) try: - region.statistics.get_page_based_accesses( + result = region.statistics.get_visits_per_language( start_date=statistics_form.cleaned_data["start_date"], end_date=statistics_form.cleaned_data["end_date"], period=statistics_form.cleaned_data["period"], - region=region, + ) + return JsonResponse(result, safe=False) + except asyncio.TimeoutError: + return JsonResponse( + {"error": "Timeout during request to Matomo API"}, status=504 + ) + except MatomoException as e: + logger.exception(e) + return JsonResponse( + {"error": "The request to the Matomo API failed."}, status=500 ) - result = region.statistics.get_visits_per_language( + +@require_POST +# pylint: disable=unused-argument +def get_page_accesses_ajax(request: HttpRequest, region_slug: str) -> JsonResponse: + """ + Ajax method to request the app hits for a certain timerange distinguished by languages. + + :param request: The current request + :param region_slug: The slug of the current region + :return: A JSON with all API-Hits of the requested time period + """ + region = request.region + + if not region.statistics_enabled: + return JsonResponse( + {"error": "Statistics are not enabled for this region."}, status=500 + ) + + statistics_form = StatisticsFilterForm(data=request.POST) + + if not statistics_form.is_valid(): + return JsonResponse( + {"errors": statistics_form.errors}, + status=400, + ) + + try: + result = region.statistics.get_page_accesses( start_date=statistics_form.cleaned_data["start_date"], end_date=statistics_form.cleaned_data["end_date"], period=statistics_form.cleaned_data["period"], + region=region, ) return JsonResponse(result, safe=False) except asyncio.TimeoutError: diff --git a/integreat_cms/matomo_api/matomo_api_client.py b/integreat_cms/matomo_api/matomo_api_client.py index 3c2437566b..f5474b56c5 100644 --- a/integreat_cms/matomo_api/matomo_api_client.py +++ b/integreat_cms/matomo_api/matomo_api_client.py @@ -3,8 +3,8 @@ import asyncio import logging import re -from datetime import date, datetime from collections.abc import Mapping +from datetime import date, datetime from typing import TYPE_CHECKING from urllib.parse import urlencode @@ -14,7 +14,7 @@ from django.utils.translation import gettext_lazy as _ from ..cms.constants import language_color, matomo_periods -from .utils import get_translation_slug +from .utils import async_get_translation_slug if TYPE_CHECKING: import sys @@ -529,146 +529,86 @@ def get_access_data( ), ] return data_entries, legend_entries - """ - def get_page_based_statistics( - self, - start_date: date, - end_date: date, - period: str, - region: Region, - ) -> list[dict[str, Any]]: - Returns the statistics for each page of a region - :param start_date: Start date - :param end_date: End date - :param period: The period (one of :attr:`~integreat_cms.cms.constants.matomo_periods.CHOICES`) - :param region: The region - :return: ? - :raises ~integreat_cms.matomo_api.matomo_api_client.MatomoException: When a :class:`~aiohttp.ClientError` was raised during a - query_params = { - "date": f"{start_date},{end_date}", - "expanded": "1", - "filter_limit": "-1", - "format_metrics": "1", - "idSite": self.matomo_id, - "method": "VisitsSummary.getActions", - "period": period, - } - logger.debug( - "Query params: %r", - query_params, - ) - pages = region.get_pages() - languages = list(self.languages) - - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - # Execute async request to Matomo API - logger.debug("Fetching visits for languages %r asynchronously.", languages) - datasets = loop.run_until_complete( - self.get_page_based_statistics_async(loop, query_params, languages, pages) - ) - return datasets - - async def get_page_based_statistics_async( + async def get_page_accesses_async( self, loop: AbstractEventLoop, query_params: dict[str, Any], languages: list[Language], pages: list[Page], ) -> list[dict[str, Any]]: - Async wrapper to fetch the total visits with :mod:`aiohttp`. - Opens a :class:`~aiohttp.ClientSession`, creates a :class:`~asyncio.Task` for each language to call - :func:`~integreat_cms.matomo_api.matomo_api_client.MatomoApiClient.fetch` and waits for all tasks to finish with - :func:`~asyncio.gather`. - The returned list of gathered results has the correct order in which the tasks were created (at first the - ordered list of languages and the last element is the task for the total visits). - Called from :func:`~integreat_cms.matomo_api.matomo_api_client.MatomoApiClient.get_visits_per_language`. - + """ :param loop: The asyncio event loop :param query_params: The parameters which are passed to the Matomo API :param languages: The list of languages which should be retrieved - :param pages: The list of pages for which the data should be retrieved + :param pages: The list of pages which should be retrieved :raises ~integreat_cms.matomo_api.matomo_api_client.MatomoException: When a :class:`~aiohttp.ClientError` was raised during a Matomo API request - - :return: The list of gathered results - create_translation_slugs(pages, languages) + :return: + """ + translation_slugs = await async_get_translation_slug(pages, languages) async with aiohttp.ClientSession() as session: # Create tasks for visits by language tasks = [ loop.create_task( - self.fetch( + self.retrieve_accesses_for_page( session, - **query_params, - segment=f"pageUrl=@/{language.slug}/wp-json/extensions/v3/,pageUrl=@/api/v3/{self.region_slug}/{language.slug}/", + query_params, + page_id=page_id, + lang_slug=lang_slug, + full_slug=full_slug, ) ) - for language in languages - ] - - result = await asyncio.gather(*tasks) - # We're not retrieving the matomo id as part of the tasks, thus we know that the result is a list of dicts, not a list of list of ints. - if TYPE_CHECKING: - - def is_dict_list( - lst: list[dict[str, Any] | list[int]] - ) -> TypeGuard[list[dict[str, Any]]]: - return all(isinstance(d, dict) for d in lst) - - assert is_dict_list(result) - return result - """ - - async def get_visits_per_page_and_language_async( - self, - loop: AbstractEventLoop, - query_params: dict[str, Any], - languages: list[Language], - pages: list[Page], - region_slug: str, - ) -> list[dict[str, Any]]: - translation_slugs = get_translation_slug(pages, languages) - print(translation_slugs) - async with aiohttp.ClientSession() as session: - # Create tasks for visits by language - tasks = [ - loop.create_task( - self.nice_function(session, query_params, region_slug=region_slug, page_id=page_id, lang_slug=lang_slug, full_slug=full_slug) - ) for page_id, langs in translation_slugs.items() for lang_slug, full_slug in langs.items() ] # Wait for all tasks to finish and collect the results # (the results are sorted in the order the tasks were created) result = await asyncio.gather(*tasks) - # We're not retrieving the matomo id as part of the tasks, thus we know that the result is a list of dicts, not a list of list of ints. - if TYPE_CHECKING: - - def is_dict_list( - lst: list[dict[str, Any] | list[int]] - ) -> TypeGuard[list[dict[str, Any]]]: - return all(isinstance(d, dict) for d in lst) - - assert is_dict_list(result) return result - async def nice_function(self, session: aiohttp.ClientSession, query_params: dict[str, Any], region_slug: str, page_id: int, lang_slug: str, full_slug: str) -> dict: - print(f"full slug: {full_slug}") + async def retrieve_accesses_for_page( + self, + session: aiohttp.ClientSession, + query_params: dict[str, Any], + page_id: int, + lang_slug: str, + full_slug: str, + ) -> dict: + """ + This function retrieves the accesses for a single page (from Matomo). + + :param session: The current session + :param query_params: The parameters which are passed to the Matomo API + :param page_id: Id of page for which accesses are retrieved + :param lang_slug: Language slug for which accesses are retrieved + :param full_slug: The absolute url slug for the page + :return: dict of page and it's accesses + """ return { page_id: { - lang_slug: await self.fetch( session, **query_params, segment=f"pageUrl=@/children/?depth=2&url=/{region_slug}/{full_slug}" ) + lang_slug: await self.fetch( + session, + **query_params, + segment=f"pageUrl=@/children/?depth=2&url={full_slug}", + ) } } - def get_page_based_accesses(self, start_date: date, end_date: date, period: str, region: Region): - from ..cms.models import Page, Language - - language1 = Language.objects.get(id=1) - language2 = Language.objects.get(id=2) - language3 = Language.objects.get(id=3) - print("get_page_based_accesses") + def get_page_accesses( + self, start_date: date, end_date: date, period: str, region: Region + ) -> dict[int, dict[str, int]]: + """ + This function handles the page based accesses + :param start_date: Start date + :param end_date: End date + :param period: The period (one of :attr:`~integreat_cms.cms.constants.matomo_periods.CHOICES`) + :param region: The region for which we want our page based accesses + :return: The page accesses for the given region + :raises ~integreat_cms.matomo_api.matomo_api_client.MatomoException: When a :class:`~aiohttp.ClientError` was raised during a + Matomo API request + """ query_params = { "date": f"{start_date},{end_date}", "expanded": "1", @@ -678,88 +618,33 @@ def get_page_based_accesses(self, start_date: date, end_date: date, period: str, "method": "VisitsSummary.getActions", "period": period, } - languages = [language1, language2, language3] + languages = list(self.languages) pages = region.get_pages() loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) - # Execute async request to Matomo API logger.debug("Fetching visits for languages %r asynchronously.", languages) datasets = loop.run_until_complete( - self.get_visits_per_page_and_language_async(loop, query_params, languages, pages, region.slug) + self.get_page_accesses_async(loop, query_params, languages, pages) ) - - def deep_merge(*dicts): + + def deep_merge(*dicts: Mapping[Any, Any]) -> dict: """ Recursively merges dictionaries. Values in later dictionaries override earlier ones for non-dict values, while dictionaries are merged recursively. """ - merged = {} + merged: dict[Any, Any] = {} for d in dicts: for key, value in d.items(): - if key in merged and isinstance(merged[key], Mapping) and isinstance(value, Mapping): + if ( + key in merged + and isinstance(merged[key], Mapping) + and isinstance(value, Mapping) + ): # Recursively merge dictionaries merged[key] = deep_merge(merged[key], value) else: # Otherwise, override or add the value merged[key] = value return merged - - datasets = deep_merge(*datasets) - - """datasets = { - key: value - for result in datasets - for key, value in result.items() - }""" - print(f"Datasets: {datasets}") - - """ - # alle Daten zu holen - raw_data = [ - { - "slug": "/sprachkurse/integrationskurse", - "language_slug": "de", - "accesses": 8, - }, - { - "slug": "/language_courses/integration_courses", - "language_slug": "de", - "accesses": 8, - }, - ] - - # strukturieren - # page id raus finden - - translations = PageTranslations.objects.all() - # nicht aktuelle Versionen rausfiltern - for value in data: - # page id raus finden - translations = PageTranslations.objects.all() - # nicht aktuelle Versionen rausfiltern - for value in data: - gesuchte_translation = translations.get(slug=value.slug) - # erstelle aus der gesuchter translation ein dict - { - "id": gesuchte_translation.id, #654 - "access": value.accesses, #88 - "language_slug": value.language_slug, # "en" - "page_id": gesuchte_translation.page.id, #14 - } - # rausfinden welche page translations dazu gehören - # gibt es mit meiner page_id schon ein dict? - # wenn nein, erstelle neues dict und fülle mit informationen aus, die du hast - - # wenn ja, dann - # hänge nur meinen language slug in accesses an - # end_data.access[language_slug] = current_data.access - - # schicke ich strukturiert an AJAX - end_data = [ - { - "page_id": 14, - "accesses": {"de": 123, "en":23, "fr":312, "ar": 76543} - } - {"page_id": 1, "de": 12, "ru": 1}, - """ + return deep_merge(*datasets) diff --git a/integreat_cms/matomo_api/utils.py b/integreat_cms/matomo_api/utils.py index 55271d798e..1680f591b0 100644 --- a/integreat_cms/matomo_api/utils.py +++ b/integreat_cms/matomo_api/utils.py @@ -1,33 +1,34 @@ from __future__ import annotations -from typing import Dict, TYPE_CHECKING +from typing import TYPE_CHECKING + +from asgiref.sync import sync_to_async if TYPE_CHECKING: - from django.db.models.query import QuerySet from ..cms.models import Language, Page -"""def create_translation_slugs( - pages: QuerySet[Page], languages: list[Language] -) -> dict[Page, dict[str, str]]: - This function creates the translation slug for the matomo call. It returns then language_slug and the slug in the target translation. - translation_slugs: Dict[Page, Dict[str, str]] = {} - for page in pages: - translation_slugs[page] = {} - for language in languages: - if page_translation := page.get_translation(language.slug): - # print(page_translation.ancestor_path)Sollen wir uns Sol - translation_slugs[page][language.slug] = page_translation.slug - return translation_slugs +def get_translation_slug( + pages: list[Page], languages: list[Language] +) -> dict[int, dict[str, str]]: """ + Produce mapping of page ids and language slugs to the absolute url of the corresponding translation. + In detail, we need to construct a slug in the foreign language, for example /en/lebensmittel-und-einkaufen needs to become /en/groceries-and-shopping. -def get_translation_slug(pages: list[Page], languages: list[Language]) -> dict[int, dict[str, str]]: - translation_slugs = {} + :param pages: The list of pages for which we want the absolute url of + :param languages: The list of languages for which we want the absolute url of + :return: A dictionary of page ids, language slugs and the absolute url of the corresponding translation. + """ + translation_slugs: dict = {} for page in pages: for language in languages: if page_translation := page.get_translation(language.slug): if page.id not in translation_slugs: translation_slugs[page.id] = {} - translation_slugs[page.id][language.slug] = f"{language.slug}/{page_translation.slug}" # nach richtigen Slug noch mal recherchieren + translation_slugs[page.id][ + language.slug + ] = page_translation.get_absolute_url().rstrip("/") return translation_slugs + +async_get_translation_slug = sync_to_async(get_translation_slug, thread_sensitive=False) From bbfb58ce67469243325a9580269fdd54ed34b436 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Mon, 9 Dec 2024 22:37:15 +0100 Subject: [PATCH 18/52] Fix pylint --- integreat_cms/cms/views/pages/partial_page_tree_view.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/integreat_cms/cms/views/pages/partial_page_tree_view.py b/integreat_cms/cms/views/pages/partial_page_tree_view.py index 901ff3eb85..72e4b98715 100644 --- a/integreat_cms/cms/views/pages/partial_page_tree_view.py +++ b/integreat_cms/cms/views/pages/partial_page_tree_view.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +from ast import literal_eval from collections import defaultdict from django.http import HttpRequest, JsonResponse @@ -15,7 +16,7 @@ @permission_required("cms.view_page") @require_POST -# pylint: disable=unused-argument +# pylint: disable=unused-argument, too-many-locals def render_partial_page_tree_views( request: HttpRequest, region_slug: str, language_slug: str, is_archive: str ) -> JsonResponse: @@ -34,7 +35,7 @@ def render_partial_page_tree_views( language = region.get_language_or_404(language_slug, only_active=True) # Convert is_archive from String to Boolean - is_archive = eval(is_archive) + is_archive = literal_eval(is_archive) backend_language = Language.objects.filter(slug=get_language()).first() From c375fde0f01fdbae6af12bfeeb96e60ec26a309c Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Tue, 10 Dec 2024 10:13:47 +0100 Subject: [PATCH 19/52] [WIP] Include page tree to statistics --- .../pages/_generic_page_tree_node.html | 2 +- .../statistics/statistics_viewed_pages.html | 19 +++++++++++++++++-- .../cms/views/statistics/statistics_view.py | 3 +++ .../src/js/analytics/statistics-charts.ts | 8 ++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/integreat_cms/cms/templates/pages/_generic_page_tree_node.html b/integreat_cms/cms/templates/pages/_generic_page_tree_node.html index 6aaa4d95a9..ed8dcec9e2 100644 --- a/integreat_cms/cms/templates/pages/_generic_page_tree_node.html +++ b/integreat_cms/cms/templates/pages/_generic_page_tree_node.html @@ -65,4 +65,4 @@ {% endif %} -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html index 4b571d2675..1f543cc401 100644 --- a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -9,10 +9,13 @@ {% translate "Page views" %} {% endblock collapsible_box_title %} {% block collapsible_box_content %} -
+ {% csrf_token %}
- +
{% include "pages/_generic_page_tree.html" %} @@ -21,6 +24,18 @@ + + {% for page in pages %} + + {% include "pages/_generic_page_tree_node.html" %} + + + {% endfor %} +
+
diff --git a/integreat_cms/cms/views/statistics/statistics_view.py b/integreat_cms/cms/views/statistics/statistics_view.py index 6b6fd47aac..b4df44d61f 100644 --- a/integreat_cms/cms/views/statistics/statistics_view.py +++ b/integreat_cms/cms/views/statistics/statistics_view.py @@ -63,5 +63,8 @@ def get( { **self.get_context_data(**kwargs), "form": form, + "pages": region.get_pages(), + "region": region, + "language": region.default_language, }, ) diff --git a/integreat_cms/static/src/js/analytics/statistics-charts.ts b/integreat_cms/static/src/js/analytics/statistics-charts.ts index 1d32dec86d..2e0a88a42c 100644 --- a/integreat_cms/static/src/js/analytics/statistics-charts.ts +++ b/integreat_cms/static/src/js/analytics/statistics-charts.ts @@ -75,9 +75,17 @@ const updateChart = async (): Promise => { // Get AJAX URL const url = chart.canvas.getAttribute("data-statistics-url"); + const pageAccessesURL = document.getElementById("statistics-page-access").getAttribute("data-page-accesses-url"); try { const response = await fetch(url, parameters); + const response2 = await fetch(pageAccessesURL, parameters); + + if (response.status === HTTP_STATUS_OK) { + // The response text contains the data from Matomo as JSON. + const data = (await response2.json()) as AjaxResponse; + console.log(data); + } if (response.status === HTTP_STATUS_OK) { // The response text contains the data from Matomo as JSON. From 68ccfe18deea674bb10658515f4e8b882d772a85 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Tue, 10 Dec 2024 17:48:46 +0100 Subject: [PATCH 20/52] Javascript code --- integreat_cms/static/src/index.ts | 1 + .../src/js/analytics/statistics-charts.ts | 8 -- .../js/analytics/statistics-page-accesses.ts | 91 +++++++++++++++++++ 3 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 integreat_cms/static/src/js/analytics/statistics-page-accesses.ts diff --git a/integreat_cms/static/src/index.ts b/integreat_cms/static/src/index.ts index b81aae6203..66d96a1769 100644 --- a/integreat_cms/static/src/index.ts +++ b/integreat_cms/static/src/index.ts @@ -72,6 +72,7 @@ import "./js/mfa/login"; import "./js/offers/zammad"; import "./js/analytics/statistics-charts"; +import "./js/analytics/statistics-page-accesses"; import "./js/analytics/translation_coverage"; import "./js/analytics/linkcheck-widget"; import "./js/analytics/hix-list"; diff --git a/integreat_cms/static/src/js/analytics/statistics-charts.ts b/integreat_cms/static/src/js/analytics/statistics-charts.ts index 2e0a88a42c..1d32dec86d 100644 --- a/integreat_cms/static/src/js/analytics/statistics-charts.ts +++ b/integreat_cms/static/src/js/analytics/statistics-charts.ts @@ -75,17 +75,9 @@ const updateChart = async (): Promise => { // Get AJAX URL const url = chart.canvas.getAttribute("data-statistics-url"); - const pageAccessesURL = document.getElementById("statistics-page-access").getAttribute("data-page-accesses-url"); try { const response = await fetch(url, parameters); - const response2 = await fetch(pageAccessesURL, parameters); - - if (response.status === HTTP_STATUS_OK) { - // The response text contains the data from Matomo as JSON. - const data = (await response2.json()) as AjaxResponse; - console.log(data); - } if (response.status === HTTP_STATUS_OK) { // The response text contains the data from Matomo as JSON. diff --git a/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts b/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts new file mode 100644 index 0000000000..12f5e573fc --- /dev/null +++ b/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts @@ -0,0 +1,91 @@ +export {}; + +export type AjaxResponse = { + pageId: number; + accesses: object; +}; + +export type Access = { + languageSlug: string; + accessesOverTime: object; +} + +const calculateAllAccessesPerPage = (accessesOverTime: object): number => { + console.log(accessesOverTime) + let accesses: number = 0; + Object.values(accessesOverTime).forEach((entry) => { + if (entry) { + accesses += entry; + } + }) + return accesses; +} + +const updateChart = async (): Promise => { + // const chart = Chart.instances[0]; + + const chartNetworkError = document.getElementById("chart-network-error"); + const chartServerError = document.getElementById("chart-server-error"); + const chartHeavyTrafficError = document.getElementById("chart-heavy-traffic-error"); + const chartLoading = document.getElementById("chart-loading"); + + chartNetworkError.classList.add("hidden"); + chartServerError.classList.add("hidden"); + chartHeavyTrafficError.classList.add("hidden"); + + let parameters = {}; + + const HTTP_STATUS_OK = 200; + // const HTTP_STATUS_BAD_REQUEST = 400; + // const HTTP_STATUS_GATEWAY_TIMEOUT = 504; + + const statisticsForm = document.getElementById("statistics-form") as HTMLFormElement; + + // If form exists (which is the case on the statistics page), perform some extra steps + if (statisticsForm) { + parameters = { + method: "POST", + body: new FormData(statisticsForm), + }; + } + + const pageAccessesURL = document.getElementById("statistics-page-access").getAttribute("data-page-accesses-url"); + const accessFields = document.getElementsByClassName("accesses"); + try { + const response = await fetch(pageAccessesURL, parameters); + + if (response.status === HTTP_STATUS_OK) { + const data = (await response.json()) as AjaxResponse; + Object.entries(data).forEach((values) => { + Array.from(accessFields).forEach((accessField) => { + const id = values[0] + const pageId = accessField.parentElement.getAttribute("id").replace("page-", ""); + if (id === pageId) { + const accesses = values[1] + const editableAccessField = accessField + let allAccesses: number = 0 + Object.entries(accesses).forEach((access) => { + const languageSlug = access[0] + const accessesOverTime = access[1] + allAccesses += calculateAllAccessesPerPage(accessesOverTime) + console.log(languageSlug, accesses) + // calculateAccessesPerLanguage(languageSlug, accessesOverTime) + // console.log(accessesOverTime) + }) + editableAccessField.textContent = String(allAccesses); + console.log(allAccesses) + } + }); + }); + } + } catch (error) { + console.error("Network error during fetch:", error); + chartNetworkError.classList.remove("hidden"); + } finally { + chartLoading.classList.add("hidden"); + } +}; + +window.addEventListener("load", async () => { + updateChart(); +}); From a9fa6ae705e70e2423115770e6fd68a2bad3b744 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Thu, 12 Dec 2024 18:08:27 +0100 Subject: [PATCH 21/52] [WIP] First idea for graph --- .../statistics/statistics_viewed_pages.html | 11 +++- .../cms/views/statistics/statistics_view.py | 1 + integreat_cms/locale/de/LC_MESSAGES/django.po | 4 ++ .../js/analytics/statistics-page-accesses.ts | 62 ++++++++++++------- 4 files changed, 54 insertions(+), 24 deletions(-) diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html index 1f543cc401..7dc6b3f27a 100644 --- a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -31,7 +31,16 @@ data-drop-position="last-child" class="page-row drop drop-on border-t border-b border-solid border-gray-200 hover:bg-gray-100 level-{{ page.relative_depth }} {% if parent_id %}hidden parent-page-{{ parent_id }}{% endif %}"> {% include "pages/_generic_page_tree_node.html" %} - + + {% for language in languages %} + + {% endfor %} + {% comment %} {% endcomment %} + + {% endfor %} diff --git a/integreat_cms/cms/views/statistics/statistics_view.py b/integreat_cms/cms/views/statistics/statistics_view.py index b4df44d61f..df251c4d7c 100644 --- a/integreat_cms/cms/views/statistics/statistics_view.py +++ b/integreat_cms/cms/views/statistics/statistics_view.py @@ -66,5 +66,6 @@ def get( "pages": region.get_pages(), "region": region, "language": region.default_language, + "languages": region.active_languages, }, ) diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index bfbb482962..275a95b9bc 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -8452,6 +8452,10 @@ msgstr "Tabellendokument/CSV" msgid "Page views" msgstr "Seitenzugriffe" +#: cms/templates/statistics/statistics_viewed_pages.html +msgid "Accesses in total" +msgstr "Zugriffe insgesamt" + #: cms/templates/translations/translations_management.html msgid "Manage machine translations" msgstr "Maschinelle Übersetzungen verwalten" diff --git a/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts b/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts index 12f5e573fc..6adc99e569 100644 --- a/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts +++ b/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts @@ -1,5 +1,3 @@ -export {}; - export type AjaxResponse = { pageId: number; accesses: object; @@ -8,18 +6,32 @@ export type AjaxResponse = { export type Access = { languageSlug: string; accessesOverTime: object; -} +}; -const calculateAllAccessesPerPage = (accessesOverTime: object): number => { - console.log(accessesOverTime) +const countAccesses = (accessesOverTime: object): number => { let accesses: number = 0; Object.values(accessesOverTime).forEach((entry) => { - if (entry) { - accesses += entry; - } - }) + accesses += entry; + }); return accesses; -} +}; + +const setAccessesPerLanguage = ( + accessField: Element, + languageSlug: string, + accessesOverTime: object, + allAccesses: number +) => { + const parentElement = accessField as HTMLElement; + const childElement = parentElement.querySelector(`.accesses span[data-language-slug="${languageSlug}"]`); + const languageColor = childElement.getAttribute("data-language-color"); + const accesses = countAccesses(accessesOverTime); + const width = allAccesses !== 0 ? Math.floor((accesses / allAccesses) * 100) : 0; + const languageColorString = `bg-[${languageColor}%]`; + childElement.classList.add(`w-[${width}%]`); + childElement.classList.add(languageColorString); + console.log(languageColor); +}; const updateChart = async (): Promise => { // const chart = Chart.instances[0]; @@ -58,24 +70,28 @@ const updateChart = async (): Promise => { const data = (await response.json()) as AjaxResponse; Object.entries(data).forEach((values) => { Array.from(accessFields).forEach((accessField) => { - const id = values[0] + const id = values[0]; const pageId = accessField.parentElement.getAttribute("id").replace("page-", ""); if (id === pageId) { - const accesses = values[1] - const editableAccessField = accessField - let allAccesses: number = 0 + const accesses = values[1]; + const allAccessesField = Array.from(accessField.parentElement?.children || []).find( + (el) => el !== accessField && el.classList.contains("total-accesses") + ); + const editableAllAccessField = allAccessesField; + let allAccesses: number = 0; + Object.entries(accesses).forEach((access) => { + const accessesOverTime = access[1]; + allAccesses += countAccesses(accessesOverTime); + }); + editableAllAccessField.textContent = `${String(allAccesses)} ${editableAllAccessField.getAttribute("data-translation")}`; Object.entries(accesses).forEach((access) => { - const languageSlug = access[0] - const accessesOverTime = access[1] - allAccesses += calculateAllAccessesPerPage(accessesOverTime) - console.log(languageSlug, accesses) - // calculateAccessesPerLanguage(languageSlug, accessesOverTime) - // console.log(accessesOverTime) - }) - editableAccessField.textContent = String(allAccesses); - console.log(allAccesses) + const languageSlug = access[0]; + const accessesOverTime = access[1]; + setAccessesPerLanguage(accessField, languageSlug, accessesOverTime, allAccesses); + }); } }); + // console.log(data) }); } } catch (error) { From edc3114bda446c4f3d5fb4e90a3ae37cc14d0d55 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Thu, 12 Dec 2024 18:35:58 +0100 Subject: [PATCH 22/52] Show bars --- .../cms/templates/statistics/statistics_viewed_pages.html | 5 ++--- .../static/src/js/analytics/statistics-page-accesses.ts | 7 +++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html index 7dc6b3f27a..e8353e908e 100644 --- a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -31,13 +31,12 @@ data-drop-position="last-child" class="page-row drop drop-on border-t border-b border-solid border-gray-200 hover:bg-gray-100 level-{{ page.relative_depth }} {% if parent_id %}hidden parent-page-{{ parent_id }}{% endif %}"> {% include "pages/_generic_page_tree_node.html" %} - + {% for language in languages %} + class="cursor-pointer bg-[{{ language.language_color }}] h-9"> {% endfor %} - {% comment %} {% endcomment %} diff --git a/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts b/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts index 6adc99e569..c2d4cfd40c 100644 --- a/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts +++ b/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts @@ -27,10 +27,9 @@ const setAccessesPerLanguage = ( const languageColor = childElement.getAttribute("data-language-color"); const accesses = countAccesses(accessesOverTime); const width = allAccesses !== 0 ? Math.floor((accesses / allAccesses) * 100) : 0; - const languageColorString = `bg-[${languageColor}%]`; - childElement.classList.add(`w-[${width}%]`); - childElement.classList.add(languageColorString); - console.log(languageColor); + (childElement as HTMLElement).style.backgroundColor = languageColor; + (childElement as HTMLElement).style.width = `${String(width)}%`; + (childElement as HTMLElement).title = String(accesses); }; const updateChart = async (): Promise => { From 03085110ed33661c22f17d8577a121f00b6e8258 Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Wed, 27 Nov 2024 15:09:08 +0000 Subject: [PATCH 23/52] Merge page_tree_archived into page_tree --- .../cms/templates/pages/page_tree.html | 376 ++++++++++-------- .../cms/views/pages/page_tree_view.py | 3 +- integreat_cms/locale/de/LC_MESSAGES/django.po | 18 +- 3 files changed, 223 insertions(+), 174 deletions(-) diff --git a/integreat_cms/cms/templates/pages/page_tree.html b/integreat_cms/cms/templates/pages/page_tree.html index fd39690ae5..60b05b42b0 100644 --- a/integreat_cms/cms/templates/pages/page_tree.html +++ b/integreat_cms/cms/templates/pages/page_tree.html @@ -9,21 +9,36 @@ {% with filter_form.filters_visible as filters_visible %}
-

- {% translate "Page Tree" %} - -

- - {% translate "Archived pages" %} - + {% if is_archive %} +

+ {% translate "Archived Pages" %} +

+ + {% translate "Back to pages" %} + + {% else %} +

+ {% translate "Page Tree" %} + +

+ + {% translate "Archived pages" %} + + {% endif %}
- {% include "generic_language_switcher.html" with target="pages" %} - {% include "_search_input.html" with object_type="page" related_form="page-filter-form" search_query=filter_form.cleaned_data.query %} + {% if is_archive %} + {% include "generic_language_switcher.html" with target="archived_pages" %} + {% include "_search_input.html" with object_type="page" object_archived=True related_form="page-filter-form" %} + {% else %} + {% include "generic_language_switcher.html" with target="pages" %} + {% include "_search_input.html" with object_type="page" related_form="page-filter-form" search_query=filter_form.cleaned_data.query %} + {% endif %}
{% if request.user.expert_mode %} @@ -32,19 +47,21 @@

{% translate "Hide filters" %} {% endif %} - {% if can_edit_pages %} - {% if request.region.default_language == language %} - - {% translate "Create page" %} - - {% else %} - {% blocktranslate trimmed asvar disabled_button_title with request.region.default_language.translated_name as default_language %} - You can only create pages in the default language ({{ default_language }}). - {% endblocktranslate %} - + {% if not is_archive %} + {% if can_edit_pages %} + {% if request.region.default_language == language %} + + {% translate "Create page" %} + + {% else %} + {% blocktranslate trimmed asvar disabled_button_title with request.region.default_language.translated_name as default_language %} + You can only create pages in the default language ({{ default_language }}). + {% endblocktranslate %} + + {% endif %} {% endif %} {% endif %}

@@ -80,9 +97,11 @@

{% translate "Title in" %} {{ backend_language.translated_name }} {% endif %} - - {% translate "Tags" %} - + {% if not is_archive %} + + {% translate "Tags" %} + + {% endif %}
{% spaceless %} @@ -109,35 +128,52 @@

- {% for page in pages %} - {% include "pages/page_tree_node.html" %} - {% if forloop.last %} - - -
- {# djlint:off H020 #} - - {# djlint:on #} -
+ {% if is_archive %} + {% for page in pages %} + {% get_translation page language.slug as page_translation %} + {% include "pages/page_tree_archived_node.html" with page_translation=page_translation %} + {% empty %} + + + {% if filter_form.is_enabled %} + {% translate "No archived pages found with these filters." %} + {% else %} + {% translate "No pages archived yet." %} + {% endif %} - {% endif %} - {% empty %} - - - - - {% if filter_form.is_enabled %} - {% translate "No pages found with these filters." %} - {% else %} - {% translate "No pages available yet." %} - {% endif %} - - - {% endfor %} + {% endfor %} + {% else %} + {% for page in pages %} + {% include "pages/page_tree_node.html" %} + {% if forloop.last %} + + +
+ {# djlint:off H020 #} + + {# djlint:on #} +
+ + + {% endif %} + {% empty %} + + + + + {% if filter_form.is_enabled %} + {% translate "No pages found with these filters." %} + {% else %} + {% translate "No pages available yet." %} + {% endif %} + + + {% endfor %} + {% endif %}

@@ -170,94 +206,102 @@

- {% if can_edit_pages %} - - {% endif %} - {% if MT_PERMITTED %} - {% translate "pages" as content_type %} - {% if MT_PROVIDER %} - + {% endif %} + {% if MT_PERMITTED %} + {% translate "pages" as content_type %} + {% if MT_PROVIDER %} + + {% else %} + + {% endif %} + {% endif %} + {% if language.can_be_pdf_exported %} + {% else %} + {% endif %} + {% if request.user.expert_mode %} + {% with language_translated_name=language.translated_name %} + {% blocktranslate trimmed asvar xliff_export_public %} + Export published pages for {{ language_translated_name }} translation + {% endblocktranslate %} + {% blocktranslate trimmed asvar xliff_export_all %} + Export unpublished (⚠️) and published pages for {{ language_translated_name }} translation + {% endblocktranslate %} + {% if language == request.region.default_language %} + {% blocktranslate trimmed asvar xliff_export_disabled %} + You cannot export XLIFF files for the default language + {% endblocktranslate %} + + + {% else %} + + + {% endif %} + {% endwith %} + {% endif %} + {% if request.user.is_superuser or request.user.is_staff %} + + {% translate "pages" as content_type %} + + {% endif %} - {% endif %} - {% if language.can_be_pdf_exported %} - + {% if has_pages_in_translation %} + + {% endif %} {% else %} - - {% endif %} - {% if request.user.expert_mode %} - {% with language_translated_name=language.translated_name %} - {% blocktranslate trimmed asvar xliff_export_public %} - Export published pages for {{ language_translated_name }} translation - {% endblocktranslate %} - {% blocktranslate trimmed asvar xliff_export_all %} - Export unpublished (⚠️) and published pages for {{ language_translated_name }} translation - {% endblocktranslate %} - {% if language == request.region.default_language %} - {% blocktranslate trimmed asvar xliff_export_disabled %} - You cannot export XLIFF files for the default language - {% endblocktranslate %} - - - {% else %} - - - {% endif %} - {% endwith %} - {% endif %} - {% if request.user.is_superuser or request.user.is_staff %} - - {% translate "pages" as content_type %} - - - {% endif %} - {% if has_pages_in_translation %} - + {% if can_edit_pages %} + + {% endif %} {% endif %}

- {% if request.user.expert_mode and can_edit_pages %} -
-

- {% translate "Import XLIFF files" %} -

-
- {% translate "Supported file extensions" %}: .zip, .xlf, .xliff + {% if not is_archive %} + {% if request.user.expert_mode and can_edit_pages %} +
+

+ {% translate "Import XLIFF files" %} +

+
+ {% translate "Supported file extensions" %}: .zip, .xlf, .xliff +
+
+ + + + {% csrf_token %} + +
-
- - - - {% csrf_token %} - -
-
+ {% endif %} {% endif %} {% include "../generic_confirmation_dialog.html" %} {% include "../tutorials/page_tree.html" with tutorial_id="page-tree" hidden=request.user.page_tree_tutorial_seen %} diff --git a/integreat_cms/cms/views/pages/page_tree_view.py b/integreat_cms/cms/views/pages/page_tree_view.py index 19430164dc..1e601b1ac8 100644 --- a/integreat_cms/cms/views/pages/page_tree_view.py +++ b/integreat_cms/cms/views/pages/page_tree_view.py @@ -136,7 +136,7 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: return render( request, - self.template_name, + self.template, { **self.get_context_data(**kwargs), "pages": pages, @@ -154,5 +154,6 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: currently_in_translation=True, ).count() > 0, + "is_archive": self.archived, }, ) diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index 504beb0f26..65b968c292 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -7384,6 +7384,16 @@ msgstr "" " Dieses Feld freilassen, um einen eindeutigen Permalink aus dem Titel zu " "generieren" +#: cms/templates/pages/page_tree.html +#: cms/templates/pages/page_tree_archived.html +msgid "Archived Pages" +msgstr "Archivierte Seiten" + +#: cms/templates/pages/page_tree.html +#: cms/templates/pages/page_tree_archived.html +msgid "Back to pages" +msgstr "Zurück zu Seiten" + #: cms/templates/pages/page_tree.html msgid "Page Tree" msgstr "Seiten-Baum" @@ -7479,14 +7489,6 @@ msgstr "und {} weitere Dateien" msgid "Import" msgstr "Importieren" -#: cms/templates/pages/page_tree_archived.html -msgid "Archived Pages" -msgstr "Archivierte Seiten" - -#: cms/templates/pages/page_tree_archived.html -msgid "Back to pages" -msgstr "Zurück zu Seiten" - #: cms/templates/pages/page_tree_archived.html msgid "No archived pages found with these filters." msgstr "Keine archivierten Seiten mit diesen Filtern gefunden." From 562f95276248fa1b09838596618dc2029d930306 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Wed, 27 Nov 2024 18:02:04 +0100 Subject: [PATCH 24/52] Fix translation format --- integreat_cms/locale/de/LC_MESSAGES/django.po | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index 65b968c292..3013b1cb9d 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -7417,6 +7417,16 @@ msgstr "" msgid "Tags" msgstr "Tags" +#: cms/templates/pages/page_tree.html +#: cms/templates/pages/page_tree_archived.html +msgid "No archived pages found with these filters." +msgstr "Keine archivierten Seiten mit diesen Filtern gefunden." + +#: cms/templates/pages/page_tree.html +#: cms/templates/pages/page_tree_archived.html +msgid "No pages archived yet." +msgstr "Noch keine Seiten archiviert." + #: cms/templates/pages/page_tree.html msgid "No pages found with these filters." msgstr "Keine Seiten mit diesen Filtern gefunden." @@ -7469,6 +7479,11 @@ msgstr "" msgid "Export selected pages for multilingual translation" msgstr "Ausgewählte Seiten für die mehrsprachige Übersetzung exportieren" +#: cms/templates/pages/page_tree.html +#: cms/templates/pages/page_tree_archived.html +msgid "Restore pages" +msgstr "Seiten wiederherstellen" + #: cms/templates/pages/page_tree.html msgid "Import XLIFF files" msgstr "XLIFF-Dateien importieren" @@ -7489,18 +7504,6 @@ msgstr "und {} weitere Dateien" msgid "Import" msgstr "Importieren" -#: cms/templates/pages/page_tree_archived.html -msgid "No archived pages found with these filters." -msgstr "Keine archivierten Seiten mit diesen Filtern gefunden." - -#: cms/templates/pages/page_tree_archived.html -msgid "No pages archived yet." -msgstr "Noch keine Seiten archiviert." - -#: cms/templates/pages/page_tree_archived.html -msgid "Restore pages" -msgstr "Seiten wiederherstellen" - #: cms/templates/pages/page_tree_archived_node.html msgid "Drag & drop is disabled for archived pages." msgstr "Drag & drop ist für archivierte Seiten nicht möglich." From 836506b8753c58bba9825d83524f506a5a2b7be6 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Wed, 27 Nov 2024 18:22:26 +0100 Subject: [PATCH 25/52] Add template for statistics about viewed pages --- .../templates/statistics/statistics_overview.html | 1 + .../statistics/statistics_viewed_pages.html | 12 ++++++++++++ integreat_cms/locale/de/LC_MESSAGES/django.po | 4 ++++ 3 files changed, 17 insertions(+) create mode 100644 integreat_cms/cms/templates/statistics/statistics_viewed_pages.html diff --git a/integreat_cms/cms/templates/statistics/statistics_overview.html b/integreat_cms/cms/templates/statistics/statistics_overview.html index 2a7c754b34..25ed45faba 100644 --- a/integreat_cms/cms/templates/statistics/statistics_overview.html +++ b/integreat_cms/cms/templates/statistics/statistics_overview.html @@ -11,6 +11,7 @@

{% include "statistics/statistics_chart.html" with box_id="statistics_chart" %} + {% include "statistics/statistics_viewed_pages.html" with box_id="statistics_viewed_pages" %}
{% include "statistics/statistics_sidebar.html" with box_id="statistics_sidebar" %} diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html new file mode 100644 index 0000000000..a86b09a10b --- /dev/null +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -0,0 +1,12 @@ +{% extends "_collapsible_box.html" %} +{% load i18n %} +{% load static %} +{% load widget_tweaks %} +{% block collapsible_box_icon %} + chart-column-increasing +{% endblock collapsible_box_icon %} +{% block collapsible_box_title %} + {% translate "Page views" %} +{% endblock collapsible_box_title %} +{% block collapsible_box_content %} +{% endblock collapsible_box_content %} diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index 3013b1cb9d..b1f4e287c8 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -8527,6 +8527,10 @@ msgstr "Bild/PNG" msgid "Table Document/CSV" msgstr "Tabellendokument/CSV" +#: cms/templates/statistics/statistics_viewed_pages.html +msgid "Page views" +msgstr "Seitenzugriffe" + #: cms/templates/translations/translations_management.html msgid "Manage machine translations" msgstr "Maschinelle Übersetzungen verwalten" From bc93bc60e431945d96e930e89afbc55a364bb948 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Wed, 27 Nov 2024 18:57:48 +0100 Subject: [PATCH 26/52] Add _generic_page_tree.html --- .../templates/pages/_generic_page_tree.html | 25 +++++++++++++++++++ .../cms/templates/pages/page_tree.html | 19 +------------- .../statistics/statistics_viewed_pages.html | 12 +++++++++ integreat_cms/locale/de/LC_MESSAGES/django.po | 5 ++-- 4 files changed, 41 insertions(+), 20 deletions(-) create mode 100644 integreat_cms/cms/templates/pages/_generic_page_tree.html diff --git a/integreat_cms/cms/templates/pages/_generic_page_tree.html b/integreat_cms/cms/templates/pages/_generic_page_tree.html new file mode 100644 index 0000000000..5316316b87 --- /dev/null +++ b/integreat_cms/cms/templates/pages/_generic_page_tree.html @@ -0,0 +1,25 @@ +{% load i18n %} +{% load static %} +{% load content_filters %} +{% load page_filters %} +{% load rules %} +{% block content %} + + + + {% if not filter_form.is_enabled %} + + {% translate "Hierarchy" %} + + {% endif %} + + {% translate "Title in" %} {{ language.translated_name }} + + {% get_current_language as LANGUAGE_CODE %} + {% get_language LANGUAGE_CODE as backend_language %} + {% if backend_language and backend_language != language %} + + {% translate "Title in" %} {{ backend_language.translated_name }} + + {% endif %} +{% endblock content %} diff --git a/integreat_cms/cms/templates/pages/page_tree.html b/integreat_cms/cms/templates/pages/page_tree.html index 60b05b42b0..c49b522423 100644 --- a/integreat_cms/cms/templates/pages/page_tree.html +++ b/integreat_cms/cms/templates/pages/page_tree.html @@ -79,24 +79,7 @@

class="w-full mt-4 rounded border-2 border-solid border-gray-200 shadow bg-white table-auto"> - - - - {% if not filter_form.is_enabled %} - - {% translate "Hierarchy" %} - - {% endif %} - - {% translate "Title in" %} {{ language.translated_name }} - - {% get_current_language as LANGUAGE_CODE %} - {% get_language LANGUAGE_CODE as backend_language %} - {% if backend_language and backend_language != language %} - - {% translate "Title in" %} {{ backend_language.translated_name }} - - {% endif %} + {% include "pages/_generic_page_tree.html" %} {% if not is_archive %} {% translate "Tags" %} diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html index a86b09a10b..dd0daef926 100644 --- a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -9,4 +9,16 @@ {% translate "Page views" %} {% endblock collapsible_box_title %} {% block collapsible_box_content %} +
+ {% csrf_token %} +
+ + + + {% include "pages/_generic_page_tree.html" %} + + +
+
+
{% endblock collapsible_box_content %} diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index b1f4e287c8..310671efc1 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -3490,7 +3490,8 @@ msgstr "Ob die Seite explizit archiviert ist oder nicht" #: cms/models/pages/abstract_base_page_translation.py #: cms/templates/events/event_form.html cms/templates/events/event_list.html -#: cms/templates/pages/page_form.html cms/templates/pages/page_tree.html +#: cms/templates/pages/_generic_page_tree.html +#: cms/templates/pages/page_form.html #: cms/templates/pages/page_tree_archived.html cms/templates/pois/poi_form.html #: cms/templates/pois/poi_list.html cms/templates/pois/poi_list_archived.html msgid "Title in" @@ -6760,7 +6761,7 @@ msgid "Create language tree node" msgstr "Sprach-Knoten hinzufügen" #: cms/templates/languagetreenodes/languagetreenode_list.html -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/_generic_page_tree.html #: cms/templates/pages/page_tree_archived.html msgid "Hierarchy" msgstr "Hierarchie" From ce301cca3dcd77fa76322c4dba43caf361a1bdb9 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Wed, 27 Nov 2024 19:15:20 +0100 Subject: [PATCH 27/52] Add additional column to page tree on statistics --- .../cms/templates/statistics/statistics_viewed_pages.html | 3 +++ integreat_cms/locale/de/LC_MESSAGES/django.po | 1 + 2 files changed, 4 insertions(+) diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html index dd0daef926..4b571d2675 100644 --- a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -16,6 +16,9 @@ {% include "pages/_generic_page_tree.html" %} + + {% translate "Accesses" %} ({{ form.start_date.value|date:"d.m.Y" }} - {{ form.end_date.value|date:"d.m.Y" }}) + diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index 310671efc1..d6789250a0 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -8480,6 +8480,7 @@ msgid "Shown languages" msgstr "Angezeigte Sprachen" #: cms/templates/statistics/_statistics_legend.html +#: cms/templates/statistics/statistics_viewed_pages.html msgid "Accesses" msgstr "Zugriffe" From 4f4c99d14bcf0739a9ceff93333b68fee544371b Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Fri, 29 Nov 2024 18:04:32 +0000 Subject: [PATCH 28/52] Fix missing backend titles for root pages in page tree and deleting tags for archive nodes --- .../cms/templates/pages/page_tree.html | 71 +++++++------------ .../cms/templates/pages/page_tree_node.html | 24 ++++--- 2 files changed, 42 insertions(+), 53 deletions(-) diff --git a/integreat_cms/cms/templates/pages/page_tree.html b/integreat_cms/cms/templates/pages/page_tree.html index c49b522423..b595b9fa62 100644 --- a/integreat_cms/cms/templates/pages/page_tree.html +++ b/integreat_cms/cms/templates/pages/page_tree.html @@ -111,52 +111,35 @@

- {% if is_archive %} - {% for page in pages %} - {% get_translation page language.slug as page_translation %} - {% include "pages/page_tree_archived_node.html" with page_translation=page_translation %} - {% empty %} - - - {% if filter_form.is_enabled %} - {% translate "No archived pages found with these filters." %} - {% else %} - {% translate "No pages archived yet." %} - {% endif %} - - - {% endfor %} - {% else %} - {% for page in pages %} - {% include "pages/page_tree_node.html" %} - {% if forloop.last %} - - -
- {# djlint:off H020 #} - - {# djlint:on #} -
- - - {% endif %} - {% empty %} - - - - - {% if filter_form.is_enabled %} - {% translate "No pages found with these filters." %} - {% else %} - {% translate "No pages available yet." %} - {% endif %} + {% for page in pages %} + {% include "pages/page_tree_node.html" with is_archive=is_archive %} + {% if forloop.last %} + + +
+ {# djlint:off H020 #} + + {# djlint:on #} +
- {% endfor %} - {% endif %} + {% endif %} + {% empty %} + + + + + {% if filter_form.is_enabled %} + {% translate "No pages found with these filters." %} + {% else %} + {% translate "No pages available yet." %} + {% endif %} + + + {% endfor %}

diff --git a/integreat_cms/cms/templates/pages/page_tree_node.html b/integreat_cms/cms/templates/pages/page_tree_node.html index 21203e4c53..0f62afc186 100644 --- a/integreat_cms/cms/templates/pages/page_tree_node.html +++ b/integreat_cms/cms/templates/pages/page_tree_node.html @@ -61,11 +61,15 @@ {% endif %} - {% if backend_language and backend_language != language %} + {% if not backend_language %} + {% get_current_language as LANGUAGE_CODE %} + {% get_language LANGUAGE_CODE as backend_language %} + {% endif %} + {% if backend_language != language %} + class="block py-1.5 px-2 overflow-hidden max-w-xs whitespace-nowrap text-ellipsis text-gray-800" + title="{% if page.backend_translation %} {{ page.backend_translation.title }}{% endif %}">
{% if page.backend_translation %} {{ page.backend_translation.title }} @@ -76,12 +80,14 @@ {% endif %} - -
- {% for tag in page_translation.tags %} - {{ tag }} - {% endfor %} -
+ {% if not is_archive %} + +
+ {% for tag in page_translation.tags %} + {{ tag }} + {% endfor %} +
+ {% endif %}
From 4f914106d546d3bff9c583095e3aa07a5420149a Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Mon, 2 Dec 2024 12:51:43 +0000 Subject: [PATCH 29/52] Merging page_tree_node and page_tree_archived_node --- .../cms/templates/pages/page_tree.html | 2 +- .../cms/templates/pages/page_tree_node.html | 117 ++++++++++++------ integreat_cms/locale/de/LC_MESSAGES/django.po | 18 ++- 3 files changed, 86 insertions(+), 51 deletions(-) diff --git a/integreat_cms/cms/templates/pages/page_tree.html b/integreat_cms/cms/templates/pages/page_tree.html index b595b9fa62..a949941db9 100644 --- a/integreat_cms/cms/templates/pages/page_tree.html +++ b/integreat_cms/cms/templates/pages/page_tree.html @@ -112,7 +112,7 @@

{% for page in pages %} - {% include "pages/page_tree_node.html" with is_archive=is_archive %} + {% include "pages/page_tree_node.html" with is_archive=is_archive %} {% if forloop.last %} + class="block py-1.5 px-2 overflow-hidden max-w-xs whitespace-nowrap text-ellipsis text-gray-800" + title="{% if page.backend_translation %} {{ page.backend_translation.title }}{% endif %}">
{% if page.backend_translation %} {{ page.backend_translation.title }} @@ -87,7 +87,7 @@ {{ tag }} {% endfor %}
- {% endif %} + {% endif %}
@@ -139,9 +139,25 @@
-
- {{ page_translation.get_status_display }} -
+ {% if is_archive %} + {% if page.explicitly_archived %} +
+ {% translate "Archived" %} +
+ {% else %} +
+ {% translate "Archived, because a parent page is archived" %} +
+ {% endif %} + {% else %} +
+ {{ page_translation.get_status_display }} +
+ {% endif %}
@@ -164,43 +180,64 @@ {% endif %} - {% if page_translation.status == PUBLIC %} - - - - {% else %} - - {% endif %} - - - - {% if can_edit_pages %} - {% if page.mirroring_pages.exists %} - - {% else %} - {% endif %} + + + + {% if can_edit_pages %} + {% if page.mirroring_pages.exists %} + + {% else %} + + {% endif %} + {% endif %} + {% else %} + {% if can_edit_pages %} + {% if page.explicitly_archived %} + + {% else %} + + + + {% endif %} + {% endif %} {% endif %} {% if perms.cms.delete_page %} {# djlint:off H023 #} @@ -228,7 +265,7 @@ {% endif %} {# djlint:on #} {% endif %} - {% if request.region.short_urls_enabled and request.user.expert_mode %} + {% if not is_archive and request.region.short_urls_enabled and request.user.expert_mode %} {% if page_translation %} Date: Tue, 3 Dec 2024 09:27:44 +0000 Subject: [PATCH 30/52] Disable dragable tree node for archived tree nodes --- .../cms/templates/pages/page_tree_node.html | 18 +++++++++--------- integreat_cms/locale/de/LC_MESSAGES/django.po | 8 +++++++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/integreat_cms/cms/templates/pages/page_tree_node.html b/integreat_cms/cms/templates/pages/page_tree_node.html index 96b1b2017f..36c041a522 100644 --- a/integreat_cms/cms/templates/pages/page_tree_node.html +++ b/integreat_cms/cms/templates/pages/page_tree_node.html @@ -30,21 +30,21 @@ {% has_perm 'cms.change_page_object' request.user as can_edit_pages %} + data-node-descendants="{{ page|get_descendant_ids }}" + class="drag cursor-move text-gray-800 inline-block pl-4 align-middle" + draggable="{% if can_edit_pages and not is_archive %}true{% else %}false{% endif %}" + title="{% if not is_archive %}{% translate "Change the order and position of the pages with drag & drop." %}{% else %}{% translate "Drag & drop is disabled for archived pages." %}{% endif %}"> {% if can_edit_pages %} {% endif %} {% if not page.parent_id and not page.is_leaf or page.parent_id and page.cached_children|length > 0 %} + data-expand-title="{% translate "Expand all subpages" %}" + data-collapse-title="{% translate "Collapse all subpages" %}" + data-page-id="{{ page.id }}" + data-page-children="{{ page|get_children_ids }}" + data-page-tree-id="{{ page.tree_id }}"> {% endif %} diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index 2e38747cba..5fd22a39ec 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -1685,7 +1685,7 @@ msgstr "Versteckt" #: cms/models/pages/page_translation.py cms/models/regions/region.py #: cms/templates/events/event_form.html cms/templates/pages/page_form.html #: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pois/poi_form.html +#: cms/templates/pages/page_tree_node.html cms/templates/pois/poi_form.html msgid "Archived" msgstr "Archiviert" @@ -5374,6 +5374,7 @@ msgstr "Kontakt archivieren" #: cms/templates/contacts/contact_form.html #: cms/templates/pages/page_form_sidebar/actions_box.html #: cms/templates/pages/page_tree_archived_node.html +#: cms/templates/pages/page_tree_node.html msgid "Restore page" msgstr "Seite wiederherstellen" @@ -7202,6 +7203,7 @@ msgstr "Übersetzungsprozess abbrechen" #: cms/templates/pages/page_form.html #: cms/templates/pages/page_tree_archived_node.html +#: cms/templates/pages/page_tree_node.html msgid "Archived, because a parent page is archived" msgstr "Archiviert, weil eine übergeordnete Seite archiviert ist" @@ -7504,6 +7506,7 @@ msgid "No pages archived yet." msgstr "Noch keine Seiten archiviert." #: cms/templates/pages/page_tree_archived_node.html +#: cms/templates/pages/page_tree_node.html msgid "Drag & drop is disabled for archived pages." msgstr "Drag & drop ist für archivierte Seiten nicht möglich." @@ -7518,10 +7521,12 @@ msgid "Collapse all subpages" msgstr "Alle Unterseiten einklappen" #: cms/templates/pages/page_tree_archived_node.html +#: cms/templates/pages/page_tree_node.html msgid "This page is archived." msgstr "Diese Seite ist archiviert." #: cms/templates/pages/page_tree_archived_node.html +#: cms/templates/pages/page_tree_node.html msgid "" "This page is archived, because at least one of its parent pages is archived." msgstr "" @@ -7539,6 +7544,7 @@ msgid "You cannot preview an empty page." msgstr "Eine leere Seite hat keine Vorschau" #: cms/templates/pages/page_tree_archived_node.html +#: cms/templates/pages/page_tree_node.html msgid "To restore this page, you have to restore its archived parent page." msgstr "" "Um diese Seite wiederherzustellen, müssen Sie ihre archivierte übergeordnete " From a022d8f701f047a034a908b1c5afe3a822af2eaf Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Fri, 6 Dec 2024 13:59:04 +0100 Subject: [PATCH 31/52] [WIP] Page based access --- .../views/statistics/statistics_actions.py | 7 ++ integreat_cms/matomo_api/matomo_api_client.py | 96 ++++++++++++++++++- integreat_cms/matomo_api/utils.py | 23 +++++ 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 integreat_cms/matomo_api/utils.py diff --git a/integreat_cms/cms/views/statistics/statistics_actions.py b/integreat_cms/cms/views/statistics/statistics_actions.py index 8a1214dfe2..d039263e5c 100644 --- a/integreat_cms/cms/views/statistics/statistics_actions.py +++ b/integreat_cms/cms/views/statistics/statistics_actions.py @@ -88,6 +88,13 @@ def get_visits_per_language_ajax( ) try: + region.statistics.get_page_based_statistics( + start_date=statistics_form.cleaned_data["start_date"], + end_date=statistics_form.cleaned_data["end_date"], + period=statistics_form.cleaned_data["period"], + region=region, + ) + result = region.statistics.get_visits_per_language( start_date=statistics_form.cleaned_data["start_date"], end_date=statistics_form.cleaned_data["end_date"], diff --git a/integreat_cms/matomo_api/matomo_api_client.py b/integreat_cms/matomo_api/matomo_api_client.py index e0af86421f..055e73ef8e 100644 --- a/integreat_cms/matomo_api/matomo_api_client.py +++ b/integreat_cms/matomo_api/matomo_api_client.py @@ -13,6 +13,7 @@ from django.utils.translation import gettext_lazy as _ from ..cms.constants import language_color, matomo_periods +from .utils import create_translation_slugs if TYPE_CHECKING: import sys @@ -23,7 +24,7 @@ from aiohttp import ClientSession from django.utils.functional import Promise - from ..cms.models import Language, Region + from ..cms.models import Language, Page, Region logger = logging.getLogger(__name__) @@ -527,3 +528,96 @@ def get_access_data( ), ] return data_entries, legend_entries + + def get_page_based_statistics( + self, + start_date: date, + end_date: date, + period: str, + region: Region, + ) -> list[dict[str, Any]]: + """ + Returns the statistics for each page of a region + + :param start_date: Start date + :param end_date: End date + :param period: The period (one of :attr:`~integreat_cms.cms.constants.matomo_periods.CHOICES`) + :param region: The region + :return: ? + :raises ~integreat_cms.matomo_api.matomo_api_client.MatomoException: When a :class:`~aiohttp.ClientError` was raised during a + """ + query_params = { + "date": f"{start_date},{end_date}", + "expanded": "1", + "filter_limit": "-1", + "format_metrics": "1", + "idSite": self.matomo_id, + "method": "VisitsSummary.getActions", + "period": period, + } + logger.debug( + "Query params: %r", + query_params, + ) + pages = region.get_pages() + languages = list(self.languages) + + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + # Execute async request to Matomo API + logger.debug("Fetching visits for languages %r asynchronously.", languages) + datasets = loop.run_until_complete( + self.get_page_based_statistics_async(loop, query_params, languages, pages) + ) + return datasets + + async def get_page_based_statistics_async( + self, + loop: AbstractEventLoop, + query_params: dict[str, Any], + languages: list[Language], + pages: list[Page], + ) -> list[dict[str, Any]]: + """ + Async wrapper to fetch the total visits with :mod:`aiohttp`. + Opens a :class:`~aiohttp.ClientSession`, creates a :class:`~asyncio.Task` for each language to call + :func:`~integreat_cms.matomo_api.matomo_api_client.MatomoApiClient.fetch` and waits for all tasks to finish with + :func:`~asyncio.gather`. + The returned list of gathered results has the correct order in which the tasks were created (at first the + ordered list of languages and the last element is the task for the total visits). + Called from :func:`~integreat_cms.matomo_api.matomo_api_client.MatomoApiClient.get_visits_per_language`. + + :param loop: The asyncio event loop + :param query_params: The parameters which are passed to the Matomo API + :param languages: The list of languages which should be retrieved + :param pages: The list of pages for which the data should be retrieved + :raises ~integreat_cms.matomo_api.matomo_api_client.MatomoException: When a :class:`~aiohttp.ClientError` was raised during a + Matomo API request + + :return: The list of gathered results + """ + create_translation_slugs(pages, languages) + async with aiohttp.ClientSession() as session: + # Create tasks for visits by language + tasks = [ + loop.create_task( + self.fetch( + session, + **query_params, + segment=f"pageUrl=@/{language.slug}/wp-json/extensions/v3/,pageUrl=@/api/v3/{self.region_slug}/{language.slug}/", + ) + ) + for language in languages + ] + + result = await asyncio.gather(*tasks) + # We're not retrieving the matomo id as part of the tasks, thus we know that the result is a list of dicts, not a list of list of ints. + if TYPE_CHECKING: + + def is_dict_list( + lst: list[dict[str, Any] | list[int]] + ) -> TypeGuard[list[dict[str, Any]]]: + return all(isinstance(d, dict) for d in lst) + + assert is_dict_list(result) + return result diff --git a/integreat_cms/matomo_api/utils.py b/integreat_cms/matomo_api/utils.py new file mode 100644 index 0000000000..0bfa220c26 --- /dev/null +++ b/integreat_cms/matomo_api/utils.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from typing import Dict, TYPE_CHECKING + +if TYPE_CHECKING: + from django.db.models.query import QuerySet + from ..cms.models import Language, Page + + +def create_translation_slugs( + pages: QuerySet[Page], languages: list[Language] +) -> dict[Page, dict[str, str]]: + """ + This function creates the translation slug for the matomo call. It returns then language_slug and the slug in the target translation. + """ + translation_slugs: Dict[Page, Dict[str, str]] = {} + for page in pages: + translation_slugs[page] = {} + for language in languages: + if page_translation := page.get_translation(language.slug): + # print(page_translation.ancestor_path) + translation_slugs[page][language.slug] = page_translation.slug + return translation_slugs From ef21cf893eb3ff8a74efdb85e05fb3b8022082b8 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Sat, 7 Dec 2024 12:51:29 +0100 Subject: [PATCH 32/52] [WIP] Restored commit --- .../views/statistics/statistics_actions.py | 2 +- integreat_cms/matomo_api/matomo_api_client.py | 154 +++++++++++++++++- integreat_cms/matomo_api/utils.py | 18 +- 3 files changed, 163 insertions(+), 11 deletions(-) diff --git a/integreat_cms/cms/views/statistics/statistics_actions.py b/integreat_cms/cms/views/statistics/statistics_actions.py index d039263e5c..ce2806d770 100644 --- a/integreat_cms/cms/views/statistics/statistics_actions.py +++ b/integreat_cms/cms/views/statistics/statistics_actions.py @@ -88,7 +88,7 @@ def get_visits_per_language_ajax( ) try: - region.statistics.get_page_based_statistics( + region.statistics.get_page_based_accesses( start_date=statistics_form.cleaned_data["start_date"], end_date=statistics_form.cleaned_data["end_date"], period=statistics_form.cleaned_data["period"], diff --git a/integreat_cms/matomo_api/matomo_api_client.py b/integreat_cms/matomo_api/matomo_api_client.py index 055e73ef8e..3c2437566b 100644 --- a/integreat_cms/matomo_api/matomo_api_client.py +++ b/integreat_cms/matomo_api/matomo_api_client.py @@ -4,6 +4,7 @@ import logging import re from datetime import date, datetime +from collections.abc import Mapping from typing import TYPE_CHECKING from urllib.parse import urlencode @@ -13,7 +14,7 @@ from django.utils.translation import gettext_lazy as _ from ..cms.constants import language_color, matomo_periods -from .utils import create_translation_slugs +from .utils import get_translation_slug if TYPE_CHECKING: import sys @@ -528,7 +529,7 @@ def get_access_data( ), ] return data_entries, legend_entries - + """ def get_page_based_statistics( self, start_date: date, @@ -536,7 +537,6 @@ def get_page_based_statistics( period: str, region: Region, ) -> list[dict[str, Any]]: - """ Returns the statistics for each page of a region :param start_date: Start date @@ -545,7 +545,6 @@ def get_page_based_statistics( :param region: The region :return: ? :raises ~integreat_cms.matomo_api.matomo_api_client.MatomoException: When a :class:`~aiohttp.ClientError` was raised during a - """ query_params = { "date": f"{start_date},{end_date}", "expanded": "1", @@ -578,7 +577,6 @@ async def get_page_based_statistics_async( languages: list[Language], pages: list[Page], ) -> list[dict[str, Any]]: - """ Async wrapper to fetch the total visits with :mod:`aiohttp`. Opens a :class:`~aiohttp.ClientSession`, creates a :class:`~asyncio.Task` for each language to call :func:`~integreat_cms.matomo_api.matomo_api_client.MatomoApiClient.fetch` and waits for all tasks to finish with @@ -595,7 +593,6 @@ async def get_page_based_statistics_async( Matomo API request :return: The list of gathered results - """ create_translation_slugs(pages, languages) async with aiohttp.ClientSession() as session: # Create tasks for visits by language @@ -621,3 +618,148 @@ def is_dict_list( assert is_dict_list(result) return result + """ + + async def get_visits_per_page_and_language_async( + self, + loop: AbstractEventLoop, + query_params: dict[str, Any], + languages: list[Language], + pages: list[Page], + region_slug: str, + ) -> list[dict[str, Any]]: + translation_slugs = get_translation_slug(pages, languages) + print(translation_slugs) + async with aiohttp.ClientSession() as session: + # Create tasks for visits by language + tasks = [ + loop.create_task( + self.nice_function(session, query_params, region_slug=region_slug, page_id=page_id, lang_slug=lang_slug, full_slug=full_slug) + ) + for page_id, langs in translation_slugs.items() + for lang_slug, full_slug in langs.items() + ] + # Wait for all tasks to finish and collect the results + # (the results are sorted in the order the tasks were created) + result = await asyncio.gather(*tasks) + # We're not retrieving the matomo id as part of the tasks, thus we know that the result is a list of dicts, not a list of list of ints. + if TYPE_CHECKING: + + def is_dict_list( + lst: list[dict[str, Any] | list[int]] + ) -> TypeGuard[list[dict[str, Any]]]: + return all(isinstance(d, dict) for d in lst) + + assert is_dict_list(result) + return result + + async def nice_function(self, session: aiohttp.ClientSession, query_params: dict[str, Any], region_slug: str, page_id: int, lang_slug: str, full_slug: str) -> dict: + print(f"full slug: {full_slug}") + return { + page_id: { + lang_slug: await self.fetch( session, **query_params, segment=f"pageUrl=@/children/?depth=2&url=/{region_slug}/{full_slug}" ) + } + } + + def get_page_based_accesses(self, start_date: date, end_date: date, period: str, region: Region): + from ..cms.models import Page, Language + + language1 = Language.objects.get(id=1) + language2 = Language.objects.get(id=2) + language3 = Language.objects.get(id=3) + print("get_page_based_accesses") + + query_params = { + "date": f"{start_date},{end_date}", + "expanded": "1", + "filter_limit": "-1", + "format_metrics": "1", + "idSite": self.matomo_id, + "method": "VisitsSummary.getActions", + "period": period, + } + languages = [language1, language2, language3] + pages = region.get_pages() + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + # Execute async request to Matomo API + logger.debug("Fetching visits for languages %r asynchronously.", languages) + datasets = loop.run_until_complete( + self.get_visits_per_page_and_language_async(loop, query_params, languages, pages, region.slug) + ) + + def deep_merge(*dicts): + """ + Recursively merges dictionaries. Values in later dictionaries override earlier ones + for non-dict values, while dictionaries are merged recursively. + """ + merged = {} + for d in dicts: + for key, value in d.items(): + if key in merged and isinstance(merged[key], Mapping) and isinstance(value, Mapping): + # Recursively merge dictionaries + merged[key] = deep_merge(merged[key], value) + else: + # Otherwise, override or add the value + merged[key] = value + return merged + + datasets = deep_merge(*datasets) + + """datasets = { + key: value + for result in datasets + for key, value in result.items() + }""" + print(f"Datasets: {datasets}") + + """ + # alle Daten zu holen + raw_data = [ + { + "slug": "/sprachkurse/integrationskurse", + "language_slug": "de", + "accesses": 8, + }, + { + "slug": "/language_courses/integration_courses", + "language_slug": "de", + "accesses": 8, + }, + ] + + # strukturieren + # page id raus finden + + translations = PageTranslations.objects.all() + # nicht aktuelle Versionen rausfiltern + for value in data: + # page id raus finden + + translations = PageTranslations.objects.all() + # nicht aktuelle Versionen rausfiltern + for value in data: + gesuchte_translation = translations.get(slug=value.slug) + # erstelle aus der gesuchter translation ein dict + { + "id": gesuchte_translation.id, #654 + "access": value.accesses, #88 + "language_slug": value.language_slug, # "en" + "page_id": gesuchte_translation.page.id, #14 + } + # rausfinden welche page translations dazu gehören + # gibt es mit meiner page_id schon ein dict? + # wenn nein, erstelle neues dict und fülle mit informationen aus, die du hast + + # wenn ja, dann + # hänge nur meinen language slug in accesses an + # end_data.access[language_slug] = current_data.access + + # schicke ich strukturiert an AJAX + end_data = [ + { + "page_id": 14, + "accesses": {"de": 123, "en":23, "fr":312, "ar": 76543} + } + {"page_id": 1, "de": 12, "ru": 1}, + """ diff --git a/integreat_cms/matomo_api/utils.py b/integreat_cms/matomo_api/utils.py index 0bfa220c26..55271d798e 100644 --- a/integreat_cms/matomo_api/utils.py +++ b/integreat_cms/matomo_api/utils.py @@ -7,17 +7,27 @@ from ..cms.models import Language, Page -def create_translation_slugs( +"""def create_translation_slugs( pages: QuerySet[Page], languages: list[Language] ) -> dict[Page, dict[str, str]]: - """ This function creates the translation slug for the matomo call. It returns then language_slug and the slug in the target translation. - """ translation_slugs: Dict[Page, Dict[str, str]] = {} for page in pages: translation_slugs[page] = {} for language in languages: if page_translation := page.get_translation(language.slug): - # print(page_translation.ancestor_path) + # print(page_translation.ancestor_path)Sollen wir uns Sol translation_slugs[page][language.slug] = page_translation.slug return translation_slugs + """ + +def get_translation_slug(pages: list[Page], languages: list[Language]) -> dict[int, dict[str, str]]: + translation_slugs = {} + for page in pages: + for language in languages: + if page_translation := page.get_translation(language.slug): + if page.id not in translation_slugs: + translation_slugs[page.id] = {} + translation_slugs[page.id][language.slug] = f"{language.slug}/{page_translation.slug}" # nach richtigen Slug noch mal recherchieren + return translation_slugs + From 4801e17e9a6056ebaa8e80d069e5b7b518524784 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Mon, 9 Dec 2024 22:27:47 +0100 Subject: [PATCH 33/52] Add page accesses calls --- integreat_cms/cms/urls/protected.py | 5 + .../cms/views/statistics/__init__.py | 6 +- .../views/statistics/statistics_actions.py | 43 +++- integreat_cms/matomo_api/matomo_api_client.py | 235 +++++------------- integreat_cms/matomo_api/utils.py | 35 +-- 5 files changed, 128 insertions(+), 196 deletions(-) diff --git a/integreat_cms/cms/urls/protected.py b/integreat_cms/cms/urls/protected.py index d9e8db4711..8e2ba5d016 100644 --- a/integreat_cms/cms/urls/protected.py +++ b/integreat_cms/cms/urls/protected.py @@ -712,6 +712,11 @@ statistics.get_visits_per_language_ajax, name="statistics_visits_per_language", ), + path( + "page-based-accesses/", + statistics.get_page_accesses_ajax, + name="statistics_page_based_accesses", + ), ] ), ), diff --git a/integreat_cms/cms/views/statistics/__init__.py b/integreat_cms/cms/views/statistics/__init__.py index c5427853da..cd438b9b60 100644 --- a/integreat_cms/cms/views/statistics/__init__.py +++ b/integreat_cms/cms/views/statistics/__init__.py @@ -4,5 +4,9 @@ from __future__ import annotations -from .statistics_actions import get_total_visits_ajax, get_visits_per_language_ajax +from .statistics_actions import ( + get_page_accesses_ajax, + get_total_visits_ajax, + get_visits_per_language_ajax, +) from .statistics_view import AnalyticsView diff --git a/integreat_cms/cms/views/statistics/statistics_actions.py b/integreat_cms/cms/views/statistics/statistics_actions.py index ce2806d770..a03320f690 100644 --- a/integreat_cms/cms/views/statistics/statistics_actions.py +++ b/integreat_cms/cms/views/statistics/statistics_actions.py @@ -88,17 +88,54 @@ def get_visits_per_language_ajax( ) try: - region.statistics.get_page_based_accesses( + result = region.statistics.get_visits_per_language( start_date=statistics_form.cleaned_data["start_date"], end_date=statistics_form.cleaned_data["end_date"], period=statistics_form.cleaned_data["period"], - region=region, + ) + return JsonResponse(result, safe=False) + except asyncio.TimeoutError: + return JsonResponse( + {"error": "Timeout during request to Matomo API"}, status=504 + ) + except MatomoException as e: + logger.exception(e) + return JsonResponse( + {"error": "The request to the Matomo API failed."}, status=500 ) - result = region.statistics.get_visits_per_language( + +@require_POST +# pylint: disable=unused-argument +def get_page_accesses_ajax(request: HttpRequest, region_slug: str) -> JsonResponse: + """ + Ajax method to request the app hits for a certain timerange distinguished by languages. + + :param request: The current request + :param region_slug: The slug of the current region + :return: A JSON with all API-Hits of the requested time period + """ + region = request.region + + if not region.statistics_enabled: + return JsonResponse( + {"error": "Statistics are not enabled for this region."}, status=500 + ) + + statistics_form = StatisticsFilterForm(data=request.POST) + + if not statistics_form.is_valid(): + return JsonResponse( + {"errors": statistics_form.errors}, + status=400, + ) + + try: + result = region.statistics.get_page_accesses( start_date=statistics_form.cleaned_data["start_date"], end_date=statistics_form.cleaned_data["end_date"], period=statistics_form.cleaned_data["period"], + region=region, ) return JsonResponse(result, safe=False) except asyncio.TimeoutError: diff --git a/integreat_cms/matomo_api/matomo_api_client.py b/integreat_cms/matomo_api/matomo_api_client.py index 3c2437566b..f5474b56c5 100644 --- a/integreat_cms/matomo_api/matomo_api_client.py +++ b/integreat_cms/matomo_api/matomo_api_client.py @@ -3,8 +3,8 @@ import asyncio import logging import re -from datetime import date, datetime from collections.abc import Mapping +from datetime import date, datetime from typing import TYPE_CHECKING from urllib.parse import urlencode @@ -14,7 +14,7 @@ from django.utils.translation import gettext_lazy as _ from ..cms.constants import language_color, matomo_periods -from .utils import get_translation_slug +from .utils import async_get_translation_slug if TYPE_CHECKING: import sys @@ -529,146 +529,86 @@ def get_access_data( ), ] return data_entries, legend_entries - """ - def get_page_based_statistics( - self, - start_date: date, - end_date: date, - period: str, - region: Region, - ) -> list[dict[str, Any]]: - Returns the statistics for each page of a region - :param start_date: Start date - :param end_date: End date - :param period: The period (one of :attr:`~integreat_cms.cms.constants.matomo_periods.CHOICES`) - :param region: The region - :return: ? - :raises ~integreat_cms.matomo_api.matomo_api_client.MatomoException: When a :class:`~aiohttp.ClientError` was raised during a - query_params = { - "date": f"{start_date},{end_date}", - "expanded": "1", - "filter_limit": "-1", - "format_metrics": "1", - "idSite": self.matomo_id, - "method": "VisitsSummary.getActions", - "period": period, - } - logger.debug( - "Query params: %r", - query_params, - ) - pages = region.get_pages() - languages = list(self.languages) - - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - # Execute async request to Matomo API - logger.debug("Fetching visits for languages %r asynchronously.", languages) - datasets = loop.run_until_complete( - self.get_page_based_statistics_async(loop, query_params, languages, pages) - ) - return datasets - - async def get_page_based_statistics_async( + async def get_page_accesses_async( self, loop: AbstractEventLoop, query_params: dict[str, Any], languages: list[Language], pages: list[Page], ) -> list[dict[str, Any]]: - Async wrapper to fetch the total visits with :mod:`aiohttp`. - Opens a :class:`~aiohttp.ClientSession`, creates a :class:`~asyncio.Task` for each language to call - :func:`~integreat_cms.matomo_api.matomo_api_client.MatomoApiClient.fetch` and waits for all tasks to finish with - :func:`~asyncio.gather`. - The returned list of gathered results has the correct order in which the tasks were created (at first the - ordered list of languages and the last element is the task for the total visits). - Called from :func:`~integreat_cms.matomo_api.matomo_api_client.MatomoApiClient.get_visits_per_language`. - + """ :param loop: The asyncio event loop :param query_params: The parameters which are passed to the Matomo API :param languages: The list of languages which should be retrieved - :param pages: The list of pages for which the data should be retrieved + :param pages: The list of pages which should be retrieved :raises ~integreat_cms.matomo_api.matomo_api_client.MatomoException: When a :class:`~aiohttp.ClientError` was raised during a Matomo API request - - :return: The list of gathered results - create_translation_slugs(pages, languages) + :return: + """ + translation_slugs = await async_get_translation_slug(pages, languages) async with aiohttp.ClientSession() as session: # Create tasks for visits by language tasks = [ loop.create_task( - self.fetch( + self.retrieve_accesses_for_page( session, - **query_params, - segment=f"pageUrl=@/{language.slug}/wp-json/extensions/v3/,pageUrl=@/api/v3/{self.region_slug}/{language.slug}/", + query_params, + page_id=page_id, + lang_slug=lang_slug, + full_slug=full_slug, ) ) - for language in languages - ] - - result = await asyncio.gather(*tasks) - # We're not retrieving the matomo id as part of the tasks, thus we know that the result is a list of dicts, not a list of list of ints. - if TYPE_CHECKING: - - def is_dict_list( - lst: list[dict[str, Any] | list[int]] - ) -> TypeGuard[list[dict[str, Any]]]: - return all(isinstance(d, dict) for d in lst) - - assert is_dict_list(result) - return result - """ - - async def get_visits_per_page_and_language_async( - self, - loop: AbstractEventLoop, - query_params: dict[str, Any], - languages: list[Language], - pages: list[Page], - region_slug: str, - ) -> list[dict[str, Any]]: - translation_slugs = get_translation_slug(pages, languages) - print(translation_slugs) - async with aiohttp.ClientSession() as session: - # Create tasks for visits by language - tasks = [ - loop.create_task( - self.nice_function(session, query_params, region_slug=region_slug, page_id=page_id, lang_slug=lang_slug, full_slug=full_slug) - ) for page_id, langs in translation_slugs.items() for lang_slug, full_slug in langs.items() ] # Wait for all tasks to finish and collect the results # (the results are sorted in the order the tasks were created) result = await asyncio.gather(*tasks) - # We're not retrieving the matomo id as part of the tasks, thus we know that the result is a list of dicts, not a list of list of ints. - if TYPE_CHECKING: - - def is_dict_list( - lst: list[dict[str, Any] | list[int]] - ) -> TypeGuard[list[dict[str, Any]]]: - return all(isinstance(d, dict) for d in lst) - - assert is_dict_list(result) return result - async def nice_function(self, session: aiohttp.ClientSession, query_params: dict[str, Any], region_slug: str, page_id: int, lang_slug: str, full_slug: str) -> dict: - print(f"full slug: {full_slug}") + async def retrieve_accesses_for_page( + self, + session: aiohttp.ClientSession, + query_params: dict[str, Any], + page_id: int, + lang_slug: str, + full_slug: str, + ) -> dict: + """ + This function retrieves the accesses for a single page (from Matomo). + + :param session: The current session + :param query_params: The parameters which are passed to the Matomo API + :param page_id: Id of page for which accesses are retrieved + :param lang_slug: Language slug for which accesses are retrieved + :param full_slug: The absolute url slug for the page + :return: dict of page and it's accesses + """ return { page_id: { - lang_slug: await self.fetch( session, **query_params, segment=f"pageUrl=@/children/?depth=2&url=/{region_slug}/{full_slug}" ) + lang_slug: await self.fetch( + session, + **query_params, + segment=f"pageUrl=@/children/?depth=2&url={full_slug}", + ) } } - def get_page_based_accesses(self, start_date: date, end_date: date, period: str, region: Region): - from ..cms.models import Page, Language - - language1 = Language.objects.get(id=1) - language2 = Language.objects.get(id=2) - language3 = Language.objects.get(id=3) - print("get_page_based_accesses") + def get_page_accesses( + self, start_date: date, end_date: date, period: str, region: Region + ) -> dict[int, dict[str, int]]: + """ + This function handles the page based accesses + :param start_date: Start date + :param end_date: End date + :param period: The period (one of :attr:`~integreat_cms.cms.constants.matomo_periods.CHOICES`) + :param region: The region for which we want our page based accesses + :return: The page accesses for the given region + :raises ~integreat_cms.matomo_api.matomo_api_client.MatomoException: When a :class:`~aiohttp.ClientError` was raised during a + Matomo API request + """ query_params = { "date": f"{start_date},{end_date}", "expanded": "1", @@ -678,88 +618,33 @@ def get_page_based_accesses(self, start_date: date, end_date: date, period: str, "method": "VisitsSummary.getActions", "period": period, } - languages = [language1, language2, language3] + languages = list(self.languages) pages = region.get_pages() loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) - # Execute async request to Matomo API logger.debug("Fetching visits for languages %r asynchronously.", languages) datasets = loop.run_until_complete( - self.get_visits_per_page_and_language_async(loop, query_params, languages, pages, region.slug) + self.get_page_accesses_async(loop, query_params, languages, pages) ) - - def deep_merge(*dicts): + + def deep_merge(*dicts: Mapping[Any, Any]) -> dict: """ Recursively merges dictionaries. Values in later dictionaries override earlier ones for non-dict values, while dictionaries are merged recursively. """ - merged = {} + merged: dict[Any, Any] = {} for d in dicts: for key, value in d.items(): - if key in merged and isinstance(merged[key], Mapping) and isinstance(value, Mapping): + if ( + key in merged + and isinstance(merged[key], Mapping) + and isinstance(value, Mapping) + ): # Recursively merge dictionaries merged[key] = deep_merge(merged[key], value) else: # Otherwise, override or add the value merged[key] = value return merged - - datasets = deep_merge(*datasets) - - """datasets = { - key: value - for result in datasets - for key, value in result.items() - }""" - print(f"Datasets: {datasets}") - - """ - # alle Daten zu holen - raw_data = [ - { - "slug": "/sprachkurse/integrationskurse", - "language_slug": "de", - "accesses": 8, - }, - { - "slug": "/language_courses/integration_courses", - "language_slug": "de", - "accesses": 8, - }, - ] - - # strukturieren - # page id raus finden - - translations = PageTranslations.objects.all() - # nicht aktuelle Versionen rausfiltern - for value in data: - # page id raus finden - translations = PageTranslations.objects.all() - # nicht aktuelle Versionen rausfiltern - for value in data: - gesuchte_translation = translations.get(slug=value.slug) - # erstelle aus der gesuchter translation ein dict - { - "id": gesuchte_translation.id, #654 - "access": value.accesses, #88 - "language_slug": value.language_slug, # "en" - "page_id": gesuchte_translation.page.id, #14 - } - # rausfinden welche page translations dazu gehören - # gibt es mit meiner page_id schon ein dict? - # wenn nein, erstelle neues dict und fülle mit informationen aus, die du hast - - # wenn ja, dann - # hänge nur meinen language slug in accesses an - # end_data.access[language_slug] = current_data.access - - # schicke ich strukturiert an AJAX - end_data = [ - { - "page_id": 14, - "accesses": {"de": 123, "en":23, "fr":312, "ar": 76543} - } - {"page_id": 1, "de": 12, "ru": 1}, - """ + return deep_merge(*datasets) diff --git a/integreat_cms/matomo_api/utils.py b/integreat_cms/matomo_api/utils.py index 55271d798e..1680f591b0 100644 --- a/integreat_cms/matomo_api/utils.py +++ b/integreat_cms/matomo_api/utils.py @@ -1,33 +1,34 @@ from __future__ import annotations -from typing import Dict, TYPE_CHECKING +from typing import TYPE_CHECKING + +from asgiref.sync import sync_to_async if TYPE_CHECKING: - from django.db.models.query import QuerySet from ..cms.models import Language, Page -"""def create_translation_slugs( - pages: QuerySet[Page], languages: list[Language] -) -> dict[Page, dict[str, str]]: - This function creates the translation slug for the matomo call. It returns then language_slug and the slug in the target translation. - translation_slugs: Dict[Page, Dict[str, str]] = {} - for page in pages: - translation_slugs[page] = {} - for language in languages: - if page_translation := page.get_translation(language.slug): - # print(page_translation.ancestor_path)Sollen wir uns Sol - translation_slugs[page][language.slug] = page_translation.slug - return translation_slugs +def get_translation_slug( + pages: list[Page], languages: list[Language] +) -> dict[int, dict[str, str]]: """ + Produce mapping of page ids and language slugs to the absolute url of the corresponding translation. + In detail, we need to construct a slug in the foreign language, for example /en/lebensmittel-und-einkaufen needs to become /en/groceries-and-shopping. -def get_translation_slug(pages: list[Page], languages: list[Language]) -> dict[int, dict[str, str]]: - translation_slugs = {} + :param pages: The list of pages for which we want the absolute url of + :param languages: The list of languages for which we want the absolute url of + :return: A dictionary of page ids, language slugs and the absolute url of the corresponding translation. + """ + translation_slugs: dict = {} for page in pages: for language in languages: if page_translation := page.get_translation(language.slug): if page.id not in translation_slugs: translation_slugs[page.id] = {} - translation_slugs[page.id][language.slug] = f"{language.slug}/{page_translation.slug}" # nach richtigen Slug noch mal recherchieren + translation_slugs[page.id][ + language.slug + ] = page_translation.get_absolute_url().rstrip("/") return translation_slugs + +async_get_translation_slug = sync_to_async(get_translation_slug, thread_sensitive=False) From b882d076aab48f565398db64b044777ac7084632 Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Mon, 9 Dec 2024 14:37:09 +0000 Subject: [PATCH 34/52] Fix loading of childnodes --- .../cms/templates/pages/page_tree.html | 2 +- integreat_cms/cms/urls/protected.py | 13 ++++++++++--- .../cms/views/pages/page_tree_view.py | 17 +++++++++-------- .../cms/views/pages/partial_page_tree_view.py | 18 +++++++----------- 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/integreat_cms/cms/templates/pages/page_tree.html b/integreat_cms/cms/templates/pages/page_tree.html index a949941db9..d367c56015 100644 --- a/integreat_cms/cms/templates/pages/page_tree.html +++ b/integreat_cms/cms/templates/pages/page_tree.html @@ -75,7 +75,7 @@

{% csrf_token %}
- diff --git a/integreat_cms/cms/urls/protected.py b/integreat_cms/cms/urls/protected.py index 8e2ba5d016..5fcb73f785 100644 --- a/integreat_cms/cms/urls/protected.py +++ b/integreat_cms/cms/urls/protected.py @@ -647,9 +647,16 @@ name="render_mirrored_page_field", ), path( - "partial-page-tree/", - pages.render_partial_page_tree_views, - name="get_page_tree_ajax", + "/", + include( + [ + path( + "partial-page-tree/", + pages.render_partial_page_tree_views, + name="get_page_tree_ajax", + ), + ] + ), ), ] ), diff --git a/integreat_cms/cms/views/pages/page_tree_view.py b/integreat_cms/cms/views/pages/page_tree_view.py index 1e601b1ac8..428df28ab3 100644 --- a/integreat_cms/cms/views/pages/page_tree_view.py +++ b/integreat_cms/cms/views/pages/page_tree_view.py @@ -109,9 +109,7 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ), ) else: - messages.warning( - request, _("You don't have the permission to edit or create pages.") - ) + messages.warning(request, _("You don't have the permission to edit or create pages.")) # Initialize page filter form filter_form = PageFilterForm(data=request.GET) @@ -125,11 +123,14 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ) # Cache tree structure to reduce database queries - pages = ( - page_queryset.prefetch_major_translations() - .prefetch_related("mirroring_pages") - .cache_tree(archived=self.archived) - ) + pages = page_queryset.prefetch_major_translations().prefetch_related("mirroring_pages").cache_tree(archived=self.archived) + + # When only archived pages are requested, remove implicitly archived pages since they are handeld by loading the partial page tree + if self.archived: + archived_pages = region.archived_pages + for page in archived_pages: + if page.implicitly_archived: + pages.remove(page) # Filter pages according to given filters, if any pages = filter_form.apply(pages, language_slug) diff --git a/integreat_cms/cms/views/pages/partial_page_tree_view.py b/integreat_cms/cms/views/pages/partial_page_tree_view.py index af1a6cad73..aff2fc2afd 100644 --- a/integreat_cms/cms/views/pages/partial_page_tree_view.py +++ b/integreat_cms/cms/views/pages/partial_page_tree_view.py @@ -15,11 +15,8 @@ @permission_required("cms.view_page") @require_POST -def render_partial_page_tree_views( - request: HttpRequest, - region_slug: str, # pylint: disable=unused-argument - language_slug: str, -) -> JsonResponse: +# pylint: disable=unused-argument +def render_partial_page_tree_views(request: HttpRequest, region_slug: str, language_slug: str, is_archive: bool) -> JsonResponse: r""" Retrieve the rendered subtree of a given root page @@ -33,14 +30,12 @@ def render_partial_page_tree_views( region = request.region language = region.get_language_or_404(language_slug, only_active=True) + # Convert is_archive from String to Boolean + is_archive = eval(is_archive) + backend_language = Language.objects.filter(slug=get_language()).first() - all_pages = ( - region.pages.filter(tree_id__in=requested_tree_ids) - .prefetch_major_translations() - .prefetch_related("mirroring_pages") - .cache_tree(archived=False) - ) + all_pages = region.pages.filter(tree_id__in=requested_tree_ids).prefetch_major_translations().prefetch_related("mirroring_pages").cache_tree(archived=is_archive) pages_by_id = defaultdict(list) for page in all_pages: @@ -67,6 +62,7 @@ def render_partial_page_tree_views( "language": language, "languages": region.active_languages, "parent_id": parent.id, + "is_archive": is_archive, }, request, ) From 5b66426bd6fbd153b4a5389c749906b96523a98e Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Mon, 9 Dec 2024 14:46:47 +0000 Subject: [PATCH 35/52] Fix codestyle --- .../cms/templates/pages/page_tree_node.html | 18 +++++++++--------- .../cms/views/pages/page_tree_view.py | 10 ++++++++-- .../cms/views/pages/partial_page_tree_view.py | 11 +++++++++-- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/integreat_cms/cms/templates/pages/page_tree_node.html b/integreat_cms/cms/templates/pages/page_tree_node.html index 36c041a522..6b78638db4 100644 --- a/integreat_cms/cms/templates/pages/page_tree_node.html +++ b/integreat_cms/cms/templates/pages/page_tree_node.html @@ -30,21 +30,21 @@ {% has_perm 'cms.change_page_object' request.user as can_edit_pages %} + {% if not filter_form.is_enabled %} + {% has_perm 'cms.change_page_object' request.user as can_edit_pages %} + + {% endif %} + + {% if not backend_language %} + {% get_current_language as LANGUAGE_CODE %} + {% get_language LANGUAGE_CODE as backend_language %} + {% endif %} + {% if backend_language != language %} + + {% endif %} +{% endblock content %} \ No newline at end of file diff --git a/integreat_cms/cms/templates/pages/page_tree_node.html b/integreat_cms/cms/templates/pages/page_tree_node.html index 6b78638db4..2f7c32fc66 100644 --- a/integreat_cms/cms/templates/pages/page_tree_node.html +++ b/integreat_cms/cms/templates/pages/page_tree_node.html @@ -20,66 +20,7 @@ data-drop-id="{{ page.id }}" data-drop-position="last-child" class="page-row drop drop-on border-t border-b border-solid border-gray-200 hover:bg-gray-100 level-{{ page.relative_depth }} {% if parent_id %}hidden parent-page-{{ parent_id }}{% endif %}"> - - {% if not filter_form.is_enabled %} - {% has_perm 'cms.change_page_object' request.user as can_edit_pages %} - - {% endif %} - - {% if not backend_language %} - {% get_current_language as LANGUAGE_CODE %} - {% get_language LANGUAGE_CODE as backend_language %} - {% endif %} - {% if backend_language != language %} - - {% endif %} + {% include "pages/_generic_page_tree_node.html" %} {% if not is_archive %} - - {% if not filter_form.is_enabled %} - - {% endif %} - - {% if backend_language and backend_language != language %} - - {% endif %} - - - - - diff --git a/integreat_cms/cms/views/pages/page_tree_view.py b/integreat_cms/cms/views/pages/page_tree_view.py index 0668ed0529..ce22617a6c 100644 --- a/integreat_cms/cms/views/pages/page_tree_view.py +++ b/integreat_cms/cms/views/pages/page_tree_view.py @@ -33,8 +33,6 @@ class PageTreeView(TemplateView, PageContextMixin, MachineTranslationContextMixi #: Template for list of non-archived pages template = "pages/page_tree.html" - #: Template for list of archived pages - template_archived = "pages/page_tree_archived.html" #: Whether or not to show archived pages archived = False #: The translation model of this list view (used to determine whether machine translations are permitted) diff --git a/integreat_cms/cms/views/pages/partial_page_tree_view.py b/integreat_cms/cms/views/pages/partial_page_tree_view.py index 0a02adf4b6..901ff3eb85 100644 --- a/integreat_cms/cms/views/pages/partial_page_tree_view.py +++ b/integreat_cms/cms/views/pages/partial_page_tree_view.py @@ -16,7 +16,9 @@ @permission_required("cms.view_page") @require_POST # pylint: disable=unused-argument -def render_partial_page_tree_views(request: HttpRequest, region_slug: str, language_slug: str, is_archive: str) -> JsonResponse: +def render_partial_page_tree_views( + request: HttpRequest, region_slug: str, language_slug: str, is_archive: str +) -> JsonResponse: r""" Retrieve the rendered subtree of a given root page @@ -36,7 +38,12 @@ def render_partial_page_tree_views(request: HttpRequest, region_slug: str, langu backend_language = Language.objects.filter(slug=get_language()).first() - all_pages = region.pages.filter(tree_id__in=requested_tree_ids).prefetch_major_translations().prefetch_related("mirroring_pages").cache_tree(archived=is_archive) + all_pages = ( + region.pages.filter(tree_id__in=requested_tree_ids) + .prefetch_major_translations() + .prefetch_related("mirroring_pages") + .cache_tree(archived=is_archive) + ) pages_by_id = defaultdict(list) for page in all_pages: diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index 376405b5c3..75302d1573 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -1681,11 +1681,18 @@ msgstr "Aktiv" msgid "Hidden" msgstr "Versteckt" +<<<<<<< HEAD #: cms/constants/region_status.py cms/models/contact/contact.py #: cms/models/pages/page_translation.py cms/models/regions/region.py #: cms/templates/events/event_form.html cms/templates/pages/page_form.html #: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html cms/templates/pois/poi_form.html +======= +#: cms/constants/region_status.py cms/models/pages/page_translation.py +#: cms/models/regions/region.py cms/templates/events/event_form.html +#: cms/templates/pages/page_form.html cms/templates/pages/page_tree_node.html +#: cms/templates/pois/poi_form.html +>>>>>>> 718358e82 (Cleaning up and deleting page_tree_archived files) msgid "Archived" msgstr "Archiviert" @@ -1758,7 +1765,6 @@ msgstr "Rechts nach Links" #: cms/constants/translation_status.py cms/templates/_form_language_tabs.html #: cms/templates/events/event_list_row.html #: cms/templates/imprint/imprint_sbs.html cms/templates/pages/page_sbs.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html cms/templates/pois/poi_list_row.html msgid "Translation up-to-date" msgstr "Übersetzung ist aktuell" @@ -1766,7 +1772,6 @@ msgstr "Übersetzung ist aktuell" #: cms/constants/translation_status.py cms/templates/_form_language_tabs.html #: cms/templates/events/event_list_row.html #: cms/templates/imprint/imprint_sbs.html cms/templates/pages/page_sbs.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html cms/templates/pois/poi_list_row.html msgid "Currently in translation" msgstr "Wird derzeit übersetzt" @@ -1774,14 +1779,12 @@ msgstr "Wird derzeit übersetzt" #: cms/constants/translation_status.py cms/templates/_form_language_tabs.html #: cms/templates/events/event_list_row.html #: cms/templates/imprint/imprint_sbs.html cms/templates/pages/page_sbs.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html cms/templates/pois/poi_list_row.html msgid "Translation outdated" msgstr "Übersetzung ist veraltet" #: cms/constants/translation_status.py cms/templates/_form_language_tabs.html #: cms/templates/events/event_list_row.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html #: cms/templates/poicategories/poicategory_list_row.html #: cms/templates/pois/poi_list_row.html @@ -2026,9 +2029,7 @@ msgstr "Kategorie" #: cms/templates/imprint/imprint_form.html #: cms/templates/imprint/imprint_sbs.html #: cms/templates/linkcheck/links_by_filter.html -#: cms/templates/pages/page_sbs.html -#: cms/templates/pages/page_tree_archived.html -#: cms/templates/pois/poi_list_archived.html +#: cms/templates/pages/page_sbs.html cms/templates/pois/poi_list_archived.html #: cms/templates/push_notifications/push_notification_form.html #: cms/templates/regions/region_list.html msgid "Status" @@ -3491,8 +3492,7 @@ msgstr "Ob die Seite explizit archiviert ist oder nicht" #: cms/models/pages/abstract_base_page_translation.py #: cms/templates/events/event_form.html cms/templates/events/event_list.html #: cms/templates/pages/_generic_page_tree.html -#: cms/templates/pages/page_form.html -#: cms/templates/pages/page_tree_archived.html cms/templates/pois/poi_form.html +#: cms/templates/pages/page_form.html cms/templates/pois/poi_form.html #: cms/templates/pois/poi_list.html cms/templates/pois/poi_list_archived.html msgid "Title in" msgstr "Titel auf" @@ -4779,7 +4779,6 @@ msgstr "Inhalt ansehen" #: cms/templates/_related_contents_table.html #: cms/templates/events/event_list_row.html #: cms/templates/pages/_generic_page_tree_node.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pois/poi_list_archived_row.html #: cms/templates/pois/poi_list_row.html msgid "Translation not available" @@ -5373,7 +5372,6 @@ msgstr "Kontakt archivieren" #: cms/templates/contacts/contact_form.html #: cms/templates/pages/page_form_sidebar/actions_box.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html msgid "Restore page" msgstr "Seite wiederherstellen" @@ -5440,8 +5438,7 @@ msgstr "Webseite" #: cms/templates/linkcheck/links_by_filter.html #: cms/templates/offertemplates/offertemplate_list.html #: cms/templates/organizations/organization_list.html -#: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html cms/templates/pois/poi_list.html +#: cms/templates/pages/page_tree.html cms/templates/pois/poi_list.html #: cms/templates/pois/poi_list_archived.html cms/templates/roles/role_list.html msgid "Options" msgstr "Optionen" @@ -5467,8 +5464,7 @@ msgstr "Kontakte ausgewählt" #: cms/templates/languagetreenodes/languagetreenode_list.html #: cms/templates/linkcheck/links_by_filter.html #: cms/templates/organizations/organization_list.html -#: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html cms/templates/pois/poi_list.html +#: cms/templates/pages/page_tree.html cms/templates/pois/poi_list.html #: cms/templates/pois/poi_list_archived.html msgid "Select bulk action" msgstr "Mehrfachaktion auswählen" @@ -5495,8 +5491,7 @@ msgstr "Kontakte löschen" #: cms/templates/linkcheck/links_by_filter.html #: cms/templates/organizations/organization_list.html #: cms/templates/pages/_page_xliff_export_overlay.html -#: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html cms/templates/pois/poi_list.html +#: cms/templates/pages/page_tree.html cms/templates/pois/poi_list.html #: cms/templates/pois/poi_list_archived.html msgid "Execute" msgstr "Ausführen" @@ -6044,9 +6039,7 @@ msgstr "Zurück zu Veranstaltungen" #: cms/templates/feedback/admin_feedback_list_archived.html #: cms/templates/feedback/region_feedback_list.html #: cms/templates/feedback/region_feedback_list_archived.html -#: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html -#: cms/templates/users/user_list.html +#: cms/templates/pages/page_tree.html cms/templates/users/user_list.html msgid "Show filters" msgstr "Filter einblenden" @@ -6055,9 +6048,7 @@ msgstr "Filter einblenden" #: cms/templates/feedback/admin_feedback_list_archived.html #: cms/templates/feedback/region_feedback_list.html #: cms/templates/feedback/region_feedback_list_archived.html -#: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html -#: cms/templates/users/user_list.html +#: cms/templates/pages/page_tree.html cms/templates/users/user_list.html msgid "Hide filters" msgstr "Filter ausblenden" @@ -6243,7 +6234,6 @@ msgstr "Zuletzt geändert von" #: cms/templates/pages/_page_filter_form.html #: cms/templates/pages/_page_xliff_import_diff.html #: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html #: cms/templates/push_notifications/push_notification_list.html #: cms/templates/push_notifications/push_notification_template_list.html #: cms/templates/regions/region_list.html @@ -6763,7 +6753,6 @@ msgstr "Sprach-Knoten hinzufügen" #: cms/templates/languagetreenodes/languagetreenode_list.html #: cms/templates/pages/_generic_page_tree.html -#: cms/templates/pages/page_tree_archived.html msgid "Hierarchy" msgstr "Hierarchie" @@ -7114,17 +7103,14 @@ msgid "Change the order and position of the pages with drag & drop." msgstr "Ändern Sie die Reihenfolge und Position der Seiten mit Drag & Drop." #: cms/templates/pages/_generic_page_tree_node.html -#: cms/templates/pages/page_tree_archived_node.html msgid "Drag & drop is disabled for archived pages." msgstr "Drag & drop ist für archivierte Seiten nicht möglich." #: cms/templates/pages/_generic_page_tree_node.html -#: cms/templates/pages/page_tree_archived_node.html msgid "Expand all subpages" msgstr "Alle Unterseiten ausklappen" #: cms/templates/pages/_generic_page_tree_node.html -#: cms/templates/pages/page_tree_archived_node.html msgid "Collapse all subpages" msgstr "Alle Unterseiten einklappen" @@ -7220,9 +7206,7 @@ msgstr "Übersetzung wird durchgeführt" msgid "Cancel translation process" msgstr "Übersetzungsprozess abbrechen" -#: cms/templates/pages/page_form.html -#: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/page_form.html cms/templates/pages/page_tree_node.html msgid "Archived, because a parent page is archived" msgstr "Archiviert, weil eine übergeordnete Seite archiviert ist" @@ -7277,13 +7261,11 @@ msgstr[1] "" "diesen Seiten entfernen:" #: cms/templates/pages/page_form_sidebar/actions_box.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html msgid "Delete page" msgstr "Seite löschen" #: cms/templates/pages/page_form_sidebar/actions_box.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html cms/views/pages/page_actions.py msgid "You cannot delete a page which has subpages." msgstr "Sie können keine Seite löschen, die Unterseiten besitzt." @@ -7300,7 +7282,6 @@ msgstr[1] "" "verschieben:" #: cms/templates/pages/page_form_sidebar/actions_box.html -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html cms/views/pages/page_actions.py msgid "" "This page cannot be deleted because it was embedded as live content from " @@ -7407,12 +7388,10 @@ msgstr "" "generieren" #: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html msgid "Archived Pages" msgstr "Archivierte Seiten" #: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html msgid "Back to pages" msgstr "Zurück zu Seiten" @@ -7492,7 +7471,6 @@ msgid "Export selected pages for multilingual translation" msgstr "Ausgewählte Seiten für die mehrsprachige Übersetzung exportieren" #: cms/templates/pages/page_tree.html -#: cms/templates/pages/page_tree_archived.html msgid "Restore pages" msgstr "Seiten wiederherstellen" @@ -7516,20 +7494,10 @@ msgstr "und {} weitere Dateien" msgid "Import" msgstr "Importieren" -#: cms/templates/pages/page_tree_archived.html -msgid "No archived pages found with these filters." -msgstr "Keine archivierten Seiten mit diesen Filtern gefunden." - -#: cms/templates/pages/page_tree_archived.html -msgid "No pages archived yet." -msgstr "Noch keine Seiten archiviert." - -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html msgid "This page is archived." msgstr "Diese Seite ist archiviert." -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html msgid "" "This page is archived, because at least one of its parent pages is archived." @@ -7537,27 +7505,14 @@ msgstr "" "Diese Seite ist archiviert, weil eine ihrer übergeordneten Seiten archiviert " "ist." -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html msgid "View page as preview" msgstr "Seite als Vorschau ansehen" -#: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html msgid "You cannot preview an empty page." msgstr "Eine leere Seite hat keine Vorschau" -#: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pages/page_tree_node.html -msgid "To restore this page, you have to restore its archived parent page." -msgstr "" -"Um diese Seite wiederherzustellen, müssen Sie ihre archivierte übergeordnete " -"Seite wiederherstellen." - -#: cms/templates/pages/page_tree_archived_node.html -msgid "This also involves non-archived subpages." -msgstr "Dies betrifft auch nicht-archivierte Unterseiten." - #: cms/templates/pages/page_tree_node.html msgid "Open page in web app" msgstr "Seite in der Web-App öffnen" @@ -7580,6 +7535,12 @@ msgstr "" "Diese Seite kann nicht archiviert werden, da sie von einer anderen Seite als " "Live-Inhalt eingebunden wurde." +#: cms/templates/pages/page_tree_node.html +msgid "To restore this page, you have to restore its archived parent page." +msgstr "" +"Um diese Seite wiederherzustellen, müssen Sie ihre archivierte übergeordnete " +"Seite wiederherstellen." + #: cms/templates/pages/page_tree_node.html msgid "This also involves archived subpages." msgstr "Dies betrifft auch archivierte Unterseiten." @@ -11219,6 +11180,14 @@ msgstr "" #~ msgid "%s with %s" #~ msgstr "%s mit %s" +#~ msgid "No archived pages found with these filters." +#~ msgstr "Keine archivierten Seiten mit diesen Filtern gefunden." + +#~ msgid "No pages archived yet." +#~ msgstr "Noch keine Seiten archiviert." + +#~ msgid "This also involves non-archived subpages." +#~ msgstr "Dies betrifft auch nicht-archivierte Unterseiten." #~ msgid "Individual languages can be hidden by clicking on the labels." #~ msgstr "" From 5df5e37d03998e245ba34dcd62ecfc5a9ccc0a07 Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Mon, 9 Dec 2024 16:59:21 +0000 Subject: [PATCH 39/52] Rename page_tree to pages_page_tree --- .../templates/pages/_page_tree_children.html | 2 +- .../{page_tree.html => pages_page_tree.html} | 2 +- ...ee_node.html => pages_page_tree_node.html} | 0 .../cms/views/pages/page_tree_view.py | 2 +- integreat_cms/locale/de/LC_MESSAGES/django.po | 176 +++++++++--------- .../static/src/js/tree-drag-and-drop.ts | 2 +- 6 files changed, 96 insertions(+), 88 deletions(-) rename integreat_cms/cms/templates/pages/{page_tree.html => pages_page_tree.html} (99%) rename integreat_cms/cms/templates/pages/{page_tree_node.html => pages_page_tree_node.html} (100%) diff --git a/integreat_cms/cms/templates/pages/_page_tree_children.html b/integreat_cms/cms/templates/pages/_page_tree_children.html index cfd8b73c28..b084ca457e 100644 --- a/integreat_cms/cms/templates/pages/_page_tree_children.html +++ b/integreat_cms/cms/templates/pages/_page_tree_children.html @@ -3,7 +3,7 @@
+ data-node-descendants="{{ page|get_descendant_ids }}" + class="drag cursor-move text-gray-800 inline-block pl-4 align-middle" + draggable="{% if can_edit_pages and not is_archive %}true{% else %}false{% endif %}" + title="{% if not is_archive %}{% translate "Change the order and position of the pages with drag & drop." %}{% else %}{% translate "Drag & drop is disabled for archived pages." %}{% endif %}"> {% if can_edit_pages %} {% endif %} {% if not page.parent_id and not page.is_leaf or page.parent_id and page.cached_children|length > 0 %} + data-expand-title="{% translate "Expand all subpages" %}" + data-collapse-title="{% translate "Collapse all subpages" %}" + data-page-id="{{ page.id }}" + data-page-children="{{ page|get_children_ids }}" + data-page-tree-id="{{ page.tree_id }}"> {% endif %} diff --git a/integreat_cms/cms/views/pages/page_tree_view.py b/integreat_cms/cms/views/pages/page_tree_view.py index 428df28ab3..0668ed0529 100644 --- a/integreat_cms/cms/views/pages/page_tree_view.py +++ b/integreat_cms/cms/views/pages/page_tree_view.py @@ -109,7 +109,9 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ), ) else: - messages.warning(request, _("You don't have the permission to edit or create pages.")) + messages.warning( + request, _("You don't have the permission to edit or create pages.") + ) # Initialize page filter form filter_form = PageFilterForm(data=request.GET) @@ -123,7 +125,11 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ) # Cache tree structure to reduce database queries - pages = page_queryset.prefetch_major_translations().prefetch_related("mirroring_pages").cache_tree(archived=self.archived) + pages = ( + page_queryset.prefetch_major_translations() + .prefetch_related("mirroring_pages") + .cache_tree(archived=self.archived) + ) # When only archived pages are requested, remove implicitly archived pages since they are handeld by loading the partial page tree if self.archived: diff --git a/integreat_cms/cms/views/pages/partial_page_tree_view.py b/integreat_cms/cms/views/pages/partial_page_tree_view.py index aff2fc2afd..21c3c3a2be 100644 --- a/integreat_cms/cms/views/pages/partial_page_tree_view.py +++ b/integreat_cms/cms/views/pages/partial_page_tree_view.py @@ -16,7 +16,9 @@ @permission_required("cms.view_page") @require_POST # pylint: disable=unused-argument -def render_partial_page_tree_views(request: HttpRequest, region_slug: str, language_slug: str, is_archive: bool) -> JsonResponse: +def render_partial_page_tree_views( + request: HttpRequest, region_slug: str, language_slug: str, is_archive: bool +) -> JsonResponse: r""" Retrieve the rendered subtree of a given root page @@ -35,7 +37,12 @@ def render_partial_page_tree_views(request: HttpRequest, region_slug: str, langu backend_language = Language.objects.filter(slug=get_language()).first() - all_pages = region.pages.filter(tree_id__in=requested_tree_ids).prefetch_major_translations().prefetch_related("mirroring_pages").cache_tree(archived=is_archive) + all_pages = ( + region.pages.filter(tree_id__in=requested_tree_ids) + .prefetch_major_translations() + .prefetch_related("mirroring_pages") + .cache_tree(archived=is_archive) + ) pages_by_id = defaultdict(list) for page in all_pages: From 160d621fe6abd64d4048154815d76ad414b4a4a6 Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Mon, 9 Dec 2024 16:08:40 +0000 Subject: [PATCH 36/52] Add _generic_page_tree_node.hmtl --- .../pages/_generic_page_tree_node.html | 68 +++++++++++++++++++ .../cms/templates/pages/page_tree_node.html | 61 +---------------- .../cms/views/pages/partial_page_tree_view.py | 12 +--- 3 files changed, 72 insertions(+), 69 deletions(-) create mode 100644 integreat_cms/cms/templates/pages/_generic_page_tree_node.html diff --git a/integreat_cms/cms/templates/pages/_generic_page_tree_node.html b/integreat_cms/cms/templates/pages/_generic_page_tree_node.html new file mode 100644 index 0000000000..6aaa4d95a9 --- /dev/null +++ b/integreat_cms/cms/templates/pages/_generic_page_tree_node.html @@ -0,0 +1,68 @@ +{% load i18n %} +{% load content_filters %} +{% load page_filters %} +{% load tree_filters %} +{% load rules %} +{% get_translation page language.slug as page_translation %} +{% block content %} + + + + + {% if can_edit_pages %} + + {% endif %} + + {% if not page.parent_id and not page.is_leaf or page.parent_id and page.cached_children|length > 0 %} + + + + {% endif %} + + + {% if page_translation %} + {{ page_translation.title }} + {% else %} + {% translate "Translation not available" %} + {% endif %} + + + +
+ {% if page.backend_translation %} + {{ page.backend_translation.title }} + {% else %} + {% translate "Translation not available" %} + {% endif %} +
+
+
- - - - {% if can_edit_pages %} - - {% endif %} - - {% if not page.parent_id and not page.is_leaf or page.parent_id and page.cached_children|length > 0 %} - - - - {% endif %} - - - {% if page_translation %} - {{ page_translation.title }} - {% else %} - {% translate "Translation not available" %} - {% endif %} - - - -
- {% if page.backend_translation %} - {{ page.backend_translation.title }} - {% else %} - {% translate "Translation not available" %} - {% endif %} -
-
-
diff --git a/integreat_cms/cms/views/pages/partial_page_tree_view.py b/integreat_cms/cms/views/pages/partial_page_tree_view.py index 21c3c3a2be..0a02adf4b6 100644 --- a/integreat_cms/cms/views/pages/partial_page_tree_view.py +++ b/integreat_cms/cms/views/pages/partial_page_tree_view.py @@ -16,15 +16,14 @@ @permission_required("cms.view_page") @require_POST # pylint: disable=unused-argument -def render_partial_page_tree_views( - request: HttpRequest, region_slug: str, language_slug: str, is_archive: bool -) -> JsonResponse: +def render_partial_page_tree_views(request: HttpRequest, region_slug: str, language_slug: str, is_archive: str) -> JsonResponse: r""" Retrieve the rendered subtree of a given root page :param request: The current request :param region_slug: The slug of the current region :param language_slug: The slug of the current language + :param is_archive: True when only archive pages are requested, False otherwise :return: The rendered template responses """ requested_tree_ids = [int(i) for i in json.loads(request.body.decode("utf-8"))] @@ -37,12 +36,7 @@ def render_partial_page_tree_views( backend_language = Language.objects.filter(slug=get_language()).first() - all_pages = ( - region.pages.filter(tree_id__in=requested_tree_ids) - .prefetch_major_translations() - .prefetch_related("mirroring_pages") - .cache_tree(archived=is_archive) - ) + all_pages = region.pages.filter(tree_id__in=requested_tree_ids).prefetch_major_translations().prefetch_related("mirroring_pages").cache_tree(archived=is_archive) pages_by_id = defaultdict(list) for page in all_pages: From 89fd0fd9eb443f3a2dfec02c869693acb662b00d Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Mon, 9 Dec 2024 16:11:24 +0000 Subject: [PATCH 37/52] update translations --- integreat_cms/locale/de/LC_MESSAGES/django.po | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index 5fd22a39ec..376405b5c3 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -4778,8 +4778,8 @@ msgstr "Inhalt ansehen" #: cms/templates/_related_contents_table.html #: cms/templates/events/event_list_row.html +#: cms/templates/pages/_generic_page_tree_node.html #: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pages/page_tree_node.html #: cms/templates/pois/poi_list_archived_row.html #: cms/templates/pois/poi_list_row.html msgid "Translation not available" @@ -7109,6 +7109,25 @@ msgstr "Organisation wiederherstellen" msgid "Archive organization" msgstr "Organisationen archivieren" +#: cms/templates/pages/_generic_page_tree_node.html +msgid "Change the order and position of the pages with drag & drop." +msgstr "Ändern Sie die Reihenfolge und Position der Seiten mit Drag & Drop." + +#: cms/templates/pages/_generic_page_tree_node.html +#: cms/templates/pages/page_tree_archived_node.html +msgid "Drag & drop is disabled for archived pages." +msgstr "Drag & drop ist für archivierte Seiten nicht möglich." + +#: cms/templates/pages/_generic_page_tree_node.html +#: cms/templates/pages/page_tree_archived_node.html +msgid "Expand all subpages" +msgstr "Alle Unterseiten ausklappen" + +#: cms/templates/pages/_generic_page_tree_node.html +#: cms/templates/pages/page_tree_archived_node.html +msgid "Collapse all subpages" +msgstr "Alle Unterseiten einklappen" + #: cms/templates/pages/_page_order_table.html msgid "Change the position of the page with drag & drop." msgstr "Ändern Sie die Position der Seite mit Drag & Drop." @@ -7505,21 +7524,6 @@ msgstr "Keine archivierten Seiten mit diesen Filtern gefunden." msgid "No pages archived yet." msgstr "Noch keine Seiten archiviert." -#: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pages/page_tree_node.html -msgid "Drag & drop is disabled for archived pages." -msgstr "Drag & drop ist für archivierte Seiten nicht möglich." - -#: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pages/page_tree_node.html -msgid "Expand all subpages" -msgstr "Alle Unterseiten ausklappen" - -#: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pages/page_tree_node.html -msgid "Collapse all subpages" -msgstr "Alle Unterseiten einklappen" - #: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pages/page_tree_node.html msgid "This page is archived." @@ -7554,10 +7558,6 @@ msgstr "" msgid "This also involves non-archived subpages." msgstr "Dies betrifft auch nicht-archivierte Unterseiten." -#: cms/templates/pages/page_tree_node.html -msgid "Change the order and position of the pages with drag & drop." -msgstr "Ändern Sie die Reihenfolge und Position der Seiten mit Drag & Drop." - #: cms/templates/pages/page_tree_node.html msgid "Open page in web app" msgstr "Seite in der Web-App öffnen" From 962f0c56cbf4a21932525c89a8609383476c7e8e Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Mon, 9 Dec 2024 16:37:31 +0000 Subject: [PATCH 38/52] Cleaning up and deleting page_tree_archived files --- .../templates/pages/page_tree_archived.html | 126 ------------- .../pages/page_tree_archived_node.html | 170 ------------------ .../cms/views/pages/page_tree_view.py | 2 - .../cms/views/pages/partial_page_tree_view.py | 11 +- integreat_cms/locale/de/LC_MESSAGES/django.po | 89 +++------ 5 files changed, 38 insertions(+), 360 deletions(-) delete mode 100644 integreat_cms/cms/templates/pages/page_tree_archived.html delete mode 100644 integreat_cms/cms/templates/pages/page_tree_archived_node.html diff --git a/integreat_cms/cms/templates/pages/page_tree_archived.html b/integreat_cms/cms/templates/pages/page_tree_archived.html deleted file mode 100644 index 92050a645e..0000000000 --- a/integreat_cms/cms/templates/pages/page_tree_archived.html +++ /dev/null @@ -1,126 +0,0 @@ -{% extends "_base.html" %} -{% load i18n %} -{% load static %} -{% load content_filters %} -{% load page_filters %} -{% load rules %} -{% block content %} - {% has_perm 'cms.change_page_object' request.user as can_edit_pages %} - {% with filter_form.filters_visible as filters_visible %} -
-
-

- {% translate "Archived Pages" %} -

- - {% translate "Back to pages" %} - -
-
-
- {% include "generic_language_switcher.html" with target="archived_pages" %} - {% include "_search_input.html" with object_type="page" object_archived=True related_form="page-filter-form" %} -
- {% if request.user.expert_mode %} - - {% endif %} -
-
-
- {% include "pages/_page_filter_form.html" %} -
- {% endwith %} -
-
- - {% csrf_token %} - - - - - - {% if not filter_form.is_enabled %} - - {% endif %} - - {% get_current_language as LANGUAGE_CODE %} - {% get_language LANGUAGE_CODE as backend_language %} - {% if backend_language and backend_language != language %} - - {% endif %} - - - - - - - - {% for page in pages %} - {% get_translation page language.slug as page_translation %} - {% include "pages/page_tree_archived_node.html" with page_translation=page_translation %} - {% empty %} - - - - {% endfor %} - -
- - - {% translate "Hierarchy" %} - - {% translate "Title in" %} {{ language.translated_name }} - - {% translate "Title in" %} {{ backend_language.translated_name }} - -
- {% spaceless %} - {% for lang in languages %} - {% if lang != request.region.default_language %} - - - - {% endif %} - {% endfor %} - {% endspaceless %} -
-
- {% translate "Status" %} - - {% translate "Last updated" %} - - {% translate "Options" %} -
- {% if filter_form.is_enabled %} - {% translate "No archived pages found with these filters." %} - {% else %} - {% translate "No pages archived yet." %} - {% endif %} -
- {% if can_edit_pages %} -
- - -
- {% endif %} -
-
- {% include "../generic_confirmation_dialog.html" %} - {% include "pages/_page_preview_overlay.html" %} -{% endblock content %} diff --git a/integreat_cms/cms/templates/pages/page_tree_archived_node.html b/integreat_cms/cms/templates/pages/page_tree_archived_node.html deleted file mode 100644 index 1d6e6d72b8..0000000000 --- a/integreat_cms/cms/templates/pages/page_tree_archived_node.html +++ /dev/null @@ -1,170 +0,0 @@ -{% load i18n %} -{% load content_filters %} -{% load page_filters %} -{% load tree_filters %} -
- - - - - - {% if page.cached_children|length > 0 %} - - - - {% endif %} - - - {% if page_translation %} - {{ page_translation.title }} - {% else %} - {% translate "Translation not available" %} - {% endif %} - - - - {% if page.backend_translation %} - {{ page.backend_translation.title }} - {% else %} - {% translate "Translation not available" %} - {% endif %} - - - - - {% if page.explicitly_archived %} -
- {% translate "Archived" %} -
- {% else %} -
- {% translate "Archived, because a parent page is archived" %} -
- {% endif %} -
-
- {{ page_translation.last_updated|date:"SHORT_DATE_FORMAT" }} -
-
- {% if page_translation.content or page.mirrored_page %} - - {% else %} - - {% endif %} - {% if can_edit_pages %} - {% if page.explicitly_archived %} - - {% else %} - - - - {% endif %} - {% endif %} - {% if perms.cms.delete_page %} - {% if not page.is_leaf %} - {# djlint:off H023 #} - - - - {# djlint:on #} - {% elif page.mirroring_pages.exists %} - - {% else %} - - {% endif %} - {% endif %} -
{% for page in pages %} - {% include "pages/page_tree_node.html" %} + {% include "pages/pages_page_tree_node.html" %} {% endfor %}
diff --git a/integreat_cms/cms/templates/pages/page_tree.html b/integreat_cms/cms/templates/pages/pages_page_tree.html similarity index 99% rename from integreat_cms/cms/templates/pages/page_tree.html rename to integreat_cms/cms/templates/pages/pages_page_tree.html index d367c56015..9d9e6916c0 100644 --- a/integreat_cms/cms/templates/pages/page_tree.html +++ b/integreat_cms/cms/templates/pages/pages_page_tree.html @@ -112,7 +112,7 @@

{% for page in pages %} - {% include "pages/page_tree_node.html" with is_archive=is_archive %} + {% include "pages/pages_page_tree_node.html" with is_archive=is_archive %} {% if forloop.last %} >>>>>> 718358e82 (Cleaning up and deleting page_tree_archived files) msgid "Archived" @@ -1765,27 +1766,30 @@ msgstr "Rechts nach Links" #: cms/constants/translation_status.py cms/templates/_form_language_tabs.html #: cms/templates/events/event_list_row.html #: cms/templates/imprint/imprint_sbs.html cms/templates/pages/page_sbs.html -#: cms/templates/pages/page_tree_node.html cms/templates/pois/poi_list_row.html +#: cms/templates/pages/pages_page_tree_node.html +#: cms/templates/pois/poi_list_row.html msgid "Translation up-to-date" msgstr "Übersetzung ist aktuell" #: cms/constants/translation_status.py cms/templates/_form_language_tabs.html #: cms/templates/events/event_list_row.html #: cms/templates/imprint/imprint_sbs.html cms/templates/pages/page_sbs.html -#: cms/templates/pages/page_tree_node.html cms/templates/pois/poi_list_row.html +#: cms/templates/pages/pages_page_tree_node.html +#: cms/templates/pois/poi_list_row.html msgid "Currently in translation" msgstr "Wird derzeit übersetzt" #: cms/constants/translation_status.py cms/templates/_form_language_tabs.html #: cms/templates/events/event_list_row.html #: cms/templates/imprint/imprint_sbs.html cms/templates/pages/page_sbs.html -#: cms/templates/pages/page_tree_node.html cms/templates/pois/poi_list_row.html +#: cms/templates/pages/pages_page_tree_node.html +#: cms/templates/pois/poi_list_row.html msgid "Translation outdated" msgstr "Übersetzung ist veraltet" #: cms/constants/translation_status.py cms/templates/_form_language_tabs.html #: cms/templates/events/event_list_row.html -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/pages_page_tree_node.html #: cms/templates/poicategories/poicategory_list_row.html #: cms/templates/pois/poi_list_row.html msgid "Translation missing" @@ -1793,8 +1797,8 @@ msgstr "Übersetzung fehlt" #: cms/constants/translation_status.py cms/templates/_form_language_tabs.html #: cms/templates/events/event_list_row.html -#: cms/templates/pages/page_tree_node.html #: cms/templates/pages/page_xliff_import_view.html +#: cms/templates/pages/pages_page_tree_node.html #: cms/templates/pois/poi_list_row.html msgid "Machine translated" msgstr "Maschinelle Übersetzung" @@ -2158,7 +2162,7 @@ msgstr "" #: cms/forms/pages/page_filter_form.py cms/templates/content_versions.html #: cms/templates/events/event_form.html cms/templates/events/event_list.html -#: cms/templates/pages/page_form.html cms/templates/pages/page_tree.html +#: cms/templates/pages/page_form.html cms/templates/pages/pages_page_tree.html #: cms/templates/pois/poi_form.html cms/templates/pois/poi_list.html msgid "Publication status" msgstr "Veröffentlichungsstatus" @@ -3628,7 +3632,7 @@ msgstr "" msgid "page" msgstr "Seite" -#: cms/models/pages/page.py cms/templates/pages/page_tree.html +#: cms/models/pages/page.py cms/templates/pages/pages_page_tree.html msgid "pages" msgstr "Seiten" @@ -5372,7 +5376,7 @@ msgstr "Kontakt archivieren" #: cms/templates/contacts/contact_form.html #: cms/templates/pages/page_form_sidebar/actions_box.html -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/pages_page_tree_node.html msgid "Restore page" msgstr "Seite wiederherstellen" @@ -5438,7 +5442,7 @@ msgstr "Webseite" #: cms/templates/linkcheck/links_by_filter.html #: cms/templates/offertemplates/offertemplate_list.html #: cms/templates/organizations/organization_list.html -#: cms/templates/pages/page_tree.html cms/templates/pois/poi_list.html +#: cms/templates/pages/pages_page_tree.html cms/templates/pois/poi_list.html #: cms/templates/pois/poi_list_archived.html cms/templates/roles/role_list.html msgid "Options" msgstr "Optionen" @@ -5464,7 +5468,7 @@ msgstr "Kontakte ausgewählt" #: cms/templates/languagetreenodes/languagetreenode_list.html #: cms/templates/linkcheck/links_by_filter.html #: cms/templates/organizations/organization_list.html -#: cms/templates/pages/page_tree.html cms/templates/pois/poi_list.html +#: cms/templates/pages/pages_page_tree.html cms/templates/pois/poi_list.html #: cms/templates/pois/poi_list_archived.html msgid "Select bulk action" msgstr "Mehrfachaktion auswählen" @@ -5491,7 +5495,7 @@ msgstr "Kontakte löschen" #: cms/templates/linkcheck/links_by_filter.html #: cms/templates/organizations/organization_list.html #: cms/templates/pages/_page_xliff_export_overlay.html -#: cms/templates/pages/page_tree.html cms/templates/pois/poi_list.html +#: cms/templates/pages/pages_page_tree.html cms/templates/pois/poi_list.html #: cms/templates/pois/poi_list_archived.html msgid "Execute" msgstr "Ausführen" @@ -6039,7 +6043,7 @@ msgstr "Zurück zu Veranstaltungen" #: cms/templates/feedback/admin_feedback_list_archived.html #: cms/templates/feedback/region_feedback_list.html #: cms/templates/feedback/region_feedback_list_archived.html -#: cms/templates/pages/page_tree.html cms/templates/users/user_list.html +#: cms/templates/pages/pages_page_tree.html cms/templates/users/user_list.html msgid "Show filters" msgstr "Filter einblenden" @@ -6048,7 +6052,7 @@ msgstr "Filter einblenden" #: cms/templates/feedback/admin_feedback_list_archived.html #: cms/templates/feedback/region_feedback_list.html #: cms/templates/feedback/region_feedback_list_archived.html -#: cms/templates/pages/page_tree.html cms/templates/users/user_list.html +#: cms/templates/pages/pages_page_tree.html cms/templates/users/user_list.html msgid "Hide filters" msgstr "Filter ausblenden" @@ -6104,35 +6108,35 @@ msgstr "Veranstaltungen ausgewählt" msgid "Archive events" msgstr "Veranstaltungen archivieren" -#: cms/templates/events/event_list.html cms/templates/pages/page_tree.html -#: cms/templates/pois/poi_list.html +#: cms/templates/events/event_list.html +#: cms/templates/pages/pages_page_tree.html cms/templates/pois/poi_list.html #, python-format msgid "Publish %(content_type)s" msgstr "%(content_type)s veröffentlichen" -#: cms/templates/events/event_list.html cms/templates/pages/page_tree.html -#: cms/templates/pois/poi_list.html +#: cms/templates/events/event_list.html +#: cms/templates/pages/pages_page_tree.html cms/templates/pois/poi_list.html #, python-format msgid "Draft %(content_type)s" msgstr "%(content_type)s als Entwurf speichern" -#: cms/templates/events/event_list.html cms/templates/pages/page_tree.html -#: cms/templates/pois/poi_list.html +#: cms/templates/events/event_list.html +#: cms/templates/pages/pages_page_tree.html cms/templates/pois/poi_list.html #, python-format msgid "Machine translate %(content_type)s via %(provider)s to %(language)s" msgstr "" "%(content_type)s maschinell via %(provider)s auf %(language)s übersetzen" -#: cms/templates/events/event_list.html cms/templates/pages/page_tree.html -#: cms/templates/pois/poi_list.html +#: cms/templates/events/event_list.html +#: cms/templates/pages/pages_page_tree.html cms/templates/pois/poi_list.html msgid "" "This language is not supported by any available machine translation provider." msgstr "" "Diese Sprache wird von keinem verfügbaren Anbieter maschineller " "Übersetzungen unterstützt." -#: cms/templates/events/event_list.html cms/templates/pages/page_tree.html -#: cms/templates/pois/poi_list.html +#: cms/templates/events/event_list.html +#: cms/templates/pages/pages_page_tree.html cms/templates/pois/poi_list.html #, python-format msgid "Machine translate %(content_type)s to %(language)s" msgstr "%(content_type)s maschinell auf %(language)s übersetzen" @@ -6233,7 +6237,7 @@ msgstr "Zuletzt geändert von" #: cms/templates/offertemplates/offertemplate_list.html #: cms/templates/pages/_page_filter_form.html #: cms/templates/pages/_page_xliff_import_diff.html -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html #: cms/templates/push_notifications/push_notification_list.html #: cms/templates/push_notifications/push_notification_template_list.html #: cms/templates/regions/region_list.html @@ -7139,7 +7143,7 @@ msgid "Please select languages for multilingual export." msgstr "Bitte wählen Sie Sprachen für den mehrsprachigen Export." #: cms/templates/pages/_page_xliff_export_overlay.html -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "You cannot export XLIFF files for the default language" msgstr "Sie können keine XLIFF-Dateien für die Standard-Sprache exportieren" @@ -7202,11 +7206,12 @@ msgstr "Neue Seite erstellen" msgid "Translation in progress" msgstr "Übersetzung wird durchgeführt" -#: cms/templates/pages/page_form.html cms/templates/pages/page_tree.html +#: cms/templates/pages/page_form.html cms/templates/pages/pages_page_tree.html msgid "Cancel translation process" msgstr "Übersetzungsprozess abbrechen" -#: cms/templates/pages/page_form.html cms/templates/pages/page_tree_node.html +#: cms/templates/pages/page_form.html +#: cms/templates/pages/pages_page_tree_node.html msgid "Archived, because a parent page is archived" msgstr "Archiviert, weil eine übergeordnete Seite archiviert ist" @@ -7230,7 +7235,7 @@ msgstr[1] "" "übergeordneten Seiten wiederherstellen:" #: cms/templates/pages/page_form_sidebar/actions_box.html -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/pages_page_tree_node.html msgid "Archive page" msgstr "Seite archivieren" @@ -7261,12 +7266,13 @@ msgstr[1] "" "diesen Seiten entfernen:" #: cms/templates/pages/page_form_sidebar/actions_box.html -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/pages_page_tree_node.html msgid "Delete page" msgstr "Seite löschen" #: cms/templates/pages/page_form_sidebar/actions_box.html -#: cms/templates/pages/page_tree_node.html cms/views/pages/page_actions.py +#: cms/templates/pages/pages_page_tree_node.html +#: cms/views/pages/page_actions.py msgid "You cannot delete a page which has subpages." msgstr "Sie können keine Seite löschen, die Unterseiten besitzt." @@ -7282,7 +7288,8 @@ msgstr[1] "" "verschieben:" #: cms/templates/pages/page_form_sidebar/actions_box.html -#: cms/templates/pages/page_tree_node.html cms/views/pages/page_actions.py +#: cms/templates/pages/pages_page_tree_node.html +#: cms/views/pages/page_actions.py msgid "" "This page cannot be deleted because it was embedded as live content from " "another page." @@ -7387,77 +7394,93 @@ msgstr "" " Dieses Feld freilassen, um einen eindeutigen Permalink aus dem Titel zu " "generieren" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/page_xliff_import_view.html +msgid "Review XLIFF Import" +msgstr "XLIFF Import überprüfen" + +#: cms/templates/pages/page_xliff_import_view.html +msgid "" +"Please review the changes that will be applied to the page translations " +"below." +msgstr "" +"Bitte überprüfen Sie die Änderungen, die an den Seiten-Übersetzungen " +"vorgenommen werden." + +#: cms/templates/pages/page_xliff_import_view.html +msgid "Abort" +msgstr "Abbrechen" + +#: cms/templates/pages/pages_page_tree.html msgid "Archived Pages" msgstr "Archivierte Seiten" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Back to pages" msgstr "Zurück zu Seiten" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Page Tree" msgstr "Seiten-Baum" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Archived pages" msgstr "Archivierte Seiten" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Create page" msgstr "Seite erstellen" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html #, python-format msgid "" "You can only create pages in the default language (%(default_language)s)." msgstr "" "Sie können Seiten nur in der Standard-Sprache (%(default_language)s) anlegen." -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Tags" msgstr "Tags" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "No pages found with these filters." msgstr "Keine Seiten mit diesen Filtern gefunden." -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "No pages available yet." msgstr "Noch keine Seiten vorhanden." -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Expand all" msgstr "Alle ausklappen" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Collapse all" msgstr "Alle einklappen" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Pages selected" msgstr "Seiten ausgewählt" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Archive pages" msgstr "Seiten archivieren" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Export published pages as PDF" msgstr "Veröffentlichte Seiten als PDF exportieren" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Currently PDF-Export is not available in this language." msgstr "Derzeit ist der PDF-Export in dieser Sprache nicht verfügbar." -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html #, python-format msgid "Export published pages for %(language_translated_name)s translation" msgstr "" "Nur veröffentlichte Seiten für die %(language_translated_name)s-Übersetzung " "exportieren" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html #, python-format msgid "" "Export unpublished (⚠️) and published pages for %(language_translated_name)s " @@ -7466,68 +7489,69 @@ msgstr "" "Nicht (⚠️) veröffentlichte und veröffentlichte Seiten für die " "%(language_translated_name)s-Übersetzung exportieren" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Export selected pages for multilingual translation" msgstr "Ausgewählte Seiten für die mehrsprachige Übersetzung exportieren" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Restore pages" msgstr "Seiten wiederherstellen" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Import XLIFF files" msgstr "XLIFF-Dateien importieren" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Supported file extensions" msgstr "Unterstützte Dateiendungen" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Select files" msgstr "Dateien auswählen" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "and {} other files" msgstr "und {} weitere Dateien" -#: cms/templates/pages/page_tree.html +#: cms/templates/pages/pages_page_tree.html msgid "Import" msgstr "Importieren" -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/pages_page_tree_node.html msgid "This page is archived." msgstr "Diese Seite ist archiviert." -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/pages_page_tree_node.html msgid "" "This page is archived, because at least one of its parent pages is archived." msgstr "" "Diese Seite ist archiviert, weil eine ihrer übergeordneten Seiten archiviert " "ist." -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/pages_page_tree_node.html msgid "View page as preview" msgstr "Seite als Vorschau ansehen" -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/pages_page_tree_node.html msgid "You cannot preview an empty page." msgstr "Eine leere Seite hat keine Vorschau" -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/pages_page_tree_node.html msgid "Open page in web app" msgstr "Seite in der Web-App öffnen" -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/pages_page_tree_node.html msgid "This page cannot be opened in the web app, because it is not public." msgstr "" "Diese Seite kann nicht in der Web-App geöffnet werden, weil sie nicht " "veröffentlicht ist." -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/pages_page_tree_node.html msgid "Edit page" msgstr "Seite bearbeiten" -#: cms/templates/pages/page_tree_node.html cms/views/pages/page_actions.py +#: cms/templates/pages/pages_page_tree_node.html +#: cms/views/pages/page_actions.py msgid "" "This page cannot be archived because it was embedded as live content from " "another page." @@ -7535,40 +7559,24 @@ msgstr "" "Diese Seite kann nicht archiviert werden, da sie von einer anderen Seite als " "Live-Inhalt eingebunden wurde." -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/pages_page_tree_node.html msgid "To restore this page, you have to restore its archived parent page." msgstr "" "Um diese Seite wiederherzustellen, müssen Sie ihre archivierte übergeordnete " "Seite wiederherstellen." -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/pages_page_tree_node.html msgid "This also involves archived subpages." msgstr "Dies betrifft auch archivierte Unterseiten." -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/pages_page_tree_node.html msgid "Copy short link" msgstr "Kurz-URL kopieren" -#: cms/templates/pages/page_tree_node.html +#: cms/templates/pages/pages_page_tree_node.html msgid "You cannot copy a short link of a page which has no translation." msgstr "Sie können keine Kurz-URL zu einer fehlenden Übersetzung kopieren." -#: cms/templates/pages/page_xliff_import_view.html -msgid "Review XLIFF Import" -msgstr "XLIFF Import überprüfen" - -#: cms/templates/pages/page_xliff_import_view.html -msgid "" -"Please review the changes that will be applied to the page translations " -"below." -msgstr "" -"Bitte überprüfen Sie die Änderungen, die an den Seiten-Übersetzungen " -"vorgenommen werden." - -#: cms/templates/pages/page_xliff_import_view.html -msgid "Abort" -msgstr "Abbrechen" - #: cms/templates/pagination.html msgid "Number of entries per page" msgstr "Anzahl Einträge pro Seite" diff --git a/integreat_cms/static/src/js/tree-drag-and-drop.ts b/integreat_cms/static/src/js/tree-drag-and-drop.ts index 493dae953c..19f0d7b841 100644 --- a/integreat_cms/static/src/js/tree-drag-and-drop.ts +++ b/integreat_cms/static/src/js/tree-drag-and-drop.ts @@ -1,6 +1,6 @@ /** * This file contains all event handlers and functions which are needed for drag & drop functionality in trees. - * Currently, this is used in integreat_cms/cms/templates/pages/page_tree.html and integreat_cms/cms/templates/language_tree/language_tree.html + * Currently, this is used in integreat_cms/cms/templates/pages/pages_page_tree.html and integreat_cms/cms/templates/language_tree/language_tree.html */ import { off, on } from "./utils/wrapped-events"; From 495e71b6c1aeec81c219f736f20e529d7f13ce98 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Mon, 9 Dec 2024 22:37:15 +0100 Subject: [PATCH 40/52] Fix pylint --- integreat_cms/cms/views/pages/partial_page_tree_view.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/integreat_cms/cms/views/pages/partial_page_tree_view.py b/integreat_cms/cms/views/pages/partial_page_tree_view.py index 901ff3eb85..72e4b98715 100644 --- a/integreat_cms/cms/views/pages/partial_page_tree_view.py +++ b/integreat_cms/cms/views/pages/partial_page_tree_view.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +from ast import literal_eval from collections import defaultdict from django.http import HttpRequest, JsonResponse @@ -15,7 +16,7 @@ @permission_required("cms.view_page") @require_POST -# pylint: disable=unused-argument +# pylint: disable=unused-argument, too-many-locals def render_partial_page_tree_views( request: HttpRequest, region_slug: str, language_slug: str, is_archive: str ) -> JsonResponse: @@ -34,7 +35,7 @@ def render_partial_page_tree_views( language = region.get_language_or_404(language_slug, only_active=True) # Convert is_archive from String to Boolean - is_archive = eval(is_archive) + is_archive = literal_eval(is_archive) backend_language = Language.objects.filter(slug=get_language()).first() From 7a82550d87b9f55b2797cfeb5deb5c52e7234498 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Tue, 10 Dec 2024 10:13:47 +0100 Subject: [PATCH 41/52] [WIP] Include page tree to statistics --- .../pages/_generic_page_tree_node.html | 2 +- .../statistics/statistics_viewed_pages.html | 19 +++++++++++++++++-- .../cms/views/statistics/statistics_view.py | 3 +++ .../src/js/analytics/statistics-charts.ts | 8 ++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/integreat_cms/cms/templates/pages/_generic_page_tree_node.html b/integreat_cms/cms/templates/pages/_generic_page_tree_node.html index 6aaa4d95a9..ed8dcec9e2 100644 --- a/integreat_cms/cms/templates/pages/_generic_page_tree_node.html +++ b/integreat_cms/cms/templates/pages/_generic_page_tree_node.html @@ -65,4 +65,4 @@ {% endif %} -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html index 4b571d2675..1f543cc401 100644 --- a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -9,10 +9,13 @@ {% translate "Page views" %} {% endblock collapsible_box_title %} {% block collapsible_box_content %} -
+ {% csrf_token %}
- +
{% include "pages/_generic_page_tree.html" %} @@ -21,6 +24,18 @@ + + {% for page in pages %} + + {% include "pages/_generic_page_tree_node.html" %} + + + {% endfor %} +
+
diff --git a/integreat_cms/cms/views/statistics/statistics_view.py b/integreat_cms/cms/views/statistics/statistics_view.py index 6b6fd47aac..b4df44d61f 100644 --- a/integreat_cms/cms/views/statistics/statistics_view.py +++ b/integreat_cms/cms/views/statistics/statistics_view.py @@ -63,5 +63,8 @@ def get( { **self.get_context_data(**kwargs), "form": form, + "pages": region.get_pages(), + "region": region, + "language": region.default_language, }, ) diff --git a/integreat_cms/static/src/js/analytics/statistics-charts.ts b/integreat_cms/static/src/js/analytics/statistics-charts.ts index 1d32dec86d..2e0a88a42c 100644 --- a/integreat_cms/static/src/js/analytics/statistics-charts.ts +++ b/integreat_cms/static/src/js/analytics/statistics-charts.ts @@ -75,9 +75,17 @@ const updateChart = async (): Promise => { // Get AJAX URL const url = chart.canvas.getAttribute("data-statistics-url"); + const pageAccessesURL = document.getElementById("statistics-page-access").getAttribute("data-page-accesses-url"); try { const response = await fetch(url, parameters); + const response2 = await fetch(pageAccessesURL, parameters); + + if (response.status === HTTP_STATUS_OK) { + // The response text contains the data from Matomo as JSON. + const data = (await response2.json()) as AjaxResponse; + console.log(data); + } if (response.status === HTTP_STATUS_OK) { // The response text contains the data from Matomo as JSON. From 71c19abda9b3c324edb3cc3c30ee0d852d959136 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Tue, 10 Dec 2024 17:48:46 +0100 Subject: [PATCH 42/52] Javascript code --- integreat_cms/static/src/index.ts | 1 + .../src/js/analytics/statistics-charts.ts | 8 -- .../js/analytics/statistics-page-accesses.ts | 91 +++++++++++++++++++ 3 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 integreat_cms/static/src/js/analytics/statistics-page-accesses.ts diff --git a/integreat_cms/static/src/index.ts b/integreat_cms/static/src/index.ts index b81aae6203..66d96a1769 100644 --- a/integreat_cms/static/src/index.ts +++ b/integreat_cms/static/src/index.ts @@ -72,6 +72,7 @@ import "./js/mfa/login"; import "./js/offers/zammad"; import "./js/analytics/statistics-charts"; +import "./js/analytics/statistics-page-accesses"; import "./js/analytics/translation_coverage"; import "./js/analytics/linkcheck-widget"; import "./js/analytics/hix-list"; diff --git a/integreat_cms/static/src/js/analytics/statistics-charts.ts b/integreat_cms/static/src/js/analytics/statistics-charts.ts index 2e0a88a42c..1d32dec86d 100644 --- a/integreat_cms/static/src/js/analytics/statistics-charts.ts +++ b/integreat_cms/static/src/js/analytics/statistics-charts.ts @@ -75,17 +75,9 @@ const updateChart = async (): Promise => { // Get AJAX URL const url = chart.canvas.getAttribute("data-statistics-url"); - const pageAccessesURL = document.getElementById("statistics-page-access").getAttribute("data-page-accesses-url"); try { const response = await fetch(url, parameters); - const response2 = await fetch(pageAccessesURL, parameters); - - if (response.status === HTTP_STATUS_OK) { - // The response text contains the data from Matomo as JSON. - const data = (await response2.json()) as AjaxResponse; - console.log(data); - } if (response.status === HTTP_STATUS_OK) { // The response text contains the data from Matomo as JSON. diff --git a/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts b/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts new file mode 100644 index 0000000000..12f5e573fc --- /dev/null +++ b/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts @@ -0,0 +1,91 @@ +export {}; + +export type AjaxResponse = { + pageId: number; + accesses: object; +}; + +export type Access = { + languageSlug: string; + accessesOverTime: object; +} + +const calculateAllAccessesPerPage = (accessesOverTime: object): number => { + console.log(accessesOverTime) + let accesses: number = 0; + Object.values(accessesOverTime).forEach((entry) => { + if (entry) { + accesses += entry; + } + }) + return accesses; +} + +const updateChart = async (): Promise => { + // const chart = Chart.instances[0]; + + const chartNetworkError = document.getElementById("chart-network-error"); + const chartServerError = document.getElementById("chart-server-error"); + const chartHeavyTrafficError = document.getElementById("chart-heavy-traffic-error"); + const chartLoading = document.getElementById("chart-loading"); + + chartNetworkError.classList.add("hidden"); + chartServerError.classList.add("hidden"); + chartHeavyTrafficError.classList.add("hidden"); + + let parameters = {}; + + const HTTP_STATUS_OK = 200; + // const HTTP_STATUS_BAD_REQUEST = 400; + // const HTTP_STATUS_GATEWAY_TIMEOUT = 504; + + const statisticsForm = document.getElementById("statistics-form") as HTMLFormElement; + + // If form exists (which is the case on the statistics page), perform some extra steps + if (statisticsForm) { + parameters = { + method: "POST", + body: new FormData(statisticsForm), + }; + } + + const pageAccessesURL = document.getElementById("statistics-page-access").getAttribute("data-page-accesses-url"); + const accessFields = document.getElementsByClassName("accesses"); + try { + const response = await fetch(pageAccessesURL, parameters); + + if (response.status === HTTP_STATUS_OK) { + const data = (await response.json()) as AjaxResponse; + Object.entries(data).forEach((values) => { + Array.from(accessFields).forEach((accessField) => { + const id = values[0] + const pageId = accessField.parentElement.getAttribute("id").replace("page-", ""); + if (id === pageId) { + const accesses = values[1] + const editableAccessField = accessField + let allAccesses: number = 0 + Object.entries(accesses).forEach((access) => { + const languageSlug = access[0] + const accessesOverTime = access[1] + allAccesses += calculateAllAccessesPerPage(accessesOverTime) + console.log(languageSlug, accesses) + // calculateAccessesPerLanguage(languageSlug, accessesOverTime) + // console.log(accessesOverTime) + }) + editableAccessField.textContent = String(allAccesses); + console.log(allAccesses) + } + }); + }); + } + } catch (error) { + console.error("Network error during fetch:", error); + chartNetworkError.classList.remove("hidden"); + } finally { + chartLoading.classList.add("hidden"); + } +}; + +window.addEventListener("load", async () => { + updateChart(); +}); From b0ffcc6c911c19a159e6c42dcad953fc248b22bc Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Thu, 12 Dec 2024 18:08:27 +0100 Subject: [PATCH 43/52] [WIP] First idea for graph --- .../statistics/statistics_viewed_pages.html | 11 +++- .../cms/views/statistics/statistics_view.py | 1 + integreat_cms/locale/de/LC_MESSAGES/django.po | 4 ++ .../js/analytics/statistics-page-accesses.ts | 62 ++++++++++++------- 4 files changed, 54 insertions(+), 24 deletions(-) diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html index 1f543cc401..7dc6b3f27a 100644 --- a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -31,7 +31,16 @@ data-drop-position="last-child" class="page-row drop drop-on border-t border-b border-solid border-gray-200 hover:bg-gray-100 level-{{ page.relative_depth }} {% if parent_id %}hidden parent-page-{{ parent_id }}{% endif %}"> {% include "pages/_generic_page_tree_node.html" %} - + + {% for language in languages %} + + {% endfor %} + {% comment %} {% endcomment %} + + {% endfor %} diff --git a/integreat_cms/cms/views/statistics/statistics_view.py b/integreat_cms/cms/views/statistics/statistics_view.py index b4df44d61f..df251c4d7c 100644 --- a/integreat_cms/cms/views/statistics/statistics_view.py +++ b/integreat_cms/cms/views/statistics/statistics_view.py @@ -66,5 +66,6 @@ def get( "pages": region.get_pages(), "region": region, "language": region.default_language, + "languages": region.active_languages, }, ) diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index 291907f6f3..9dc8466c1e 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -8506,6 +8506,10 @@ msgstr "Tabellendokument/CSV" msgid "Page views" msgstr "Seitenzugriffe" +#: cms/templates/statistics/statistics_viewed_pages.html +msgid "Accesses in total" +msgstr "Zugriffe insgesamt" + #: cms/templates/translations/translations_management.html msgid "Manage machine translations" msgstr "Maschinelle Übersetzungen verwalten" diff --git a/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts b/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts index 12f5e573fc..6adc99e569 100644 --- a/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts +++ b/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts @@ -1,5 +1,3 @@ -export {}; - export type AjaxResponse = { pageId: number; accesses: object; @@ -8,18 +6,32 @@ export type AjaxResponse = { export type Access = { languageSlug: string; accessesOverTime: object; -} +}; -const calculateAllAccessesPerPage = (accessesOverTime: object): number => { - console.log(accessesOverTime) +const countAccesses = (accessesOverTime: object): number => { let accesses: number = 0; Object.values(accessesOverTime).forEach((entry) => { - if (entry) { - accesses += entry; - } - }) + accesses += entry; + }); return accesses; -} +}; + +const setAccessesPerLanguage = ( + accessField: Element, + languageSlug: string, + accessesOverTime: object, + allAccesses: number +) => { + const parentElement = accessField as HTMLElement; + const childElement = parentElement.querySelector(`.accesses span[data-language-slug="${languageSlug}"]`); + const languageColor = childElement.getAttribute("data-language-color"); + const accesses = countAccesses(accessesOverTime); + const width = allAccesses !== 0 ? Math.floor((accesses / allAccesses) * 100) : 0; + const languageColorString = `bg-[${languageColor}%]`; + childElement.classList.add(`w-[${width}%]`); + childElement.classList.add(languageColorString); + console.log(languageColor); +}; const updateChart = async (): Promise => { // const chart = Chart.instances[0]; @@ -58,24 +70,28 @@ const updateChart = async (): Promise => { const data = (await response.json()) as AjaxResponse; Object.entries(data).forEach((values) => { Array.from(accessFields).forEach((accessField) => { - const id = values[0] + const id = values[0]; const pageId = accessField.parentElement.getAttribute("id").replace("page-", ""); if (id === pageId) { - const accesses = values[1] - const editableAccessField = accessField - let allAccesses: number = 0 + const accesses = values[1]; + const allAccessesField = Array.from(accessField.parentElement?.children || []).find( + (el) => el !== accessField && el.classList.contains("total-accesses") + ); + const editableAllAccessField = allAccessesField; + let allAccesses: number = 0; + Object.entries(accesses).forEach((access) => { + const accessesOverTime = access[1]; + allAccesses += countAccesses(accessesOverTime); + }); + editableAllAccessField.textContent = `${String(allAccesses)} ${editableAllAccessField.getAttribute("data-translation")}`; Object.entries(accesses).forEach((access) => { - const languageSlug = access[0] - const accessesOverTime = access[1] - allAccesses += calculateAllAccessesPerPage(accessesOverTime) - console.log(languageSlug, accesses) - // calculateAccessesPerLanguage(languageSlug, accessesOverTime) - // console.log(accessesOverTime) - }) - editableAccessField.textContent = String(allAccesses); - console.log(allAccesses) + const languageSlug = access[0]; + const accessesOverTime = access[1]; + setAccessesPerLanguage(accessField, languageSlug, accessesOverTime, allAccesses); + }); } }); + // console.log(data) }); } } catch (error) { From c502c28ebe61f61f30cb801ee09a90027d0296c8 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Thu, 12 Dec 2024 18:35:58 +0100 Subject: [PATCH 44/52] Show bars --- .../cms/templates/statistics/statistics_viewed_pages.html | 5 ++--- .../static/src/js/analytics/statistics-page-accesses.ts | 7 +++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html index 7dc6b3f27a..e8353e908e 100644 --- a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -31,13 +31,12 @@ data-drop-position="last-child" class="page-row drop drop-on border-t border-b border-solid border-gray-200 hover:bg-gray-100 level-{{ page.relative_depth }} {% if parent_id %}hidden parent-page-{{ parent_id }}{% endif %}"> {% include "pages/_generic_page_tree_node.html" %} - + {% for language in languages %} + class="cursor-pointer bg-[{{ language.language_color }}] h-9"> {% endfor %} - {% comment %} {% endcomment %} diff --git a/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts b/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts index 6adc99e569..c2d4cfd40c 100644 --- a/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts +++ b/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts @@ -27,10 +27,9 @@ const setAccessesPerLanguage = ( const languageColor = childElement.getAttribute("data-language-color"); const accesses = countAccesses(accessesOverTime); const width = allAccesses !== 0 ? Math.floor((accesses / allAccesses) * 100) : 0; - const languageColorString = `bg-[${languageColor}%]`; - childElement.classList.add(`w-[${width}%]`); - childElement.classList.add(languageColorString); - console.log(languageColor); + (childElement as HTMLElement).style.backgroundColor = languageColor; + (childElement as HTMLElement).style.width = `${String(width)}%`; + (childElement as HTMLElement).title = String(accesses); }; const updateChart = async (): Promise => { From b1da1bcc453449f73a3672b48cd6ad108f35fba5 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Sat, 28 Dec 2024 11:56:59 +0100 Subject: [PATCH 45/52] Add option to change time range and language selection --- .../statistics/_statistics_legend_item.html | 3 +- .../statistics/statistics_sidebar.html | 2 +- .../statistics/statistics_viewed_pages.html | 21 ++++- integreat_cms/locale/de/LC_MESSAGES/django.po | 21 ++++- .../src/js/analytics/statistics-charts.ts | 1 + .../js/analytics/statistics-page-accesses.ts | 88 ++++++++++++++----- 6 files changed, 106 insertions(+), 30 deletions(-) diff --git a/integreat_cms/cms/templates/statistics/_statistics_legend_item.html b/integreat_cms/cms/templates/statistics/_statistics_legend_item.html index 71cb76e791..d55adedffc 100644 --- a/integreat_cms/cms/templates/statistics/_statistics_legend_item.html +++ b/integreat_cms/cms/templates/statistics/_statistics_legend_item.html @@ -3,7 +3,8 @@ + id="{{ name|slugify }}" + data-language-slug="{{ language.slug }}" />
{% if language %} diff --git a/integreat_cms/cms/templates/statistics/statistics_sidebar.html b/integreat_cms/cms/templates/statistics/statistics_sidebar.html index 7b6052c00f..dcc7fddd5a 100644 --- a/integreat_cms/cms/templates/statistics/statistics_sidebar.html +++ b/integreat_cms/cms/templates/statistics/statistics_sidebar.html @@ -45,7 +45,7 @@

- diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html index e8353e908e..eda5b070ea 100644 --- a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -15,12 +15,22 @@ data-page-accesses-url="{% url 'statistics_page_based_accesses' region_slug=request.region.slug %}"> {% csrf_token %}
+ + + {% include "pages/_generic_page_tree.html" %} @@ -31,11 +41,16 @@ data-drop-position="last-child" class="page-row drop drop-on border-t border-b border-solid border-gray-200 hover:bg-gray-100 level-{{ page.relative_depth }} {% if parent_id %}hidden parent-page-{{ parent_id }}{% endif %}"> {% include "pages/_generic_page_tree_node.html" %} - - \ No newline at end of file + diff --git a/integreat_cms/cms/views/pages/partial_page_tree_view.py b/integreat_cms/cms/views/pages/partial_page_tree_view.py index 81c23243ce..5e99e8d5d7 100644 --- a/integreat_cms/cms/views/pages/partial_page_tree_view.py +++ b/integreat_cms/cms/views/pages/partial_page_tree_view.py @@ -31,6 +31,7 @@ def render_partial_page_tree_views( :param region_slug: The slug of the current region :param language_slug: The slug of the current language :param is_archive: True when only archive pages are requested, False otherwise + :param is_statistics: True when this page tree is for statistics, if for pages it's False :return: The rendered template responses """ requested_tree_ids = [int(i) for i in json.loads(request.body.decode("utf-8"))] @@ -46,7 +47,12 @@ def render_partial_page_tree_views( backend_language = Language.objects.filter(slug=get_language()).first() - all_pages = region.pages.filter(tree_id__in=requested_tree_ids).prefetch_major_translations().prefetch_related("mirroring_pages").cache_tree(archived=is_archive) + all_pages = ( + region.pages.filter(tree_id__in=requested_tree_ids) + .prefetch_major_translations() + .prefetch_related("mirroring_pages") + .cache_tree(archived=is_archive) + ) pages_by_id = defaultdict(list) for page in all_pages: From 120f93ca4e63fa8a99c75cd403db0588c1884659 Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Mon, 13 Jan 2025 15:49:02 +0100 Subject: [PATCH 52/52] Update lucide version --- package-lock.json | 22 ++++++++++------------ package.json | 4 ++-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index c15f54d28e..651b6b4b12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "workspace", + "name": "integreat-cms", "lockfileVersion": 3, "requires": true, "packages": { @@ -44,8 +44,8 @@ "htmldiff-js": "1.0.5", "jest": "^29.7.0", "jsdom": "^24.1.0", - "lucide": "^0.469.0", - "lucide-preact": "^0.469.0", + "lucide": "^0.471.0", + "lucide-preact": "^0.471.0", "maplibre-gl": "^4.3.2", "mini-css-extract-plugin": "^2.9.0", "postcss-cli": "^11.0.0", @@ -13596,18 +13596,16 @@ } }, "node_modules/lucide": { - "version": "0.469.0", - "resolved": "https://registry.npmjs.org/lucide/-/lucide-0.469.0.tgz", - "integrity": "sha512-ZmMtOekJ6g1UFqTvV9aWbkk+u/QxpfDpgUrbNj8jJEK+CtBdHVQ4akCA0TcTyeOvnL8RWo271UzFTimBRT0gKw==", - "dev": true, - "license": "ISC" + "version": "0.471.0", + "resolved": "https://registry.npmjs.org/lucide/-/lucide-0.471.0.tgz", + "integrity": "sha512-q464ocnqGoI1c0aRLiU3l3GYIZBeddlo2s1+XrkkXjfQo+Y5djyNuVH/pa4zMZyM5RHPF4vlf8FBzHJQNzrvPw==", + "dev": true }, "node_modules/lucide-preact": { - "version": "0.469.0", - "resolved": "https://registry.npmjs.org/lucide-preact/-/lucide-preact-0.469.0.tgz", - "integrity": "sha512-Dr6VWSRy98W1WVVjinWDxXPFDkljZv/CemVUMDQj1Xjz89q3YQuHLy9piRls3J7FMxSMmJ+Euf/9Pf0smicwdA==", + "version": "0.471.0", + "resolved": "https://registry.npmjs.org/lucide-preact/-/lucide-preact-0.471.0.tgz", + "integrity": "sha512-jXgrd9qBU2eQ9KZjgcgJx+abHX5+jWfyiJsy9mnNtwxWFThW1Wl+LzhHy9ItBLanWBAvA0NDIFIB7g9V/HdQVg==", "dev": true, - "license": "ISC", "peerDependencies": { "preact": "^10.5.13" } diff --git a/package.json b/package.json index f0bbc086c2..c8b9bd16eb 100644 --- a/package.json +++ b/package.json @@ -47,8 +47,8 @@ "htmldiff-js": "1.0.5", "jest": "^29.7.0", "jsdom": "^24.1.0", - "lucide": "^0.469.0", - "lucide-preact": "^0.469.0", + "lucide": "^0.471.0", + "lucide-preact": "^0.471.0", "maplibre-gl": "^4.3.2", "mini-css-extract-plugin": "^2.9.0", "postcss-cli": "^11.0.0",
- {% translate "Accesses" %} ({{ form.start_date.value|date:"d.m.Y" }} - {{ form.end_date.value|date:"d.m.Y" }}) + {% translate "Accesses" %} ({{ form.start_date.value|date:"d.m.Y" }} - {{ form.end_date.value|date:"d.m.Y" }})
+ +
+ {% translate "Loading..." %} +
{% for language in languages %} + class="cursor-pointer bg-[{{ language.language_color }}] h-9"> + {% endfor %}
; const updateChart = async (): Promise => { // Get Chart instance const chart = Chart.instances[0]; + (window as any).chart = chart; // Get HTML elements const chartNetworkError = document.getElementById("chart-network-error"); diff --git a/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts b/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts index c2d4cfd40c..48501747b6 100644 --- a/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts +++ b/integreat_cms/static/src/js/analytics/statistics-page-accesses.ts @@ -1,3 +1,5 @@ +import { Chart } from "chart.js"; + export type AjaxResponse = { pageId: number; accesses: object; @@ -8,6 +10,8 @@ export type Access = { accessesOverTime: object; }; +let chart: Chart | null = null; + const countAccesses = (accessesOverTime: object): number => { let accesses: number = 0; Object.values(accessesOverTime).forEach((entry) => { @@ -26,33 +30,43 @@ const setAccessesPerLanguage = ( const childElement = parentElement.querySelector(`.accesses span[data-language-slug="${languageSlug}"]`); const languageColor = childElement.getAttribute("data-language-color"); const accesses = countAccesses(accessesOverTime); - const width = allAccesses !== 0 ? Math.floor((accesses / allAccesses) * 100) : 0; + const width = allAccesses !== 0 ? (accesses / allAccesses) * 100 : 0; (childElement as HTMLElement).style.backgroundColor = languageColor; (childElement as HTMLElement).style.width = `${String(width)}%`; (childElement as HTMLElement).title = String(accesses); }; -const updateChart = async (): Promise => { - // const chart = Chart.instances[0]; +const hideLoader = () => { + const loaderIcons = document.getElementsByClassName("page-accesses-loading"); + Array.from(loaderIcons).forEach((loaderIcon) => { + loaderIcon.classList.add("hidden"); + }); +}; + +const setDates = () => { + const unformattedStartDate = (document.getElementById("id_start_date") as HTMLInputElement).value; + const unformattedEndDate = (document.getElementById("id_end_date") as HTMLInputElement).value; + document.getElementById("date-range-start").innerHTML = new Date(unformattedStartDate).toLocaleDateString(); + document.getElementById("date-range-end").innerHTML = new Date(unformattedEndDate).toLocaleDateString(); +}; - const chartNetworkError = document.getElementById("chart-network-error"); - const chartServerError = document.getElementById("chart-server-error"); - const chartHeavyTrafficError = document.getElementById("chart-heavy-traffic-error"); - const chartLoading = document.getElementById("chart-loading"); +const updatePageAccesses = async (): Promise => { + const pageAccessesNetworkError = document.getElementById("page-accesses-network-error"); + const pageAccessesServerError = document.getElementById("page-accesses-server-error"); + const pageAccessesHeavyTrafficError = document.getElementById("page-accesses-heavy-traffic-error"); - chartNetworkError.classList.add("hidden"); - chartServerError.classList.add("hidden"); - chartHeavyTrafficError.classList.add("hidden"); + pageAccessesNetworkError.classList.add("hidden"); + pageAccessesServerError.classList.add("hidden"); + pageAccessesHeavyTrafficError.classList.add("hidden"); let parameters = {}; const HTTP_STATUS_OK = 200; - // const HTTP_STATUS_BAD_REQUEST = 400; - // const HTTP_STATUS_GATEWAY_TIMEOUT = 504; + const HTTP_STATUS_BAD_REQUEST = 400; + const HTTP_STATUS_GATEWAY_TIMEOUT = 504; const statisticsForm = document.getElementById("statistics-form") as HTMLFormElement; - // If form exists (which is the case on the statistics page), perform some extra steps if (statisticsForm) { parameters = { method: "POST", @@ -64,9 +78,9 @@ const updateChart = async (): Promise => { const accessFields = document.getElementsByClassName("accesses"); try { const response = await fetch(pageAccessesURL, parameters); - if (response.status === HTTP_STATUS_OK) { const data = (await response.json()) as AjaxResponse; + const languageSlugToDatasetId: Map = new Map(); Object.entries(data).forEach((values) => { Array.from(accessFields).forEach((accessField) => { const id = values[0]; @@ -79,28 +93,60 @@ const updateChart = async (): Promise => { const editableAllAccessField = allAccessesField; let allAccesses: number = 0; Object.entries(accesses).forEach((access) => { - const accessesOverTime = access[1]; - allAccesses += countAccesses(accessesOverTime); + const languageSlug = access[0]; + if (languageSlugToDatasetId.get(languageSlug) === undefined) { + const fullLabel = document + .querySelector(`#chart-legend [data-language-slug="${languageSlug}"]`) + .getAttribute("data-chart-item"); + const datasetId = chart.data.datasets.findIndex( + (dataset) => dataset.label === fullLabel + ); + languageSlugToDatasetId.set(languageSlug, datasetId); + } + if (chart.isDatasetVisible(languageSlugToDatasetId.get(languageSlug))) { + const accessesOverTime = access[1]; + allAccesses += countAccesses(accessesOverTime); + } }); editableAllAccessField.textContent = `${String(allAccesses)} ${editableAllAccessField.getAttribute("data-translation")}`; Object.entries(accesses).forEach((access) => { const languageSlug = access[0]; const accessesOverTime = access[1]; - setAccessesPerLanguage(accessField, languageSlug, accessesOverTime, allAccesses); + if (chart.isDatasetVisible(languageSlugToDatasetId.get(languageSlug))) { + setAccessesPerLanguage(accessField, languageSlug, accessesOverTime, allAccesses); + } else { + setAccessesPerLanguage(accessField, languageSlug, {}, allAccesses); + } }); } }); - // console.log(data) + }); + } else if (response.status === HTTP_STATUS_BAD_REQUEST) { + Array.from(accessFields).forEach((accessField) => { + const editableAllAccessField = accessField; + editableAllAccessField.textContent = accessField.getAttribute("data-client-error"); + }); + } else if (response.status === HTTP_STATUS_GATEWAY_TIMEOUT) { + Array.from(accessFields).forEach((accessField) => { + const editableAllAccessField = accessField; + editableAllAccessField.textContent = accessField.getAttribute("data-server-error"); }); } } catch (error) { console.error("Network error during fetch:", error); - chartNetworkError.classList.remove("hidden"); + pageAccessesNetworkError.classList.remove("hidden"); } finally { - chartLoading.classList.add("hidden"); + hideLoader(); + setDates(); } }; window.addEventListener("load", async () => { - updateChart(); + updatePageAccesses(); + chart = Chart.instances[0]; + const oldUpdate = chart.update; + chart.update = async () => { + oldUpdate.call(chart); + updatePageAccesses(); + }; }); From 424448fec88377ba56f83bcf3892152110f651de Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Tue, 7 Jan 2025 15:57:07 +0000 Subject: [PATCH 46/52] Disable editable page tree in statistics --- .../templates/pages/_generic_page_tree_node.html | 4 ++-- .../cms/templates/pages/pages_page_tree.html | 2 +- .../cms/templates/pages/pages_page_tree_node.html | 4 ++-- .../statistics/statistics_viewed_pages.html | 3 ++- integreat_cms/cms/urls/protected.py | 13 ++++++++++--- .../cms/views/pages/partial_page_tree_view.py | 9 ++++++++- .../cms/views/statistics/statistics_view.py | 1 + 7 files changed, 26 insertions(+), 10 deletions(-) diff --git a/integreat_cms/cms/templates/pages/_generic_page_tree_node.html b/integreat_cms/cms/templates/pages/_generic_page_tree_node.html index ed8dcec9e2..bdff06b8f9 100644 --- a/integreat_cms/cms/templates/pages/_generic_page_tree_node.html +++ b/integreat_cms/cms/templates/pages/_generic_page_tree_node.html @@ -17,8 +17,8 @@ + draggable="{% if can_edit_pages and not is_archive and not is_statistics %}true{% else %}false{% endif %}" + title="{% if not is_archive and not is_statistics %}{% translate "Change the order and position of the pages with drag & drop." %}{% else %}{% translate "Drag & drop is disabled for archived pages and statistics." %}{% endif %}"> {% if can_edit_pages %} {% endif %} diff --git a/integreat_cms/cms/templates/pages/pages_page_tree.html b/integreat_cms/cms/templates/pages/pages_page_tree.html index 9d9e6916c0..f8ad036c90 100644 --- a/integreat_cms/cms/templates/pages/pages_page_tree.html +++ b/integreat_cms/cms/templates/pages/pages_page_tree.html @@ -75,7 +75,7 @@

{% csrf_token %}
- diff --git a/integreat_cms/cms/templates/pages/pages_page_tree_node.html b/integreat_cms/cms/templates/pages/pages_page_tree_node.html index 2f7c32fc66..bcf5458408 100644 --- a/integreat_cms/cms/templates/pages/pages_page_tree_node.html +++ b/integreat_cms/cms/templates/pages/pages_page_tree_node.html @@ -28,8 +28,8 @@ {{ tag }} {% endfor %} - {% endif %} - + + {% endif %} + {% include "pages/_generic_page_tree_node.html" %} + + + \ No newline at end of file diff --git a/integreat_cms/cms/views/pages/partial_page_tree_view.py b/integreat_cms/cms/views/pages/partial_page_tree_view.py index 84bbb1739a..81c23243ce 100644 --- a/integreat_cms/cms/views/pages/partial_page_tree_view.py +++ b/integreat_cms/cms/views/pages/partial_page_tree_view.py @@ -74,6 +74,7 @@ def render_partial_page_tree_views( "languages": region.active_languages, "parent_id": parent.id, "is_archive": is_archive, + "is_statistics": is_statistics, }, request, ) diff --git a/integreat_cms/cms/views/statistics/statistics_view.py b/integreat_cms/cms/views/statistics/statistics_view.py index bfd1cb7e17..53e9aafa6e 100644 --- a/integreat_cms/cms/views/statistics/statistics_view.py +++ b/integreat_cms/cms/views/statistics/statistics_view.py @@ -55,13 +55,18 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponseRe form = StatisticsFilterForm() + page_queryset = region.pages.filter(lft=1) + + # Cache tree structure to reduce database queries + pages = page_queryset.prefetch_major_translations().prefetch_related("mirroring_pages").cache_tree(archived=False) + return render( request, self.template_name, { **self.get_context_data(**kwargs), "form": form, - "pages": region.get_pages(), + "pages": pages, "region": region, "language": region.default_language, "languages": region.active_languages, diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index c481e46cd4..fbff798e90 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -5029,7 +5029,7 @@ msgstr "Bitte kontaktieren Sie eine:n Administrator:in." #: cms/templates/pois/poi_form_sidebar/position_box.html #: cms/templates/statistics/_statistics_widget.html #: cms/templates/statistics/statistics_chart.html -#: cms/templates/statistics/statistics_viewed_pages.html +#: cms/templates/statistics/statistics_viewed_pages_node.html msgid "Loading..." msgstr "Laden..." @@ -7105,7 +7105,8 @@ msgstr "Ändern Sie die Reihenfolge und Position der Seiten mit Drag & Drop." #: cms/templates/pages/_generic_page_tree_node.html msgid "Drag & drop is disabled for archived pages and statistics." -msgstr "Drag & drop ist für archivierte Seiten und in Statistiken nicht möglich." +msgstr "" +"Drag & drop ist für archivierte Seiten und in Statistiken nicht möglich." #: cms/templates/pages/_generic_page_tree_node.html msgid "Expand all subpages" @@ -8504,11 +8505,11 @@ msgstr "Tabellendokument/CSV" msgid "Page views" msgstr "Seitenzugriffe" -#: cms/templates/statistics/statistics_viewed_pages.html +#: cms/templates/statistics/statistics_viewed_pages_node.html msgid "This page has no accesses in the selected time span" msgstr "Diese Seite hat keine Zugriffe für den ausgewählten Zeitraum" -#: cms/templates/statistics/statistics_viewed_pages.html +#: cms/templates/statistics/statistics_viewed_pages_node.html msgid "Accesses in total" msgstr "Zugriffe insgesamt" @@ -11166,7 +11167,9 @@ msgstr "" "gehört ({})." #~ msgid "There has been a client error. Please try again later" -#~ msgstr "Es gab einen Fehler mit dem Klienten. Bitte versuchen Sie es später erneut." +#~ msgstr "" +#~ "Es gab einen Fehler mit dem Klienten. Bitte versuchen Sie es später " +#~ "erneut." #~ msgid "There has been a server error. Please try again later" #~ msgstr "Zu viele Anfragen. Bitte versuchen Sie es später erneut." @@ -11210,15 +11213,6 @@ msgstr "" #~ msgid "%s with %s" #~ msgstr "%s mit %s" -#~ msgid "No archived pages found with these filters." -#~ msgstr "Keine archivierten Seiten mit diesen Filtern gefunden." - -#~ msgid "No pages archived yet." -#~ msgstr "Noch keine Seiten archiviert." - -#~ msgid "This also involves non-archived subpages." -#~ msgstr "Dies betrifft auch nicht-archivierte Unterseiten." - #~ msgid "Individual languages can be hidden by clicking on the labels." #~ msgstr "" #~ "Einzelne Sprachen können durch Anklicken der Beschriftungen ausgeblendet " From 4990aeaa76d42d23ccc76969b0822dbfd0020908 Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Sat, 11 Jan 2025 10:04:16 +0000 Subject: [PATCH 50/52] Fix archived page tree --- .../pages/_generic_page_tree_node.html | 20 +- integreat_cms/cms/urls/protected.py | 2652 +++++++++-------- .../cms/views/pages/page_tree_view.py | 10 +- .../cms/views/statistics/statistics_view.py | 10 +- 4 files changed, 1436 insertions(+), 1256 deletions(-) diff --git a/integreat_cms/cms/templates/pages/_generic_page_tree_node.html b/integreat_cms/cms/templates/pages/_generic_page_tree_node.html index bdff06b8f9..14981e120d 100644 --- a/integreat_cms/cms/templates/pages/_generic_page_tree_node.html +++ b/integreat_cms/cms/templates/pages/_generic_page_tree_node.html @@ -23,15 +23,17 @@ {% endif %} - {% if not page.parent_id and not page.is_leaf or page.parent_id and page.cached_children|length > 0 %} - - - + {% if not is_archive %} + {% if not page.parent_id and not page.is_leaf or page.parent_id and page.cached_children|length > 0 %} + + + + {% endif %} {% endif %} {% endif %} diff --git a/integreat_cms/cms/urls/protected.py b/integreat_cms/cms/urls/protected.py index 54cc7fb22c..c17d4d651e 100644 --- a/integreat_cms/cms/urls/protected.py +++ b/integreat_cms/cms/urls/protected.py @@ -67,83 +67,89 @@ media_ajax_urlpatterns: list[URLPattern] = [ path( "media/", - include([ - path( - "directory/", - include([ - path( - "path/", - media.get_directory_path_ajax, - name="mediacenter_directory_path", - ), - path( - "content/", - media.get_directory_content_ajax, - name="mediacenter_get_directory_content", - ), - path( - "create/", - media.create_directory_ajax, - name="mediacenter_create_directory", - ), - path( - "update/", - media.edit_directory_ajax, - name="mediacenter_edit_directory", - ), - path( - "delete/", - media.delete_directory_ajax, - name="mediacenter_delete_directory", - ), - ]), - ), - path( - "file/", - include([ - path( - "upload/", - media.upload_file_ajax, - name="mediacenter_upload_file", - ), - path( - "edit/", - media.edit_file_ajax, - name="mediacenter_edit_file", - ), - path( - "move/", - media.move_file_ajax, - name="mediacenter_move_file", - ), - path( - "delete/", - media.delete_file_ajax, - name="mediacenter_delete_file", - ), - path( - "replace/", - media.replace_file_ajax, - name="mediacenter_replace_file", + include( + [ + path( + "directory/", + include( + [ + path( + "path/", + media.get_directory_path_ajax, + name="mediacenter_directory_path", + ), + path( + "content/", + media.get_directory_content_ajax, + name="mediacenter_get_directory_content", + ), + path( + "create/", + media.create_directory_ajax, + name="mediacenter_create_directory", + ), + path( + "update/", + media.edit_directory_ajax, + name="mediacenter_edit_directory", + ), + path( + "delete/", + media.delete_directory_ajax, + name="mediacenter_delete_directory", + ), + ] ), - path( - "get-usages/", - media.get_file_usages_ajax, - name="mediacenter_get_file_usages", + ), + path( + "file/", + include( + [ + path( + "upload/", + media.upload_file_ajax, + name="mediacenter_upload_file", + ), + path( + "edit/", + media.edit_file_ajax, + name="mediacenter_edit_file", + ), + path( + "move/", + media.move_file_ajax, + name="mediacenter_move_file", + ), + path( + "delete/", + media.delete_file_ajax, + name="mediacenter_delete_file", + ), + path( + "replace/", + media.replace_file_ajax, + name="mediacenter_replace_file", + ), + path( + "get-usages/", + media.get_file_usages_ajax, + name="mediacenter_get_file_usages", + ), + ] ), - ]), - ), - path( - "search/", - media.get_query_search_results_ajax, - name="mediacenter_get_search_result", - ), - path( - "filter/unused/", - media.get_unused_media_files_ajax, - name="mediacenter_filter_unused_media_files", - ), - ]), + ), + path( + "search/", + media.get_query_search_results_ajax, + name="mediacenter_get_search_result", + ), + path( + "filter/unused/", + media.get_unused_media_files_ajax, + name="mediacenter_filter_unused_media_files", + ), + ] + ), ), ] @@ -151,54 +157,62 @@ user_settings_urlpatterns: list[URLPattern] = [ path( "user-settings/", - include([ - path("", settings.UserSettingsView.as_view(), name="user_settings"), - path( - "totp/", - include([ - path( - "", - settings.TOTPRegisterView.as_view(), - name="register_totp", - ), - ]), - ), - path( - "totp/", - include([ - path( - "delete/", - settings.TOTPDeleteView.as_view(), - name="delete_totp", - ), - ]), - ), - path( - "mfa/", - include([ - path( - "authenticate/", - settings.AuthenticateModifyMfaView.as_view(), - name="authenticate_modify_mfa", - ), - path( - "get-challenge/", - settings.GetMfaChallengeView.as_view(), - name="get_mfa_challenge", + include( + [ + path("", settings.UserSettingsView.as_view(), name="user_settings"), + path( + "totp/", + include( + [ + path( + "", + settings.TOTPRegisterView.as_view(), + name="register_totp", + ), + ] ), - path( - "register/", - settings.RegisterUserFidoKeyView.as_view(), - name="register_new_fido_key", + ), + path( + "totp/", + include( + [ + path( + "delete/", + settings.TOTPDeleteView.as_view(), + name="delete_totp", + ), + ] ), - path( - "delete//", - settings.DeleteUserFidoKeyView.as_view(), - name="delete_fido_key", + ), + path( + "mfa/", + include( + [ + path( + "authenticate/", + settings.AuthenticateModifyMfaView.as_view(), + name="authenticate_modify_mfa", + ), + path( + "get-challenge/", + settings.GetMfaChallengeView.as_view(), + name="get_mfa_challenge", + ), + path( + "register/", + settings.RegisterUserFidoKeyView.as_view(), + name="register_new_fido_key", + ), + path( + "delete//", + settings.DeleteUserFidoKeyView.as_view(), + name="delete_fido_key", + ), + ] ), - ]), - ), - ]), + ), + ] + ), ), ] @@ -225,278 +239,328 @@ ), path( "region-condition/", - include([ - path( - "", - region_condition.RegionConditionView.as_view(), - name="region_condition", - ), - path( - "export//", - region_condition.export_region_conditions, - name="export_region_conditions", - ), - ]), + include( + [ + path( + "", + region_condition.RegionConditionView.as_view(), + name="region_condition", + ), + path( + "export//", + region_condition.export_region_conditions, + name="export_region_conditions", + ), + ] + ), ), path( "linkcheck/", - include([ - path( - "", - linkcheck.LinkcheckRedirectView.as_view(), - name="linkcheck_landing", - ), - path( - "/", - include([ - path( - "", - linkcheck.LinkcheckListView.as_view(), - name="linkcheck", - ), - path( - "/", - linkcheck.LinkcheckListView.as_view(), - name="edit_url", + include( + [ + path( + "", + linkcheck.LinkcheckRedirectView.as_view(), + name="linkcheck_landing", + ), + path( + "/", + include( + [ + path( + "", + linkcheck.LinkcheckListView.as_view(), + name="linkcheck", + ), + path( + "/", + linkcheck.LinkcheckListView.as_view(), + name="edit_url", + ), + ] ), - ]), - ), - path( - "search_and_replace_link", - linkcheck.LinkReplaceView.as_view(), - name="search_and_replace_link", - ), - ]), + ), + path( + "search_and_replace_link", + linkcheck.LinkReplaceView.as_view(), + name="search_and_replace_link", + ), + ] + ), ), path( "regions/", - include([ - path("", regions.RegionListView.as_view(), name="regions"), - path( - "new/", - form_views.CustomCreateView.as_view(form_class=RegionForm), - name="new_region", - ), - path( - "/", - include([ - path( - "edit/", - regions.RegionUpdateView.as_view(), - name="edit_region", + include( + [ + path("", regions.RegionListView.as_view(), name="regions"), + path( + "new/", + form_views.CustomCreateView.as_view(form_class=RegionForm), + name="new_region", + ), + path( + "/", + include( + [ + path( + "edit/", + regions.RegionUpdateView.as_view(), + name="edit_region", + ), + path( + "delete/", regions.delete_region, name="delete_region" + ), + ] ), - path("delete/", regions.delete_region, name="delete_region"), - ]), - ), - ]), + ), + ] + ), ), path("media-library/", media.AdminMediaListView.as_view(), name="media_admin"), path( "languages/", - include([ - path( - "", - list_views.ModelListView.as_view(queryset=Language.objects.all().prefetch_related("language_tree_nodes")), - name="languages", - ), - path( - "new/", - form_views.CustomCreateView.as_view(form_class=LanguageForm), - name="new_language", - ), - path( - "/", - include([ - path( - "edit/", - form_views.CustomUpdateView.as_view(form_class=LanguageForm), - name="edit_language", - ), - path( - "delete/", - delete_views.CustomDeleteView.as_view( - model=Language, - ), - name="delete_language", + include( + [ + path( + "", + list_views.ModelListView.as_view( + queryset=Language.objects.all().prefetch_related( + "language_tree_nodes" + ) + ), + name="languages", + ), + path( + "new/", + form_views.CustomCreateView.as_view(form_class=LanguageForm), + name="new_language", + ), + path( + "/", + include( + [ + path( + "edit/", + form_views.CustomUpdateView.as_view( + form_class=LanguageForm + ), + name="edit_language", + ), + path( + "delete/", + delete_views.CustomDeleteView.as_view( + model=Language, + ), + name="delete_language", + ), + ] ), - ]), - ), - ]), + ), + ] + ), ), path( "users/", - include([ - path("", users.UserListView.as_view(), name="users"), - path("new/", users.UserFormView.as_view(), name="new_user"), - path( - "/", - include([ - path( - "edit/", - users.UserFormView.as_view(), - name="edit_user", - ), - path("delete/", users.delete_user, name="delete_user"), - path( - "resend-activation-link", - users.resend_activation_link, - name="resend_activation_link", + include( + [ + path("", users.UserListView.as_view(), name="users"), + path("new/", users.UserFormView.as_view(), name="new_user"), + path( + "/", + include( + [ + path( + "edit/", + users.UserFormView.as_view(), + name="edit_user", + ), + path("delete/", users.delete_user, name="delete_user"), + path( + "resend-activation-link", + users.resend_activation_link, + name="resend_activation_link", + ), + ] ), - ]), - ), - ]), + ), + ] + ), ), path( "location-categories/", - include([ - path( - "", - list_views.ModelListView.as_view( - model=POICategory, - extra_context={"languages": Language.objects.all()}, + include( + [ + path( + "", + list_views.ModelListView.as_view( + model=POICategory, + extra_context={"languages": Language.objects.all()}, + ), + name="poicategories", ), - name="poicategories", - ), - path( - "new/", - poi_categories.POICategoryCreateView.as_view(), - name="new_poicategory", - ), - path( - "/", - include([ - path( - "edit/", - poi_categories.POICategoryUpdateView.as_view(), - name="edit_poicategory", - ), - path( - "delete/", - poi_categories.POICategoryDeleteView.as_view(), - name="delete_poicategory", + path( + "new/", + poi_categories.POICategoryCreateView.as_view(), + name="new_poicategory", + ), + path( + "/", + include( + [ + path( + "edit/", + poi_categories.POICategoryUpdateView.as_view(), + name="edit_poicategory", + ), + path( + "delete/", + poi_categories.POICategoryDeleteView.as_view(), + name="delete_poicategory", + ), + ] ), - ]), - ), - ]), + ), + ] + ), ), path( "roles/", - include([ - path("", list_views.ModelListView.as_view(model=Role), name="roles"), - path("new/", roles.RoleFormView.as_view(), name="new_role"), - path( - "/", - include([ - path( - "edit/", - roles.RoleFormView.as_view(), - name="edit_role", - ), - path( - "delete/", - roles.RoleFormView.as_view(), - name="delete_role", + include( + [ + path("", list_views.ModelListView.as_view(model=Role), name="roles"), + path("new/", roles.RoleFormView.as_view(), name="new_role"), + path( + "/", + include( + [ + path( + "edit/", + roles.RoleFormView.as_view(), + name="edit_role", + ), + path( + "delete/", + roles.RoleFormView.as_view(), + name="delete_role", + ), + ] ), - ]), - ), - ]), + ), + ] + ), ), path( "feedback/", - include([ - path( - "", - feedback.AdminFeedbackListView.as_view(), - name="admin_feedback", - ), - path( - "archived/", - feedback.AdminFeedbackListView.as_view(archived=True), - name="admin_feedback_archived", - ), - path( - "mark-read/", - feedback.mark_admin_feedback_as_read, - name="mark_admin_feedback_as_read", - ), - path( - "mark-unread/", - feedback.mark_admin_feedback_as_unread, - name="mark_admin_feedback_as_unread", - ), - path( - "archive/", - feedback.archive_admin_feedback, - name="archive_admin_feedback", - ), - path( - "restore/", - feedback.restore_admin_feedback, - name="restore_admin_feedback", - ), - path( - "delete/", - feedback.delete_admin_feedback, - name="delete_admin_feedback", - ), - ]), + include( + [ + path( + "", + feedback.AdminFeedbackListView.as_view(), + name="admin_feedback", + ), + path( + "archived/", + feedback.AdminFeedbackListView.as_view(archived=True), + name="admin_feedback_archived", + ), + path( + "mark-read/", + feedback.mark_admin_feedback_as_read, + name="mark_admin_feedback_as_read", + ), + path( + "mark-unread/", + feedback.mark_admin_feedback_as_unread, + name="mark_admin_feedback_as_unread", + ), + path( + "archive/", + feedback.archive_admin_feedback, + name="archive_admin_feedback", + ), + path( + "restore/", + feedback.restore_admin_feedback, + name="restore_admin_feedback", + ), + path( + "delete/", + feedback.delete_admin_feedback, + name="delete_admin_feedback", + ), + ] + ), ), path( "offer-templates/", - include([ - path( - "", - list_views.ModelListView.as_view(queryset=OfferTemplate.objects.all().prefetch_related("regions")), - name="offertemplates", - ), - path( - "new/", - form_views.CustomCreateView.as_view(form_class=OfferTemplateForm), - name="new_offertemplate", - ), - path( - "/", - include([ - path( - "edit/", - form_views.CustomUpdateView.as_view(form_class=OfferTemplateForm), - name="edit_offertemplate", - ), - path( - "delete/", - delete_views.CustomDeleteView.as_view(model=OfferTemplate, protect_manytomany="regions"), - name="delete_offertemplate", + include( + [ + path( + "", + list_views.ModelListView.as_view( + queryset=OfferTemplate.objects.all().prefetch_related("regions") + ), + name="offertemplates", + ), + path( + "new/", + form_views.CustomCreateView.as_view(form_class=OfferTemplateForm), + name="new_offertemplate", + ), + path( + "/", + include( + [ + path( + "edit/", + form_views.CustomUpdateView.as_view( + form_class=OfferTemplateForm + ), + name="edit_offertemplate", + ), + path( + "delete/", + delete_views.CustomDeleteView.as_view( + model=OfferTemplate, protect_manytomany="regions" + ), + name="delete_offertemplate", + ), + ] ), - ]), - ), - ]), + ), + ] + ), ), path("", include(user_settings_urlpatterns)), path( "ajax/", - include([ - path("", include(media_ajax_urlpatterns)), - path( - "chat/", - include([ - path( - "send-message/", - chat.send_chat_message, - name="send_chat_message", - ), - path( - "delete-message//", - chat.delete_chat_message, - name="delete_chat_message", + include( + [ + path("", include(media_ajax_urlpatterns)), + path( + "chat/", + include( + [ + path( + "send-message/", + chat.send_chat_message, + name="send_chat_message", + ), + path( + "delete-message//", + chat.delete_chat_message, + name="delete_chat_message", + ), + ] ), - ]), - ), - path( - "search/", - utils.search_content_ajax, - name="search_content_ajax", - ), - ]), + ), + path( + "search/", + utils.search_content_ajax, + name="search_content_ajax", + ), + ] + ), ), path( "release-notes/", @@ -505,1059 +569,1167 @@ ), path( "/", - include([ - path("", dashboard.DashboardView.as_view(), name="dashboard"), - path( - "ajax/", - include([ - path( - "dashboard/", - include([ - path( - "broken-links/", - dashboard.DashboardView.get_broken_links_context, - name="get_broken_links_ajax", - ) - ]), - ), - path("", include(media_ajax_urlpatterns)), - path( - "/", - include([ - path( - "/", - include([ - path( - "cancel-translation-process/", - pages.cancel_translation_process_ajax, - name="cancel_translation_process_ajax", - ), - path( - "preview/", - pages.preview_page_ajax, - name="preview_page_ajax", - ), - path( - "content/", - pages.get_page_content_ajax, - name="get_page_content_ajax", - ), - ]), - ), - path( - "search/", - utils.search_content_ajax, - name="search_content_ajax", - ), - path( - "/slugify/", - utils.slugify_ajax, - name="slugify_ajax", - ), - path( - "/word_count/", - utils.build_json_for_machine_translation, - name="word_count", + include( + [ + path("", dashboard.DashboardView.as_view(), name="dashboard"), + path( + "ajax/", + include( + [ + path( + "dashboard/", + include( + [ + path( + "broken-links/", + dashboard.DashboardView.get_broken_links_context, + name="get_broken_links_ajax", + ) + ] + ), ), - ]), - ), - path( - "render/", - include([ + path("", include(media_ajax_urlpatterns)), path( "/", - include([ - path( - "mirrored-page-field/", - pages.render_mirrored_page_field, - name="render_mirrored_page_field", - ), - path( - "/", - include([ - path( - "/", - include([ + include( + [ + path( + "/", + include( + [ + path( + "cancel-translation-process/", + pages.cancel_translation_process_ajax, + name="cancel_translation_process_ajax", + ), path( - "partial-page-tree/", - pages.render_partial_page_tree_views, - name="get_page_tree_ajax", + "preview/", + pages.preview_page_ajax, + name="preview_page_ajax", ), - ]), + path( + "content/", + pages.get_page_content_ajax, + name="get_page_content_ajax", + ), + ] ), - ]), - ), - ]), - ), - path( - "page-order-table/", - include([ - path( - "", - include(page_order_table_urlpatterns), - ), - path( - "parent-/", - include(page_order_table_urlpatterns), - ), - ]), - ), - ]), - ), - path( - "chat/", - include([ - path( - "send-message/", - chat.send_chat_message, - name="send_chat_message", + ), + path( + "search/", + utils.search_content_ajax, + name="search_content_ajax", + ), + path( + "/slugify/", + utils.slugify_ajax, + name="slugify_ajax", + ), + path( + "/word_count/", + utils.build_json_for_machine_translation, + name="word_count", + ), + ] + ), ), path( - "delete-message//", - chat.delete_chat_message, - name="delete_chat_message", + "render/", + include( + [ + path( + "/", + include( + [ + path( + "mirrored-page-field/", + pages.render_mirrored_page_field, + name="render_mirrored_page_field", + ), + path( + "/", + include( + [ + path( + "/", + include( + [ + path( + "partial-page-tree/", + pages.render_partial_page_tree_views, + name="get_page_tree_ajax", + ), + ] + ), + ), + ] + ), + ), + ] + ), + ), + path( + "page-order-table/", + include( + [ + path( + "", + include( + page_order_table_urlpatterns + ), + ), + path( + "parent-/", + include( + page_order_table_urlpatterns + ), + ), + ] + ), + ), + ] + ), ), - ]), - ), - path( - "get_hix_score/", - utils.get_hix_score, - name="get_hix_score", - ), - path( - "statistics/", - include([ path( - "total-views/", - statistics.get_total_visits_ajax, - name="statistics_total_visits", + "chat/", + include( + [ + path( + "send-message/", + chat.send_chat_message, + name="send_chat_message", + ), + path( + "delete-message//", + chat.delete_chat_message, + name="delete_chat_message", + ), + ] + ), ), path( - "update-chart/", - statistics.get_visits_per_language_ajax, - name="statistics_visits_per_language", + "get_hix_score/", + utils.get_hix_score, + name="get_hix_score", ), path( - "page-based-accesses/", - statistics.get_page_accesses_ajax, - name="statistics_page_based_accesses", + "statistics/", + include( + [ + path( + "total-views/", + statistics.get_total_visits_ajax, + name="statistics_total_visits", + ), + path( + "update-chart/", + statistics.get_visits_per_language_ajax, + name="statistics_visits_per_language", + ), + path( + "page-based-accesses/", + statistics.get_page_accesses_ajax, + name="statistics_page_based_accesses", + ), + ] + ), ), - ]), - ), - path( - "page-permission/", - include([ path( - "grant/", - pages.grant_page_permission_ajax, - name="grant_page_permission_ajax", + "page-permission/", + include( + [ + path( + "grant/", + pages.grant_page_permission_ajax, + name="grant_page_permission_ajax", + ), + path( + "revoke/", + pages.revoke_page_permission_ajax, + name="revoke_page_permission_ajax", + ), + ] + ), ), path( - "revoke/", - pages.revoke_page_permission_ajax, - name="revoke_page_permission_ajax", + "content-edit-lock/", + include( + [ + path( + "heartbeat/", + utils.content_edit_lock_heartbeat, + name="content_edit_lock_heartbeat", + ), + path( + "release/", + utils.content_edit_lock_release, + name="content_edit_lock_release", + ), + ] + ), ), - ]), - ), - path( - "content-edit-lock/", - include([ path( - "heartbeat/", - utils.content_edit_lock_heartbeat, - name="content_edit_lock_heartbeat", + "search-poi/", + events.search_poi_ajax, + name="search_poi_ajax", ), path( - "release/", - utils.content_edit_lock_release, - name="content_edit_lock_release", + "search/", + utils.search_content_ajax, + name="search_content_ajax", ), - ]), - ), - path( - "search-poi/", - events.search_poi_ajax, - name="search_poi_ajax", - ), - path( - "search/", - utils.search_content_ajax, - name="search_content_ajax", - ), - path( - "search/contact/", - utils.search_contact_ajax, - name="search_contact_ajax", - ), - path( - "dismiss-tutorial//", - settings.DismissTutorial.as_view(), - name="dismiss_tutorial", - ), - path( - "locations/auto-complete-address/", - pois.auto_complete_address, - name="auto_complete_poi_address", - ), - path( - "locations/get-address-from-coordinates/", - pois.get_address_from_coordinates, - name="get_address_from_coordinates", - ), - ]), - ), - path( - "analytics/", - include([ - path( - "statistics/", - statistics.AnalyticsView.as_view(), - name="statistics", - ), - path( - "translation-coverage/", - analytics.TranslationCoverageView.as_view(), - name="translation_coverage", - ), - path( - "linkcheck/", - include([ path( - "", - linkcheck.LinkcheckRedirectView.as_view(), - name="linkcheck_landing", + "search/contact/", + utils.search_contact_ajax, + name="search_contact_ajax", ), path( - "stats", - linkcheck.LinkcheckStatsView.as_view(), - name="linkcheck_stats", + "dismiss-tutorial//", + settings.DismissTutorial.as_view(), + name="dismiss_tutorial", ), path( - "/", - include([ - path( - "", - linkcheck.LinkcheckListView.as_view(), - name="linkcheck", - ), - path( - "/", - linkcheck.LinkcheckListView.as_view(), - name="edit_url", - ), - ]), + "locations/auto-complete-address/", + pois.auto_complete_address, + name="auto_complete_poi_address", ), path( - "search_and_replace_link", - linkcheck.LinkReplaceView.as_view(), - name="search_and_replace_link", + "locations/get-address-from-coordinates/", + pois.get_address_from_coordinates, + name="get_address_from_coordinates", ), - ]), + ] ), - ]), - ), - path( - "translations/", - include( - [ - path( - "manage/", - translations.TranslationsManagementView.as_view(), - name="translations_management", - ), - ], ), - ), - path( - "external-calendars/", - include([ - path( - "", - external_calendars.ExternalCalendarList.as_view(), - name="external_calendar_list", - ), - path( - "new/", - external_calendars.ExternalCalendarFormView.as_view(), - name="new_external_calendar", - ), - path( - "/", - include([ + path( + "analytics/", + include( + [ path( - "edit/", - external_calendars.ExternalCalendarFormView.as_view(), - name="edit_external_calendar", - ), - path( - "delete/", - external_calendars.delete_external_calendar, - name="delete_external_calendar", + "statistics/", + statistics.AnalyticsView.as_view(), + name="statistics", ), - ]), - ), - ]), - ), - path( - "pages/", - include([ - path("", pages.PageTreeView.as_view(), name="pages"), - path( - "/", - include([ path( - "", - pages.PageTreeView.as_view(), - name="pages", + "translation-coverage/", + analytics.TranslationCoverageView.as_view(), + name="translation_coverage", ), path( - "new/", - pages.PageFormView.as_view(), - name="new_page", - ), - path( - "archived/", - pages.PageTreeView.as_view(archived=True), - name="archived_pages", - ), - path( - "xliff/", + "linkcheck/", include( [ path( - "download/", - pages.ExportXliffView.as_view(prefetch_translations=True), - name="download_xliff", - ), - path( - "only-public/", - pages.ExportXliffView.as_view( - only_public=True, - prefetch_public_translations=True, - ), - name="download_xliff_only_public", + "", + linkcheck.LinkcheckRedirectView.as_view(), + name="linkcheck_landing", ), path( - "multiple-languages/", - pages.ExportMultiLanguageXliffView.as_view(), - name="download_xliff_multiple_languages", + "stats", + linkcheck.LinkcheckStatsView.as_view(), + name="linkcheck_stats", ), path( - "upload/", - pages.upload_xliff, - name="upload_xliff", + "/", + include( + [ + path( + "", + linkcheck.LinkcheckListView.as_view(), + name="linkcheck", + ), + path( + "/", + linkcheck.LinkcheckListView.as_view(), + name="edit_url", + ), + ] + ), ), path( - "import//", - pages.PageXliffImportView.as_view(), - name="import_xliff", + "search_and_replace_link", + linkcheck.LinkReplaceView.as_view(), + name="search_and_replace_link", ), - ], - ), - ), - path( - "export/", - pages.GeneratePdfView.as_view(), - name="export_pdf", - ), - path( - "bulk-archive/", - bulk_action_views.BulkArchiveView.as_view( - model=Page, - ), - name="bulk_archive_pages", - ), - path( - "bulk-restore/", - bulk_action_views.BulkRestoreView.as_view( - model=Page, + ] ), - name="bulk_restore_pages", ), + ] + ), + ), + path( + "translations/", + include( + [ path( - "machine-translate/", - bulk_action_views.BulkMachineTranslationView.as_view( - model=Page, - form=PageTranslationForm, - ), - name="machine_translation_pages", + "manage/", + translations.TranslationsManagementView.as_view(), + name="translations_management", ), + ], + ), + ), + path( + "external-calendars/", + include( + [ path( - "bulk-publish/", - bulk_action_views.BulkPublishingView.as_view( - model=Page, - ), - name="publish_multiple_pages", + "", + external_calendars.ExternalCalendarList.as_view(), + name="external_calendar_list", ), path( - "bulk-draft/", - bulk_action_views.BulkDraftingView.as_view( - model=Page, - ), - name="draft_multiple_pages", + "new/", + external_calendars.ExternalCalendarFormView.as_view(), + name="new_external_calendar", ), path( - "bulk-cancel-translation-process/", - pages.CancelTranslationProcess.as_view( - model=Page, - ), - name="cancel_translation_process", - ), - path( - "/", - include([ - path( - "edit/", - include( - [ - path( - "", - pages.PageFormView.as_view(), - name="edit_page", - ), - path( - "side-by-side/", - pages.PageSideBySideView.as_view(), - name="sbs_edit_page", - ), - ], - ), - ), - path( - "versions/", - include( - [ - path( - "", - pages.PageVersionView.as_view(), - name="page_versions", - ), - path( - "/", - pages.PageVersionView.as_view(), - name="page_versions", - ), - ], - ), - ), - path( - "archive/", - pages.archive_page, - name="archive_page", - ), - path( - "restore/", - pages.restore_page, - name="restore_page", - ), - path( - "refresh/", - pages.refresh_date, - name="refresh_page", - ), - path( - "delete/", - pages.delete_page, - name="delete_page", - ), - # warning: the move url is also hardcoded in src/integreat_cms/static/js/tree_drag_and_drop.js - path( - "move///", - pages.move_page, - name="move_page", - ), - ]), - ), - ]), - ), - ]), - ), - path( - "imprint/", - include([ - path( - "", - imprint.ImprintFormView.as_view(), - name="edit_imprint", - ), - path( - "/", - include([ - path( - "edit/", + "/", include( [ path( - "", - imprint.ImprintFormView.as_view(), - name="edit_imprint", + "edit/", + external_calendars.ExternalCalendarFormView.as_view(), + name="edit_external_calendar", ), path( - "side-by-side/", - imprint.ImprintSideBySideView.as_view(), - name="sbs_edit_imprint", + "delete/", + external_calendars.delete_external_calendar, + name="delete_external_calendar", ), - ], + ] ), ), + ] + ), + ), + path( + "pages/", + include( + [ + path("", pages.PageTreeView.as_view(), name="pages"), path( - "versions/", + "/", include( [ path( "", - imprint.ImprintVersionView.as_view(), - name="imprint_versions", + pages.PageTreeView.as_view(), + name="pages", + ), + path( + "new/", + pages.PageFormView.as_view(), + name="new_page", + ), + path( + "archived/", + pages.PageTreeView.as_view(archived=True), + name="archived_pages", + ), + path( + "xliff/", + include( + [ + path( + "download/", + pages.ExportXliffView.as_view( + prefetch_translations=True + ), + name="download_xliff", + ), + path( + "only-public/", + pages.ExportXliffView.as_view( + only_public=True, + prefetch_public_translations=True, + ), + name="download_xliff_only_public", + ), + path( + "multiple-languages/", + pages.ExportMultiLanguageXliffView.as_view(), + name="download_xliff_multiple_languages", + ), + path( + "upload/", + pages.upload_xliff, + name="upload_xliff", + ), + path( + "import//", + pages.PageXliffImportView.as_view(), + name="import_xliff", + ), + ], + ), + ), + path( + "export/", + pages.GeneratePdfView.as_view(), + name="export_pdf", + ), + path( + "bulk-archive/", + bulk_action_views.BulkArchiveView.as_view( + model=Page, + ), + name="bulk_archive_pages", + ), + path( + "bulk-restore/", + bulk_action_views.BulkRestoreView.as_view( + model=Page, + ), + name="bulk_restore_pages", + ), + path( + "machine-translate/", + bulk_action_views.BulkMachineTranslationView.as_view( + model=Page, + form=PageTranslationForm, + ), + name="machine_translation_pages", + ), + path( + "bulk-publish/", + bulk_action_views.BulkPublishingView.as_view( + model=Page, + ), + name="publish_multiple_pages", + ), + path( + "bulk-draft/", + bulk_action_views.BulkDraftingView.as_view( + model=Page, + ), + name="draft_multiple_pages", ), path( - "", - imprint.ImprintVersionView.as_view(), - name="imprint_versions", + "bulk-cancel-translation-process/", + pages.CancelTranslationProcess.as_view( + model=Page, + ), + name="cancel_translation_process", ), - ], + path( + "/", + include( + [ + path( + "edit/", + include( + [ + path( + "", + pages.PageFormView.as_view(), + name="edit_page", + ), + path( + "side-by-side/", + pages.PageSideBySideView.as_view(), + name="sbs_edit_page", + ), + ], + ), + ), + path( + "versions/", + include( + [ + path( + "", + pages.PageVersionView.as_view(), + name="page_versions", + ), + path( + "/", + pages.PageVersionView.as_view(), + name="page_versions", + ), + ], + ), + ), + path( + "archive/", + pages.archive_page, + name="archive_page", + ), + path( + "restore/", + pages.restore_page, + name="restore_page", + ), + path( + "refresh/", + pages.refresh_date, + name="refresh_page", + ), + path( + "delete/", + pages.delete_page, + name="delete_page", + ), + # warning: the move url is also hardcoded in src/integreat_cms/static/js/tree_drag_and_drop.js + path( + "move///", + pages.move_page, + name="move_page", + ), + ] + ), + ), + ] ), ), - ]), - ), - path( - "delete/", - imprint.delete_imprint, - name="delete_imprint", - ), - ]), - ), - path( - "organizations/", - include([ - path( - "", - organizations.OrganizationListView.as_view(), - name="organizations", - ), - path( - "new/", - organizations.OrganizationFormView.as_view(), - name="new_organization", - ), - path( - "archived/", - organizations.OrganizationListView.as_view(archived=True), - name="archived_organizations", + ] ), - path( - "bulk-archive/", - organizations.ArchiveBulkAction.as_view(), - name="bulk_archive_organization", - ), - path( - "bulk-restore/", - organizations.RestoreBulkAction.as_view(), - name="bulk_restore_organization", - ), - path( - "bulk-delete/", - organizations.DeleteBulkAction.as_view(), - name="bulk_delete_organization", - ), - path( - "/", - include([ - path( - "edit/", - organizations.OrganizationFormView.as_view(), - name="edit_organization", - ), + ), + path( + "imprint/", + include( + [ path( - "delete/", - organizations.delete, - name="delete_organization", + "", + imprint.ImprintFormView.as_view(), + name="edit_imprint", ), path( - "archive/", - organizations.archive, - name="archive_organization", + "/", + include( + [ + path( + "edit/", + include( + [ + path( + "", + imprint.ImprintFormView.as_view(), + name="edit_imprint", + ), + path( + "side-by-side/", + imprint.ImprintSideBySideView.as_view(), + name="sbs_edit_imprint", + ), + ], + ), + ), + path( + "versions/", + include( + [ + path( + "", + imprint.ImprintVersionView.as_view(), + name="imprint_versions", + ), + path( + "", + imprint.ImprintVersionView.as_view(), + name="imprint_versions", + ), + ], + ), + ), + ] + ), ), path( - "restore/", - organizations.restore, - name="restore_organization", + "delete/", + imprint.delete_imprint, + name="delete_imprint", ), - ]), + ] ), - ]), - ), - path( - "events/", - include([ - path("", events.EventListView.as_view(), name="events"), - path( - "/", - include([ + ), + path( + "organizations/", + include( + [ path( "", - events.EventListView.as_view(), - name="events", - ), - path( - "archived/", - events.EventListView.as_view(archived=True), - name="events_archived", + organizations.OrganizationListView.as_view(), + name="organizations", ), path( "new/", - events.EventFormView.as_view(), - name="new_event", + organizations.OrganizationFormView.as_view(), + name="new_organization", ), path( - "machine-translate/", - bulk_action_views.BulkMachineTranslationView.as_view(model=Event, form=EventTranslationForm), - name="machine_translation_events", + "archived/", + organizations.OrganizationListView.as_view( + archived=True + ), + name="archived_organizations", ), path( "bulk-archive/", - bulk_action_views.BulkArchiveView.as_view(model=Event), - name="bulk_archive_events", + organizations.ArchiveBulkAction.as_view(), + name="bulk_archive_organization", ), path( "bulk-restore/", - bulk_action_views.BulkRestoreView.as_view(model=Event), - name="bulk_restore_events", + organizations.RestoreBulkAction.as_view(), + name="bulk_restore_organization", ), path( - "bulk-publish/", - bulk_action_views.BulkPublishingView.as_view( - model=Event, + "bulk-delete/", + organizations.DeleteBulkAction.as_view(), + name="bulk_delete_organization", + ), + path( + "/", + include( + [ + path( + "edit/", + organizations.OrganizationFormView.as_view(), + name="edit_organization", + ), + path( + "delete/", + organizations.delete, + name="delete_organization", + ), + path( + "archive/", + organizations.archive, + name="archive_organization", + ), + path( + "restore/", + organizations.restore, + name="restore_organization", + ), + ] ), - name="publish_multiple_events", ), + ] + ), + ), + path( + "events/", + include( + [ + path("", events.EventListView.as_view(), name="events"), path( - "bulk-draft/", - bulk_action_views.BulkDraftingView.as_view( - model=Event, + "/", + include( + [ + path( + "", + events.EventListView.as_view(), + name="events", + ), + path( + "archived/", + events.EventListView.as_view(archived=True), + name="events_archived", + ), + path( + "new/", + events.EventFormView.as_view(), + name="new_event", + ), + path( + "machine-translate/", + bulk_action_views.BulkMachineTranslationView.as_view( + model=Event, form=EventTranslationForm + ), + name="machine_translation_events", + ), + path( + "bulk-archive/", + bulk_action_views.BulkArchiveView.as_view( + model=Event + ), + name="bulk_archive_events", + ), + path( + "bulk-restore/", + bulk_action_views.BulkRestoreView.as_view( + model=Event + ), + name="bulk_restore_events", + ), + path( + "bulk-publish/", + bulk_action_views.BulkPublishingView.as_view( + model=Event, + ), + name="publish_multiple_events", + ), + path( + "bulk-draft/", + bulk_action_views.BulkDraftingView.as_view( + model=Event, + ), + name="draft_multiple_events", + ), + path( + "/", + include( + [ + path( + "edit/", + events.EventFormView.as_view(), + name="edit_event", + ), + path( + "copy/", + events.copy, + name="copy_event", + ), + path( + "archive/", + events.archive, + name="archive_event", + ), + path( + "restore/", + events.restore, + name="restore_event", + ), + path( + "delete/", + events.delete, + name="delete_event", + ), + path( + "versions/", + include( + [ + path( + "", + events.EventVersionView.as_view(), + name="event_versions", + ), + path( + "/", + events.EventVersionView.as_view(), + name="event_versions", + ), + ], + ), + ), + ] + ), + ), + ] ), - name="draft_multiple_events", - ), - path( - "/", - include([ - path( - "edit/", - events.EventFormView.as_view(), - name="edit_event", - ), - path( - "copy/", - events.copy, - name="copy_event", - ), - path( - "archive/", - events.archive, - name="archive_event", - ), - path( - "restore/", - events.restore, - name="restore_event", - ), - path( - "delete/", - events.delete, - name="delete_event", - ), - path( - "versions/", - include( - [ - path( - "", - events.EventVersionView.as_view(), - name="event_versions", - ), - path( - "/", - events.EventVersionView.as_view(), - name="event_versions", - ), - ], - ), - ), - ]), - ), - ]), + ), + ] ), - ]), - ), - path( - "pois/", - include([ - path("", pois.POIListView.as_view(), name="pois"), - path( - "/", - include([ + ), + path( + "pois/", + include( + [ + path("", pois.POIListView.as_view(), name="pois"), + path( + "/", + include( + [ + path( + "", + pois.POIListView.as_view(), + name="pois", + ), + path( + "archived/", + pois.POIListView.as_view(archived=True), + name="archived_pois", + ), + path( + "new/", + pois.POIFormView.as_view(), + name="new_poi", + ), + path( + "machine-translate/", + bulk_action_views.BulkMachineTranslationView.as_view( + model=POI, form=POITranslationForm + ), + name="machine_translation_pois", + ), + path( + "bulk-archive/", + bulk_action_views.BulkArchiveView.as_view( + model=POI + ), + name="bulk_archive_pois", + ), + path( + "bulk-restore/", + bulk_action_views.BulkRestoreView.as_view( + model=POI + ), + name="bulk_restore_pois", + ), + path( + "bulk-publish/", + bulk_action_views.BulkPublishingView.as_view( + model=POI, + ), + name="publish_multiple_pois", + ), + path( + "bulk-draft/", + bulk_action_views.BulkDraftingView.as_view( + model=POI, + ), + name="draft_multiple_pois", + ), + path( + "show-poi-form-ajax//", + pois.POIFormAjaxView.as_view(), + name="show_poi_form_ajax", + ), + path( + "create-poi-ajax/", + pois.POIFormAjaxView.as_view(), + name="create_poi_ajax", + ), + path( + "/", + include( + [ + path( + "view/", + pois.view_poi, + name="view_poi", + ), + path( + "edit/", + pois.POIFormView.as_view(), + name="edit_poi", + ), + path( + "archive/", + pois.archive_poi, + name="archive_poi", + ), + path( + "restore/", + pois.restore_poi, + name="restore_poi", + ), + path( + "delete/", + pois.delete_poi, + name="delete_poi", + ), + path( + "versions/", + include( + [ + path( + "", + pois.POIVersionView.as_view(), + name="poi_versions", + ), + path( + "/", + pois.POIVersionView.as_view(), + name="poi_versions", + ), + ], + ), + ), + ] + ), + ), + ] + ), + ), + ] + ), + ), + path( + "contact/", + include( + [ path( "", - pois.POIListView.as_view(), - name="pois", + contacts.ContactListView.as_view(), + name="contacts", ), path( "archived/", - pois.POIListView.as_view(archived=True), - name="archived_pois", + contacts.ContactListView.as_view(archived=True), + name="archived_contacts", ), path( "new/", - pois.POIFormView.as_view(), - name="new_poi", - ), - path( - "machine-translate/", - bulk_action_views.BulkMachineTranslationView.as_view(model=POI, form=POITranslationForm), - name="machine_translation_pois", + contacts.ContactFormView.as_view(), + name="new_contact", ), path( "bulk-archive/", - bulk_action_views.BulkArchiveView.as_view(model=POI), - name="bulk_archive_pois", + contacts.ArchiveContactBulkAction.as_view(), + name="bulk_archive_contacts", ), path( "bulk-restore/", - bulk_action_views.BulkRestoreView.as_view(model=POI), - name="bulk_restore_pois", + contacts.RestoreContactBulkAction.as_view(), + name="bulk_restore_contacts", ), path( - "bulk-publish/", - bulk_action_views.BulkPublishingView.as_view( - model=POI, - ), - name="publish_multiple_pois", + "bulk-delete/", + contacts.DeleteContactBulkAction.as_view(), + name="bulk_delete_contacts", ), path( - "bulk-draft/", - bulk_action_views.BulkDraftingView.as_view( - model=POI, + "/", + include( + [ + path( + "", + utils.get_contact, + name="get_contact", + ), + path( + "raw/", + utils.get_contact_raw, + name="get_contact_raw", + ), + path( + "edit/", + contacts.ContactFormView.as_view(), + name="edit_contact", + ), + path( + "copy/", + contacts.copy_contact, + name="copy_contact", + ), + path( + "archive/", + contacts.archive_contact, + name="archive_contact", + ), + path( + "restore/", + contacts.restore_contact, + name="restore_contact", + ), + path( + "delete/", + contacts.delete_contact, + name="delete_contact", + ), + ] ), - name="draft_multiple_pois", - ), - path( - "show-poi-form-ajax//", - pois.POIFormAjaxView.as_view(), - name="show_poi_form_ajax", - ), - path( - "create-poi-ajax/", - pois.POIFormAjaxView.as_view(), - name="create_poi_ajax", - ), - path( - "/", - include([ - path( - "view/", - pois.view_poi, - name="view_poi", - ), - path( - "edit/", - pois.POIFormView.as_view(), - name="edit_poi", - ), - path( - "archive/", - pois.archive_poi, - name="archive_poi", - ), - path( - "restore/", - pois.restore_poi, - name="restore_poi", - ), - path( - "delete/", - pois.delete_poi, - name="delete_poi", - ), - path( - "versions/", - include( - [ - path( - "", - pois.POIVersionView.as_view(), - name="poi_versions", - ), - path( - "/", - pois.POIVersionView.as_view(), - name="poi_versions", - ), - ], - ), - ), - ]), - ), - ]), - ), - ]), - ), - path( - "contact/", - include([ - path( - "", - contacts.ContactListView.as_view(), - name="contacts", - ), - path( - "archived/", - contacts.ContactListView.as_view(archived=True), - name="archived_contacts", - ), - path( - "new/", - contacts.ContactFormView.as_view(), - name="new_contact", - ), - path( - "bulk-archive/", - contacts.ArchiveContactBulkAction.as_view(), - name="bulk_archive_contacts", - ), - path( - "bulk-restore/", - contacts.RestoreContactBulkAction.as_view(), - name="bulk_restore_contacts", - ), - path( - "bulk-delete/", - contacts.DeleteContactBulkAction.as_view(), - name="bulk_delete_contacts", + ), + ] ), - path( - "/", - include([ + ), + path( + "feedback/", + include( + [ path( "", - utils.get_contact, - name="get_contact", + feedback.RegionFeedbackListView.as_view(), + name="region_feedback", ), path( - "raw/", - utils.get_contact_raw, - name="get_contact_raw", + "archived/", + feedback.RegionFeedbackListView.as_view(archived=True), + name="region_feedback_archived", ), path( - "edit/", - contacts.ContactFormView.as_view(), - name="edit_contact", + "mark-read/", + feedback.mark_region_feedback_as_read, + name="mark_region_feedback_as_read", ), path( - "copy/", - contacts.copy_contact, - name="copy_contact", + "mark-unread/", + feedback.mark_region_feedback_as_unread, + name="mark_region_feedback_as_unread", ), path( "archive/", - contacts.archive_contact, - name="archive_contact", + feedback.archive_region_feedback, + name="archive_region_feedback", ), path( "restore/", - contacts.restore_contact, - name="restore_contact", + feedback.restore_region_feedback, + name="restore_region_feedback", ), path( "delete/", - contacts.delete_contact, - name="delete_contact", + feedback.delete_region_feedback, + name="delete_region_feedback", ), - ]), - ), - ]), - ), - path( - "feedback/", - include([ - path( - "", - feedback.RegionFeedbackListView.as_view(), - name="region_feedback", - ), - path( - "archived/", - feedback.RegionFeedbackListView.as_view(archived=True), - name="region_feedback_archived", - ), - path( - "mark-read/", - feedback.mark_region_feedback_as_read, - name="mark_region_feedback_as_read", - ), - path( - "mark-unread/", - feedback.mark_region_feedback_as_unread, - name="mark_region_feedback_as_unread", - ), - path( - "archive/", - feedback.archive_region_feedback, - name="archive_region_feedback", - ), - path( - "restore/", - feedback.restore_region_feedback, - name="restore_region_feedback", - ), - path( - "delete/", - feedback.delete_region_feedback, - name="delete_region_feedback", - ), - path( - "export//", - feedback.export_region_feedback, - name="export_region_feedback", - ), - ]), - ), - path( - "push-notifications/", - include([ - path( - "", - push_notifications.PushNotificationListView.as_view(), - name="push_notifications", + path( + "export//", + feedback.export_region_feedback, + name="export_region_feedback", + ), + ] ), - path( - "/", - include([ + ), + path( + "push-notifications/", + include( + [ path( "", push_notifications.PushNotificationListView.as_view(), name="push_notifications", ), path( - "templates/", - push_notifications.PushNotificationListView.as_view(templates=True), - name="push_notifications_templates", + "/", + include( + [ + path( + "", + push_notifications.PushNotificationListView.as_view(), + name="push_notifications", + ), + path( + "templates/", + push_notifications.PushNotificationListView.as_view( + templates=True + ), + name="push_notifications_templates", + ), + path( + "new/", + push_notifications.PushNotificationFormView.as_view(), + name="new_push_notification", + ), + path( + "/", + include( + [ + path( + "edit/", + push_notifications.PushNotificationFormView.as_view(), + name="edit_push_notification", + ), + ] + ), + ), + ] + ), + ), + ] + ), + ), + path( + "language-tree/", + include( + [ + path( + "", + language_tree.LanguageTreeView.as_view(), + name="languagetreenodes", ), path( "new/", - push_notifications.PushNotificationFormView.as_view(), - name="new_push_notification", + language_tree.LanguageTreeNodeCreateView.as_view(), + name="new_languagetreenode", ), path( - "/", - include([ - path( - "edit/", - push_notifications.PushNotificationFormView.as_view(), - name="edit_push_notification", - ), - ]), + "bulk-make-visible/", + language_tree.BulkMakeVisibleView.as_view(), + name="bulk_make_languagetreenodes_visible", ), - ]), - ), - ]), - ), - path( - "language-tree/", - include([ - path( - "", - language_tree.LanguageTreeView.as_view(), - name="languagetreenodes", - ), - path( - "new/", - language_tree.LanguageTreeNodeCreateView.as_view(), - name="new_languagetreenode", - ), - path( - "bulk-make-visible/", - language_tree.BulkMakeVisibleView.as_view(), - name="bulk_make_languagetreenodes_visible", - ), - path( - "bulk-hide/", - language_tree.BulkHideView.as_view(), - name="bulk_hide_languagetreenodes", - ), - path( - "bulk-activate/", - language_tree.BulkActivateView.as_view(), - name="bulk_activate_languagetreenodes", - ), - path( - "bulk-disable/", - language_tree.BulkDisableView.as_view(), - name="bulk_disable_languagetreenodes", - ), - path( - "/", - include([ path( - "edit/", - language_tree.LanguageTreeNodeUpdateView.as_view(form_class=LanguageTreeNodeForm), - name="edit_languagetreenode", + "bulk-hide/", + language_tree.BulkHideView.as_view(), + name="bulk_hide_languagetreenodes", ), path( - "delete/", - language_tree.delete_language_tree_node, - name="delete_languagetreenode", + "bulk-activate/", + language_tree.BulkActivateView.as_view(), + name="bulk_activate_languagetreenodes", ), - # warning: the move url is also hardcoded in src/integreat_cms/static/js/tree_drag_and_drop.js path( - "move///", - language_tree.move_language_tree_node, - name="move_languagetreenode", + "bulk-disable/", + language_tree.BulkDisableView.as_view(), + name="bulk_disable_languagetreenodes", ), - ]), - ), - ]), - ), - path("media-library/", media.MediaListView.as_view(), name="media"), - path( - "users/", - include([ - path( - "", - users.RegionUserListView.as_view(), - name="region_users", - ), - path( - "new/", - users.RegionUserFormView.as_view(), - name="new_region_user", + path( + "/", + include( + [ + path( + "edit/", + language_tree.LanguageTreeNodeUpdateView.as_view( + form_class=LanguageTreeNodeForm + ), + name="edit_languagetreenode", + ), + path( + "delete/", + language_tree.delete_language_tree_node, + name="delete_languagetreenode", + ), + # warning: the move url is also hardcoded in src/integreat_cms/static/js/tree_drag_and_drop.js + path( + "move///", + language_tree.move_language_tree_node, + name="move_languagetreenode", + ), + ] + ), + ), + ] ), - path( - "/", - include([ + ), + path("media-library/", media.MediaListView.as_view(), name="media"), + path( + "users/", + include( + [ path( - "edit/", - users.RegionUserFormView.as_view(), - name="edit_region_user", + "", + users.RegionUserListView.as_view(), + name="region_users", ), path( - "delete/", - users.delete_region_user, - name="delete_region_user", + "new/", + users.RegionUserFormView.as_view(), + name="new_region_user", ), path( - "resend-activation-link/", - users.resend_activation_link_region, - name="resend_activation_link_region", + "/", + include( + [ + path( + "edit/", + users.RegionUserFormView.as_view(), + name="edit_region_user", + ), + path( + "delete/", + users.delete_region_user, + name="delete_region_user", + ), + path( + "resend-activation-link/", + users.resend_activation_link_region, + name="resend_activation_link_region", + ), + ] + ), ), - ]), + ] ), - ]), - ), - path("", include(user_settings_urlpatterns)), - path( - "release-notes/", - release_notes.ReleaseNotesView.as_view(), - name="release_notes", - ), - ]), + ), + path("", include(user_settings_urlpatterns)), + path( + "release-notes/", + release_notes.ReleaseNotesView.as_view(), + name="release_notes", + ), + ] + ), ), ] diff --git a/integreat_cms/cms/views/pages/page_tree_view.py b/integreat_cms/cms/views/pages/page_tree_view.py index 62b7d633a8..51cb27f23d 100644 --- a/integreat_cms/cms/views/pages/page_tree_view.py +++ b/integreat_cms/cms/views/pages/page_tree_view.py @@ -130,11 +130,11 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ) # When only archived pages are requested, remove implicitly archived pages since they are handeld by loading the partial page tree - if self.archived: - archived_pages = region.archived_pages - for page in archived_pages: - if page.implicitly_archived: - pages.remove(page) + # if self.archived: + # archived_pages = region.archived_pages + # for page in archived_pages: + # if page.implicitly_archived: + # pages.remove(page) # Filter pages according to given filters, if any pages = filter_form.apply(pages, language_slug) diff --git a/integreat_cms/cms/views/statistics/statistics_view.py b/integreat_cms/cms/views/statistics/statistics_view.py index 53e9aafa6e..d1da944013 100644 --- a/integreat_cms/cms/views/statistics/statistics_view.py +++ b/integreat_cms/cms/views/statistics/statistics_view.py @@ -33,7 +33,9 @@ class AnalyticsView(TemplateView): #: The context dict passed to the template (see :class:`~django.views.generic.base.ContextMixin`) extra_context = {"current_menu_item": "statistics"} - def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponseRedirect: + def get( + self, request: HttpRequest, *args: Any, **kwargs: Any + ) -> HttpResponseRedirect: r""" Render statistics of access numbers tracked by Matomo @@ -58,7 +60,11 @@ def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponseRe page_queryset = region.pages.filter(lft=1) # Cache tree structure to reduce database queries - pages = page_queryset.prefetch_major_translations().prefetch_related("mirroring_pages").cache_tree(archived=False) + pages = ( + page_queryset.prefetch_major_translations() + .prefetch_related("mirroring_pages") + .cache_tree(archived=False) + ) return render( request, From 7af0938d4d8c7d26ec0070fbc74ed59aaf5f886b Mon Sep 17 00:00:00 2001 From: JoeyStk Date: Sat, 11 Jan 2025 18:20:57 +0100 Subject: [PATCH 51/52] Fix code styles --- .../cms/templates/pages/_generic_page_tree_node.html | 10 +++++----- .../cms/templates/pages/_page_tree_children.html | 2 +- .../templates/statistics/statistics_viewed_pages.html | 8 +++++--- .../statistics/statistics_viewed_pages_node.html | 6 +++--- .../cms/views/pages/partial_page_tree_view.py | 8 +++++++- 5 files changed, 21 insertions(+), 13 deletions(-) diff --git a/integreat_cms/cms/templates/pages/_generic_page_tree_node.html b/integreat_cms/cms/templates/pages/_generic_page_tree_node.html index 14981e120d..b26ff405be 100644 --- a/integreat_cms/cms/templates/pages/_generic_page_tree_node.html +++ b/integreat_cms/cms/templates/pages/_generic_page_tree_node.html @@ -26,11 +26,11 @@ {% if not is_archive %} {% if not page.parent_id and not page.is_leaf or page.parent_id and page.cached_children|length > 0 %} + data-expand-title="{% translate "Expand all subpages" %}" + data-collapse-title="{% translate "Collapse all subpages" %}" + data-page-id="{{ page.id }}" + data-page-children="{{ page|get_children_ids }}" + data-page-tree-id="{{ page.tree_id }}"> {% endif %} diff --git a/integreat_cms/cms/templates/pages/_page_tree_children.html b/integreat_cms/cms/templates/pages/_page_tree_children.html index f013415790..da94b4d5de 100644 --- a/integreat_cms/cms/templates/pages/_page_tree_children.html +++ b/integreat_cms/cms/templates/pages/_page_tree_children.html @@ -3,7 +3,7 @@
diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html index e8353e908e..0a1d918b23 100644 --- a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -15,7 +15,8 @@ data-page-accesses-url="{% url 'statistics_page_based_accesses' region_slug=request.region.slug %}"> {% csrf_token %}
- +
{% include "pages/_generic_page_tree.html" %} diff --git a/integreat_cms/cms/urls/protected.py b/integreat_cms/cms/urls/protected.py index 5fcb73f785..c17d4d651e 100644 --- a/integreat_cms/cms/urls/protected.py +++ b/integreat_cms/cms/urls/protected.py @@ -651,9 +651,16 @@ include( [ path( - "partial-page-tree/", - pages.render_partial_page_tree_views, - name="get_page_tree_ajax", + "/", + include( + [ + path( + "partial-page-tree/", + pages.render_partial_page_tree_views, + name="get_page_tree_ajax", + ), + ] + ), ), ] ), diff --git a/integreat_cms/cms/views/pages/partial_page_tree_view.py b/integreat_cms/cms/views/pages/partial_page_tree_view.py index 72e4b98715..f9fffb67d8 100644 --- a/integreat_cms/cms/views/pages/partial_page_tree_view.py +++ b/integreat_cms/cms/views/pages/partial_page_tree_view.py @@ -18,7 +18,11 @@ @require_POST # pylint: disable=unused-argument, too-many-locals def render_partial_page_tree_views( - request: HttpRequest, region_slug: str, language_slug: str, is_archive: str + request: HttpRequest, + region_slug: str, + language_slug: str, + is_archive: str, + is_statistics: str, ) -> JsonResponse: r""" Retrieve the rendered subtree of a given root page @@ -37,6 +41,9 @@ def render_partial_page_tree_views( # Convert is_archive from String to Boolean is_archive = literal_eval(is_archive) + # Convert is_statistics from String to Boolean + is_statistics = literal_eval(is_statistics) + backend_language = Language.objects.filter(slug=get_language()).first() all_pages = ( diff --git a/integreat_cms/cms/views/statistics/statistics_view.py b/integreat_cms/cms/views/statistics/statistics_view.py index df251c4d7c..b20ab02854 100644 --- a/integreat_cms/cms/views/statistics/statistics_view.py +++ b/integreat_cms/cms/views/statistics/statistics_view.py @@ -67,5 +67,6 @@ def get( "region": region, "language": region.default_language, "languages": region.active_languages, + "is_statistics": True, }, ) From 9305396027635bc5a5e9d3fdcd4ca418b259fb31 Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Tue, 7 Jan 2025 15:58:40 +0000 Subject: [PATCH 47/52] Upgrade lucide dependicy --- package-lock.json | 20 +++++++++++--------- package.json | 4 ++-- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3fc61817af..c15f54d28e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,10 +1,9 @@ { - "name": "integreat-cms", + "name": "workspace", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "integreat-cms", "devDependencies": { "@babel/cli": "^7.24.7", "@babel/core": "^7.24.7", @@ -45,8 +44,8 @@ "htmldiff-js": "1.0.5", "jest": "^29.7.0", "jsdom": "^24.1.0", - "lucide": "^0.383.0", - "lucide-preact": "^0.383.0", + "lucide": "^0.469.0", + "lucide-preact": "^0.469.0", "maplibre-gl": "^4.3.2", "mini-css-extract-plugin": "^2.9.0", "postcss-cli": "^11.0.0", @@ -4843,6 +4842,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@orchidjs/sifter/-/sifter-1.0.3.tgz", "integrity": "sha512-zCZbwKegHytfsPm8Amcfh7v/4vHqTAaOu6xFswBYcn8nznBOuseu6COB2ON7ez0tFV0mKL0nRNnCiZZA+lU9/g==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@orchidjs/unicode-variants": "^1.0.4" @@ -4852,6 +4852,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/@orchidjs/unicode-variants/-/unicode-variants-1.0.4.tgz", "integrity": "sha512-NvVBRnZNE+dugiXERFsET1JlKZfM5lJDEpSMilKW4bToYJ7pxf0Zne78xyXB2ny2c2aHfJ6WLnz1AaTNHAmQeQ==", + "dev": true, "license": "Apache-2.0" }, "node_modules/@pkgjs/parseargs": { @@ -13595,15 +13596,15 @@ } }, "node_modules/lucide": { - "version": "0.383.0", - "resolved": "https://registry.npmjs.org/lucide/-/lucide-0.383.0.tgz", - "integrity": "sha512-9hmwt/UPcfxUh2cVWBV9vOagFsWPFQsGUkp2XBlwFmMX6zoj3TLrs+HknihuSotnHuWB9pvhm+XUB7C5DOINRQ==", + "version": "0.469.0", + "resolved": "https://registry.npmjs.org/lucide/-/lucide-0.469.0.tgz", + "integrity": "sha512-ZmMtOekJ6g1UFqTvV9aWbkk+u/QxpfDpgUrbNj8jJEK+CtBdHVQ4akCA0TcTyeOvnL8RWo271UzFTimBRT0gKw==", "dev": true, "license": "ISC" }, "node_modules/lucide-preact": { - "version": "0.383.0", - "resolved": "https://registry.npmjs.org/lucide-preact/-/lucide-preact-0.383.0.tgz", + "version": "0.469.0", + "resolved": "https://registry.npmjs.org/lucide-preact/-/lucide-preact-0.469.0.tgz", "integrity": "sha512-Dr6VWSRy98W1WVVjinWDxXPFDkljZv/CemVUMDQj1Xjz89q3YQuHLy9piRls3J7FMxSMmJ+Euf/9Pf0smicwdA==", "dev": true, "license": "ISC", @@ -18217,6 +18218,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/tom-select/-/tom-select-2.3.1.tgz", "integrity": "sha512-QS4vnOcB6StNGqX4sGboGXL2fkhBF2gIBB+8Hwv30FZXYPn0CyYO8kkdATRvwfCTThxiR4WcXwKJZ3cOmtI9eg==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@orchidjs/sifter": "^1.0.3", diff --git a/package.json b/package.json index a56ee65cc6..f0bbc086c2 100644 --- a/package.json +++ b/package.json @@ -47,8 +47,8 @@ "htmldiff-js": "1.0.5", "jest": "^29.7.0", "jsdom": "^24.1.0", - "lucide": "^0.383.0", - "lucide-preact": "^0.383.0", + "lucide": "^0.469.0", + "lucide-preact": "^0.469.0", "maplibre-gl": "^4.3.2", "mini-css-extract-plugin": "^2.9.0", "postcss-cli": "^11.0.0", From b183cc51e00f7cdc237b488997c590091baafc29 Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Wed, 8 Jan 2025 10:39:08 +0000 Subject: [PATCH 48/52] Update translations --- integreat_cms/locale/de/LC_MESSAGES/django.po | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index 9dc8466c1e..20babc66e9 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -1681,19 +1681,11 @@ msgstr "Aktiv" msgid "Hidden" msgstr "Versteckt" -<<<<<<< HEAD #: cms/constants/region_status.py cms/models/contact/contact.py #: cms/models/pages/page_translation.py cms/models/regions/region.py #: cms/templates/events/event_form.html cms/templates/pages/page_form.html -#: cms/templates/pages/page_tree_archived_node.html -#: cms/templates/pages/page_tree_node.html cms/templates/pois/poi_form.html -======= -#: cms/constants/region_status.py cms/models/pages/page_translation.py -#: cms/models/regions/region.py cms/templates/events/event_form.html -#: cms/templates/pages/page_form.html #: cms/templates/pages/pages_page_tree_node.html #: cms/templates/pois/poi_form.html ->>>>>>> 718358e82 (Cleaning up and deleting page_tree_archived files) msgid "Archived" msgstr "Archiviert" @@ -7107,8 +7099,8 @@ msgid "Change the order and position of the pages with drag & drop." msgstr "Ändern Sie die Reihenfolge und Position der Seiten mit Drag & Drop." #: cms/templates/pages/_generic_page_tree_node.html -msgid "Drag & drop is disabled for archived pages." -msgstr "Drag & drop ist für archivierte Seiten nicht möglich." +msgid "Drag & drop is disabled for archived pages and statistics." +msgstr "Drag & drop ist für archivierte Seiten und in Statistiken nicht möglich." #: cms/templates/pages/_generic_page_tree_node.html msgid "Expand all subpages" @@ -9242,8 +9234,8 @@ msgstr "Die Version {} existiert nicht." msgid "" "%s %s can not change its status as it was imported from an external calendar" msgstr "" -"Der Status von %s %s kann nicht geändert " -"werden, da diese Veranstaltung aus einem externen Kalender importiert wurde" +"Der Status von %s %s kann nicht geändert werden, da diese Veranstaltung aus " +"einem externen Kalender importiert wurde" #: cms/views/content_version_view.py msgid "You cannot reject changes if there is no version to return to." @@ -11192,6 +11184,7 @@ msgstr "" #~ msgid "%s with %s" #~ msgstr "%s mit %s" + #~ msgid "No archived pages found with these filters." #~ msgstr "Keine archivierten Seiten mit diesen Filtern gefunden." From 29809d12cca1bd404b65d0b15ec05fd356252b2d Mon Sep 17 00:00:00 2001 From: jarlhengstmengel Date: Wed, 8 Jan 2025 14:41:28 +0000 Subject: [PATCH 49/52] Fix page tree --- .../templates/pages/_page_tree_children.html | 6 ++++- .../statistics/statistics_viewed_pages.html | 24 ++--------------- .../statistics_viewed_pages_node.html | 26 +++++++++++++++++++ .../cms/views/pages/partial_page_tree_view.py | 1 + .../cms/views/statistics/statistics_view.py | 7 ++++- integreat_cms/locale/de/LC_MESSAGES/django.po | 22 ++++++---------- 6 files changed, 48 insertions(+), 38 deletions(-) create mode 100644 integreat_cms/cms/templates/statistics/statistics_viewed_pages_node.html diff --git a/integreat_cms/cms/templates/pages/_page_tree_children.html b/integreat_cms/cms/templates/pages/_page_tree_children.html index b084ca457e..f013415790 100644 --- a/integreat_cms/cms/templates/pages/_page_tree_children.html +++ b/integreat_cms/cms/templates/pages/_page_tree_children.html @@ -3,7 +3,11 @@
{% for page in pages %} - {% include "pages/pages_page_tree_node.html" %} + {%if is_statistics %} + {% include "statistics/statistics_viewed_pages_node.html" %} + {% else %} + {% include "pages/pages_page_tree_node.html" %} + {% endif %} {% endfor %}
diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html index f54ca131f8..2e2a6cb0b4 100644 --- a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -25,7 +25,7 @@ - @@ -37,27 +37,7 @@ {% for page in pages %} - - {% include "pages/_generic_page_tree_node.html" %} - - - + {% include "statistics/statistics_viewed_pages_node.html" with is_archive=is_archive%} {% endfor %}
-
- {% translate "Loading..." %} -
- {% for language in languages %} - - - {% endfor %} -
-
diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages_node.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages_node.html new file mode 100644 index 0000000000..83c9577b47 --- /dev/null +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages_node.html @@ -0,0 +1,26 @@ +{% load i18n %} +{% load content_filters %} +{% load page_filters %} +{% load tree_filters %} +{% load rules %} +
+
+ {% translate "Loading..." %} +
+ {% for language in languages %} + + + {% endfor %} +
+
{% for page in pages %} - {%if is_statistics %} + {% if is_statistics %} {% include "statistics/statistics_viewed_pages_node.html" %} {% else %} {% include "pages/pages_page_tree_node.html" %} diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html index 2e2a6cb0b4..5d4231efe8 100644 --- a/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages.html @@ -25,8 +25,10 @@ -
+
{% include "pages/_generic_page_tree.html" %} @@ -37,7 +39,7 @@ {% for page in pages %} - {% include "statistics/statistics_viewed_pages_node.html" with is_archive=is_archive%} + {% include "statistics/statistics_viewed_pages_node.html" with is_archive=is_archive %} {% endfor %}
diff --git a/integreat_cms/cms/templates/statistics/statistics_viewed_pages_node.html b/integreat_cms/cms/templates/statistics/statistics_viewed_pages_node.html index 83c9577b47..65742ccbbd 100644 --- a/integreat_cms/cms/templates/statistics/statistics_viewed_pages_node.html +++ b/integreat_cms/cms/templates/statistics/statistics_viewed_pages_node.html @@ -15,12 +15,12 @@
{% for language in languages %} + data-language-slug="{{ language.slug }}" + class="cursor-pointer bg-[{{ language.language_color }}] h-9"> {% endfor %}