defmodule Mobilizon.GraphQL.Resolvers.Media do @moduledoc """ Handles the media-related GraphQL calls """ alias Mobilizon.Actors.Actor alias Mobilizon.Medias alias Mobilizon.Medias.Media alias Mobilizon.Users.User import Mobilizon.Web.Gettext @doc """ Get media for an event See Mobilizon.Web.Resolvers.Event.create_event/3 """ def media(%{picture_id: media_id} = _parent, _args, _resolution) do do_fetch_media(media_id) end def media(%{picture: media} = _parent, _args, _resolution), do: {:ok, media} def media(_parent, %{id: media_id}, _resolution), do: do_fetch_media(media_id) def media(_parent, _args, _resolution), do: {:ok, nil} def medias(%{media: medias}, _args, _resolution) do {:ok, Enum.map(medias, &transform_media/1)} end @spec do_fetch_media(nil) :: {:error, nil} defp do_fetch_media(nil), do: {:error, nil} @spec do_fetch_media(String.t()) :: {:ok, Media.t()} | {:error, :not_found} defp do_fetch_media(media_id) do case Medias.get_media(media_id) do %Media{} = media -> {:ok, transform_media(media)} nil -> {:error, :not_found} end end @spec upload_media(map, map, map) :: {:ok, Media.t()} | {:error, any} def upload_media( _parent, %{file: %Plug.Upload{} = file} = args, %{context: %{current_actor: %Actor{id: actor_id}}} ) do with {:ok, %{ name: _name, url: url, content_type: content_type, size: size } = uploaded} <- Mobilizon.Web.Upload.store(file), args <- args |> Map.put(:url, url) |> Map.put(:size, size) |> Map.put(:content_type, content_type), {:ok, media = %Media{}} <- Medias.create_media(%{ file: args, actor_id: actor_id, metadata: Map.take(uploaded, [:width, :height, :blurhash]) }) do {:ok, transform_media(media)} else {:error, :mime_type_not_allowed} -> {:error, dgettext("errors", "File doesn't have an allowed MIME type.")} error -> {:error, error} end end def upload_media(_parent, _args, _resolution), do: {:error, :unauthenticated} @doc """ Remove a media that the user owns """ @spec remove_media(map(), map(), map()) :: {:ok, Media.t()} | {:error, :unauthorized} | {:error, :unauthenticated} | {:error, :not_found} def remove_media(_parent, %{id: media_id}, %{context: %{current_user: %User{} = user}}) do with {:media, %Media{actor_id: actor_id} = media} <- {:media, Medias.get_media(media_id)}, {:is_owned, %Actor{} = _actor} <- User.owns_actor(user, actor_id) do Medias.delete_media(media) else {:media, nil} -> {:error, :not_found} {:is_owned, _} -> {:error, :unauthorized} {:error, :enofile} -> {:error, "File not found"} end end def remove_media(_parent, _args, _resolution), do: {:error, :unauthenticated} @doc """ Return the total media size for an actor """ @spec actor_size(map(), map(), map()) :: {:ok, integer()} | {:error, :unauthorized} | {:error, :unauthenticated} def actor_size(%Actor{id: actor_id}, _args, %{ context: %{current_user: %User{} = user} }) do if can_get_actor_size?(user, actor_id) do {:ok, Medias.media_size_for_actor(actor_id)} else {:error, :unauthorized} end end def actor_size(_parent, _args, _resolution), do: {:error, :unauthenticated} @doc """ Return the total media size for a local user """ @spec user_size(map(), map(), map()) :: {:ok, integer()} | {:error, :unauthorized} | {:error, :unauthenticated} def user_size(%User{id: user_id}, _args, %{ context: %{current_user: %User{} = logged_user} }) do if can_get_user_size?(logged_user, user_id) do {:ok, Medias.media_size_for_user(user_id)} else {:error, :unauthorized} end end def user_size(_parent, _args, _resolution), do: {:error, :unauthenticated} @spec transform_media(Media.t()) :: map() defp transform_media(%Media{id: id, file: file, metadata: metadata}) do %{ name: file.name, url: file.url, id: id, content_type: file.content_type, size: file.size, metadata: metadata } end @spec can_get_user_size?(User.t(), integer()) :: boolean() defp can_get_actor_size?(%User{role: role} = user, actor_id) do role in [:moderator, :administrator] || owns_actor?(User.owns_actor(user, actor_id)) end @spec owns_actor?({:is_owned, Actor.t() | nil}) :: boolean() defp owns_actor?({:is_owned, %Actor{} = _actor}), do: true defp owns_actor?({:is_owned, _}), do: false @spec can_get_user_size?(User.t(), integer()) :: boolean() defp can_get_user_size?(%User{role: role, id: logged_user_id}, user_id) do user_id == logged_user_id || role in [:moderator, :administrator] end end