digitalfabrik/integreat-cms

View on GitHub
integreat_cms/cms/views/pages/page_bulk_actions.py

Summary

Maintainability
A
0 mins
Test Coverage
D
63%
from __future__ import annotations

import logging
from typing import TYPE_CHECKING

from django.contrib import messages
from django.shortcuts import get_object_or_404
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext_lazy
from django.views.decorators.cache import never_cache
from django.views.generic.list import MultipleObjectMixin

from ....xliff.utils import pages_to_xliff_file
from ...constants import translation_status
from ...models import Page
from ...utils.pdf_utils import generate_pdf
from ...utils.stringify_list import iter_to_string
from ...utils.translation_utils import gettext_many_lazy as __
from ...utils.translation_utils import translate_link
from ..bulk_action_views import BulkActionView
from .page_actions import cancel_translation_process_ajax

if TYPE_CHECKING:
    from typing import Any

    from django.http import HttpRequest, HttpResponse
    from django.http.response import HttpResponseRedirect

logger = logging.getLogger(__name__)


class PageBulkActionMixin(MultipleObjectMixin):
    """
    Mixin for page bulk actions
    """

    #: The model of this :class:`~integreat_cms.cms.views.bulk_action_views.BulkActionView`
    model = Page


class GeneratePdfView(PageBulkActionMixin, BulkActionView):
    """
    Bulk action for generating a PDF document of the content
    """

    #: Whether the view requires change permissions
    require_change_permission = False
    #: Whether the public translation objects should be prefetched
    prefetch_public_translations = True

    @method_decorator(never_cache)
    def post(
        self, request: HttpRequest, *args: Any, **kwargs: Any
    ) -> HttpResponseRedirect:
        r"""
        Apply the bulk action on every item in the queryset and redirect

        :param request: The current request
        :param \*args: The supplied arguments
        :param \**kwargs: The supplied keyword arguments
        :return: The redirect
        """
        # Generate PDF document and redirect to it
        return generate_pdf(
            request.region,
            str(kwargs.get("language_slug")),
            self.get_queryset(),
        )


class ExportXliffView(PageBulkActionMixin, BulkActionView):
    """
    Bulk action for generating XLIFF files for translations
    """

    #: Whether only public translation should be exported
    only_public = False
    #: Whether the view requires change permissions
    require_change_permission = False

    def post(
        self, request: HttpRequest, *args: Any, **kwargs: Any
    ) -> HttpResponseRedirect:
        r"""
        Function for handling a XLIFF export request for pages.
        The pages get extracted from request.GET attribute and the request is forwarded to :func:`~integreat_cms.xliff.utils.pages_to_xliff_file`

        :param request: The current request
        :param \*args: The supplied arguments
        :param \**kwargs: The supplied keyword arguments
        :return: The redirect
        """
        target_language = get_object_or_404(
            self.request.region.language_tree_nodes,
            language__slug=kwargs.get("language_slug"),
            parent__isnull=False,
        ).language

        if xliff_file_url := pages_to_xliff_file(
            request,
            self.get_queryset(),
            [target_language],
            only_public=self.only_public,
        ):
            # Insert link with automatic download into success message
            message = __(
                (
                    _(
                        "XLIFF file with published pages only for translation to {} successfully created."
                    )
                    if self.only_public
                    else _(
                        "XLIFF file with unpublished and published pages for translation to {} successfully created."
                    )
                ).format(target_language),
                _(
                    "If the download does not start automatically, please click <a>here</a>.",
                ),
            )
            messages.success(
                request,
                translate_link(
                    message,
                    attributes={
                        "href": xliff_file_url,
                        "class": "font-bold underline hover:no-underline",
                        "data-auto-download": "",
                        "download": "",
                    },
                ),
            )

        # Let the base view handle the redirect
        return super().post(request, *args, **kwargs)


