digitalfabrik/integreat-cms

View on GitHub
integreat_cms/cms/views/media/media_actions.py

Summary

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

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

from django.db.models import ProtectedError, Q
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_POST

from ....api.decorators import json_response
from ...decorators import permission_required
from ...forms import (
    CreateDirectoryForm,
    DirectoryForm,
    MediaFileForm,
    MediaMoveForm,
    ReplaceMediaFileForm,
    UploadMediaFileForm,
)
from ...models import Directory, MediaFile

if TYPE_CHECKING:
    from typing import Any

    from django.http import HttpRequest

logger = logging.getLogger(__name__)


@permission_required("cms.view_directory")
@permission_required("cms.view_mediafile")
@json_response
# pylint: disable=unused-argument
def get_directory_path_ajax(
    request: HttpRequest, region_slug: str | None = None
) -> JsonResponse:
    """
    View provides the frontend with the current directory path for the breadcrumbs.

    :param request: The current request
    :param region_slug: The slug of the current region
    :return: JSON response which indicates error or success
    """
    region = request.region

    directory_path: list[dict[str, Any]] = []

    if request.GET.get("directory"):
        directory = get_object_or_404(
            Directory.objects.filter(Q(region=region) | Q(region__isnull=True)),
            id=request.GET.get("directory"),
        )

        while directory:
            directory_path.insert(0, directory.serialize())
            directory = directory.parent

    return JsonResponse({"data": directory_path})


@permission_required("cms.view_directory")
@permission_required("cms.view_mediafile")
@json_response
# pylint: disable=unused-argument
def get_directory_content_ajax(
    request: HttpRequest, region_slug: str | None = None
) -> JsonResponse:
    """
    View provides the frontend with the content of a directory via AJAX.

    :param request: The current request
    :param region_slug: The slug of the current region
    :return: JSON response which indicates error or success
    """
    region = request.region

    directory = None
    if request.GET.get("directory"):
        directory = get_object_or_404(
            Directory.objects.filter(
                Q(region=region) | Q(region__isnull=True, is_hidden=False),
            ),
            id=request.GET.get("directory"),
        )

    media_files = MediaFile.objects.filter(
        Q(region=region) | Q(region__isnull=True, is_hidden=False),
        Q(parent_directory=directory),
    )
    directories = Directory.objects.filter(
        Q(region=region) | Q(region__isnull=True, is_hidden=False), parent=directory
    )

    result = [d.serialize() for d in list(directories) + list(media_files)]

    return JsonResponse({"data": result})


@json_response
@permission_required("cms.view_directory")
@permission_required("cms.view_mediafile")
# pylint: disable=unused-argument
def get_query_search_results_ajax(
    request: HttpRequest, region_slug: str | None = None
) -> JsonResponse:
    """
    View to search the media library

    :param request: The current request
    :param region_slug: The slug of the current region
    :return: JSON response with the search result
    """
    query = request.GET.get("query")
    logger.debug("Media library searched with query %r", query)
    region = request.region

    media_files = MediaFile.search(region, query)
    directories = Directory.search(region, query)
    result = [d.serialize() for d in list(directories) + list(media_files)]

    logger.debug("Media library search results: %r", result)
    return JsonResponse({"data": result})


@json_response
@permission_required("cms.view_mediafile")
# pylint: disable=unused-argument
def get_file_usages_ajax(
    request: HttpRequest, region_slug: str | None = None
) -> JsonResponse:
    """
    View to search unused media files

    :param request: The current request
    :param region_slug: The slug of the current region
    :return: JSON response with the search result
    """

    file = get_object_or_404(
        MediaFile.objects.filter(
            Q(region=request.region) | Q(region__isnull=True, is_hidden=False),
        ),
        id=request.GET.get("file"),
    )

    return JsonResponse({"data": file.serialize_usages()})


@json_response
@permission_required("cms.view_directory")
@permission_required("cms.view_mediafile")
# pylint: disable=unused-argument
def get_unused_media_files_ajax(
    request: HttpRequest, region_slug: str | None = None
) -> JsonResponse:
    """
    View to search unused media files

    :param request: The current request
    :param region_slug: The slug of the current region
    :return: JSON response with the search result
    """

    unused_media_files = MediaFile.objects.filter(
        Q(region=request.region) | Q(region__isnull=True, is_hidden=False)
    ).filter_unused()

    result = [d.serialize() for d in unused_media_files]

    return JsonResponse({"data": result})


