From 4d2813ae41c60db3027cd1e44c97aa147b00001d Mon Sep 17 00:00:00 2001 From: tusooa Date: Tue, 3 Jan 2023 02:31:23 -0500 Subject: [PATCH] Accept multilang descriptions when uploading attachments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/upload.ex | 42 +++++++++++++++---- .../api_spec/operations/media_operation.ex | 10 +++++ .../controllers/media_controller.ex | 6 ++- .../web/activity_pub/activity_pub_test.exs | 7 ++++ .../controllers/media_controller_test.exs | 36 ++++++++++++++++ 5 files changed, 90 insertions(+), 11 deletions(-) diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index e6c4845482..d61d4391c1 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -62,6 +62,7 @@ defmodule Pleroma.Upload do height: integer(), blurhash: String.t(), description: String.t(), + description_map: map(), path: String.t() } defstruct [ @@ -73,21 +74,44 @@ defmodule Pleroma.Upload do :height, :blurhash, :description, + :description_map, :path ] @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config) defp get_description(upload) do - case {upload.description, Pleroma.Config.get([Pleroma.Upload, :default_description])} do - {description, _} when is_binary(description) -> description + case {upload, Pleroma.Config.get([Pleroma.Upload, :default_description])} do + {%{description_map: %{} = description_map}, _} -> description_map + {%{description: description}, _} when is_binary(description) -> description {_, :filename} -> upload.name {_, str} when is_binary(str) -> str _ -> "" end end - @spec store(source, options :: [option()]) :: {:ok, map()} | {:error, any()} + defp validate_description_limit(%{} = description) do + len = Enum.reduce(description, 0, fn {_, content}, acc -> String.length(content) + acc end) + + len <= Pleroma.Config.get([:instance, :description_limit]) + end + + defp validate_description_limit(description) when is_binary(description) do + String.length(description) <= Pleroma.Config.get([:instance, :description_limit]) + end + + defp description_fields(%{} = description) do + %{ + "name" => Pleroma.MultiLanguage.map_to_str(description, multiline: false), + "nameMap" => description + } + end + + defp description_fields(description) when is_binary(description) do + %{"name" => description} + end + + @spec store(source, options :: [option()]) :: {:ok, Map.t()} | {:error, any()} @doc "Store a file. If using a `Plug.Upload{}` as the source, be sure to use `Majic.Plug` to ensure its content_type and filename is correct." def store(upload, opts \\ []) do opts = get_opts(opts) @@ -96,9 +120,7 @@ def store(upload, opts \\ []) do upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"}, {:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload), description = get_description(upload), - {_, true} <- - {:description_limit, - String.length(description) <= Pleroma.Config.get([:instance, :description_limit])}, + {_, true} <- {:description_limit, validate_description_limit(description)}, {:ok, url_spec} <- Pleroma.Uploaders.Uploader.put_file(opts.uploader, upload) do {:ok, %{ @@ -113,9 +135,9 @@ def store(upload, opts \\ []) do } |> Maps.put_if_present("width", upload.width) |> Maps.put_if_present("height", upload.height) - ], - "name" => description + ] } + |> Map.merge(description_fields(description)) |> Maps.put_if_present("blurhash", upload.blurhash)} else {:description_limit, _} -> @@ -156,6 +178,7 @@ defp get_opts(opts) do uploader: Keyword.get(opts, :uploader, Pleroma.Config.get([__MODULE__, :uploader])), filters: Keyword.get(opts, :filters, Pleroma.Config.get([__MODULE__, :filters])), description: Keyword.get(opts, :description), + description_map: Keyword.get(opts, :description_map), base_url: base_url() } end @@ -168,7 +191,8 @@ defp prepare_upload(%Plug.Upload{} = file, opts) do name: file.filename, tempfile: file.path, content_type: file.content_type, - description: opts.description + description: opts.description, + description_map: opts.description_map }} end end diff --git a/lib/pleroma/web/api_spec/operations/media_operation.ex b/lib/pleroma/web/api_spec/operations/media_operation.ex index e6df212467..4aa58f2a85 100644 --- a/lib/pleroma/web/api_spec/operations/media_operation.ex +++ b/lib/pleroma/web/api_spec/operations/media_operation.ex @@ -47,6 +47,11 @@ defp create_request do type: :string, description: "A plain-text description of the media, for accessibility purposes." }, + description_map: + Helpers.multilang_map_of(%Schema{ + type: :string, + description: "A plain-text description of the media, for accessibility purposes." + }), focus: %Schema{ type: :string, description: "Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0." @@ -88,6 +93,11 @@ defp update_request do type: :string, description: "A plain-text description of the media, for accessibility purposes." }, + description_map: + Helpers.multilang_map_of(%Schema{ + type: :string, + description: "A plain-text description of the media, for accessibility purposes." + }), focus: %Schema{ type: :string, description: "Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0." diff --git a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex index a861273a8a..fb67943396 100644 --- a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex @@ -28,7 +28,8 @@ def create( ActivityPub.upload( file, actor: user.ap_id, - description: Map.get(data, :description) + description: Map.get(data, :description), + description_map: Map.get(data, :description_map) ) do attachment_data = Map.put(object.data, "id", object.id) @@ -48,7 +49,8 @@ def create2( ActivityPub.upload( file, actor: user.ap_id, - description: Map.get(data, :description) + description: Map.get(data, :description), + description_map: Map.get(data, :description_map) ) do attachment_data = Map.put(object.data, "id", object.id) diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index 8e91d00e00..ec18507bbc 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -1410,6 +1410,13 @@ test "sets a description if given", %{test_file: file} do assert object.data["name"] == "a cool file" end + test "sets a multilang description if given", %{test_file: file} do + {:ok, %Object{} = object} = + ActivityPub.upload(file, description_map: %{"a" => "mew", "b" => "lol"}) + + assert object.data["nameMap"] == %{"a" => "mew", "b" => "lol"} + end + test "it sets the default description depending on the configuration", %{test_file: file} do clear_config([Pleroma.Upload, :default_description]) diff --git a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs index b92fd8afad..7b886da6a6 100644 --- a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs @@ -49,6 +49,24 @@ test "/api/v1/media", %{conn: conn, image: image} do assert object.data["actor"] == User.ap_id(conn.assigns[:user]) end + test "/api/v1/media, multilang", %{conn: conn, image: image} do + media = + conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/v1/media", %{ + "file" => image, + "description_map" => %{"a" => "mew", "b" => "lol"} + }) + |> json_response_and_validate_schema(:ok) + + assert media["type"] == "image" + assert media["description_map"] == %{"a" => "mew", "b" => "lol"} + assert media["id"] + + object = Object.get_by_id(media["id"]) + assert object.data["actor"] == User.ap_id(conn.assigns[:user]) + end + test "/api/v2/media", %{conn: conn, user: user, image: image} do desc = "Description of the image" @@ -75,6 +93,24 @@ test "/api/v2/media", %{conn: conn, user: user, image: image} do assert object.data["actor"] == user.ap_id end + test "/api/v2/media, multilang", %{conn: conn, image: image} do + media = + conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/v2/media", %{ + "file" => image, + "description_map" => %{"a" => "mew", "b" => "lol"} + }) + |> json_response_and_validate_schema(202) + + assert media["type"] == "image" + assert media["description_map"] == %{"a" => "mew", "b" => "lol"} + assert media["id"] + + object = Object.get_by_id(media["id"]) + assert object.data["actor"] == User.ap_id(conn.assigns[:user]) + end + test "/api/v2/media, upload_limit", %{conn: conn, user: user} do desc = "Description of the binary"