class ExportMultiLanguageXliffView(PageBulkActionMixin, BulkActionView):
    """
    Bulk action for generating XLIFF files for translations in multiple languages.
    """

    #: Whether the view requires change permissions
    require_change_permission = False

    def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
        r"""
        Function for handling a XLIFF export request for pages and multiple languages.
        The pages get extracted from request.GET attribute and the request is forwarded to :func:`~integreat_cms.xliff.utils.pages_to_xliff_file`

        :param request: The current request
        :param \*args: The supplied arguments
        :param \**kwargs: The supplied keyword arguments
        :return: The redirect
        """

        target_languages = [
            language
            for language in self.request.region.active_languages
            if language.slug in self.request.POST.getlist("selected_language_slugs[]")
        ]

        if xliff_file_url := pages_to_xliff_file(
            request, self.get_queryset(), target_languages, only_public=False
        ):
            # Insert link with automatic download into success message
            message = __(
                _(
                    "XLIFF file for translation to selected languages successfully created."
                ),
                _(
                    "If the download does not start automatically, please click <a>here</a>.",
                ),
            )
            messages.success(
                request,
                translate_link(
                    message,
                    attributes={
                        "href": xliff_file_url,
                        "class": "font-bold underline hover:no-underline",
                        "data-auto-download": "",
                        "download": "",
                    },
                ),
            )

        # Let the base view handle the redirect
        return super().post(request, *args, **kwargs)


class CancelTranslationProcess(PageBulkActionMixin, BulkActionView):
    """
    Bulk action to cancel translation process
    """

    def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
        r"""
        Function to cancel the translation process for multiple pages of the current language at once

        :param request: The current request
        :param \*args: The supplied arguments
        :param \**kwargs: The supplied keyword arguments
        :return: The redirect
        """

        language_slug = kwargs["language_slug"]
        not_in_translation = []
        cancel_successful = []
        cancel_failed = []

        for content_object in self.get_queryset():
            if (
                not content_object.get_translation_state(language_slug)
                == translation_status.IN_TRANSLATION
            ):
                not_in_translation.append(content_object.best_translation.title)
            else:
                cancelation_response = cancel_translation_process_ajax(
                    request,
                    region_slug=content_object.region,
                    language_slug=language_slug,
                    page_id=content_object.id,
                )
                if cancelation_response.status_code == 200:
                    cancel_successful.append(content_object.best_translation.title)
                if cancelation_response.status_code == 404:
                    cancel_failed.append(content_object.best_translation.title)

        if not_in_translation:
            messages.success(
                request,
                ngettext_lazy(
                    "{model_name} {object_names} was not in translation process.",
                    "The following {model_name_plural} were not in translation process: {object_names}",
                    len(not_in_translation),
                ).format(
                    model_name=self.model._meta.verbose_name.title(),
                    model_name_plural=self.model._meta.verbose_name_plural,
                    object_names=iter_to_string(not_in_translation),
                ),
            )
        if cancel_successful:
            messages.success(
                request,
                ngettext_lazy(
                    "Translation process was successfully cancelled for {model_name} {object_names}.",
                    "Translation process was successfully cancelled for the following {model_name_plural}: {object_names}",
                    len(cancel_successful),
                ).format(
                    model_name=self.model._meta.verbose_name.title(),
                    model_name_plural=self.model._meta.verbose_name_plural,
                    object_names=iter_to_string(cancel_successful),
                ),
            )

        if cancel_failed:
            messages.error(
                request,
                ngettext_lazy(
                    "Translation process could not be successfully cancelled for {model_name} {object_names}.",
                    "Translation process could not be successfully cancelled for the following {model_name_plural}: {object_names}",
                    len(cancel_failed),
                ).format(
                    model_name=self.model._meta.verbose_name.title(),
                    model_name_plural=self.model._meta.verbose_name_plural,
                    object_names=iter_to_string(cancel_failed),
                ),
            )
        return super().post(request, *args, **kwargs)