digitalfabrik/integreat-cms

View on GitHub
integreat_cms/cms/views/regions/region_actions.py

Summary

Maintainability
A
0 mins
Test Coverage
A
95%
"""
This module contains view actions for region objects.
"""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

from django.contrib import messages
from django.contrib.auth import get_user_model
from django.shortcuts import get_object_or_404, redirect
from django.utils.html import format_html, format_html_join
from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_POST
from linkcheck.listeners import disable_listeners

from ...decorators import permission_required
from ...models import Contact, Page, PushNotification, Region

if TYPE_CHECKING:
    from typing import Any

    from django.http import HttpRequest, HttpResponseRedirect

logger = logging.getLogger(__name__)


@require_POST
@permission_required("cms.delete_region")
def delete_region(
    request: HttpRequest, *args: Any, **kwargs: Any
) -> HttpResponseRedirect:
    r"""
    This view deletes a region. All content is cascade deleted. Region users, who are not assigned to any other region,
    are manually removed.

    :param request: The current request
    :param \*args: The supplied arguments
    :param \**kwargs: The supplied keyword arguments
    :return: A redirection to the media library
    """

    region = get_object_or_404(Region, slug=kwargs.get("slug"))

    # Check whether region can be safely deleted
    mirrored_pages = Page.objects.filter(
        region=region, mirroring_pages__isnull=False
    ).distinct()
    if len(mirrored_pages) > 0:
        messages.error(
            request,
            format_html(
                "{}<ul class='list-disc pl-4'>{}</ul>",
                _(
                    "Region could not be deleted, because the following pages are mirrored in other regions:"
                ),
                format_html_join(
                    "\n",
                    "<li><a href='{}' class='underline hover:no-underline'>{}</a> ({} {})</li>",
                    [
                        (
                            page.best_translation.backend_edit_link,
                            page.best_translation.title,
                            _("is mirrored here:"),
                            format_html_join(
                                ", ",
                                "<a href='{}' class='underline hover:no-underline'>{}</a>",
                                [
                                    (
                                        mirrored_page.best_translation.backend_edit_link,
                                        mirrored_page.best_translation.title,
                                    )
                                    for mirrored_page in page.mirroring_pages.all()
                                ],
                            ),
                        )
                        for page in mirrored_pages
                    ],
                ),
            ),
        )
        return redirect(
            "edit_region",
            **{
                "slug": region.slug,
            },
        )

    # Remove hierarchy to prevent ProtectedError when children get deleted before their parents
    region.pages.update(parent=None)
    region.language_tree_nodes.update(parent=None)
    region.media_directories.update(parent=None)
    region.files.update(parent_directory=None)
    # Prevent ProtectedError when location gets deleted before their events
    region.events.update(location=None)
    # Prevent ProtectedError when media files get deleted before their usages as organization logo
    region.organizations.all().delete()
    # Prevent ProtectedError when location gets deleted before their contacts
    Contact.objects.filter(location__region=region).delete()
    # Prevent IntegrityError when multiple feedback objects exist
    region.feedback.all().delete()
    # Disable linkchecking while deleting this region
    # Active linkchecking would drastically slow performance and the links will be fixed with the next run of the findlinks management command anyway
    with disable_listeners():
        # Delete region and cascade delete all contents
        deleted_objects = region.delete()
    logger.info(
        "%r deleted %r, cascade deleted objects: %r",
        request.user,
        region,
        deleted_objects,
    )
    # Get orphan users who aren't superuser or staff and don't have a region assigned
    # (Creating users with these combination is impossible, so they were region users of the deleted region before)
    orphan_users = get_user_model().objects.filter(
        is_superuser=False, is_staff=False, regions=None
    )
    if orphan_users.exists():
        logger.info(
            "Deleting orphan users: %r",
            orphan_users,
        )
        orphan_users.delete()
    # Get orphan push notifications
    orphan_pns = PushNotification.objects.filter(regions=None)
    if orphan_pns.exists():
        logger.info(
            "Deleting orphan PushNotifications: %r",
            orphan_pns,
        )
        orphan_pns.delete()

    messages.success(request, _("Region was successfully deleted"))

    return redirect("regions")