@require_POST
@permission_required("cms.upload_mediafile")
@json_response
# pylint: disable=unused-argument
def upload_file_ajax(
    request: HttpRequest, region_slug: str | None = None
) -> JsonResponse:
    """
    View to create a file via an AJAX upload.

    :param request: The current request
    :param region_slug: The slug of the current region
    :return: JSON response which indicates error or success
    """
    region = request.region

    media_file_form = UploadMediaFileForm(data=request.POST, files=request.FILES)

    # Set region so it can be checked whether it's the same like the parent directory
    media_file_form.instance.region = region

    if not media_file_form.is_valid():
        return HttpResponse(
            ". ".join(
                message["text"] for message in media_file_form.get_error_messages()
            ),
            status=400,
        )

    # Save form
    media_file = media_file_form.save()

    return JsonResponse(
        {
            "messages": [
                {
                    "type": "success",
                    "text": _('File "{}" was uploaded successfully').format(
                        media_file.name
                    ),
                }
            ],
            "file": media_file.serialize(),
        }
    )


@require_POST
@permission_required("cms.change_mediafile")
@json_response
# pylint: disable=unused-argument
def edit_file_ajax(
    request: HttpRequest, region_slug: str | None = None
) -> JsonResponse:
    """
    View provides the edit of a file via AJAX.

    :param request: The current request
    :param region_slug: The slug of the current region
    :return: JSON response which indicates error or success
    """
    region = request.region

    media_file = get_object_or_404(
        MediaFile.objects.filter(region=region), id=request.POST.get("id")
    )

    media_file_form = MediaFileForm(data=request.POST, instance=media_file)

    if not media_file_form.is_valid():
        return JsonResponse(
            {
                "messages": media_file_form.get_error_messages(),
            },
            status=400,
        )

    if not media_file_form.has_changed():
        return JsonResponse(
            {
                "messages": [
                    {
                        "type": "info",
                        "text": _("No changes detected"),
                    }
                ],
                "file": media_file.serialize(),
            }
        )

    # Save form
    media_file_form.save()

    return JsonResponse(
        {
            "messages": [
                {
                    "type": "success",
                    "text": _('File "{}" was saved successfully').format(
                        media_file.name
                    ),
                }
            ],
            "file": media_file.serialize(),
        }
    )


@require_POST
@permission_required("cms.replace_mediafile")
@json_response
# pylint: disable=unused-argument
def replace_file_ajax(
    request: HttpRequest, region_slug: str | None = None
) -> JsonResponse:
    """
    View provides the replacement of a file via AJAX.

    :param request: The current request
    :param region_slug: The slug of the current region
    :return: JSON response which indicates error or success
    """
    region = request.region

    media_file = get_object_or_404(
        MediaFile.objects.filter(region=region), id=request.POST.get("id")
    )

    media_file_form = ReplaceMediaFileForm(
        user=request.user, data=request.POST, instance=media_file, files=request.FILES
    )

    if not media_file_form.is_valid():
        return JsonResponse(
            {
                "messages": media_file_form.get_error_messages(),
            },
            status=400,
        )

    # Save form
    media_file_form.save()
    logger.info("%r was replaced by %r", media_file, request.user)

    return JsonResponse(
        {
            "messages": [
                {
                    "type": "success",
                    "text": _('File "{}" was saved successfully').format(
                        media_file.name
                    ),
                }
            ],
            "file": media_file.serialize(),
        }
    )


@require_POST
@permission_required("cms.delete_mediafile")
@json_response
# pylint: disable=unused-argument
def delete_file_ajax(
    request: HttpRequest, region_slug: str | None = None
) -> JsonResponse:
    """
    View to delete a file via an AJAX call.

    :param request: The current request
    :param region_slug: The slug of the current region
    :return: JSON response which indicates error or success
    """
    region = request.region

    media_file = get_object_or_404(
        MediaFile.objects.filter(region=region), id=request.POST.get("id")
    )

    # Check if the media file is in use
    if not media_file.is_deletable:
        return JsonResponse(
            {
                "messages": [
                    {
                        "type": "warning",
                        "text": _(
                            'File "{}" cannot be deleted because it is used as an icon or content.'
                        ).format(media_file.name),
                    }
                ],
            },
            status=400,
        )

    # Delete corresponding physical files
    media_file.file.delete()
    media_file.thumbnail.delete()
    # Delete database entry
    media_file.delete()

    return JsonResponse(
        {
            "messages": [
                {
                    "type": "success",
                    "text": _('File "{}" was successfully deleted').format(
                        media_file.name
                    ),
                }
            ],
            "file": media_file.serialize(),
        }
    )


