diff --git a/lib/pleroma/web/emoji_api/emoji_api_controller.ex b/lib/pleroma/web/emoji_api/emoji_api_controller.ex index 4873129c4..dc3dcf1ea 100644 --- a/lib/pleroma/web/emoji_api/emoji_api_controller.ex +++ b/lib/pleroma/web/emoji_api/emoji_api_controller.ex @@ -223,7 +223,7 @@ keeping it in cache for #{div(cache_ms, 1000)}s") end end - def update_metadata(conn, %{"name" => name, "new_data" => new_data}) do + def update_metadata(conn, %{"pack_name" => name, "new_data" => new_data}) do pack_dir = Path.join(@emoji_dir_path, name) pack_file_p = Path.join(pack_dir, "pack.json") @@ -274,4 +274,134 @@ keeping it in cache for #{div(cache_ms, 1000)}s") e end end + + def update_file( + conn, + %{"pack_name" => pack_name, "action" => action, "shortcode" => shortcode} = params + ) do + pack_dir = Path.join(@emoji_dir_path, pack_name) + pack_file_p = Path.join(pack_dir, "pack.json") + + full_pack = Jason.decode!(File.read!(pack_file_p)) + + res = + case action do + "add" -> + unless Map.has_key?(full_pack["files"], shortcode) do + with %{"file" => %Plug.Upload{filename: filename, path: upload_path}} <- params do + # If there was a file name provided with the request, use it, otherwise just use the + # uploaded file name + filename = + if Map.has_key?(params, "filename") do + params["filename"] + else + filename + end + + file_path = Path.join(pack_dir, filename) + + # If the name contains directories, create them + if String.contains?(file_path, "/") do + File.mkdir_p!(Path.dirname(file_path)) + end + + # Copy the uploaded file from the temporary directory + File.copy!(upload_path, file_path) + + updated_full_pack = put_in(full_pack, ["files", shortcode], filename) + + {:ok, updated_full_pack} + else + _ -> {:error, conn |> put_status(:bad_request) |> text("\"file\" not provided")} + end + else + {:error, + conn + |> put_status(:conflict) + |> text("An emoji with the \"#{shortcode}\" shortcode already exists")} + end + + "remove" -> + if Map.has_key?(full_pack["files"], shortcode) do + {emoji_file_path, updated_full_pack} = pop_in(full_pack, ["files", shortcode]) + + emoji_file_path = Path.join(pack_dir, emoji_file_path) + + # Delete the emoji file + File.rm!(emoji_file_path) + + # If the old directory has no more files, remove it + if String.contains?(emoji_file_path, "/") do + dir = Path.dirname(emoji_file_path) + + if Enum.empty?(File.ls!(dir)) do + File.rmdir!(dir) + end + end + + {:ok, updated_full_pack} + else + {:error, + conn |> put_status(:bad_request) |> text("Emoji \"#{shortcode}\" does not exist")} + end + + "update" -> + if Map.has_key?(full_pack["files"], shortcode) do + with %{"new_shortcode" => new_shortcode, "new_filename" => new_filename} <- params do + # First, remove the old shortcode, saving the old path + {old_emoji_file_path, updated_full_pack} = pop_in(full_pack, ["files", shortcode]) + old_emoji_file_path = Path.join(pack_dir, old_emoji_file_path) + new_emoji_file_path = Path.join(pack_dir, new_filename) + + # If the name contains directories, create them + if String.contains?(new_emoji_file_path, "/") do + File.mkdir_p!(Path.dirname(new_emoji_file_path)) + end + + # Move/Rename the old filename to a new filename + # These are probably on the same filesystem, so just rename should work + :ok = File.rename(old_emoji_file_path, new_emoji_file_path) + + # If the old directory has no more files, remove it + if String.contains?(old_emoji_file_path, "/") do + dir = Path.dirname(old_emoji_file_path) + + if Enum.empty?(File.ls!(dir)) do + File.rmdir!(dir) + end + end + + # Then, put in the new shortcode with the new path + updated_full_pack = + put_in(updated_full_pack, ["files", new_shortcode], new_filename) + + {:ok, updated_full_pack} + else + _ -> + {:error, + conn + |> put_status(:bad_request) + |> text("new_shortcode or new_file were not specified")} + end + else + {:error, + conn |> put_status(:bad_request) |> text("Emoji \"#{shortcode}\" does not exist")} + end + + _ -> + {:error, conn |> put_status(:bad_request) |> text("Unknown action: #{action}")} + end + + case res do + {:ok, updated_full_pack} -> + # Write the emoji pack file + File.write!(pack_file_p, Jason.encode!(updated_full_pack, pretty: true)) + + # Return the modified file list + conn |> json(updated_full_pack["files"]) + + {:error, e} -> + e + end + end end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 471d09c43..acd6f740b 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -218,7 +218,8 @@ defmodule Pleroma.Web.Router do # Modifying packs pipe_through([:admin_api, :oauth_write]) - post("/update_metadata/:name", EmojiAPIController, :update_metadata) + post("/update_file/:pack_name", EmojiAPIController, :update_file) + post("/update_metadata/:pack_name", EmojiAPIController, :update_metadata) delete("/delete/:name", EmojiAPIController, :delete) post("/download_from", EmojiAPIController, :download_from) end diff --git a/test/web/emoji_api_controller_test.exs b/test/web/emoji_api_controller_test.exs index 759a4dc04..6d3603da5 100644 --- a/test/web/emoji_api_controller_test.exs +++ b/test/web/emoji_api_controller_test.exs @@ -85,11 +85,10 @@ defmodule Pleroma.Web.EmojiAPI.EmojiAPIControllerTest do admin = insert(:user, info: %{is_admin: true}) - conn = build_conn() + conn = build_conn() |> assign(:user, admin) assert conn |> put_req_header("content-type", "application/json") - |> assign(:user, admin) |> post( emoji_api_path( conn, @@ -108,7 +107,6 @@ defmodule Pleroma.Web.EmojiAPI.EmojiAPIControllerTest do assert File.exists?("#{@emoji_dir_path}/test_pack2/blank.png") assert conn - |> assign(:user, admin) |> delete(emoji_api_path(conn, :delete, "test_pack2")) |> response(200) == "ok" @@ -116,11 +114,10 @@ defmodule Pleroma.Web.EmojiAPI.EmojiAPIControllerTest do # non-shared, downloaded from the fallback URL - conn = build_conn() + conn = build_conn() |> assign(:user, admin) assert conn |> put_req_header("content-type", "application/json") - |> assign(:user, admin) |> post( emoji_api_path( conn, @@ -139,7 +136,6 @@ defmodule Pleroma.Web.EmojiAPI.EmojiAPIControllerTest do assert File.exists?("#{@emoji_dir_path}/test_pack_nonshared2/blank.png") assert conn - |> assign(:user, admin) |> delete(emoji_api_path(conn, :delete, "test_pack_nonshared2")) |> response(200) == "ok" @@ -240,4 +236,65 @@ defmodule Pleroma.Web.EmojiAPI.EmojiAPIControllerTest do |> text_response(:bad_request) =~ "does not have all" end end + + test "updating pack files" do + pack_file = "#{@emoji_dir_path}/test_pack/pack.json" + original_content = File.read!(pack_file) + + on_exit(fn -> + File.write!(pack_file, original_content) + + File.rm_rf!("#{@emoji_dir_path}/test_pack/dir") + File.rm_rf!("#{@emoji_dir_path}/test_pack/dir_2") + end) + + admin = insert(:user, info: %{is_admin: true}) + + conn = build_conn() + + same_name = %{ + "action" => "add", + "shortcode" => "blank", + "filename" => "dir/blank.png", + "file" => %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_dir_path}/test_pack/blank.png" + } + } + + different_name = %{same_name | "shortcode" => "blank_2"} + + conn = conn |> assign(:user, admin) + + assert conn + |> post(emoji_api_path(conn, :update_file, "test_pack"), same_name) + |> text_response(:conflict) =~ "already exists" + + assert conn + |> post(emoji_api_path(conn, :update_file, "test_pack"), different_name) + |> json_response(200) == %{"blank" => "blank.png", "blank_2" => "dir/blank.png"} + + assert File.exists?("#{@emoji_dir_path}/test_pack/dir/blank.png") + + assert conn + |> post(emoji_api_path(conn, :update_file, "test_pack"), %{ + "action" => "update", + "shortcode" => "blank_2", + "new_shortcode" => "blank_3", + "new_filename" => "dir_2/blank_3.png" + }) + |> json_response(200) == %{"blank" => "blank.png", "blank_3" => "dir_2/blank_3.png"} + + refute File.exists?("#{@emoji_dir_path}/test_pack/dir/") + assert File.exists?("#{@emoji_dir_path}/test_pack/dir_2/blank_3.png") + + assert conn + |> post(emoji_api_path(conn, :update_file, "test_pack"), %{ + "action" => "remove", + "shortcode" => "blank_3" + }) + |> json_response(200) == %{"blank" => "blank.png"} + + refute File.exists?("#{@emoji_dir_path}/test_pack/dir_2/") + end end