From 54e9cb5c2db580bc12441f3651fa87a7b976137d Mon Sep 17 00:00:00 2001 From: Sadposter Date: Mon, 20 May 2019 12:39:23 +0100 Subject: [PATCH 1/6] Add API endpoints for a custom user mascot --- docs/api/pleroma_api.md | 39 ++++++++++++++++++++++ lib/pleroma/user/info.ex | 21 ++++++++++++ .../web/mastodon_api/mastodon_api_controller.ex | 36 +++++++++++++++++++- lib/pleroma/web/router.ex | 3 ++ 4 files changed, 98 insertions(+), 1 deletion(-) diff --git a/docs/api/pleroma_api.md b/docs/api/pleroma_api.md index dd0b6ca73..4d99a2d2b 100644 --- a/docs/api/pleroma_api.md +++ b/docs/api/pleroma_api.md @@ -252,6 +252,45 @@ See [Admin-API](Admin-API.md) ] ``` +## `/api/v1/pleroma/mascot` +### Gets user mascot image +* Method `GET` +* Authentication: required + +* Response: JSON. Returns a mastodon media attachment entity. +* Example response: +```json +{ + "id": "abcdefg", + "url": "https://pleroma.example.org/media/abcdefg.png", + "type": "image", + "pleroma": { + "mime_type": "image/png" + } +} +``` + +### Updates user mascot image +* Method `PUT` +* Authentication: required +* Params: + * `image`: Multipart image +* Response: JSON. Returns a mastodon media attachment entity + when successful, otherwise returns HTTP 415 `{"error": "error_msg"}` +* Example response: +```json +{ + "id": "abcdefg", + "url": "https://pleroma.example.org/media/abcdefg.png", + "type": "image", + "pleroma": { + "mime_type": "image/png" + } +} +``` +* Note: Behaves exactly the same as `POST /api/v1/upload`. + Can only accept images - any attempt to upload non-image files will be met with `HTTP 415 Unsupported Media Type`. + ## `/api/pleroma/notification_settings` ### Updates user notification settings * Method `PUT` diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 5f0cefc00..ffcd06e3e 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -43,6 +43,19 @@ defmodule Pleroma.User.Info do field(:hide_favorites, :boolean, default: true) field(:pinned_activities, {:array, :string}, default: []) field(:flavour, :string, default: nil) + + field(:mascot, :map, + default: %{ + id: "pleromatan", + url: "/images/pleroma-fox-tan-smol.png", + type: "image", + preview_url: "/images/pleroma-fox-tan-smol.png", + pleroma: %{ + mime_type: "image/png" + } + } + ) + field(:emoji, {:array, :map}, default: []) field(:notification_settings, :map, @@ -248,6 +261,14 @@ defmodule Pleroma.User.Info do |> validate_required([:flavour]) end + def mascot_update(info, url) do + params = %{mascot: url} + + info + |> cast(params, [:mascot]) + |> validate_required([:mascot]) + end + def set_source_data(info, source_data) do params = %{source_data: source_data} diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 1051861ff..67f363859 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -707,6 +707,40 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end + def set_mascot(%{assigns: %{user: user}} = conn, %{"file" => file}) do + with {:ok, object} <- ActivityPub.upload(file, actor: User.ap_id(user)), + %{} = attachment_data <- Map.put(object.data, "id", object.id), + %{type: type} = rendered <- + StatusView.render("attachment.json", %{attachment: attachment_data}) do + # Reject if not an image + if type == "image" do + # Sure! + # Save to the user's info + info_changeset = User.Info.mascot_update(user.info, rendered) + + user_changeset = + user + |> Ecto.Changeset.change() + |> Ecto.Changeset.put_embed(:info, info_changeset) + + {:ok, _user} = User.update_and_set_cache(user_changeset) + + conn + |> json(rendered) + else + conn + |> send_resp(415, Jason.encode!(%{"error" => "mascots can only be images"})) + end + end + end + + def get_mascot(%{assigns: %{user: user}} = conn, _params) do + %{info: %{mascot: mascot}} = user + + conn + |> json(mascot) + end + def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do with %Activity{data: %{"object" => object}} <- Repo.get(Activity, id), %Object{data: %{"likes" => likes}} <- Object.normalize(object) do @@ -1329,7 +1363,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do display_sensitive_media: false, reduce_motion: false, max_toot_chars: limit, - mascot: "/images/pleroma-fox-tan-smol.png" + mascot: Map.get(user.info.mascot, "url", "/images/pleroma-fox-tan-smol.png") }, rights: %{ delete_others_notice: present?(user.info.is_moderator), diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 6a4e4a1d4..4c29b24eb 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -352,6 +352,9 @@ defmodule Pleroma.Web.Router do post("/pleroma/flavour/:flavour", MastodonAPIController, :set_flavour) + get("/pleroma/mascot", MastodonAPIController, :get_mascot) + put("/pleroma/mascot", MastodonAPIController, :set_mascot) + post("/reports", MastodonAPIController, :reports) end From e81f0fc6d45249dd70656c58af926c21c70c482f Mon Sep 17 00:00:00 2001 From: Sadposter Date: Mon, 20 May 2019 12:58:06 +0100 Subject: [PATCH 2/6] Add mascot get/set tests --- .../web/mastodon_api/mastodon_api_controller.ex | 1 + test/fixtures/sound.mp3 | Bin 0 -> 521 bytes .../mastodon_api/mastodon_api_controller_test.exs | 65 +++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 test/fixtures/sound.mp3 diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 67f363859..d7f095a1f 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -729,6 +729,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> json(rendered) else conn + |> put_resp_content_type("application/json") |> send_resp(415, Jason.encode!(%{"error" => "mascots can only be images"})) end end diff --git a/test/fixtures/sound.mp3 b/test/fixtures/sound.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..9f0f661a3bc34b6366304e9fd1d4fe8513411083 GIT binary patch literal 521 zcmezWd%_V0bP$o5mkt!;2VzDB1}091|Fj1{y8?V1eO-<93=IrecEX$_s-VK;;K;!E q0OXGES_-dW5+jBF|6AY)1M>j}#w9=>D=;vaG%zr*zym6jY5)LcXO3V1 literal 0 HcmV?d00001 diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index cbff141c8..87e1c105d 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -1455,6 +1455,71 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do assert object.data["actor"] == User.ap_id(user) end + test "mascot upload", %{conn: conn} do + user = insert(:user) + + non_image_file = %Plug.Upload{ + content_type: "audio/mpeg", + path: Path.absname("test/fixtures/sound.mp3"), + filename: "sound.mp3" + } + + conn = + conn + |> assign(:user, user) + |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file}) + + assert json_response(conn, 415) + + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } + + conn = + build_conn() + |> assign(:user, user) + |> put("/api/v1/pleroma/mascot", %{"file" => file}) + + assert %{"id" => _, "type" => image} = json_response(conn, 200) + end + + test "mascot retrieving", %{conn: conn} do + user = insert(:user) + # When user hasn't set a mascot, we should just get pleroma tan back + conn = + conn + |> assign(:user, user) + |> get("/api/v1/pleroma/mascot") + + assert %{"url" => url} = json_response(conn, 200) + assert url =~ "pleroma-fox-tan-smol" + + # When a user sets their mascot, we should get that back + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } + + conn = + build_conn() + |> assign(:user, user) + |> put("/api/v1/pleroma/mascot", %{"file" => file}) + assert json_response(conn, 200) + + user = User.get_cached_by_id(user.id) + + conn = + build_conn() + |> assign(:user, user) + |> get("/api/v1/pleroma/mascot") + + assert %{"url" => url, "type" => "image"} = json_response(conn, 200) + assert url =~ "an_image" + end + test "hashtag timeline", %{conn: conn} do following = insert(:user) From dc916ba15f0fd77afa015084849f082065ed6f74 Mon Sep 17 00:00:00 2001 From: Sadposter Date: Mon, 20 May 2019 12:58:17 +0100 Subject: [PATCH 3/6] Format mascot tests --- test/web/mastodon_api/mastodon_api_controller_test.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 87e1c105d..1d9f5a816 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -1503,10 +1503,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do filename: "an_image.jpg" } - conn = + conn = build_conn() |> assign(:user, user) |> put("/api/v1/pleroma/mascot", %{"file" => file}) + assert json_response(conn, 200) user = User.get_cached_by_id(user.id) From 3d0d9e7a5680bcb1b79e5f4aab0e8c514fc9e5ff Mon Sep 17 00:00:00 2001 From: Sadposter Date: Mon, 20 May 2019 13:10:04 +0100 Subject: [PATCH 4/6] Use string map for default mascot --- lib/pleroma/user/info.ex | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index ffcd06e3e..e76d04d7f 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -46,12 +46,12 @@ defmodule Pleroma.User.Info do field(:mascot, :map, default: %{ - id: "pleromatan", - url: "/images/pleroma-fox-tan-smol.png", - type: "image", - preview_url: "/images/pleroma-fox-tan-smol.png", - pleroma: %{ - mime_type: "image/png" + "id" => "pleromatan", + "url" => "/images/pleroma-fox-tan-smol.png", + "type" => "image", + "preview_url" => "/images/pleroma-fox-tan-smol.png", + "pleroma" => %{ + "mime_type" => "image/png" } } ) From d835810610e5e7660a56d305bc8509e38c642942 Mon Sep 17 00:00:00 2001 From: Sadposter Date: Mon, 20 May 2019 14:19:42 +0100 Subject: [PATCH 5/6] Add changelog entry for mascot config --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87101db30..07e6bce31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Configuration: `report_uri` option - Pleroma API: User subscriptions - Pleroma API: Healthcheck endpoint +- Pleroma API: `/api/v1/pleroma/mascot` per-user frontend mascot configuration endpoints - Admin API: Endpoints for listing/revoking invite tokens - Admin API: Endpoints for making users follow/unfollow each other - Admin API: added filters (role, tags, email, name) for users endpoint From daeae8e2e7c506b72c66dea6ac790408f948ec16 Mon Sep 17 00:00:00 2001 From: Sadposter Date: Mon, 20 May 2019 16:12:55 +0100 Subject: [PATCH 6/6] Move default mascot configuration to `config/` --- config/config.exs | 13 +++++++++++++ docs/config.md | 10 ++++++++++ lib/pleroma/user.ex | 20 ++++++++++++++++++++ lib/pleroma/user/info.ex | 14 +------------- .../web/mastodon_api/mastodon_api_controller.ex | 4 ++-- 5 files changed, 46 insertions(+), 15 deletions(-) diff --git a/config/config.exs b/config/config.exs index bab47a8a2..72908266d 100644 --- a/config/config.exs +++ b/config/config.exs @@ -276,6 +276,19 @@ config :pleroma, :frontend_configurations, showInstanceSpecificPanel: true } +config :pleroma, :assets, + mascots: [ + pleroma_fox_tan: %{ + url: "/images/pleroma-fox-tan-smol.png", + mime_type: "image/png" + }, + pleroma_fox_tan_shy: %{ + url: "/images/pleroma-fox-tan-shy.png", + mime_type: "image/png" + } + ], + default_mascot: :pleroma_fox_tan + config :pleroma, :activitypub, accept_blocks: true, unfollow_blocked: true, diff --git a/docs/config.md b/docs/config.md index 450d73fda..197326bbd 100644 --- a/docs/config.md +++ b/docs/config.md @@ -203,6 +203,16 @@ This section is used to configure Pleroma-FE, unless ``:managed_config`` in ``:i * `hide_post_stats`: Hide notices statistics(repeats, favorites, …) * `hide_user_stats`: Hide profile statistics(posts, posts per day, followers, followings, …) +## :assets + +This section configures assets to be used with various frontends. Currently the only option +relates to mascots on the mastodon frontend + +* `mascots`: KeywordList of mascots, each element __MUST__ contain both a `url` and a + `mime_type` key. +* `default_mascot`: An element from `mascots` - This will be used as the default mascot + on MastoFE (default: `:pleroma_fox_tan`) + ## :mrf_simple * `media_removal`: List of instances to remove medias from * `media_nsfw`: List of instances to put medias as NSFW(sensitive) from diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 28da310ee..05fe58f7c 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1402,4 +1402,24 @@ defmodule Pleroma.User do |> put_embed(:info, info_changeset) |> update_and_set_cache() end + + def get_mascot(%{info: %{mascot: %{} = mascot}}) when not is_nil(mascot) do + mascot + end + + def get_mascot(%{info: %{mascot: mascot}}) when is_nil(mascot) do + # use instance-default + config = Pleroma.Config.get([:assets, :mascots]) + default_mascot = Pleroma.Config.get([:assets, :default_mascot]) + mascot = Keyword.get(config, default_mascot) + + %{ + "id" => "default-mascot", + "url" => mascot[:url], + "preview_url" => mascot[:url], + "pleroma" => %{ + "mime_type" => mascot[:mime_type] + } + } + end end diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index e76d04d7f..6397e2737 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -43,19 +43,7 @@ defmodule Pleroma.User.Info do field(:hide_favorites, :boolean, default: true) field(:pinned_activities, {:array, :string}, default: []) field(:flavour, :string, default: nil) - - field(:mascot, :map, - default: %{ - "id" => "pleromatan", - "url" => "/images/pleroma-fox-tan-smol.png", - "type" => "image", - "preview_url" => "/images/pleroma-fox-tan-smol.png", - "pleroma" => %{ - "mime_type" => "image/png" - } - } - ) - + field(:mascot, :map, default: nil) field(:emoji, {:array, :map}, default: []) field(:notification_settings, :map, diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index d7f095a1f..1ec0f30a1 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -736,7 +736,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def get_mascot(%{assigns: %{user: user}} = conn, _params) do - %{info: %{mascot: mascot}} = user + mascot = User.get_mascot(user) conn |> json(mascot) @@ -1364,7 +1364,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do display_sensitive_media: false, reduce_motion: false, max_toot_chars: limit, - mascot: Map.get(user.info.mascot, "url", "/images/pleroma-fox-tan-smol.png") + mascot: User.get_mascot(user)["url"] }, rights: %{ delete_others_notice: present?(user.info.is_moderator),