@require_POST
@permission_required("cms.add_directory")
@json_response
# pylint: disable=unused-argument
def create_directory_ajax(
    request: HttpRequest, region_slug: str | None = None
) -> JsonResponse:
    """
    View provides the frontend with the option to create a directory via AJAX.

    :param request: The current request
    :param region_slug: The slug of the current region
    :return: JSON response which indicates error or success
    """
    region = request.region

    directory_form = CreateDirectoryForm(data=request.POST)

    # Set region so it can be checked whether it's the same like the parent directory
    directory_form.instance.region = region

    if not directory_form.is_valid():
        return JsonResponse(
            {
                "messages": directory_form.get_error_messages(),
            },
            status=400,
        )

    # Save form
    directory = directory_form.save()

    return JsonResponse(
        {
            "messages": [
                {
                    "type": "success",
                    "text": _('Directory "{}" was created successfully').format(
                        directory.name
                    ),
                }
            ],
            "directory": directory.serialize(),
        }
    )


@require_POST
@permission_required("cms.change_directory")
@json_response
# pylint: disable=unused-argument
def edit_directory_ajax(
    request: HttpRequest, region_slug: str | None = None
) -> JsonResponse:
    """
    View provides the frontend with the option to modify a directory via AJAX.

    :param request: The current request
    :param region_slug: The slug of the current region
    :return: JSON response which indicates error or success
    """
    region = request.region

    directory = get_object_or_404(
        Directory.objects.filter(region=region), id=request.POST.get("id")
    )

    directory_form = DirectoryForm(data=request.POST, instance=directory)

    if not directory_form.is_valid():
        return JsonResponse(
            {
                "messages": directory_form.get_error_messages(),
            },
            status=400,
        )

    if not directory_form.has_changed():
        return JsonResponse(
            {
                "messages": [{"type": "info", "text": _("No changes detected")}],
                "directory": directory.serialize(),
            }
        )

    # Save form
    directory = directory_form.save()

    return JsonResponse(
        {
            "messages": [
                {
                    "type": "success",
                    "text": _('Directory "{}" was saved successfully').format(
                        directory.name
                    ),
                }
            ],
            "directory": directory.serialize(),
        }
    )


@require_POST
@permission_required("cms.delete_directory")
@json_response
# pylint: disable=unused-argument
def delete_directory_ajax(
    request: HttpRequest, region_slug: str | None = None
) -> JsonResponse:
    """
    View provides the frontend with the option to delete a directory via AJAX.

    :param request: The current request
    :param region_slug: The slug of the current region
    :return: JSON response which indicates error or success
    """
    region = request.region

    directory = get_object_or_404(
        Directory.objects.filter(region=region), id=request.POST.get("id")
    )
    serialized = directory.serialize()

    try:
        directory.delete()
    except ProtectedError:
        return JsonResponse(
            {
                "messages": [
                    {
                        "type": "warning",
                        "text": _(
                            'Directory "{}" cannot be deleted because it is not empty'
                        ).format(directory.name),
                    }
                ],
            },
            status=400,
        )

    return JsonResponse(
        {
            "messages": [
                {
                    "type": "success",
                    "text": _('Directory "{}" was successfully deleted').format(
                        directory.name
                    ),
                }
            ],
            "directory": serialized,
        }
    )


@require_POST
@permission_required("cms.change_directory")
@permission_required("cms.change_mediafile")
@json_response
# pylint: disable=unused-argument
def move_file_ajax(
    request: HttpRequest, region_slug: str | None = None
) -> JsonResponse:
    """
    This view provides the frontend with the option to move files via AJAX.

    :param request: The current request
    :param region_slug: The slug of the current region
    :return: JSON response which indicates error or success
    """

    region = request.region

    media_file = get_object_or_404(
        MediaFile.objects.filter(region=region), id=request.POST.get("mediafile_id")
    )

    media_move_form = MediaMoveForm(data=request.POST, instance=media_file)

    if not media_move_form.is_valid():
        return JsonResponse(
            {
                "messages": media_move_form.get_error_messages(),
            },
            status=400,
        )

    if not media_move_form.has_changed():
        return JsonResponse(
            {
                "messages": [{"type": "info", "text": _("No changes detected")}],
            }
        )

    # Save form
    media_file = media_move_form.save()

    return JsonResponse(
        {
            "messages": [
                {
                    "type": "success",
                    "text": _(
                        'File "{}" was moved successfully into directory "{}"'
                    ).format(media_file.name, media_file.parent_directory or "Home"),
                }
            ],
        }
    )