diff options
author | Haelwenn (lanodan) Monnier <contact@hacktivis.me> | 2022-12-23 17:05:05 +0100 |
---|---|---|
committer | Haelwenn (lanodan) Monnier <contact@hacktivis.me> | 2022-12-23 17:05:05 +0100 |
commit | 7d68d64d633a8ba43965a6f93d22cb0ae76027e0 (patch) | |
tree | 6c00d45be1aa1fe63ffff71bc2153cdff34e104f /lib | |
parent | d8e326467c30b95c5164f6e29512057dce3c2077 (diff) | |
parent | 6bce88b9e7876d32ea9146a580454053f0ef3790 (diff) | |
download | pleroma-7d68d64d633a8ba43965a6f93d22cb0ae76027e0.tar.gz pleroma-7d68d64d633a8ba43965a6f93d22cb0ae76027e0.zip |
Merge back 2.4.5
Diffstat (limited to 'lib')
631 files changed, 8296 insertions, 2537 deletions
diff --git a/lib/mix/pleroma.ex b/lib/mix/pleroma.ex index 2b6c7d6bb..2976085ba 100644 --- a/lib/mix/pleroma.ex +++ b/lib/mix/pleroma.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Pleroma do diff --git a/lib/mix/tasks/pleroma/app.ex b/lib/mix/tasks/pleroma/app.ex index 0bf7ffabc..885d071bc 100644 --- a/lib/mix/tasks/pleroma/app.ex +++ b/lib/mix/tasks/pleroma/app.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.App do diff --git a/lib/mix/tasks/pleroma/benchmark.ex b/lib/mix/tasks/pleroma/benchmark.ex index fdf99747a..f32492169 100644 --- a/lib/mix/tasks/pleroma/benchmark.ex +++ b/lib/mix/tasks/pleroma/benchmark.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Benchmark do diff --git a/lib/mix/tasks/pleroma/config.ex b/lib/mix/tasks/pleroma/config.ex index 22502a522..3a2ea44f8 100644 --- a/lib/mix/tasks/pleroma/config.ex +++ b/lib/mix/tasks/pleroma/config.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Config do @@ -286,9 +286,7 @@ defmodule Mix.Tasks.Pleroma.Config do file = File.open!(tmp_config_path) shell_info( - "Saving database configuration settings to #{tmp_config_path}. Copy it to the #{ - Path.dirname(config_path) - } manually." + "Saving database configuration settings to #{tmp_config_path}. Copy it to the #{Path.dirname(config_path)} manually." ) write_config(file, tmp_config_path, opts) @@ -306,13 +304,8 @@ defmodule Mix.Tasks.Pleroma.Config do System.cmd("mix", ["format", path]) end - if Code.ensure_loaded?(Config.Reader) do - defp config_header, do: "import Config\r\n\r\n" - defp read_file(config_file), do: Config.Reader.read_imports!(config_file) - else - defp config_header, do: "use Mix.Config\r\n\r\n" - defp read_file(config_file), do: Mix.Config.eval!(config_file) - end + defp config_header, do: "import Config\r\n\r\n" + defp read_file(config_file), do: Config.Reader.read_imports!(config_file) defp write_and_delete(config, file, delete?) do config diff --git a/lib/mix/tasks/pleroma/count_statuses.ex b/lib/mix/tasks/pleroma/count_statuses.ex index c29ea8567..c5ab8b7ab 100644 --- a/lib/mix/tasks/pleroma/count_statuses.ex +++ b/lib/mix/tasks/pleroma/count_statuses.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.CountStatuses do diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex index a973beaa9..ed560c177 100644 --- a/lib/mix/tasks/pleroma/database.ex +++ b/lib/mix/tasks/pleroma/database.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Database do @@ -154,9 +154,8 @@ defmodule Mix.Tasks.Pleroma.Database do |> join(:inner, [a], o in Object, on: fragment( - "(?->>'id') = COALESCE((?)->'object'->> 'id', (?)->>'object')", + "(?->>'id') = associated_object_id((?))", o.data, - a.data, a.data ) ) diff --git a/lib/mix/tasks/pleroma/digest.ex b/lib/mix/tasks/pleroma/digest.ex index f34fc839e..aea9c8ac5 100644 --- a/lib/mix/tasks/pleroma/digest.ex +++ b/lib/mix/tasks/pleroma/digest.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Digest do diff --git a/lib/mix/tasks/pleroma/docs.ex b/lib/mix/tasks/pleroma/docs.ex index 45cca1c74..32c02a512 100644 --- a/lib/mix/tasks/pleroma/docs.ex +++ b/lib/mix/tasks/pleroma/docs.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Docs do diff --git a/lib/mix/tasks/pleroma/ecto.ex b/lib/mix/tasks/pleroma/ecto.ex index 69564c61a..8cf77d11c 100644 --- a/lib/mix/tasks/pleroma/ecto.ex +++ b/lib/mix/tasks/pleroma/ecto.ex @@ -1,6 +1,6 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-onl +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Ecto do @doc """ diff --git a/lib/mix/tasks/pleroma/ecto/migrate.ex b/lib/mix/tasks/pleroma/ecto/migrate.ex index 8d9f44e1c..c45c930a3 100644 --- a/lib/mix/tasks/pleroma/ecto/migrate.ex +++ b/lib/mix/tasks/pleroma/ecto/migrate.ex @@ -1,6 +1,6 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-onl +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Ecto.Migrate do use Mix.Task diff --git a/lib/mix/tasks/pleroma/ecto/rollback.ex b/lib/mix/tasks/pleroma/ecto/rollback.ex index 025ebaf19..3d78eaec4 100644 --- a/lib/mix/tasks/pleroma/ecto/rollback.ex +++ b/lib/mix/tasks/pleroma/ecto/rollback.ex @@ -1,6 +1,6 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-onl +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Ecto.Rollback do use Mix.Task diff --git a/lib/mix/tasks/pleroma/email.ex b/lib/mix/tasks/pleroma/email.ex index 4ce8c9b05..37272c124 100644 --- a/lib/mix/tasks/pleroma/email.ex +++ b/lib/mix/tasks/pleroma/email.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Email do diff --git a/lib/mix/tasks/pleroma/emoji.ex b/lib/mix/tasks/pleroma/emoji.ex index 9ad4a7467..537f0715e 100644 --- a/lib/mix/tasks/pleroma/emoji.ex +++ b/lib/mix/tasks/pleroma/emoji.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Emoji do diff --git a/lib/mix/tasks/pleroma/frontend.ex b/lib/mix/tasks/pleroma/frontend.ex index 8334e0049..3c71801ed 100644 --- a/lib/mix/tasks/pleroma/frontend.ex +++ b/lib/mix/tasks/pleroma/frontend.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Frontend do diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index da27a99d0..5c93f19ff 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Instance do @@ -34,7 +34,8 @@ defmodule Mix.Tasks.Pleroma.Instance do static_dir: :string, listen_ip: :string, listen_port: :string, - strip_uploads: :string, + strip_uploads_location: :string, + read_uploads_description: :string, anonymize_uploads: :string, dedupe_uploads: :string ], @@ -161,7 +162,7 @@ defmodule Mix.Tasks.Pleroma.Instance do ) |> Path.expand() - {strip_uploads_message, strip_uploads_default} = + {strip_uploads_location_message, strip_uploads_location_default} = if Pleroma.Utils.command_available?("exiftool") do {"Do you want to strip location (GPS) data from uploaded images? This requires exiftool, it was detected as installed. (y/n)", "y"} @@ -170,12 +171,29 @@ defmodule Mix.Tasks.Pleroma.Instance do "n"} end - strip_uploads = + strip_uploads_location = get_option( options, - :strip_uploads, - strip_uploads_message, - strip_uploads_default + :strip_uploads_location, + strip_uploads_location_message, + strip_uploads_location_default + ) === "y" + + {read_uploads_description_message, read_uploads_description_default} = + if Pleroma.Utils.command_available?("exiftool") do + {"Do you want to read data from uploaded files so clients can use it to prefill fields like image description? This requires exiftool, it was detected as installed. (y/n)", + "y"} + else + {"Do you want to read data from uploaded files so clients can use it to prefill fields like image description? This requires exiftool, it was detected as not installed, please install it if you answer yes. (y/n)", + "n"} + end + + read_uploads_description = + get_option( + options, + :read_uploads_description, + read_uploads_description_message, + read_uploads_description_default ) === "y" anonymize_uploads = @@ -199,6 +217,7 @@ defmodule Mix.Tasks.Pleroma.Instance do secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8) + lv_signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8) {web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1) template_dir = Application.app_dir(:pleroma, "priv") <> "/templates" @@ -217,6 +236,7 @@ defmodule Mix.Tasks.Pleroma.Instance do secret: secret, jwt_secret: jwt_secret, signing_salt: signing_salt, + lv_signing_salt: lv_signing_salt, web_push_public_key: Base.url_encode64(web_push_public_key, padding: false), web_push_private_key: Base.url_encode64(web_push_private_key, padding: false), db_configurable?: db_configurable?, @@ -227,7 +247,8 @@ defmodule Mix.Tasks.Pleroma.Instance do listen_port: listen_port, upload_filters: upload_filters(%{ - strip: strip_uploads, + strip_location: strip_uploads_location, + read_description: read_uploads_description, anonymize: anonymize_uploads, dedupe: dedupe_uploads }) @@ -295,13 +316,20 @@ defmodule Mix.Tasks.Pleroma.Instance do defp upload_filters(filters) when is_map(filters) do enabled_filters = - if filters.strip do - [Pleroma.Upload.Filter.Exiftool] + if filters.strip_location do + [Pleroma.Upload.Filter.Exiftool.StripLocation] else [] end enabled_filters = + if filters.read_description do + enabled_filters ++ [Pleroma.Upload.Filter.Exiftool.ReadDescription] + else + enabled_filters + end + + enabled_filters = if filters.anonymize do enabled_filters ++ [Pleroma.Upload.Filter.AnonymizeFilename] else diff --git a/lib/mix/tasks/pleroma/notification_settings.ex b/lib/mix/tasks/pleroma/notification_settings.ex index e16866b6a..f0a7cc4ca 100644 --- a/lib/mix/tasks/pleroma/notification_settings.ex +++ b/lib/mix/tasks/pleroma/notification_settings.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.NotificationSettings do diff --git a/lib/mix/tasks/pleroma/openapi_spec.ex b/lib/mix/tasks/pleroma/openapi_spec.ex index 8f719c58b..884f931f8 100644 --- a/lib/mix/tasks/pleroma/openapi_spec.ex +++ b/lib/mix/tasks/pleroma/openapi_spec.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Mix.Tasks.Pleroma.OpenapiSpec do def run([path]) do # Load Pleroma application to get version info diff --git a/lib/mix/tasks/pleroma/refresh_counter_cache.ex b/lib/mix/tasks/pleroma/refresh_counter_cache.ex index 66eed8657..ad37cd20b 100644 --- a/lib/mix/tasks/pleroma/refresh_counter_cache.ex +++ b/lib/mix/tasks/pleroma/refresh_counter_cache.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.RefreshCounterCache do diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex index 01e6b4279..29cc6668c 100644 --- a/lib/mix/tasks/pleroma/relay.ex +++ b/lib/mix/tasks/pleroma/relay.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Relay do diff --git a/lib/mix/tasks/pleroma/robots_txt.ex b/lib/mix/tasks/pleroma/robots_txt.ex index 2ae430761..5124c7c40 100644 --- a/lib/mix/tasks/pleroma/robots_txt.ex +++ b/lib/mix/tasks/pleroma/robots_txt.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.RobotsTxt do diff --git a/lib/mix/tasks/pleroma/uploads.ex b/lib/mix/tasks/pleroma/uploads.ex index 333e9aa8e..bf02912fa 100644 --- a/lib/mix/tasks/pleroma/uploads.ex +++ b/lib/mix/tasks/pleroma/uploads.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Uploads do diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 53d5fc6d9..929fa1717 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.User do @@ -51,9 +51,7 @@ defmodule Mix.Tasks.Pleroma.User do A user will be created with the following information: - nickname: #{nickname} - email: #{email} - - password: #{ - if(generated_password?, do: "[generated; a reset link will be created]", else: password) - } + - password: #{if(generated_password?, do: "[generated; a reset link will be created]", else: password)} - name: #{name} - bio: #{bio} - moderator: #{if(moderator?, do: "true", else: "false")} @@ -114,15 +112,10 @@ defmodule Mix.Tasks.Pleroma.User do {:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do shell_info("Generated password reset token for #{user.nickname}") - IO.puts( - "URL: #{ - Pleroma.Web.Router.Helpers.reset_password_url( - Pleroma.Web.Endpoint, - :reset, - token.token - ) - }" - ) + url = + Pleroma.Web.Router.Helpers.reset_password_url(Pleroma.Web.Endpoint, :reset, token.token) + + IO.puts("URL: #{url}") else _ -> shell_error("No local user #{nickname}") @@ -321,9 +314,7 @@ defmodule Mix.Tasks.Pleroma.User do end shell_info( - "ID: #{invite.id} | Token: #{invite.token} | Token type: #{invite.invite_type} | Used: #{ - invite.used - }#{expire_info}#{using_info}" + "ID: #{invite.id} | Token: #{invite.token} | Token type: #{invite.invite_type} | Used: #{invite.used}#{expire_info}#{using_info}" ) end) end @@ -424,15 +415,45 @@ defmodule Mix.Tasks.Pleroma.User do users |> Enum.each(fn user -> shell_info( - "#{user.nickname} moderator: #{user.is_moderator}, admin: #{user.is_admin}, locked: #{ - user.is_locked - }, is_active: #{user.is_active}" + "#{user.nickname} moderator: #{user.is_moderator}, admin: #{user.is_admin}, locked: #{user.is_locked}, is_active: #{user.is_active}" ) end) end) |> Stream.run() end + def run(["fix_follow_state", local_user, remote_user]) do + start_pleroma() + + with {:local, %User{} = local} <- {:local, User.get_by_nickname(local_user)}, + {:remote, %User{} = remote} <- {:remote, User.get_by_nickname(remote_user)}, + {:follow_data, %{data: %{"state" => request_state}}} <- + {:follow_data, Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(local, remote)} do + calculated_state = User.following?(local, remote) + + shell_info( + "Request state is #{request_state}, vs calculated state of following=#{calculated_state}" + ) + + if calculated_state == false && request_state == "accept" do + shell_info("Discrepancy found, fixing") + Pleroma.Web.CommonAPI.reject_follow_request(local, remote) + shell_info("Relationship fixed") + else + shell_info("No discrepancy found") + end + else + {:local, _} -> + shell_error("No local user #{local_user}") + + {:remote, _} -> + shell_error("No remote user #{remote_user}") + + {:follow_data, _} -> + shell_error("No follow data for #{local_user} and #{remote_user}") + end + end + defp set_moderator(user, value) do {:ok, user} = user diff --git a/lib/phoenix/transports/web_socket/raw.ex b/lib/phoenix/transports/web_socket/raw.ex index 8ed64eb16..8cf9c32a2 100644 --- a/lib/phoenix/transports/web_socket/raw.ex +++ b/lib/phoenix/transports/web_socket/raw.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Phoenix.Transports.WebSocket.Raw do diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 6a991c48e..3556aaf9e 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Activity do @@ -53,7 +53,7 @@ defmodule Pleroma.Activity do # # ``` # |> join(:inner, [activity], o in Object, - # on: fragment("(?->>'id') = COALESCE((?)->'object'->> 'id', (?)->>'object')", + # on: fragment("(?->>'id') = associated_object_id((?))", # o.data, activity.data, activity.data)) # |> preload([activity, object], [object: object]) # ``` @@ -69,9 +69,8 @@ defmodule Pleroma.Activity do join(query, join_type, [activity], o in Object, on: fragment( - "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", + "(?->>'id') = associated_object_id(?)", o.data, - activity.data, activity.data ), as: :object @@ -302,7 +301,7 @@ defmodule Pleroma.Activity do |> Queries.by_object_id() |> Queries.exclude_type("Delete") |> select([u], u) - |> Repo.delete_all() + |> Repo.delete_all(timeout: :infinity) |> elem(1) |> Enum.find(fn %{data: %{"type" => "Create", "object" => ap_id}} when is_binary(ap_id) -> ap_id == id @@ -362,11 +361,11 @@ defmodule Pleroma.Activity do end def restrict_deactivated_users(query) do - deactivated_users = - from(u in User.Query.build(%{deactivated: true}), select: u.ap_id) - |> Repo.all() - - Activity.Queries.exclude_authors(query, deactivated_users) + query + |> join(:inner, [activity], user in User, + as: :user, + on: activity.actor == user.ap_id and user.is_active == true + ) end defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search diff --git a/lib/pleroma/activity/html.ex b/lib/pleroma/activity/html.ex index 0bf393836..706b2d36c 100644 --- a/lib/pleroma/activity/html.ex +++ b/lib/pleroma/activity/html.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Activity.HTML do @@ -8,6 +8,40 @@ defmodule Pleroma.Activity.HTML do @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + # We store a list of cache keys related to an activity in a + # separate cache, scrubber_management_cache. It has the same + # size as scrubber_cache (see application.ex). Every time we add + # a cache to scrubber_cache, we update scrubber_management_cache. + # + # The most recent write of a certain key in the management cache + # is the same as the most recent write of any record related to that + # key in the main cache. + # Assuming LRW ( https://hexdocs.pm/cachex/Cachex.Policy.LRW.html ), + # this means when the management cache is evicted by cachex, all + # related records in the main cache will also have been evicted. + + defp get_cache_keys_for(activity_id) do + with {:ok, list} when is_list(list) <- @cachex.get(:scrubber_management_cache, activity_id) do + list + else + _ -> [] + end + end + + defp add_cache_key_for(activity_id, additional_key) do + current = get_cache_keys_for(activity_id) + + unless additional_key in current do + @cachex.put(:scrubber_management_cache, activity_id, [additional_key | current]) + end + end + + def invalidate_cache_for(activity_id) do + keys = get_cache_keys_for(activity_id) + Enum.map(keys, &@cachex.del(:scrubber_cache, &1)) + @cachex.del(:scrubber_management_cache, activity_id) + end + def get_cached_scrubbed_html_for_activity( content, scrubbers, @@ -19,6 +53,8 @@ defmodule Pleroma.Activity.HTML do @cachex.fetch!(:scrubber_cache, key, fn _key -> object = Object.normalize(activity, fetch: false) + + add_cache_key_for(activity.id, key) HTML.ensure_scrubbed_html(content, scrubbers, object.data["fake"] || false, callback) end) end diff --git a/lib/pleroma/activity/ir/topics.ex b/lib/pleroma/activity/ir/topics.ex index 3cc488fa6..8249cbe27 100644 --- a/lib/pleroma/activity/ir/topics.ex +++ b/lib/pleroma/activity/ir/topics.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Activity.Ir.Topics do @@ -29,7 +29,7 @@ defmodule Pleroma.Activity.Ir.Topics do ["user", "list"] ++ visibility_tags(object, activity) end - defp visibility_tags(object, activity) do + defp visibility_tags(object, %{data: %{"type" => type}} = activity) when type != "Announce" do case Visibility.get_visibility(activity) do "public" -> if activity.local do @@ -51,6 +51,10 @@ defmodule Pleroma.Activity.Ir.Topics do end end + defp visibility_tags(_object, _activity) do + [] + end + defp item_creation_tags(tags, object, %{data: %{"type" => "Create"}} = activity) do tags ++ remote_topics(activity) ++ hashtags_to_topics(object) ++ attachment_topics(object, activity) diff --git a/lib/pleroma/activity/queries.ex b/lib/pleroma/activity/queries.ex index 4632651b0..81c44ac05 100644 --- a/lib/pleroma/activity/queries.ex +++ b/lib/pleroma/activity/queries.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Activity.Queries do @@ -52,8 +52,7 @@ defmodule Pleroma.Activity.Queries do activity in query, where: fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ANY(?)", - activity.data, + "associated_object_id((?)) = ANY(?)", activity.data, ^object_ids ) @@ -64,8 +63,7 @@ defmodule Pleroma.Activity.Queries do from(activity in query, where: fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ?", - activity.data, + "associated_object_id((?)) = ?", activity.data, ^object_id ) diff --git a/lib/pleroma/activity/search.ex b/lib/pleroma/activity/search.ex index 09671f621..0b9b24aa4 100644 --- a/lib/pleroma/activity/search.ex +++ b/lib/pleroma/activity/search.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Activity.Search do @@ -30,7 +30,7 @@ defmodule Pleroma.Activity.Search do Activity |> Activity.with_preloaded_object() |> Activity.restrict_deactivated_users() - |> restrict_public() + |> restrict_public(user) |> query_with(index_type, search_query, search_function) |> maybe_restrict_local(user) |> maybe_restrict_author(author) @@ -57,7 +57,19 @@ defmodule Pleroma.Activity.Search do def maybe_restrict_blocked(query, _), do: query - defp restrict_public(q) do + defp restrict_public(q, user) when not is_nil(user) do + intended_recipients = [ + Pleroma.Constants.as_public(), + Pleroma.Web.ActivityPub.Utils.as_local_public() + ] + + from([a, o] in q, + where: fragment("?->>'type' = 'Create'", a.data), + where: fragment("? && ?", ^intended_recipients, a.recipients) + ) + end + + defp restrict_public(q, _user) do from([a, o] in q, where: fragment("?->>'type' = 'Create'", a.data), where: ^Pleroma.Constants.as_public() in a.recipients diff --git a/lib/pleroma/announcement.ex b/lib/pleroma/announcement.ex new file mode 100644 index 000000000..d97c5e728 --- /dev/null +++ b/lib/pleroma/announcement.ex @@ -0,0 +1,160 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Announcement do + use Ecto.Schema + + import Ecto.Changeset, only: [cast: 3, validate_required: 2] + import Ecto.Query + + alias Pleroma.AnnouncementReadRelationship + alias Pleroma.Repo + + @type t :: %__MODULE__{} + @primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true} + + schema "announcements" do + field(:data, :map) + field(:starts_at, :utc_datetime) + field(:ends_at, :utc_datetime) + field(:rendered, :map) + + timestamps(type: :utc_datetime) + end + + def change(struct, params \\ %{}) do + struct + |> cast(validate_params(struct, params), [:data, :starts_at, :ends_at, :rendered]) + |> validate_required([:data]) + end + + defp validate_params(struct, params) do + base_data = + %{ + "content" => "", + "all_day" => false + } + |> Map.merge((struct && struct.data) || %{}) + + merged_data = + Map.merge(base_data, params.data) + |> Map.take(["content", "all_day"]) + + params + |> Map.merge(%{data: merged_data}) + |> add_rendered_properties() + end + + def add_rendered_properties(params) do + {content_html, _, _} = + Pleroma.Web.CommonAPI.Utils.format_input(params.data["content"], "text/plain", + mentions_format: :full + ) + + rendered = %{ + "content" => content_html + } + + params + |> Map.put(:rendered, rendered) + end + + def add(params) do + changeset = change(%__MODULE__{}, params) + + Repo.insert(changeset) + end + + def update(announcement, params) do + changeset = change(announcement, params) + + Repo.update(changeset) + end + + def list_all do + __MODULE__ + |> Repo.all() + end + + def list_paginated(%{limit: limited_number, offset: offset_number}) do + __MODULE__ + |> limit(^limited_number) + |> offset(^offset_number) + |> Repo.all() + end + + def get_by_id(id) do + Repo.get_by(__MODULE__, id: id) + end + + def delete_by_id(id) do + with announcement when not is_nil(announcement) <- get_by_id(id), + {:ok, _} <- Repo.delete(announcement) do + :ok + else + _ -> + :error + end + end + + def read_by?(announcement, user) do + AnnouncementReadRelationship.exists?(user, announcement) + end + + def mark_read_by(announcement, user) do + AnnouncementReadRelationship.mark_read(user, announcement) + end + + def render_json(announcement, opts \\ []) do + extra_params = + case Keyword.fetch(opts, :for) do + {:ok, user} when not is_nil(user) -> + %{read: read_by?(announcement, user)} + + _ -> + %{} + end + + admin_extra_params = + case Keyword.fetch(opts, :admin) do + {:ok, true} -> + %{pleroma: %{raw_content: announcement.data["content"]}} + + _ -> + %{} + end + + base = %{ + id: announcement.id, + content: announcement.rendered["content"], + starts_at: announcement.starts_at, + ends_at: announcement.ends_at, + all_day: announcement.data["all_day"], + published_at: announcement.inserted_at, + updated_at: announcement.updated_at, + mentions: [], + statuses: [], + tags: [], + emojis: [], + reactions: [] + } + + base + |> Map.merge(extra_params) + |> Map.merge(admin_extra_params) + end + + # "visible" means: + # starts_at < time < ends_at + def list_all_visible_when(time) do + __MODULE__ + |> where([a], is_nil(a.starts_at) or a.starts_at < ^time) + |> where([a], is_nil(a.ends_at) or a.ends_at > ^time) + |> Repo.all() + end + + def list_all_visible do + list_all_visible_when(DateTime.now("Etc/UTC") |> elem(1)) + end +end diff --git a/lib/pleroma/announcement_read_relationship.ex b/lib/pleroma/announcement_read_relationship.ex new file mode 100644 index 000000000..9b64404ce --- /dev/null +++ b/lib/pleroma/announcement_read_relationship.ex @@ -0,0 +1,55 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.AnnouncementReadRelationship do + use Ecto.Schema + + import Ecto.Changeset + + alias FlakeId.Ecto.CompatType + alias Pleroma.Announcement + alias Pleroma.Repo + alias Pleroma.User + + @type t :: %__MODULE__{} + + schema "announcement_read_relationships" do + belongs_to(:user, User, type: CompatType) + belongs_to(:announcement, Announcement, type: CompatType) + + timestamps(updated_at: false) + end + + def mark_read(user, announcement) do + %__MODULE__{} + |> cast(%{user_id: user.id, announcement_id: announcement.id}, [:user_id, :announcement_id]) + |> validate_required([:user_id, :announcement_id]) + |> foreign_key_constraint(:user_id) + |> foreign_key_constraint(:announcement_id) + |> unique_constraint([:user_id, :announcement_id]) + |> Repo.insert() + end + + def mark_unread(user, announcement) do + with relationship <- get(user, announcement), + {:exists, true} <- {:exists, not is_nil(relationship)}, + {:ok, _} <- Repo.delete(relationship) do + :ok + else + {:exists, false} -> + :ok + + _ -> + :error + end + end + + def get(user, announcement) do + Repo.get_by(__MODULE__, user_id: user.id, announcement_id: announcement.id) + end + + def exists?(user, announcement) do + not is_nil(get(user, announcement)) + end +end diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 92d143665..1c1db8c10 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Application do @@ -61,6 +61,11 @@ defmodule Pleroma.Application do adapter = Application.get_env(:tesla, :adapter) + if match?({Tesla.Adapter.Finch, _}, adapter) do + Logger.info("Starting Finch") + Finch.start_link(name: MyFinch) + end + if adapter == Tesla.Adapter.Gun do if version = Pleroma.OTPVersion.version() do [major, minor] = @@ -108,7 +113,17 @@ defmodule Pleroma.Application do # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html # for other strategies and supported options - opts = [strategy: :one_for_one, name: Pleroma.Supervisor] + # If we have a lot of caches, default max_restarts can cause test + # resets to fail. + # Go for the default 3 unless we're in test + max_restarts = + if @mix_env == :test do + 100 + else + 3 + end + + opts = [strategy: :one_for_one, name: Pleroma.Supervisor, max_restarts: max_restarts] result = Supervisor.start_link(children, opts) set_postgres_server_version() @@ -185,6 +200,7 @@ defmodule Pleroma.Application do build_cachex("object", default_ttl: 25_000, ttl_interval: 1000, limit: 2500), build_cachex("rich_media", default_ttl: :timer.minutes(120), limit: 5000), build_cachex("scrubber", limit: 2500), + build_cachex("scrubber_management", limit: 2500), build_cachex("idempotency", expiration: idempotency_expiration(), limit: 2500), build_cachex("web_resp", limit: 2500), build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10), @@ -234,7 +250,8 @@ defmodule Pleroma.Application do defp background_migrators do [ - Pleroma.Migrators.HashtagsTableMigrator + Pleroma.Migrators.HashtagsTableMigrator, + Pleroma.Migrators.ContextObjectsDeletionMigrator ] end diff --git a/lib/pleroma/application_requirements.ex b/lib/pleroma/application_requirements.ex index a56311a65..44b1c1705 100644 --- a/lib/pleroma/application_requirements.ex +++ b/lib/pleroma/application_requirements.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ApplicationRequirements do @@ -164,7 +164,8 @@ defmodule Pleroma.ApplicationRequirements do defp check_system_commands!(:ok) do filter_commands_statuses = [ - check_filter(Pleroma.Upload.Filter.Exiftool, "exiftool"), + check_filter(Pleroma.Upload.Filter.Exiftool.StripLocation, "exiftool"), + check_filter(Pleroma.Upload.Filter.Exiftool.ReadDescription, "exiftool"), check_filter(Pleroma.Upload.Filter.Mogrify, "mogrify"), check_filter(Pleroma.Upload.Filter.Mogrifun, "mogrify"), check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "mogrify"), diff --git a/lib/pleroma/bbs/authenticator.ex b/lib/pleroma/bbs/authenticator.ex index 241fcb53c..0f7543ff5 100644 --- a/lib/pleroma/bbs/authenticator.ex +++ b/lib/pleroma/bbs/authenticator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.BBS.Authenticator do diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index 4a2e255f7..27799338f 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.BBS.Handler do @@ -19,9 +19,7 @@ defmodule Pleroma.BBS.Handler do def on_connect(username, ip, port, method) do Logger.debug(fn -> """ - Incoming SSH shell #{inspect(self())} requested for #{username} from #{inspect(ip)}:#{ - inspect(port) - } using #{inspect(method)} + Incoming SSH shell #{inspect(self())} requested for #{username} from #{inspect(ip)}:#{inspect(port)} using #{inspect(method)} """ end) end @@ -44,8 +42,45 @@ defmodule Pleroma.BBS.Handler do def puts_activity(activity) do status = Pleroma.Web.MastodonAPI.StatusView.render("show.json", %{activity: activity}) + IO.puts("-- #{status.id} by #{status.account.display_name} (#{status.account.acct})") - IO.puts(HTML.strip_tags(status.content)) + + status.content + |> String.split("<br/>") + |> Enum.map(&HTML.strip_tags/1) + |> Enum.map(&HtmlEntities.decode/1) + |> Enum.map(&IO.puts/1) + end + + def puts_notification(activity, user) do + notification = + Pleroma.Web.MastodonAPI.NotificationView.render("show.json", %{ + notification: activity, + for: user + }) + + IO.puts( + "== (#{notification.type}) #{notification.status.id} by #{notification.account.display_name} (#{notification.account.acct})" + ) + + notification.status.content + |> String.split("<br/>") + |> Enum.map(&HTML.strip_tags/1) + |> Enum.map(&HtmlEntities.decode/1) + |> (fn x -> + case x do + [content] -> + "> " <> content + + [head | _tail] -> + # "> " <> hd <> "..." + head + |> String.slice(1, 80) + |> (fn x -> "> " <> x <> "..." end).() + end + end).() + |> IO.puts() + IO.puts("") end @@ -55,6 +90,11 @@ defmodule Pleroma.BBS.Handler do IO.puts("home - Show the home timeline") IO.puts("p <text> - Post the given text") IO.puts("r <id> <text> - Reply to the post with the given id") + IO.puts("t <id> - Show a thread from the given id") + IO.puts("n - Show notifications") + IO.puts("n read - Mark all notifactions as read") + IO.puts("f <id> - Favourites the post with the given id") + IO.puts("R <id> - Repeat the post with the given id") IO.puts("quit - Quit") state @@ -75,11 +115,53 @@ defmodule Pleroma.BBS.Handler do state end + def handle_command(%{user: user} = state, "t " <> activity_id) do + with %Activity{} = activity <- Activity.get_by_id(activity_id) do + activities = + ActivityPub.fetch_activities_for_context(activity.data["context"], %{ + blocking_user: user, + user: user, + exclude_id: activity.id + }) + + case activities do + [] -> + activity_id + |> Activity.get_by_id() + |> puts_activity() + + _ -> + activities + |> Enum.reverse() + |> Enum.each(&puts_activity/1) + end + else + _e -> IO.puts("Could not show this thread...") + end + + state + end + + def handle_command(%{user: user} = state, "n read") do + Pleroma.Notification.clear(user) + IO.puts("All notifications were marked as read") + + state + end + + def handle_command(%{user: user} = state, "n") do + user + |> Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(%{}) + |> Enum.each(&puts_notification(&1, user)) + + state + end + def handle_command(%{user: user} = state, "p " <> text) do text = String.trim(text) - with {:ok, _activity} <- CommonAPI.post(user, %{status: text}) do - IO.puts("Posted!") + with {:ok, activity} <- CommonAPI.post(user, %{status: text}) do + IO.puts("Posted! ID: #{activity.id}") else _e -> IO.puts("Could not post...") end @@ -87,6 +169,19 @@ defmodule Pleroma.BBS.Handler do state end + def handle_command(%{user: user} = state, "f " <> id) do + id = String.trim(id) + + with %Activity{} = activity <- Activity.get_by_id(id), + {:ok, _activity} <- CommonAPI.favorite(user, activity) do + IO.puts("Favourited!") + else + _e -> IO.puts("Could not Favourite...") + end + + state + end + def handle_command(state, "home") do user = state.user @@ -125,7 +220,7 @@ defmodule Pleroma.BBS.Handler do loop(%{state | counter: state.counter + 1}) - {:error, :interrupted} -> + {:input, ^input, {:error, :interrupted}} -> IO.puts("Caught Ctrl+C...") loop(%{state | counter: state.counter + 1}) diff --git a/lib/pleroma/bookmark.ex b/lib/pleroma/bookmark.ex index 83cc8e7e1..187749e86 100644 --- a/lib/pleroma/bookmark.ex +++ b/lib/pleroma/bookmark.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Bookmark do diff --git a/lib/pleroma/caching.ex b/lib/pleroma/caching.ex index 02c18564d..eb0588708 100644 --- a/lib/pleroma/caching.ex +++ b/lib/pleroma/caching.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Caching do diff --git a/lib/pleroma/captcha.ex b/lib/pleroma/captcha.ex index bad7b3a66..03910f189 100644 --- a/lib/pleroma/captcha.ex +++ b/lib/pleroma/captcha.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Captcha do diff --git a/lib/pleroma/captcha/kocaptcha.ex b/lib/pleroma/captcha/kocaptcha.ex index eac6dfa36..e786e28b9 100644 --- a/lib/pleroma/captcha/kocaptcha.ex +++ b/lib/pleroma/captcha/kocaptcha.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Captcha.Kocaptcha do diff --git a/lib/pleroma/captcha/native.ex b/lib/pleroma/captcha/native.ex index 2c6f64e66..9ba0f30be 100644 --- a/lib/pleroma/captcha/native.ex +++ b/lib/pleroma/captcha/native.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Captcha.Native do diff --git a/lib/pleroma/captcha/service.ex b/lib/pleroma/captcha/service.ex index a430fafdc..74797605f 100644 --- a/lib/pleroma/captcha/service.ex +++ b/lib/pleroma/captcha/service.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Captcha.Service do diff --git a/lib/pleroma/chat.ex b/lib/pleroma/chat.ex index bacff24b5..fe32ec08c 100644 --- a/lib/pleroma/chat.ex +++ b/lib/pleroma/chat.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Chat do diff --git a/lib/pleroma/chat/message_reference.ex b/lib/pleroma/chat/message_reference.ex index 89537d155..ea65a4a35 100644 --- a/lib/pleroma/chat/message_reference.ex +++ b/lib/pleroma/chat/message_reference.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Chat.MessageReference do diff --git a/lib/pleroma/clippy.ex b/lib/pleroma/clippy.ex index 9c674e075..bcf23ca07 100644 --- a/lib/pleroma/clippy.ex +++ b/lib/pleroma/clippy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Clippy do diff --git a/lib/pleroma/config.ex b/lib/pleroma/config.ex index 54e332595..cf1453c9b 100644 --- a/lib/pleroma/config.ex +++ b/lib/pleroma/config.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config do diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index fedd58a7e..b53b15d95 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.DeprecationWarnings do @@ -20,6 +20,177 @@ defmodule Pleroma.Config.DeprecationWarnings do "\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"} ] + def check_exiftool_filter do + filters = Config.get([Pleroma.Upload]) |> Keyword.get(:filters, []) + + if Pleroma.Upload.Filter.Exiftool in filters do + Logger.warn(""" + !!!DEPRECATION WARNING!!! + Your config is using Exiftool as a filter instead of Exiftool.StripLocation. This should work for now, but you are advised to change to the new configuration to prevent possible issues later: + + ``` + config :pleroma, Pleroma.Upload, + filters: [Pleroma.Upload.Filter.Exiftool] + ``` + + Is now + + + ``` + config :pleroma, Pleroma.Upload, + filters: [Pleroma.Upload.Filter.Exiftool.StripLocation] + ``` + """) + + new_config = + filters + |> Enum.map(fn + Pleroma.Upload.Filter.Exiftool -> Pleroma.Upload.Filter.Exiftool.StripLocation + filter -> filter + end) + + Config.put([Pleroma.Upload, :filters], new_config) + + :error + else + :ok + end + end + + def check_simple_policy_tuples do + has_strings = + Config.get([:mrf_simple]) + |> Enum.any?(fn {_, v} -> Enum.any?(v, &is_binary/1) end) + + if has_strings do + Logger.warn(""" + !!!DEPRECATION WARNING!!! + Your config is using strings in the SimplePolicy configuration instead of tuples. They should work for now, but you are advised to change to the new configuration to prevent possible issues later: + + ``` + config :pleroma, :mrf_simple, + media_removal: ["instance.tld"], + media_nsfw: ["instance.tld"], + federated_timeline_removal: ["instance.tld"], + report_removal: ["instance.tld"], + reject: ["instance.tld"], + followers_only: ["instance.tld"], + accept: ["instance.tld"], + avatar_removal: ["instance.tld"], + banner_removal: ["instance.tld"], + reject_deletes: ["instance.tld"] + ``` + + Is now + + + ``` + config :pleroma, :mrf_simple, + media_removal: [{"instance.tld", "Reason for media removal"}], + media_nsfw: [{"instance.tld", "Reason for media nsfw"}], + federated_timeline_removal: [{"instance.tld", "Reason for federated timeline removal"}], + report_removal: [{"instance.tld", "Reason for report removal"}], + reject: [{"instance.tld", "Reason for reject"}], + followers_only: [{"instance.tld", "Reason for followers only"}], + accept: [{"instance.tld", "Reason for accept"}], + avatar_removal: [{"instance.tld", "Reason for avatar removal"}], + banner_removal: [{"instance.tld", "Reason for banner removal"}], + reject_deletes: [{"instance.tld", "Reason for reject deletes"}] + ``` + """) + + new_config = + Config.get([:mrf_simple]) + |> Enum.map(fn {k, v} -> + {k, + Enum.map(v, fn + {instance, reason} -> {instance, reason} + instance -> {instance, ""} + end)} + end) + + Config.put([:mrf_simple], new_config) + + :error + else + :ok + end + end + + def check_quarantined_instances_tuples do + has_strings = Config.get([:instance, :quarantined_instances]) |> Enum.any?(&is_binary/1) + + if has_strings do + Logger.warn(""" + !!!DEPRECATION WARNING!!! + Your config is using strings in the quarantined_instances configuration instead of tuples. They should work for now, but you are advised to change to the new configuration to prevent possible issues later: + + ``` + config :pleroma, :instance, + quarantined_instances: ["instance.tld"] + ``` + + Is now + + + ``` + config :pleroma, :instance, + quarantined_instances: [{"instance.tld", "Reason for quarantine"}] + ``` + """) + + new_config = + Config.get([:instance, :quarantined_instances]) + |> Enum.map(fn + {instance, reason} -> {instance, reason} + instance -> {instance, ""} + end) + + Config.put([:instance, :quarantined_instances], new_config) + + :error + else + :ok + end + end + + def check_transparency_exclusions_tuples do + has_strings = Config.get([:mrf, :transparency_exclusions]) |> Enum.any?(&is_binary/1) + + if has_strings do + Logger.warn(""" + !!!DEPRECATION WARNING!!! + Your config is using strings in the transparency_exclusions configuration instead of tuples. They should work for now, but you are advised to change to the new configuration to prevent possible issues later: + + ``` + config :pleroma, :mrf, + transparency_exclusions: ["instance.tld"] + ``` + + Is now + + + ``` + config :pleroma, :mrf, + transparency_exclusions: [{"instance.tld", "Reason to exlude transparency"}] + ``` + """) + + new_config = + Config.get([:mrf, :transparency_exclusions]) + |> Enum.map(fn + {instance, reason} -> {instance, reason} + instance -> {instance, ""} + end) + + Config.put([:mrf, :transparency_exclusions], new_config) + + :error + else + :ok + end + end + def check_hellthread_threshold do if Config.get([:mrf_hellthread, :threshold]) do Logger.warn(""" @@ -34,20 +205,25 @@ defmodule Pleroma.Config.DeprecationWarnings do end def warn do - with :ok <- check_hellthread_threshold(), - :ok <- check_old_mrf_config(), - :ok <- check_media_proxy_whitelist_config(), - :ok <- check_welcome_message_config(), - :ok <- check_gun_pool_options(), - :ok <- check_activity_expiration_config(), - :ok <- check_remote_ip_plug_name(), - :ok <- check_uploders_s3_public_endpoint(), - :ok <- check_old_chat_shoutbox() do - :ok - else - _ -> - :error - end + [ + check_hellthread_threshold(), + check_old_mrf_config(), + check_media_proxy_whitelist_config(), + check_welcome_message_config(), + check_gun_pool_options(), + check_activity_expiration_config(), + check_remote_ip_plug_name(), + check_uploders_s3_public_endpoint(), + check_old_chat_shoutbox(), + check_quarantined_instances_tuples(), + check_transparency_exclusions_tuples(), + check_simple_policy_tuples(), + check_exiftool_filter() + ] + |> Enum.reduce(:ok, fn + :ok, :ok -> :ok + _, _ -> :error + end) end def check_welcome_message_config do @@ -135,7 +311,7 @@ defmodule Pleroma.Config.DeprecationWarnings do warning_preface = """ !!!DEPRECATION WARNING!!! - Your config is using old setting name `timeout` instead of `recv_timeout` in pool settings. Setting should work for now, but you are advised to change format to scheme with port to prevent possible issues later. + Your config is using old setting name `timeout` instead of `recv_timeout` in pool settings. The setting will not take effect until updated. """ updated_config = diff --git a/lib/pleroma/config/getting.ex b/lib/pleroma/config/getting.ex index 2cc9fe80b..f9b66bba6 100644 --- a/lib/pleroma/config/getting.ex +++ b/lib/pleroma/config/getting.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.Getting do diff --git a/lib/pleroma/config/helpers.ex b/lib/pleroma/config/helpers.ex index 9f26c3546..973a75c99 100644 --- a/lib/pleroma/config/helpers.ex +++ b/lib/pleroma/config/helpers.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.Helpers do diff --git a/lib/pleroma/config/holder.ex b/lib/pleroma/config/holder.ex index 4d186a854..7822bde89 100644 --- a/lib/pleroma/config/holder.ex +++ b/lib/pleroma/config/holder.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.Holder do diff --git a/lib/pleroma/config/loader.ex b/lib/pleroma/config/loader.ex index 2a945999e..bd85eccab 100644 --- a/lib/pleroma/config/loader.ex +++ b/lib/pleroma/config/loader.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.Loader do @@ -19,21 +19,10 @@ defmodule Pleroma.Config.Loader do :tesla ] - if Code.ensure_loaded?(Config.Reader) do - @reader Config.Reader - - def read(path), do: @reader.read!(path) - else - # support for Elixir less than 1.9 - @reader Mix.Config - def read(path) do - path - |> @reader.eval!() - |> elem(0) - end - end + @reader Config.Reader @spec read(Path.t()) :: keyword() + def read(path), do: @reader.read!(path) @spec merge(keyword(), keyword()) :: keyword() def merge(c1, c2), do: @reader.merge(c1, c2) diff --git a/lib/pleroma/config/oban.ex b/lib/pleroma/config/oban.ex index 3e63bca40..483d2bb79 100644 --- a/lib/pleroma/config/oban.ex +++ b/lib/pleroma/config/oban.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.Oban do @@ -21,9 +21,7 @@ defmodule Pleroma.Config.Oban do """ !!!OBAN CONFIG WARNING!!! You are using old workers in Oban crontab settings, which were removed. - Please, remove setting from crontab in your config file (prod.secret.exs): #{ - inspect(setting) - } + Please, remove setting from crontab in your config file (prod.secret.exs): #{inspect(setting)} """ |> Logger.warn() diff --git a/lib/pleroma/config/release_runtime_provider.ex b/lib/pleroma/config/release_runtime_provider.ex index e5e9d3dcd..91e5f1a54 100644 --- a/lib/pleroma/config/release_runtime_provider.ex +++ b/lib/pleroma/config/release_runtime_provider.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Config.ReleaseRuntimeProvider do @moduledoc """ Imports runtime config and `{env}.exported_from_db.secret.exs` for releases. diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex index d5c6081a2..44a984019 100644 --- a/lib/pleroma/config/transfer_task.ex +++ b/lib/pleroma/config/transfer_task.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.TransferTask do @@ -47,7 +47,7 @@ defmodule Pleroma.Config.TransferTask do {logger, other} = (Repo.all(ConfigDB) ++ deleted_settings) |> Enum.map(&merge_with_default/1) - |> Enum.split_with(fn {group, _, _, _} -> group in [:logger, :quack] end) + |> Enum.split_with(fn {group, _, _, _} -> group in [:logger] end) logger |> Enum.sort() @@ -104,11 +104,6 @@ defmodule Pleroma.Config.TransferTask do end # change logger configuration in runtime, without restart - defp configure({:quack, key, _, merged}) do - Logger.configure_backend(Quack.Logger, [{key, merged}]) - :ok = update_env(:quack, key, merged) - end - defp configure({_, :backends, _, merged}) do # removing current backends Enum.each(Application.get_env(:logger, :backends), &Logger.remove_backend/1) @@ -148,9 +143,7 @@ defmodule Pleroma.Config.TransferTask do rescue error -> error_msg = - "updating env causes error, group: #{inspect(group)}, key: #{inspect(key)}, value: #{ - inspect(value) - } error: #{inspect(error)}" + "updating env causes error, group: #{inspect(group)}, key: #{inspect(key)}, value: #{inspect(value)} error: #{inspect(error)}" Logger.warn(error_msg) diff --git a/lib/pleroma/config_db.ex b/lib/pleroma/config_db.ex index cb57673e3..846cede04 100644 --- a/lib/pleroma/config_db.ex +++ b/lib/pleroma/config_db.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ConfigDB do @@ -163,7 +163,6 @@ defmodule Pleroma.ConfigDB do defp only_full_update?(%ConfigDB{group: group, key: key}) do full_key_update = [ {:pleroma, :ecto_repos}, - {:quack, :meta}, {:mime, :types}, {:cors_plug, [:max_age, :methods, :expose, :headers]}, {:swarm, :node_blacklist}, @@ -386,7 +385,7 @@ defmodule Pleroma.ConfigDB do @spec module_name?(String.t()) :: boolean() def module_name?(string) do - Regex.match?(~r/^(Pleroma|Phoenix|Tesla|Quack|Ueberauth|Swoosh)\./, string) or + Regex.match?(~r/^(Pleroma|Phoenix|Tesla|Ueberauth|Swoosh)\./, string) or string in ["Oban", "Ueberauth", "ExSyslogger", "ConcurrentLimiter"] end end diff --git a/lib/pleroma/constants.ex b/lib/pleroma/constants.ex index 343ea1acb..cfb405218 100644 --- a/lib/pleroma/constants.ex +++ b/lib/pleroma/constants.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Constants do @@ -28,6 +28,42 @@ defmodule Pleroma.Constants do ~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc embed.js embed.css) ) + const(status_updatable_fields, + do: [ + "source", + "tag", + "updated", + "emoji", + "content", + "summary", + "sensitive", + "attachment", + "generator" + ] + ) + + const(updatable_object_types, + do: [ + "Note", + "Question", + "Audio", + "Video", + "Event", + "Article", + "Page" + ] + ) + + const(actor_types, + do: [ + "Application", + "Group", + "Organization", + "Person", + "Service" + ] + ) + # basic regex, just there to weed out potential mistakes # https://datatracker.ietf.org/doc/html/rfc2045#section-5.1 const(mime_regex, diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index 828e27450..42028aa51 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Conversation do diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex index e0a3af28b..4ed93e5bd 100644 --- a/lib/pleroma/conversation/participation.ex +++ b/lib/pleroma/conversation/participation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Conversation.Participation do diff --git a/lib/pleroma/conversation/participation/recipient_ship.ex b/lib/pleroma/conversation/participation/recipient_ship.ex index 094c1a176..d9a0fdff8 100644 --- a/lib/pleroma/conversation/participation/recipient_ship.ex +++ b/lib/pleroma/conversation/participation/recipient_ship.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Conversation.Participation.RecipientShip do diff --git a/lib/pleroma/counter_cache.ex b/lib/pleroma/counter_cache.ex index 1e75d19ae..d2b14bd54 100644 --- a/lib/pleroma/counter_cache.ex +++ b/lib/pleroma/counter_cache.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.CounterCache do diff --git a/lib/pleroma/data_migration.ex b/lib/pleroma/data_migration.ex index 1377af16e..8451678fc 100644 --- a/lib/pleroma/data_migration.ex +++ b/lib/pleroma/data_migration.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.DataMigration do @@ -42,4 +42,5 @@ defmodule Pleroma.DataMigration do end def populate_hashtags_table, do: get_by_name("populate_hashtags_table") + def delete_context_objects, do: get_by_name("delete_context_objects") end diff --git a/lib/pleroma/delivery.ex b/lib/pleroma/delivery.ex index 511d5cf58..5d1853b76 100644 --- a/lib/pleroma/delivery.ex +++ b/lib/pleroma/delivery.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Delivery do diff --git a/lib/pleroma/docs/generator.ex b/lib/pleroma/docs/generator.ex index e8a68fd41..6508f1947 100644 --- a/lib/pleroma/docs/generator.ex +++ b/lib/pleroma/docs/generator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Docs.Generator do diff --git a/lib/pleroma/docs/json.ex b/lib/pleroma/docs/json.ex index f22432ea4..05f46f39b 100644 --- a/lib/pleroma/docs/json.ex +++ b/lib/pleroma/docs/json.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Docs.JSON do diff --git a/lib/pleroma/docs/markdown.ex b/lib/pleroma/docs/markdown.ex index 7e54e9d58..949388eb9 100644 --- a/lib/pleroma/docs/markdown.ex +++ b/lib/pleroma/docs/markdown.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Docs.Markdown do diff --git a/lib/pleroma/docs/translator.ex b/lib/pleroma/docs/translator.ex new file mode 100644 index 000000000..13e33c87e --- /dev/null +++ b/lib/pleroma/docs/translator.ex @@ -0,0 +1,10 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Docs.Translator do + require Pleroma.Docs.Translator.Compiler + require Pleroma.Web.Gettext + + @before_compile Pleroma.Docs.Translator.Compiler +end diff --git a/lib/pleroma/docs/translator/compiler.ex b/lib/pleroma/docs/translator/compiler.ex new file mode 100644 index 000000000..5d27d9fa2 --- /dev/null +++ b/lib/pleroma/docs/translator/compiler.ex @@ -0,0 +1,119 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Docs.Translator.Compiler do + @external_resource "config/description.exs" + @raw_config Pleroma.Config.Loader.read("config/description.exs") + @raw_descriptions @raw_config[:pleroma][:config_description] + + defmacro __before_compile__(_env) do + strings = + __MODULE__.descriptions() + |> __MODULE__.extract_strings() + + quote do + def placeholder do + unquote do + Enum.map( + strings, + fn {path, type, string} -> + ctxt = msgctxt_for(path, type) + + quote do + Pleroma.Web.Gettext.dpgettext_noop( + "config_descriptions", + unquote(ctxt), + unquote(string) + ) + end + end + ) + end + end + end + end + + def descriptions do + Pleroma.Web.ActivityPub.MRF.config_descriptions() + |> Enum.reduce(@raw_descriptions, fn description, acc -> [description | acc] end) + |> Pleroma.Docs.Generator.convert_to_strings() + end + + def extract_strings(descriptions) do + descriptions + |> Enum.reduce(%{strings: [], path: []}, &process_item/2) + |> Map.get(:strings) + end + + defp process_item(entity, acc) do + current_level = + acc + |> process_desc(entity) + |> process_label(entity) + + process_children(entity, current_level) + end + + defp process_desc(acc, %{description: desc} = item) do + %{ + strings: [{acc.path ++ [key_for(item)], "description", desc} | acc.strings], + path: acc.path + } + end + + defp process_desc(acc, _) do + acc + end + + defp process_label(acc, %{label: label} = item) do + %{ + strings: [{acc.path ++ [key_for(item)], "label", label} | acc.strings], + path: acc.path + } + end + + defp process_label(acc, _) do + acc + end + + defp process_children(%{children: children} = item, acc) do + current_level = Map.put(acc, :path, acc.path ++ [key_for(item)]) + + children + |> Enum.reduce(current_level, &process_item/2) + |> Map.put(:path, acc.path) + end + + defp process_children(_, acc) do + acc + end + + def msgctxt_for(path, type) do + "config #{type} at #{Enum.join(path, " > ")}" + end + + defp convert_group({_, group}) do + group + end + + defp convert_group(group) do + group + end + + def key_for(%{group: group, key: key}) do + "#{convert_group(group)}-#{key}" + end + + def key_for(%{group: group}) do + convert_group(group) + end + + def key_for(%{key: key}) do + key + end + + def key_for(_) do + nil + end +end diff --git a/lib/pleroma/ecto_enums.ex b/lib/pleroma/ecto_enums.ex index 2a9addabc..a4890b489 100644 --- a/lib/pleroma/ecto_enums.ex +++ b/lib/pleroma/ecto_enums.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only import EctoEnum @@ -9,7 +9,9 @@ defenum(Pleroma.UserRelationship.Type, mute: 2, reblog_mute: 3, notification_mute: 4, - inverse_subscription: 5 + inverse_subscription: 5, + suggestion_dismiss: 6, + endorsement: 7 ) defenum(Pleroma.FollowingRelationship.State, diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/date_time.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/date_time.ex index 8552ae73d..b0258e86d 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/date_time.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/date_time.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime do diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex index 96674e21f..e0e4449dc 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Emoji do diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex index 45bd6070a..663dc0dee 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID do diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex index 06fed8fb3..447d536ed 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients do diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/safe_text.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/safe_text.ex index d0f5f381f..95bd3ba23 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/safe_text.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/safe_text.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.SafeText do diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/uri.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/uri.ex index f5b68648c..b8e5c9dad 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/uri.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/uri.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Uri do diff --git a/lib/pleroma/ecto_type/config/atom.ex b/lib/pleroma/ecto_type/config/atom.ex index 3bf0bca5b..c44d655e0 100644 --- a/lib/pleroma/ecto_type/config/atom.ex +++ b/lib/pleroma/ecto_type/config/atom.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.Config.Atom do diff --git a/lib/pleroma/ecto_type/config/binary_value.ex b/lib/pleroma/ecto_type/config/binary_value.ex index 908220a65..4aad0cfce 100644 --- a/lib/pleroma/ecto_type/config/binary_value.ex +++ b/lib/pleroma/ecto_type/config/binary_value.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.Config.BinaryValue do diff --git a/lib/pleroma/emails/admin_email.ex b/lib/pleroma/emails/admin_email.ex index 88bc78aec..372e5529d 100644 --- a/lib/pleroma/emails/admin_email.ex +++ b/lib/pleroma/emails/admin_email.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emails.AdminEmail do diff --git a/lib/pleroma/emails/mailer.ex b/lib/pleroma/emails/mailer.ex index c68550bee..101442130 100644 --- a/lib/pleroma/emails/mailer.ex +++ b/lib/pleroma/emails/mailer.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emails.Mailer do diff --git a/lib/pleroma/emails/new_users_digest_email.ex b/lib/pleroma/emails/new_users_digest_email.ex index 3552dedae..a9e57c7d4 100644 --- a/lib/pleroma/emails/new_users_digest_email.ex +++ b/lib/pleroma/emails/new_users_digest_email.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emails.NewUsersDigestEmail do diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex index e38c681ba..95b963764 100644 --- a/lib/pleroma/emails/user_email.ex +++ b/lib/pleroma/emails/user_email.ex @@ -1,13 +1,16 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emails.UserEmail do @moduledoc "User emails" + require Pleroma.Web.Gettext + alias Pleroma.Config alias Pleroma.User alias Pleroma.Web.Endpoint + alias Pleroma.Web.Gettext alias Pleroma.Web.Router import Swoosh.Email @@ -27,29 +30,75 @@ defmodule Pleroma.Emails.UserEmail do @spec welcome(User.t(), map()) :: Swoosh.Email.t() def welcome(user, opts \\ %{}) do - new() - |> to(recipient(user)) - |> from(Map.get(opts, :sender, sender())) - |> subject(Map.get(opts, :subject, "Welcome to #{instance_name()}!")) - |> html_body(Map.get(opts, :html, "Welcome to #{instance_name()}!")) - |> text_body(Map.get(opts, :text, "Welcome to #{instance_name()}!")) + Gettext.with_locale_or_default user.language do + new() + |> to(recipient(user)) + |> from(Map.get(opts, :sender, sender())) + |> subject( + Map.get( + opts, + :subject, + Gettext.dpgettext( + "static_pages", + "welcome email subject", + "Welcome to %{instance_name}!", + instance_name: instance_name() + ) + ) + ) + |> html_body( + Map.get( + opts, + :html, + Gettext.dpgettext( + "static_pages", + "welcome email html body", + "Welcome to %{instance_name}!", + instance_name: instance_name() + ) + ) + ) + |> text_body( + Map.get( + opts, + :text, + Gettext.dpgettext( + "static_pages", + "welcome email text body", + "Welcome to %{instance_name}!", + instance_name: instance_name() + ) + ) + ) + end end def password_reset_email(user, token) when is_binary(token) do - password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token) - - html_body = """ - <h3>Reset your password at #{instance_name()}</h3> - <p>Someone has requested password change for your account at #{instance_name()}.</p> - <p>If it was you, visit the following link to proceed: <a href="#{password_reset_url}">reset password</a>.</p> - <p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p> - """ - - new() - |> to(recipient(user)) - |> from(sender()) - |> subject("Password reset") - |> html_body(html_body) + Gettext.with_locale_or_default user.language do + password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token) + + html_body = + Gettext.dpgettext( + "static_pages", + "password reset email body", + """ + <h3>Reset your password at %{instance_name}</h3> + <p>Someone has requested password change for your account at %{instance_name}.</p> + <p>If it was you, visit the following link to proceed: <a href="%{password_reset_url}">reset password</a>.</p> + <p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p> + """, + instance_name: instance_name(), + password_reset_url: password_reset_url + ) + + new() + |> to(recipient(user)) + |> from(sender()) + |> subject( + Gettext.dpgettext("static_pages", "password reset email subject", "Password reset") + ) + |> html_body(html_body) + end end def user_invitation_email( @@ -58,73 +107,136 @@ defmodule Pleroma.Emails.UserEmail do to_email, to_name \\ nil ) do - registration_url = - Router.Helpers.redirect_url( - Endpoint, - :registration_page, - user_invite_token.token - ) + Gettext.with_locale_or_default user.language do + registration_url = + Router.Helpers.redirect_url( + Endpoint, + :registration_page, + user_invite_token.token + ) + + html_body = + Gettext.dpgettext( + "static_pages", + "user invitation email body", + """ + <h3>You are invited to %{instance_name}</h3> + <p>%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.</p> + <p>Click the following link to register: <a href="%{registration_url}">accept invitation</a>.</p> + """, + instance_name: instance_name(), + inviter_name: user.name, + registration_url: registration_url + ) - html_body = """ - <h3>You are invited to #{instance_name()}</h3> - <p>#{user.name} invites you to join #{instance_name()}, an instance of Pleroma federated social networking platform.</p> - <p>Click the following link to register: <a href="#{registration_url}">accept invitation</a>.</p> - """ - - new() - |> to(recipient(to_email, to_name)) - |> from(sender()) - |> subject("Invitation to #{instance_name()}") - |> html_body(html_body) + new() + |> to(recipient(to_email, to_name)) + |> from(sender()) + |> subject( + Gettext.dpgettext( + "static_pages", + "user invitation email subject", + "Invitation to %{instance_name}", + instance_name: instance_name() + ) + ) + |> html_body(html_body) + end end def account_confirmation_email(user) do - confirmation_url = - Router.Helpers.confirm_email_url( - Endpoint, - :confirm_email, - user.id, - to_string(user.confirmation_token) - ) + Gettext.with_locale_or_default user.language do + confirmation_url = + Router.Helpers.confirm_email_url( + Endpoint, + :confirm_email, + user.id, + to_string(user.confirmation_token) + ) + + html_body = + Gettext.dpgettext( + "static_pages", + "confirmation email body", + """ + <h3>Thank you for registering on %{instance_name}</h3> + <p>Email confirmation is required to activate the account.</p> + <p>Please click the following link to <a href="%{confirmation_url}">activate your account</a>.</p> + """, + instance_name: instance_name(), + confirmation_url: confirmation_url + ) - html_body = """ - <h3>Thank you for registering on #{instance_name()}</h3> - <p>Email confirmation is required to activate the account.</p> - <p>Please click the following link to <a href="#{confirmation_url}">activate your account</a>.</p> - """ - - new() - |> to(recipient(user)) - |> from(sender()) - |> subject("#{instance_name()} account confirmation") - |> html_body(html_body) + new() + |> to(recipient(user)) + |> from(sender()) + |> subject( + Gettext.dpgettext( + "static_pages", + "confirmation email subject", + "%{instance_name} account confirmation", + instance_name: instance_name() + ) + ) + |> html_body(html_body) + end end def approval_pending_email(user) do - html_body = """ - <h3>Awaiting Approval</h3> - <p>Your account at #{instance_name()} is being reviewed by staff. You will receive another email once your account is approved.</p> - """ - - new() - |> to(recipient(user)) - |> from(sender()) - |> subject("Your account is awaiting approval") - |> html_body(html_body) + Gettext.with_locale_or_default user.language do + html_body = + Gettext.dpgettext( + "static_pages", + "approval pending email body", + """ + <h3>Awaiting Approval</h3> + <p>Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.</p> + """, + instance_name: instance_name() + ) + + new() + |> to(recipient(user)) + |> from(sender()) + |> subject( + Gettext.dpgettext( + "static_pages", + "approval pending email subject", + "Your account is awaiting approval" + ) + ) + |> html_body(html_body) + end end def successful_registration_email(user) do - html_body = """ - <h3>Hello @#{user.nickname},</h3> - <p>Your account at #{instance_name()} has been registered successfully.</p> - <p>No further action is required to activate your account.</p> - """ - - new() - |> to(recipient(user)) - |> from(sender()) - |> subject("Account registered on #{instance_name()}") - |> html_body(html_body) + Gettext.with_locale_or_default user.language do + html_body = + Gettext.dpgettext( + "static_pages", + "successful registration email body", + """ + <h3>Hello @%{nickname},</h3> + <p>Your account at %{instance_name} has been registered successfully.</p> + <p>No further action is required to activate your account.</p> + """, + nickname: user.nickname, + instance_name: instance_name() + ) + + new() + |> to(recipient(user)) + |> from(sender()) + |> subject( + Gettext.dpgettext( + "static_pages", + "successful registration email subject", + "Account registered on %{instance_name}", + instance_name: instance_name() + ) + ) + |> html_body(html_body) + end end @doc """ @@ -134,69 +246,78 @@ defmodule Pleroma.Emails.UserEmail do """ @spec digest_email(User.t()) :: Swoosh.Email.t() | nil def digest_email(user) do - notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at) - - mentions = - notifications - |> Enum.filter(&(&1.activity.data["type"] == "Create")) - |> Enum.map(fn notification -> - object = Pleroma.Object.normalize(notification.activity, fetch: false) - - if not is_nil(object) do - object = update_in(object.data["content"], &format_links/1) - - %{ - data: notification, - object: object, - from: User.get_by_ap_id(notification.activity.actor) - } - end - end) - |> Enum.filter(& &1) - - followers = - notifications - |> Enum.filter(&(&1.activity.data["type"] == "Follow")) - |> Enum.map(fn notification -> - from = User.get_by_ap_id(notification.activity.actor) - - if not is_nil(from) do - %{ - data: notification, - object: Pleroma.Object.normalize(notification.activity, fetch: false), - from: User.get_by_ap_id(notification.activity.actor) - } - end - end) - |> Enum.filter(& &1) - - unless Enum.empty?(mentions) do - styling = Config.get([__MODULE__, :styling]) - logo = Config.get([__MODULE__, :logo]) - - html_data = %{ - instance: instance_name(), - user: user, - mentions: mentions, - followers: followers, - unsubscribe_link: unsubscribe_url(user, "digest"), - styling: styling - } - - logo_path = - if is_nil(logo) do - Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg") - else - Path.join(Config.get([:instance, :static_dir]), logo) - end - - new() - |> to(recipient(user)) - |> from(sender()) - |> subject("Your digest from #{instance_name()}") - |> put_layout(false) - |> render_body("digest.html", html_data) - |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline)) + Gettext.with_locale_or_default user.language do + notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at) + + mentions = + notifications + |> Enum.filter(&(&1.activity.data["type"] == "Create")) + |> Enum.map(fn notification -> + object = Pleroma.Object.normalize(notification.activity, fetch: false) + + if not is_nil(object) do + object = update_in(object.data["content"], &format_links/1) + + %{ + data: notification, + object: object, + from: User.get_by_ap_id(notification.activity.actor) + } + end + end) + |> Enum.filter(& &1) + + followers = + notifications + |> Enum.filter(&(&1.activity.data["type"] == "Follow")) + |> Enum.map(fn notification -> + from = User.get_by_ap_id(notification.activity.actor) + + if not is_nil(from) do + %{ + data: notification, + object: Pleroma.Object.normalize(notification.activity, fetch: false), + from: User.get_by_ap_id(notification.activity.actor) + } + end + end) + |> Enum.filter(& &1) + + unless Enum.empty?(mentions) do + styling = Config.get([__MODULE__, :styling]) + logo = Config.get([__MODULE__, :logo]) + + html_data = %{ + instance: instance_name(), + user: user, + mentions: mentions, + followers: followers, + unsubscribe_link: unsubscribe_url(user, "digest"), + styling: styling + } + + logo_path = + if is_nil(logo) do + Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg") + else + Path.join(Config.get([:instance, :static_dir]), logo) + end + + new() + |> to(recipient(user)) + |> from(sender()) + |> subject( + Gettext.dpgettext( + "static_pages", + "digest email subject", + "Your digest from %{instance_name}", + instance_name: instance_name() + ) + ) + |> put_layout(false) + |> render_body("digest.html", html_data) + |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline)) + end end end @@ -226,27 +347,47 @@ defmodule Pleroma.Emails.UserEmail do def backup_is_ready_email(backup, admin_user_id \\ nil) do %{user: user} = Pleroma.Repo.preload(backup, :user) - download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup) - - html_body = - if is_nil(admin_user_id) do - """ - <p>You requested a full backup of your Pleroma account. It's ready for download:</p> - <p><a href="#{download_url}">#{download_url}</a></p> - """ - else - admin = Pleroma.Repo.get(User, admin_user_id) - - """ - <p>Admin @#{admin.nickname} requested a full backup of your Pleroma account. It's ready for download:</p> - <p><a href="#{download_url}">#{download_url}</a></p> - """ - end - new() - |> to(recipient(user)) - |> from(sender()) - |> subject("Your account archive is ready") - |> html_body(html_body) + Gettext.with_locale_or_default user.language do + download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup) + + html_body = + if is_nil(admin_user_id) do + Gettext.dpgettext( + "static_pages", + "account archive email body - self-requested", + """ + <p>You requested a full backup of your Pleroma account. It's ready for download:</p> + <p><a href="%{download_url}">%{download_url}</a></p> + """, + download_url: download_url + ) + else + admin = Pleroma.Repo.get(User, admin_user_id) + + Gettext.dpgettext( + "static_pages", + "account archive email body - admin requested", + """ + <p>Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:</p> + <p><a href="%{download_url}">%{download_url}</a></p> + """, + admin_nickname: admin.nickname, + download_url: download_url + ) + end + + new() + |> to(recipient(user)) + |> from(sender()) + |> subject( + Gettext.dpgettext( + "static_pages", + "account archive email subject", + "Your account archive is ready" + ) + ) + |> html_body(html_body) + end end end diff --git a/lib/pleroma/emoji-test.txt b/lib/pleroma/emoji-test.txt index d3c6d12bd..87d093d64 100644 --- a/lib/pleroma/emoji-test.txt +++ b/lib/pleroma/emoji-test.txt @@ -1,13 +1,13 @@ # emoji-test.txt -# Date: 2020-09-12, 22:19:50 GMT -# © 2020 Unicode®, Inc. +# Date: 2022-08-12, 20:24:39 GMT +# © 2022 Unicode®, Inc. # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. -# For terms of use, see http://www.unicode.org/terms_of_use.html +# For terms of use, see https://www.unicode.org/terms_of_use.html # # Emoji Keyboard/Display Test Data for UTS #51 -# Version: 13.1 +# Version: 15.0 # -# For documentation and usage, see http://www.unicode.org/reports/tr51 +# For documentation and usage, see https://www.unicode.org/reports/tr51 # # This file provides data for testing which emoji forms should be in keyboards and which should also be displayed/processed. # Format: code points; status # emoji name @@ -43,6 +43,7 @@ 1F602 ; fully-qualified # 😂 E0.6 face with tears of joy 1F642 ; fully-qualified # 🙂 E1.0 slightly smiling face 1F643 ; fully-qualified # 🙃 E1.0 upside-down face +1FAE0 ; fully-qualified # 🫠 E14.0 melting face 1F609 ; fully-qualified # 😉 E0.6 winking face 1F60A ; fully-qualified # 😊 E0.6 smiling face with smiling eyes 1F607 ; fully-qualified # 😇 E1.0 smiling face with halo @@ -68,10 +69,13 @@ 1F911 ; fully-qualified # 🤑 E1.0 money-mouth face # subgroup: face-hand -1F917 ; fully-qualified # 🤗 E1.0 hugging face +1F917 ; fully-qualified # 🤗 E1.0 smiling face with open hands 1F92D ; fully-qualified # 🤭 E5.0 face with hand over mouth +1FAE2 ; fully-qualified # 🫢 E14.0 face with open eyes and hand over mouth +1FAE3 ; fully-qualified # 🫣 E14.0 face with peeking eye 1F92B ; fully-qualified # 🤫 E5.0 shushing face 1F914 ; fully-qualified # 🤔 E1.0 thinking face +1FAE1 ; fully-qualified # 🫡 E14.0 saluting face # subgroup: face-neutral-skeptical 1F910 ; fully-qualified # 🤐 E1.0 zipper-mouth face @@ -79,6 +83,7 @@ 1F610 ; fully-qualified # 😐 E0.7 neutral face 1F611 ; fully-qualified # 😑 E1.0 expressionless face 1F636 ; fully-qualified # 😶 E1.0 face without mouth +1FAE5 ; fully-qualified # 🫥 E14.0 dotted line face 1F636 200D 1F32B FE0F ; fully-qualified # 😶🌫️ E13.1 face in clouds 1F636 200D 1F32B ; minimally-qualified # 😶🌫 E13.1 face in clouds 1F60F ; fully-qualified # 😏 E0.6 smirking face @@ -87,6 +92,7 @@ 1F62C ; fully-qualified # 😬 E1.0 grimacing face 1F62E 200D 1F4A8 ; fully-qualified # 😮💨 E13.1 face exhaling 1F925 ; fully-qualified # 🤥 E3.0 lying face +1FAE8 ; fully-qualified # 🫨 E15.0 shaking face # subgroup: face-sleepy 1F60C ; fully-qualified # 😌 E0.6 relieved face @@ -105,7 +111,7 @@ 1F975 ; fully-qualified # 🥵 E11.0 hot face 1F976 ; fully-qualified # 🥶 E11.0 cold face 1F974 ; fully-qualified # 🥴 E11.0 woozy face -1F635 ; fully-qualified # 😵 E0.6 knocked-out face +1F635 ; fully-qualified # 😵 E0.6 face with crossed-out eyes 1F635 200D 1F4AB ; fully-qualified # 😵💫 E13.1 face with spiral eyes 1F92F ; fully-qualified # 🤯 E5.0 exploding head @@ -121,6 +127,7 @@ # subgroup: face-concerned 1F615 ; fully-qualified # 😕 E1.0 confused face +1FAE4 ; fully-qualified # 🫤 E14.0 face with diagonal mouth 1F61F ; fully-qualified # 😟 E1.0 worried face 1F641 ; fully-qualified # 🙁 E1.0 slightly frowning face 2639 FE0F ; fully-qualified # ☹️ E0.7 frowning face @@ -130,6 +137,7 @@ 1F632 ; fully-qualified # 😲 E0.6 astonished face 1F633 ; fully-qualified # 😳 E0.6 flushed face 1F97A ; fully-qualified # 🥺 E11.0 pleading face +1F979 ; fully-qualified # 🥹 E14.0 face holding back tears 1F626 ; fully-qualified # 😦 E1.0 frowning face with open mouth 1F627 ; fully-qualified # 😧 E1.0 anguished face 1F628 ; fully-qualified # 😨 E0.6 fearful face @@ -148,7 +156,7 @@ # subgroup: face-negative 1F624 ; fully-qualified # 😤 E0.6 face with steam from nose -1F621 ; fully-qualified # 😡 E0.6 pouting face +1F621 ; fully-qualified # 😡 E0.6 enraged face 1F620 ; fully-qualified # 😠 E0.6 angry face 1F92C ; fully-qualified # 🤬 E5.0 face with symbols on mouth 1F608 ; fully-qualified # 😈 E1.0 smiling face with horns @@ -183,8 +191,7 @@ 1F649 ; fully-qualified # 🙉 E0.6 hear-no-evil monkey 1F64A ; fully-qualified # 🙊 E0.6 speak-no-evil monkey -# subgroup: emotion -1F48B ; fully-qualified # 💋 E0.6 kiss mark +# subgroup: heart 1F48C ; fully-qualified # 💌 E0.6 love letter 1F498 ; fully-qualified # 💘 E0.6 heart with arrow 1F49D ; fully-qualified # 💝 E0.6 heart with ribbon @@ -203,14 +210,20 @@ 2764 200D 1FA79 ; unqualified # ❤🩹 E13.1 mending heart 2764 FE0F ; fully-qualified # ❤️ E0.6 red heart 2764 ; unqualified # ❤ E0.6 red heart +1FA77 ; fully-qualified # 🩷 E15.0 pink heart 1F9E1 ; fully-qualified # 🧡 E5.0 orange heart 1F49B ; fully-qualified # 💛 E0.6 yellow heart 1F49A ; fully-qualified # 💚 E0.6 green heart 1F499 ; fully-qualified # 💙 E0.6 blue heart +1FA75 ; fully-qualified # 🩵 E15.0 light blue heart 1F49C ; fully-qualified # 💜 E0.6 purple heart 1F90E ; fully-qualified # 🤎 E12.0 brown heart 1F5A4 ; fully-qualified # 🖤 E3.0 black heart +1FA76 ; fully-qualified # 🩶 E15.0 grey heart 1F90D ; fully-qualified # 🤍 E12.0 white heart + +# subgroup: emotion +1F48B ; fully-qualified # 💋 E0.6 kiss mark 1F4AF ; fully-qualified # 💯 E0.6 hundred points 1F4A2 ; fully-qualified # 💢 E0.6 anger symbol 1F4A5 ; fully-qualified # 💥 E0.6 collision @@ -219,21 +232,20 @@ 1F4A8 ; fully-qualified # 💨 E0.6 dashing away 1F573 FE0F ; fully-qualified # 🕳️ E0.7 hole 1F573 ; unqualified # 🕳 E0.7 hole -1F4A3 ; fully-qualified # 💣 E0.6 bomb 1F4AC ; fully-qualified # 💬 E0.6 speech balloon 1F441 FE0F 200D 1F5E8 FE0F ; fully-qualified # 👁️🗨️ E2.0 eye in speech bubble 1F441 200D 1F5E8 FE0F ; unqualified # 👁🗨️ E2.0 eye in speech bubble -1F441 FE0F 200D 1F5E8 ; unqualified # 👁️🗨 E2.0 eye in speech bubble +1F441 FE0F 200D 1F5E8 ; minimally-qualified # 👁️🗨 E2.0 eye in speech bubble 1F441 200D 1F5E8 ; unqualified # 👁🗨 E2.0 eye in speech bubble 1F5E8 FE0F ; fully-qualified # 🗨️ E2.0 left speech bubble 1F5E8 ; unqualified # 🗨 E2.0 left speech bubble 1F5EF FE0F ; fully-qualified # 🗯️ E0.7 right anger bubble 1F5EF ; unqualified # 🗯 E0.7 right anger bubble 1F4AD ; fully-qualified # 💭 E1.0 thought balloon -1F4A4 ; fully-qualified # 💤 E0.6 zzz +1F4A4 ; fully-qualified # 💤 E0.6 ZZZ -# Smileys & Emotion subtotal: 170 -# Smileys & Emotion subtotal: 170 w/o modifiers +# Smileys & Emotion subtotal: 180 +# Smileys & Emotion subtotal: 180 w/o modifiers # group: People & Body @@ -269,6 +281,42 @@ 1F596 1F3FD ; fully-qualified # 🖖🏽 E1.0 vulcan salute: medium skin tone 1F596 1F3FE ; fully-qualified # 🖖🏾 E1.0 vulcan salute: medium-dark skin tone 1F596 1F3FF ; fully-qualified # 🖖🏿 E1.0 vulcan salute: dark skin tone +1FAF1 ; fully-qualified # 🫱 E14.0 rightwards hand +1FAF1 1F3FB ; fully-qualified # 🫱🏻 E14.0 rightwards hand: light skin tone +1FAF1 1F3FC ; fully-qualified # 🫱🏼 E14.0 rightwards hand: medium-light skin tone +1FAF1 1F3FD ; fully-qualified # 🫱🏽 E14.0 rightwards hand: medium skin tone +1FAF1 1F3FE ; fully-qualified # 🫱🏾 E14.0 rightwards hand: medium-dark skin tone +1FAF1 1F3FF ; fully-qualified # 🫱🏿 E14.0 rightwards hand: dark skin tone +1FAF2 ; fully-qualified # 🫲 E14.0 leftwards hand +1FAF2 1F3FB ; fully-qualified # 🫲🏻 E14.0 leftwards hand: light skin tone +1FAF2 1F3FC ; fully-qualified # 🫲🏼 E14.0 leftwards hand: medium-light skin tone +1FAF2 1F3FD ; fully-qualified # 🫲🏽 E14.0 leftwards hand: medium skin tone +1FAF2 1F3FE ; fully-qualified # 🫲🏾 E14.0 leftwards hand: medium-dark skin tone +1FAF2 1F3FF ; fully-qualified # 🫲🏿 E14.0 leftwards hand: dark skin tone +1FAF3 ; fully-qualified # 🫳 E14.0 palm down hand +1FAF3 1F3FB ; fully-qualified # 🫳🏻 E14.0 palm down hand: light skin tone +1FAF3 1F3FC ; fully-qualified # 🫳🏼 E14.0 palm down hand: medium-light skin tone +1FAF3 1F3FD ; fully-qualified # 🫳🏽 E14.0 palm down hand: medium skin tone +1FAF3 1F3FE ; fully-qualified # 🫳🏾 E14.0 palm down hand: medium-dark skin tone +1FAF3 1F3FF ; fully-qualified # 🫳🏿 E14.0 palm down hand: dark skin tone +1FAF4 ; fully-qualified # 🫴 E14.0 palm up hand +1FAF4 1F3FB ; fully-qualified # 🫴🏻 E14.0 palm up hand: light skin tone +1FAF4 1F3FC ; fully-qualified # 🫴🏼 E14.0 palm up hand: medium-light skin tone +1FAF4 1F3FD ; fully-qualified # 🫴🏽 E14.0 palm up hand: medium skin tone +1FAF4 1F3FE ; fully-qualified # 🫴🏾 E14.0 palm up hand: medium-dark skin tone +1FAF4 1F3FF ; fully-qualified # 🫴🏿 E14.0 palm up hand: dark skin tone +1FAF7 ; fully-qualified # 🫷 E15.0 leftwards pushing hand +1FAF7 1F3FB ; fully-qualified # 🫷🏻 E15.0 leftwards pushing hand: light skin tone +1FAF7 1F3FC ; fully-qualified # 🫷🏼 E15.0 leftwards pushing hand: medium-light skin tone +1FAF7 1F3FD ; fully-qualified # 🫷🏽 E15.0 leftwards pushing hand: medium skin tone +1FAF7 1F3FE ; fully-qualified # 🫷🏾 E15.0 leftwards pushing hand: medium-dark skin tone +1FAF7 1F3FF ; fully-qualified # 🫷🏿 E15.0 leftwards pushing hand: dark skin tone +1FAF8 ; fully-qualified # 🫸 E15.0 rightwards pushing hand +1FAF8 1F3FB ; fully-qualified # 🫸🏻 E15.0 rightwards pushing hand: light skin tone +1FAF8 1F3FC ; fully-qualified # 🫸🏼 E15.0 rightwards pushing hand: medium-light skin tone +1FAF8 1F3FD ; fully-qualified # 🫸🏽 E15.0 rightwards pushing hand: medium skin tone +1FAF8 1F3FE ; fully-qualified # 🫸🏾 E15.0 rightwards pushing hand: medium-dark skin tone +1FAF8 1F3FF ; fully-qualified # 🫸🏿 E15.0 rightwards pushing hand: dark skin tone # subgroup: hand-fingers-partial 1F44C ; fully-qualified # 👌 E0.6 OK hand @@ -302,6 +350,12 @@ 1F91E 1F3FD ; fully-qualified # 🤞🏽 E3.0 crossed fingers: medium skin tone 1F91E 1F3FE ; fully-qualified # 🤞🏾 E3.0 crossed fingers: medium-dark skin tone 1F91E 1F3FF ; fully-qualified # 🤞🏿 E3.0 crossed fingers: dark skin tone +1FAF0 ; fully-qualified # 🫰 E14.0 hand with index finger and thumb crossed +1FAF0 1F3FB ; fully-qualified # 🫰🏻 E14.0 hand with index finger and thumb crossed: light skin tone +1FAF0 1F3FC ; fully-qualified # 🫰🏼 E14.0 hand with index finger and thumb crossed: medium-light skin tone +1FAF0 1F3FD ; fully-qualified # 🫰🏽 E14.0 hand with index finger and thumb crossed: medium skin tone +1FAF0 1F3FE ; fully-qualified # 🫰🏾 E14.0 hand with index finger and thumb crossed: medium-dark skin tone +1FAF0 1F3FF ; fully-qualified # 🫰🏿 E14.0 hand with index finger and thumb crossed: dark skin tone 1F91F ; fully-qualified # 🤟 E5.0 love-you gesture 1F91F 1F3FB ; fully-qualified # 🤟🏻 E5.0 love-you gesture: light skin tone 1F91F 1F3FC ; fully-qualified # 🤟🏼 E5.0 love-you gesture: medium-light skin tone @@ -359,6 +413,12 @@ 261D 1F3FD ; fully-qualified # ☝🏽 E1.0 index pointing up: medium skin tone 261D 1F3FE ; fully-qualified # ☝🏾 E1.0 index pointing up: medium-dark skin tone 261D 1F3FF ; fully-qualified # ☝🏿 E1.0 index pointing up: dark skin tone +1FAF5 ; fully-qualified # 🫵 E14.0 index pointing at the viewer +1FAF5 1F3FB ; fully-qualified # 🫵🏻 E14.0 index pointing at the viewer: light skin tone +1FAF5 1F3FC ; fully-qualified # 🫵🏼 E14.0 index pointing at the viewer: medium-light skin tone +1FAF5 1F3FD ; fully-qualified # 🫵🏽 E14.0 index pointing at the viewer: medium skin tone +1FAF5 1F3FE ; fully-qualified # 🫵🏾 E14.0 index pointing at the viewer: medium-dark skin tone +1FAF5 1F3FF ; fully-qualified # 🫵🏿 E14.0 index pointing at the viewer: dark skin tone # subgroup: hand-fingers-closed 1F44D ; fully-qualified # 👍 E0.6 thumbs up @@ -411,6 +471,12 @@ 1F64C 1F3FD ; fully-qualified # 🙌🏽 E1.0 raising hands: medium skin tone 1F64C 1F3FE ; fully-qualified # 🙌🏾 E1.0 raising hands: medium-dark skin tone 1F64C 1F3FF ; fully-qualified # 🙌🏿 E1.0 raising hands: dark skin tone +1FAF6 ; fully-qualified # 🫶 E14.0 heart hands +1FAF6 1F3FB ; fully-qualified # 🫶🏻 E14.0 heart hands: light skin tone +1FAF6 1F3FC ; fully-qualified # 🫶🏼 E14.0 heart hands: medium-light skin tone +1FAF6 1F3FD ; fully-qualified # 🫶🏽 E14.0 heart hands: medium skin tone +1FAF6 1F3FE ; fully-qualified # 🫶🏾 E14.0 heart hands: medium-dark skin tone +1FAF6 1F3FF ; fully-qualified # 🫶🏿 E14.0 heart hands: dark skin tone 1F450 ; fully-qualified # 👐 E0.6 open hands 1F450 1F3FB ; fully-qualified # 👐🏻 E1.0 open hands: light skin tone 1F450 1F3FC ; fully-qualified # 👐🏼 E1.0 open hands: medium-light skin tone @@ -424,6 +490,31 @@ 1F932 1F3FE ; fully-qualified # 🤲🏾 E5.0 palms up together: medium-dark skin tone 1F932 1F3FF ; fully-qualified # 🤲🏿 E5.0 palms up together: dark skin tone 1F91D ; fully-qualified # 🤝 E3.0 handshake +1F91D 1F3FB ; fully-qualified # 🤝🏻 E14.0 handshake: light skin tone +1F91D 1F3FC ; fully-qualified # 🤝🏼 E14.0 handshake: medium-light skin tone +1F91D 1F3FD ; fully-qualified # 🤝🏽 E14.0 handshake: medium skin tone +1F91D 1F3FE ; fully-qualified # 🤝🏾 E14.0 handshake: medium-dark skin tone +1F91D 1F3FF ; fully-qualified # 🤝🏿 E14.0 handshake: dark skin tone +1FAF1 1F3FB 200D 1FAF2 1F3FC ; fully-qualified # 🫱🏻🫲🏼 E14.0 handshake: light skin tone, medium-light skin tone +1FAF1 1F3FB 200D 1FAF2 1F3FD ; fully-qualified # 🫱🏻🫲🏽 E14.0 handshake: light skin tone, medium skin tone +1FAF1 1F3FB 200D 1FAF2 1F3FE ; fully-qualified # 🫱🏻🫲🏾 E14.0 handshake: light skin tone, medium-dark skin tone +1FAF1 1F3FB 200D 1FAF2 1F3FF ; fully-qualified # 🫱🏻🫲🏿 E14.0 handshake: light skin tone, dark skin tone +1FAF1 1F3FC 200D 1FAF2 1F3FB ; fully-qualified # 🫱🏼🫲🏻 E14.0 handshake: medium-light skin tone, light skin tone +1FAF1 1F3FC 200D 1FAF2 1F3FD ; fully-qualified # 🫱🏼🫲🏽 E14.0 handshake: medium-light skin tone, medium skin tone +1FAF1 1F3FC 200D 1FAF2 1F3FE ; fully-qualified # 🫱🏼🫲🏾 E14.0 handshake: medium-light skin tone, medium-dark skin tone +1FAF1 1F3FC 200D 1FAF2 1F3FF ; fully-qualified # 🫱🏼🫲🏿 E14.0 handshake: medium-light skin tone, dark skin tone +1FAF1 1F3FD 200D 1FAF2 1F3FB ; fully-qualified # 🫱🏽🫲🏻 E14.0 handshake: medium skin tone, light skin tone +1FAF1 1F3FD 200D 1FAF2 1F3FC ; fully-qualified # 🫱🏽🫲🏼 E14.0 handshake: medium skin tone, medium-light skin tone +1FAF1 1F3FD 200D 1FAF2 1F3FE ; fully-qualified # 🫱🏽🫲🏾 E14.0 handshake: medium skin tone, medium-dark skin tone +1FAF1 1F3FD 200D 1FAF2 1F3FF ; fully-qualified # 🫱🏽🫲🏿 E14.0 handshake: medium skin tone, dark skin tone +1FAF1 1F3FE 200D 1FAF2 1F3FB ; fully-qualified # 🫱🏾🫲🏻 E14.0 handshake: medium-dark skin tone, light skin tone +1FAF1 1F3FE 200D 1FAF2 1F3FC ; fully-qualified # 🫱🏾🫲🏼 E14.0 handshake: medium-dark skin tone, medium-light skin tone +1FAF1 1F3FE 200D 1FAF2 1F3FD ; fully-qualified # 🫱🏾🫲🏽 E14.0 handshake: medium-dark skin tone, medium skin tone +1FAF1 1F3FE 200D 1FAF2 1F3FF ; fully-qualified # 🫱🏾🫲🏿 E14.0 handshake: medium-dark skin tone, dark skin tone +1FAF1 1F3FF 200D 1FAF2 1F3FB ; fully-qualified # 🫱🏿🫲🏻 E14.0 handshake: dark skin tone, light skin tone +1FAF1 1F3FF 200D 1FAF2 1F3FC ; fully-qualified # 🫱🏿🫲🏼 E14.0 handshake: dark skin tone, medium-light skin tone +1FAF1 1F3FF 200D 1FAF2 1F3FD ; fully-qualified # 🫱🏿🫲🏽 E14.0 handshake: dark skin tone, medium skin tone +1FAF1 1F3FF 200D 1FAF2 1F3FE ; fully-qualified # 🫱🏿🫲🏾 E14.0 handshake: dark skin tone, medium-dark skin tone 1F64F ; fully-qualified # 🙏 E0.6 folded hands 1F64F 1F3FB ; fully-qualified # 🙏🏻 E1.0 folded hands: light skin tone 1F64F 1F3FC ; fully-qualified # 🙏🏼 E1.0 folded hands: medium-light skin tone @@ -501,6 +592,7 @@ 1F441 ; unqualified # 👁 E0.7 eye 1F445 ; fully-qualified # 👅 E0.6 tongue 1F444 ; fully-qualified # 👄 E0.6 mouth +1FAE6 ; fully-qualified # 🫦 E14.0 biting lip # subgroup: person 1F476 ; fully-qualified # 👶 E0.6 baby @@ -1380,7 +1472,7 @@ 1F575 1F3FF ; fully-qualified # 🕵🏿 E2.0 detective: dark skin tone 1F575 FE0F 200D 2642 FE0F ; fully-qualified # 🕵️♂️ E4.0 man detective 1F575 200D 2642 FE0F ; unqualified # 🕵♂️ E4.0 man detective -1F575 FE0F 200D 2642 ; unqualified # 🕵️♂ E4.0 man detective +1F575 FE0F 200D 2642 ; minimally-qualified # 🕵️♂ E4.0 man detective 1F575 200D 2642 ; unqualified # 🕵♂ E4.0 man detective 1F575 1F3FB 200D 2642 FE0F ; fully-qualified # 🕵🏻♂️ E4.0 man detective: light skin tone 1F575 1F3FB 200D 2642 ; minimally-qualified # 🕵🏻♂ E4.0 man detective: light skin tone @@ -1394,7 +1486,7 @@ 1F575 1F3FF 200D 2642 ; minimally-qualified # 🕵🏿♂ E4.0 man detective: dark skin tone 1F575 FE0F 200D 2640 FE0F ; fully-qualified # 🕵️♀️ E4.0 woman detective 1F575 200D 2640 FE0F ; unqualified # 🕵♀️ E4.0 woman detective -1F575 FE0F 200D 2640 ; unqualified # 🕵️♀ E4.0 woman detective +1F575 FE0F 200D 2640 ; minimally-qualified # 🕵️♀ E4.0 woman detective 1F575 200D 2640 ; unqualified # 🕵♀ E4.0 woman detective 1F575 1F3FB 200D 2640 FE0F ; fully-qualified # 🕵🏻♀️ E4.0 woman detective: light skin tone 1F575 1F3FB 200D 2640 ; minimally-qualified # 🕵🏻♀ E4.0 woman detective: light skin tone @@ -1472,6 +1564,12 @@ 1F477 1F3FE 200D 2640 ; minimally-qualified # 👷🏾♀ E4.0 woman construction worker: medium-dark skin tone 1F477 1F3FF 200D 2640 FE0F ; fully-qualified # 👷🏿♀️ E4.0 woman construction worker: dark skin tone 1F477 1F3FF 200D 2640 ; minimally-qualified # 👷🏿♀ E4.0 woman construction worker: dark skin tone +1FAC5 ; fully-qualified # 🫅 E14.0 person with crown +1FAC5 1F3FB ; fully-qualified # 🫅🏻 E14.0 person with crown: light skin tone +1FAC5 1F3FC ; fully-qualified # 🫅🏼 E14.0 person with crown: medium-light skin tone +1FAC5 1F3FD ; fully-qualified # 🫅🏽 E14.0 person with crown: medium skin tone +1FAC5 1F3FE ; fully-qualified # 🫅🏾 E14.0 person with crown: medium-dark skin tone +1FAC5 1F3FF ; fully-qualified # 🫅🏿 E14.0 person with crown: dark skin tone 1F934 ; fully-qualified # 🤴 E3.0 prince 1F934 1F3FB ; fully-qualified # 🤴🏻 E3.0 prince: light skin tone 1F934 1F3FC ; fully-qualified # 🤴🏼 E3.0 prince: medium-light skin tone @@ -1592,6 +1690,18 @@ 1F930 1F3FD ; fully-qualified # 🤰🏽 E3.0 pregnant woman: medium skin tone 1F930 1F3FE ; fully-qualified # 🤰🏾 E3.0 pregnant woman: medium-dark skin tone 1F930 1F3FF ; fully-qualified # 🤰🏿 E3.0 pregnant woman: dark skin tone +1FAC3 ; fully-qualified # 🫃 E14.0 pregnant man +1FAC3 1F3FB ; fully-qualified # 🫃🏻 E14.0 pregnant man: light skin tone +1FAC3 1F3FC ; fully-qualified # 🫃🏼 E14.0 pregnant man: medium-light skin tone +1FAC3 1F3FD ; fully-qualified # 🫃🏽 E14.0 pregnant man: medium skin tone +1FAC3 1F3FE ; fully-qualified # 🫃🏾 E14.0 pregnant man: medium-dark skin tone +1FAC3 1F3FF ; fully-qualified # 🫃🏿 E14.0 pregnant man: dark skin tone +1FAC4 ; fully-qualified # 🫄 E14.0 pregnant person +1FAC4 1F3FB ; fully-qualified # 🫄🏻 E14.0 pregnant person: light skin tone +1FAC4 1F3FC ; fully-qualified # 🫄🏼 E14.0 pregnant person: medium-light skin tone +1FAC4 1F3FD ; fully-qualified # 🫄🏽 E14.0 pregnant person: medium skin tone +1FAC4 1F3FE ; fully-qualified # 🫄🏾 E14.0 pregnant person: medium-dark skin tone +1FAC4 1F3FF ; fully-qualified # 🫄🏿 E14.0 pregnant person: dark skin tone 1F931 ; fully-qualified # 🤱 E5.0 breast-feeding 1F931 1F3FB ; fully-qualified # 🤱🏻 E5.0 breast-feeding: light skin tone 1F931 1F3FC ; fully-qualified # 🤱🏼 E5.0 breast-feeding: medium-light skin tone @@ -1862,6 +1972,7 @@ 1F9DF 200D 2642 ; minimally-qualified # 🧟♂ E5.0 man zombie 1F9DF 200D 2640 FE0F ; fully-qualified # 🧟♀️ E5.0 woman zombie 1F9DF 200D 2640 ; minimally-qualified # 🧟♀ E5.0 woman zombie +1F9CC ; fully-qualified # 🧌 E14.0 troll # subgroup: person-activity 1F486 ; fully-qualified # 💆 E0.6 person getting massage @@ -2208,7 +2319,7 @@ 1F3CC 1F3FF ; fully-qualified # 🏌🏿 E4.0 person golfing: dark skin tone 1F3CC FE0F 200D 2642 FE0F ; fully-qualified # 🏌️♂️ E4.0 man golfing 1F3CC 200D 2642 FE0F ; unqualified # 🏌♂️ E4.0 man golfing -1F3CC FE0F 200D 2642 ; unqualified # 🏌️♂ E4.0 man golfing +1F3CC FE0F 200D 2642 ; minimally-qualified # 🏌️♂ E4.0 man golfing 1F3CC 200D 2642 ; unqualified # 🏌♂ E4.0 man golfing 1F3CC 1F3FB 200D 2642 FE0F ; fully-qualified # 🏌🏻♂️ E4.0 man golfing: light skin tone 1F3CC 1F3FB 200D 2642 ; minimally-qualified # 🏌🏻♂ E4.0 man golfing: light skin tone @@ -2222,7 +2333,7 @@ 1F3CC 1F3FF 200D 2642 ; minimally-qualified # 🏌🏿♂ E4.0 man golfing: dark skin tone 1F3CC FE0F 200D 2640 FE0F ; fully-qualified # 🏌️♀️ E4.0 woman golfing 1F3CC 200D 2640 FE0F ; unqualified # 🏌♀️ E4.0 woman golfing -1F3CC FE0F 200D 2640 ; unqualified # 🏌️♀ E4.0 woman golfing +1F3CC FE0F 200D 2640 ; minimally-qualified # 🏌️♀ E4.0 woman golfing 1F3CC 200D 2640 ; unqualified # 🏌♀ E4.0 woman golfing 1F3CC 1F3FB 200D 2640 FE0F ; fully-qualified # 🏌🏻♀️ E4.0 woman golfing: light skin tone 1F3CC 1F3FB 200D 2640 ; minimally-qualified # 🏌🏻♀ E4.0 woman golfing: light skin tone @@ -2333,7 +2444,7 @@ 26F9 1F3FF ; fully-qualified # ⛹🏿 E2.0 person bouncing ball: dark skin tone 26F9 FE0F 200D 2642 FE0F ; fully-qualified # ⛹️♂️ E4.0 man bouncing ball 26F9 200D 2642 FE0F ; unqualified # ⛹♂️ E4.0 man bouncing ball -26F9 FE0F 200D 2642 ; unqualified # ⛹️♂ E4.0 man bouncing ball +26F9 FE0F 200D 2642 ; minimally-qualified # ⛹️♂ E4.0 man bouncing ball 26F9 200D 2642 ; unqualified # ⛹♂ E4.0 man bouncing ball 26F9 1F3FB 200D 2642 FE0F ; fully-qualified # ⛹🏻♂️ E4.0 man bouncing ball: light skin tone 26F9 1F3FB 200D 2642 ; minimally-qualified # ⛹🏻♂ E4.0 man bouncing ball: light skin tone @@ -2347,7 +2458,7 @@ 26F9 1F3FF 200D 2642 ; minimally-qualified # ⛹🏿♂ E4.0 man bouncing ball: dark skin tone 26F9 FE0F 200D 2640 FE0F ; fully-qualified # ⛹️♀️ E4.0 woman bouncing ball 26F9 200D 2640 FE0F ; unqualified # ⛹♀️ E4.0 woman bouncing ball -26F9 FE0F 200D 2640 ; unqualified # ⛹️♀ E4.0 woman bouncing ball +26F9 FE0F 200D 2640 ; minimally-qualified # ⛹️♀ E4.0 woman bouncing ball 26F9 200D 2640 ; unqualified # ⛹♀ E4.0 woman bouncing ball 26F9 1F3FB 200D 2640 FE0F ; fully-qualified # ⛹🏻♀️ E4.0 woman bouncing ball: light skin tone 26F9 1F3FB 200D 2640 ; minimally-qualified # ⛹🏻♀ E4.0 woman bouncing ball: light skin tone @@ -2368,7 +2479,7 @@ 1F3CB 1F3FF ; fully-qualified # 🏋🏿 E2.0 person lifting weights: dark skin tone 1F3CB FE0F 200D 2642 FE0F ; fully-qualified # 🏋️♂️ E4.0 man lifting weights 1F3CB 200D 2642 FE0F ; unqualified # 🏋♂️ E4.0 man lifting weights -1F3CB FE0F 200D 2642 ; unqualified # 🏋️♂ E4.0 man lifting weights +1F3CB FE0F 200D 2642 ; minimally-qualified # 🏋️♂ E4.0 man lifting weights 1F3CB 200D 2642 ; unqualified # 🏋♂ E4.0 man lifting weights 1F3CB 1F3FB 200D 2642 FE0F ; fully-qualified # 🏋🏻♂️ E4.0 man lifting weights: light skin tone 1F3CB 1F3FB 200D 2642 ; minimally-qualified # 🏋🏻♂ E4.0 man lifting weights: light skin tone @@ -2382,7 +2493,7 @@ 1F3CB 1F3FF 200D 2642 ; minimally-qualified # 🏋🏿♂ E4.0 man lifting weights: dark skin tone 1F3CB FE0F 200D 2640 FE0F ; fully-qualified # 🏋️♀️ E4.0 woman lifting weights 1F3CB 200D 2640 FE0F ; unqualified # 🏋♀️ E4.0 woman lifting weights -1F3CB FE0F 200D 2640 ; unqualified # 🏋️♀ E4.0 woman lifting weights +1F3CB FE0F 200D 2640 ; minimally-qualified # 🏋️♀ E4.0 woman lifting weights 1F3CB 200D 2640 ; unqualified # 🏋♀ E4.0 woman lifting weights 1F3CB 1F3FB 200D 2640 FE0F ; fully-qualified # 🏋🏻♀️ E4.0 woman lifting weights: light skin tone 1F3CB 1F3FB 200D 2640 ; minimally-qualified # 🏋🏻♀ E4.0 woman lifting weights: light skin tone @@ -3168,8 +3279,8 @@ 1FAC2 ; fully-qualified # 🫂 E13.0 people hugging 1F463 ; fully-qualified # 👣 E0.6 footprints -# People & Body subtotal: 2899 -# People & Body subtotal: 494 w/o modifiers +# People & Body subtotal: 2998 +# People & Body subtotal: 508 w/o modifiers # group: Component @@ -3212,6 +3323,8 @@ 1F405 ; fully-qualified # 🐅 E1.0 tiger 1F406 ; fully-qualified # 🐆 E1.0 leopard 1F434 ; fully-qualified # 🐴 E0.6 horse face +1FACE ; fully-qualified # 🫎 E15.0 moose +1FACF ; fully-qualified # 🫏 E15.0 donkey 1F40E ; fully-qualified # 🐎 E0.6 horse 1F984 ; fully-qualified # 🦄 E1.0 unicorn 1F993 ; fully-qualified # 🦓 E5.0 zebra @@ -3279,6 +3392,9 @@ 1F9A9 ; fully-qualified # 🦩 E12.0 flamingo 1F99A ; fully-qualified # 🦚 E11.0 peacock 1F99C ; fully-qualified # 🦜 E11.0 parrot +1FABD ; fully-qualified # 🪽 E15.0 wing +1F426 200D 2B1B ; fully-qualified # 🐦⬛ E15.0 black bird +1FABF ; fully-qualified # 🪿 E15.0 goose # subgroup: animal-amphibian 1F438 ; fully-qualified # 🐸 E0.6 frog @@ -3304,6 +3420,8 @@ 1F988 ; fully-qualified # 🦈 E3.0 shark 1F419 ; fully-qualified # 🐙 E0.6 octopus 1F41A ; fully-qualified # 🐚 E0.6 spiral shell +1FAB8 ; fully-qualified # 🪸 E14.0 coral +1FABC ; fully-qualified # 🪼 E15.0 jellyfish # subgroup: animal-bug 1F40C ; fully-qualified # 🐌 E0.6 snail @@ -3329,6 +3447,7 @@ 1F490 ; fully-qualified # 💐 E0.6 bouquet 1F338 ; fully-qualified # 🌸 E0.6 cherry blossom 1F4AE ; fully-qualified # 💮 E0.6 white flower +1FAB7 ; fully-qualified # 🪷 E14.0 lotus 1F3F5 FE0F ; fully-qualified # 🏵️ E0.7 rosette 1F3F5 ; unqualified # 🏵 E0.7 rosette 1F339 ; fully-qualified # 🌹 E0.6 rose @@ -3337,6 +3456,7 @@ 1F33B ; fully-qualified # 🌻 E0.6 sunflower 1F33C ; fully-qualified # 🌼 E0.6 blossom 1F337 ; fully-qualified # 🌷 E0.6 tulip +1FABB ; fully-qualified # 🪻 E15.0 hyacinth # subgroup: plant-other 1F331 ; fully-qualified # 🌱 E0.6 seedling @@ -3353,9 +3473,12 @@ 1F341 ; fully-qualified # 🍁 E0.6 maple leaf 1F342 ; fully-qualified # 🍂 E0.6 fallen leaf 1F343 ; fully-qualified # 🍃 E0.6 leaf fluttering in wind +1FAB9 ; fully-qualified # 🪹 E14.0 empty nest +1FABA ; fully-qualified # 🪺 E14.0 nest with eggs +1F344 ; fully-qualified # 🍄 E0.6 mushroom -# Animals & Nature subtotal: 147 -# Animals & Nature subtotal: 147 w/o modifiers +# Animals & Nature subtotal: 159 +# Animals & Nature subtotal: 159 w/o modifiers # group: Food & Drink @@ -3394,9 +3517,11 @@ 1F966 ; fully-qualified # 🥦 E5.0 broccoli 1F9C4 ; fully-qualified # 🧄 E12.0 garlic 1F9C5 ; fully-qualified # 🧅 E12.0 onion -1F344 ; fully-qualified # 🍄 E0.6 mushroom 1F95C ; fully-qualified # 🥜 E3.0 peanuts +1FAD8 ; fully-qualified # 🫘 E14.0 beans 1F330 ; fully-qualified # 🌰 E0.6 chestnut +1FADA ; fully-qualified # 🫚 E15.0 ginger root +1FADB ; fully-qualified # 🫛 E15.0 pea pod # subgroup: food-prepared 1F35E ; fully-qualified # 🍞 E0.6 bread @@ -3491,6 +3616,7 @@ 1F37B ; fully-qualified # 🍻 E0.6 clinking beer mugs 1F942 ; fully-qualified # 🥂 E3.0 clinking glasses 1F943 ; fully-qualified # 🥃 E3.0 tumbler glass +1FAD7 ; fully-qualified # 🫗 E14.0 pouring liquid 1F964 ; fully-qualified # 🥤 E5.0 cup with straw 1F9CB ; fully-qualified # 🧋 E13.0 bubble tea 1F9C3 ; fully-qualified # 🧃 E12.0 beverage box @@ -3504,10 +3630,11 @@ 1F374 ; fully-qualified # 🍴 E0.6 fork and knife 1F944 ; fully-qualified # 🥄 E3.0 spoon 1F52A ; fully-qualified # 🔪 E0.6 kitchen knife +1FAD9 ; fully-qualified # 🫙 E14.0 jar 1F3FA ; fully-qualified # 🏺 E1.0 amphora -# Food & Drink subtotal: 131 -# Food & Drink subtotal: 131 w/o modifiers +# Food & Drink subtotal: 135 +# Food & Drink subtotal: 135 w/o modifiers # group: Travel & Places @@ -3597,6 +3724,7 @@ 2668 FE0F ; fully-qualified # ♨️ E0.6 hot springs 2668 ; unqualified # ♨ E0.6 hot springs 1F3A0 ; fully-qualified # 🎠 E0.6 carousel horse +1F6DD ; fully-qualified # 🛝 E14.0 playground slide 1F3A1 ; fully-qualified # 🎡 E0.6 ferris wheel 1F3A2 ; fully-qualified # 🎢 E0.6 roller coaster 1F488 ; fully-qualified # 💈 E0.6 barber pole @@ -3652,6 +3780,7 @@ 1F6E2 FE0F ; fully-qualified # 🛢️ E0.7 oil drum 1F6E2 ; unqualified # 🛢 E0.7 oil drum 26FD ; fully-qualified # ⛽ E0.6 fuel pump +1F6DE ; fully-qualified # 🛞 E14.0 wheel 1F6A8 ; fully-qualified # 🚨 E0.6 police car light 1F6A5 ; fully-qualified # 🚥 E0.6 horizontal traffic light 1F6A6 ; fully-qualified # 🚦 E1.0 vertical traffic light @@ -3660,6 +3789,7 @@ # subgroup: transport-water 2693 ; fully-qualified # ⚓ E0.6 anchor +1F6DF ; fully-qualified # 🛟 E14.0 ring buoy 26F5 ; fully-qualified # ⛵ E0.6 sailboat 1F6F6 ; fully-qualified # 🛶 E3.0 canoe 1F6A4 ; fully-qualified # 🚤 E0.6 speedboat @@ -3797,8 +3927,8 @@ 1F4A7 ; fully-qualified # 💧 E0.6 droplet 1F30A ; fully-qualified # 🌊 E0.6 water wave -# Travel & Places subtotal: 264 -# Travel & Places subtotal: 264 w/o modifiers +# Travel & Places subtotal: 267 +# Travel & Places subtotal: 267 w/o modifiers # group: Activities @@ -3870,10 +4000,10 @@ 1F3AF ; fully-qualified # 🎯 E0.6 bullseye 1FA80 ; fully-qualified # 🪀 E12.0 yo-yo 1FA81 ; fully-qualified # 🪁 E12.0 kite +1F52B ; fully-qualified # 🔫 E0.6 water pistol 1F3B1 ; fully-qualified # 🎱 E0.6 pool 8 ball 1F52E ; fully-qualified # 🔮 E0.6 crystal ball 1FA84 ; fully-qualified # 🪄 E13.0 magic wand -1F9FF ; fully-qualified # 🧿 E11.0 nazar amulet 1F3AE ; fully-qualified # 🎮 E0.6 video game 1F579 FE0F ; fully-qualified # 🕹️ E0.7 joystick 1F579 ; unqualified # 🕹 E0.7 joystick @@ -3882,6 +4012,7 @@ 1F9E9 ; fully-qualified # 🧩 E11.0 puzzle piece 1F9F8 ; fully-qualified # 🧸 E11.0 teddy bear 1FA85 ; fully-qualified # 🪅 E13.0 piñata +1FAA9 ; fully-qualified # 🪩 E14.0 mirror ball 1FA86 ; fully-qualified # 🪆 E13.0 nesting dolls 2660 FE0F ; fully-qualified # ♠️ E0.6 spade suit 2660 ; unqualified # ♠ E0.6 spade suit @@ -3907,8 +4038,8 @@ 1F9F6 ; fully-qualified # 🧶 E11.0 yarn 1FAA2 ; fully-qualified # 🪢 E13.0 knot -# Activities subtotal: 95 -# Activities subtotal: 95 w/o modifiers +# Activities subtotal: 96 +# Activities subtotal: 96 w/o modifiers # group: Objects @@ -3934,6 +4065,7 @@ 1FA73 ; fully-qualified # 🩳 E12.0 shorts 1F459 ; fully-qualified # 👙 E0.6 bikini 1F45A ; fully-qualified # 👚 E0.6 woman’s clothes +1FAAD ; fully-qualified # 🪭 E15.0 folding hand fan 1F45B ; fully-qualified # 👛 E0.6 purse 1F45C ; fully-qualified # 👜 E0.6 handbag 1F45D ; fully-qualified # 👝 E0.6 clutch bag @@ -3949,6 +4081,7 @@ 1F461 ; fully-qualified # 👡 E0.6 woman’s sandal 1FA70 ; fully-qualified # 🩰 E12.0 ballet shoes 1F462 ; fully-qualified # 👢 E0.6 woman’s boot +1FAAE ; fully-qualified # 🪮 E15.0 hair pick 1F451 ; fully-qualified # 👑 E0.6 crown 1F452 ; fully-qualified # 👒 E0.6 woman’s hat 1F3A9 ; fully-qualified # 🎩 E0.6 top hat @@ -3997,6 +4130,8 @@ 1FA95 ; fully-qualified # 🪕 E12.0 banjo 1F941 ; fully-qualified # 🥁 E3.0 drum 1FA98 ; fully-qualified # 🪘 E13.0 long drum +1FA87 ; fully-qualified # 🪇 E15.0 maracas +1FA88 ; fully-qualified # 🪈 E15.0 flute # subgroup: phone 1F4F1 ; fully-qualified # 📱 E0.6 mobile phone @@ -4009,6 +4144,7 @@ # subgroup: computer 1F50B ; fully-qualified # 🔋 E0.6 battery +1FAAB ; fully-qualified # 🪫 E14.0 low battery 1F50C ; fully-qualified # 🔌 E0.6 electric plug 1F4BB ; fully-qualified # 💻 E0.6 laptop 1F5A5 FE0F ; fully-qualified # 🖥️ E0.7 desktop computer @@ -4168,7 +4304,7 @@ 1F5E1 ; unqualified # 🗡 E0.7 dagger 2694 FE0F ; fully-qualified # ⚔️ E1.0 crossed swords 2694 ; unqualified # ⚔ E1.0 crossed swords -1F52B ; fully-qualified # 🔫 E0.6 water pistol +1F4A3 ; fully-qualified # 💣 E0.6 bomb 1FA83 ; fully-qualified # 🪃 E13.0 boomerang 1F3F9 ; fully-qualified # 🏹 E1.0 bow and arrow 1F6E1 FE0F ; fully-qualified # 🛡️ E0.7 shield @@ -4207,7 +4343,9 @@ 1FA78 ; fully-qualified # 🩸 E12.0 drop of blood 1F48A ; fully-qualified # 💊 E0.6 pill 1FA79 ; fully-qualified # 🩹 E12.0 adhesive bandage +1FA7C ; fully-qualified # 🩼 E14.0 crutch 1FA7A ; fully-qualified # 🩺 E12.0 stethoscope +1FA7B ; fully-qualified # 🩻 E14.0 x-ray # subgroup: household 1F6AA ; fully-qualified # 🚪 E0.6 door @@ -4232,6 +4370,7 @@ 1F9FB ; fully-qualified # 🧻 E11.0 roll of paper 1FAA3 ; fully-qualified # 🪣 E13.0 bucket 1F9FC ; fully-qualified # 🧼 E11.0 soap +1FAE7 ; fully-qualified # 🫧 E14.0 bubbles 1FAA5 ; fully-qualified # 🪥 E13.0 toothbrush 1F9FD ; fully-qualified # 🧽 E11.0 sponge 1F9EF ; fully-qualified # 🧯 E11.0 fire extinguisher @@ -4244,11 +4383,14 @@ 1FAA6 ; fully-qualified # 🪦 E13.0 headstone 26B1 FE0F ; fully-qualified # ⚱️ E1.0 funeral urn 26B1 ; unqualified # ⚱ E1.0 funeral urn +1F9FF ; fully-qualified # 🧿 E11.0 nazar amulet +1FAAC ; fully-qualified # 🪬 E14.0 hamsa 1F5FF ; fully-qualified # 🗿 E0.6 moai 1FAA7 ; fully-qualified # 🪧 E13.0 placard +1FAAA ; fully-qualified # 🪪 E14.0 identification card -# Objects subtotal: 299 -# Objects subtotal: 299 w/o modifiers +# Objects subtotal: 310 +# Objects subtotal: 310 w/o modifiers # group: Symbols @@ -4344,6 +4486,7 @@ 262E ; unqualified # ☮ E1.0 peace symbol 1F54E ; fully-qualified # 🕎 E1.0 menorah 1F52F ; fully-qualified # 🔯 E0.6 dotted six-pointed star +1FAAF ; fully-qualified # 🪯 E15.0 khanda # subgroup: zodiac 2648 ; fully-qualified # ♈ E0.6 Aries @@ -4392,6 +4535,7 @@ 1F505 ; fully-qualified # 🔅 E1.0 dim button 1F506 ; fully-qualified # 🔆 E1.0 bright button 1F4F6 ; fully-qualified # 📶 E0.6 antenna bars +1F6DC ; fully-qualified # 🛜 E15.0 wireless 1F4F3 ; fully-qualified # 📳 E0.6 vibration mode 1F4F4 ; fully-qualified # 📴 E0.6 mobile phone off @@ -4409,6 +4553,7 @@ 2795 ; fully-qualified # ➕ E0.6 plus 2796 ; fully-qualified # ➖ E0.6 minus 2797 ; fully-qualified # ➗ E0.6 divide +1F7F0 ; fully-qualified # 🟰 E14.0 heavy equals sign 267E FE0F ; fully-qualified # ♾️ E11.0 infinity 267E ; unqualified # ♾ E11.0 infinity @@ -4581,8 +4726,8 @@ 1F533 ; fully-qualified # 🔳 E0.6 white square button 1F532 ; fully-qualified # 🔲 E0.6 black square button -# Symbols subtotal: 301 -# Symbols subtotal: 301 w/o modifiers +# Symbols subtotal: 304 +# Symbols subtotal: 304 w/o modifiers # group: Flags @@ -4597,7 +4742,7 @@ 1F3F3 200D 1F308 ; unqualified # 🏳🌈 E4.0 rainbow flag 1F3F3 FE0F 200D 26A7 FE0F ; fully-qualified # 🏳️⚧️ E13.0 transgender flag 1F3F3 200D 26A7 FE0F ; unqualified # 🏳⚧️ E13.0 transgender flag -1F3F3 FE0F 200D 26A7 ; unqualified # 🏳️⚧ E13.0 transgender flag +1F3F3 FE0F 200D 26A7 ; minimally-qualified # 🏳️⚧ E13.0 transgender flag 1F3F3 200D 26A7 ; unqualified # 🏳⚧ E13.0 transgender flag 1F3F4 200D 2620 FE0F ; fully-qualified # 🏴☠️ E11.0 pirate flag 1F3F4 200D 2620 ; minimally-qualified # 🏴☠ E11.0 pirate flag @@ -4871,9 +5016,9 @@ # Flags subtotal: 275 w/o modifiers # Status Counts -# fully-qualified : 3512 -# minimally-qualified : 817 -# unqualified : 252 +# fully-qualified : 3655 +# minimally-qualified : 827 +# unqualified : 242 # component : 9 #EOF diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex index f077fe5b4..dd65d56ae 100644 --- a/lib/pleroma/emoji.ex +++ b/lib/pleroma/emoji.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emoji do @@ -9,6 +9,7 @@ defmodule Pleroma.Emoji do """ use GenServer + alias Pleroma.Emoji.Combinations alias Pleroma.Emoji.Loader require Logger @@ -137,4 +138,17 @@ defmodule Pleroma.Emoji do end def is_unicode_emoji?(_), do: false + + emoji_qualification_map = + emojis + |> Enum.filter(&String.contains?(&1, "\uFE0F")) + |> Combinations.variate_emoji_qualification() + + for {qualified, unqualified_list} <- emoji_qualification_map do + for unqualified <- unqualified_list do + def fully_qualify_emoji(unquote(unqualified)), do: unquote(qualified) + end + end + + def fully_qualify_emoji(emoji), do: emoji end diff --git a/lib/pleroma/emoji/combinations.ex b/lib/pleroma/emoji/combinations.ex new file mode 100644 index 000000000..981c73596 --- /dev/null +++ b/lib/pleroma/emoji/combinations.ex @@ -0,0 +1,45 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Emoji.Combinations do + # FE0F is the emoji variation sequence. It is used for fully-qualifying + # emoji, and that includes emoji combinations. + # This code generates combinations per emoji: for each FE0F, all possible + # combinations of the character being removed or staying will be generated. + # This is made as an attempt to find all partially-qualified and unqualified + # versions of a fully-qualified emoji. + # I have found *no cases* for which this would be a problem, after browsing + # the entire emoji list in emoji-test.txt. This is safe, and, sadly, most + # likely sane too. + + defp qualification_combinations(codepoints) do + qualification_combinations([[]], codepoints) + end + + defp qualification_combinations(acc, []), do: acc + + defp qualification_combinations(acc, ["\uFE0F" | tail]) do + acc + |> Enum.flat_map(fn x -> [x, x ++ ["\uFE0F"]] end) + |> qualification_combinations(tail) + end + + defp qualification_combinations(acc, [codepoint | tail]) do + acc + |> Enum.map(&Kernel.++(&1, [codepoint])) + |> qualification_combinations(tail) + end + + def variate_emoji_qualification(emoji) when is_binary(emoji) do + emoji + |> String.codepoints() + |> qualification_combinations() + |> Enum.map(&List.to_string/1) + end + + def variate_emoji_qualification(emoji) when is_list(emoji) do + emoji + |> Enum.map(fn emoji -> {emoji, variate_emoji_qualification(emoji)} end) + end +end diff --git a/lib/pleroma/emoji/formatter.ex b/lib/pleroma/emoji/formatter.ex index 191451952..87fd35f13 100644 --- a/lib/pleroma/emoji/formatter.ex +++ b/lib/pleroma/emoji/formatter.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emoji.Formatter do diff --git a/lib/pleroma/emoji/loader.ex b/lib/pleroma/emoji/loader.ex index 67acd7069..97d4b8f70 100644 --- a/lib/pleroma/emoji/loader.ex +++ b/lib/pleroma/emoji/loader.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emoji.Loader do @@ -60,9 +60,7 @@ defmodule Pleroma.Emoji.Loader do if not Enum.empty?(files) do Logger.warn( - "Found files in the emoji folder. These will be ignored, please move them to a subdirectory\nFound files: #{ - Enum.join(files, ", ") - }" + "Found files in the emoji folder. These will be ignored, please move them to a subdirectory\nFound files: #{Enum.join(files, ", ")}" ) end @@ -105,6 +103,7 @@ defmodule Pleroma.Emoji.Loader do pack_file = Path.join(pack_dir, "pack.json") if File.exists?(pack_file) do + Logger.info("Loading emoji pack from JSON: #{pack_file}") contents = Jason.decode!(File.read!(pack_file)) contents["files"] @@ -117,14 +116,13 @@ defmodule Pleroma.Emoji.Loader do emoji_txt = Path.join(pack_dir, "emoji.txt") if File.exists?(emoji_txt) do + Logger.info("Loading emoji pack from emoji.txt: #{emoji_txt}") load_from_file(emoji_txt, emoji_groups) else extensions = Config.get([:emoji, :pack_extensions]) Logger.info( - "No emoji.txt found for pack \"#{pack_name}\", assuming all #{ - Enum.join(extensions, ", ") - } files are emoji" + "No emoji.txt found for pack \"#{pack_name}\", assuming all #{Enum.join(extensions, ", ")} files are emoji" ) make_shortcode_to_file_map(pack_dir, extensions) diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex index 09bfcc868..a361ea200 100644 --- a/lib/pleroma/emoji/pack.ex +++ b/lib/pleroma/emoji/pack.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emoji.Pack do diff --git a/lib/pleroma/filter.ex b/lib/pleroma/filter.ex index 82b9caf9b..db88bc021 100644 --- a/lib/pleroma/filter.ex +++ b/lib/pleroma/filter.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Filter do diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index a0c7e6e39..15664c876 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.FollowingRelationship do @@ -194,12 +194,13 @@ defmodule Pleroma.FollowingRelationship do |> join(:inner, [r], f in assoc(r, :follower)) |> where(following_id: ^origin.id) |> where([r, f], f.allow_following_move == true) + |> where([r, f], f.local == true) |> limit(50) |> preload([:follower]) |> Repo.all() |> Enum.map(fn following_relationship -> - Repo.delete(following_relationship) Pleroma.Web.CommonAPI.follow(following_relationship.follower, target) + Pleroma.Web.CommonAPI.unfollow(following_relationship.follower, origin) end) |> case do [] -> diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex index ae37946ab..a46c3e381 100644 --- a/lib/pleroma/formatter.ex +++ b/lib/pleroma/formatter.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Formatter do @@ -34,32 +34,34 @@ defmodule Pleroma.Formatter do def mention_handler("@" <> nickname, buffer, opts, acc) do case User.get_cached_by_nickname(nickname) do - %User{id: id} = user -> - user_url = user.uri || user.ap_id - nickname_text = get_nickname_text(nickname, opts) - - link = - Phoenix.HTML.Tag.content_tag( - :span, - Phoenix.HTML.Tag.content_tag( - :a, - ["@", Phoenix.HTML.Tag.content_tag(:span, nickname_text)], - "data-user": id, - class: "u-url mention", - href: user_url, - rel: "ugc" - ), - class: "h-card" - ) - |> Phoenix.HTML.safe_to_string() - - {link, %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}} + %User{} = user -> + {mention_from_user(user, opts), + %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}} _ -> {buffer, acc} end end + def mention_from_user(%User{id: id} = user, opts \\ %{mentions_format: :full}) do + user_url = user.uri || user.ap_id + nickname_text = get_nickname_text(user.nickname, opts) + + Phoenix.HTML.Tag.content_tag( + :span, + Phoenix.HTML.Tag.content_tag( + :a, + ["@", Phoenix.HTML.Tag.content_tag(:span, nickname_text)], + "data-user": id, + class: "u-url mention", + href: user_url, + rel: "ugc" + ), + class: "h-card" + ) + |> Phoenix.HTML.safe_to_string() + end + def hashtag_handler("#" <> tag = tag_text, _buffer, _opts, acc) do tag = String.downcase(tag) url = "#{Pleroma.Web.Endpoint.url()}/tag/#{tag}" diff --git a/lib/pleroma/frontend.ex b/lib/pleroma/frontend.ex index 34b7befb8..ec72fb6a4 100644 --- a/lib/pleroma/frontend.ex +++ b/lib/pleroma/frontend.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Frontend do diff --git a/lib/pleroma/gopher/server.ex b/lib/pleroma/gopher/server.ex index 1b85c49f5..0fde0adcf 100644 --- a/lib/pleroma/gopher/server.ex +++ b/lib/pleroma/gopher/server.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gopher.Server do diff --git a/lib/pleroma/gun.ex b/lib/pleroma/gun.ex index bef1c9872..c2d6b4bf2 100644 --- a/lib/pleroma/gun.ex +++ b/lib/pleroma/gun.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun do diff --git a/lib/pleroma/gun/api.ex b/lib/pleroma/gun/api.ex index 24d542781..ff2100623 100644 --- a/lib/pleroma/gun/api.ex +++ b/lib/pleroma/gun/api.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun.API do diff --git a/lib/pleroma/gun/conn.ex b/lib/pleroma/gun/conn.ex index a56625699..7c5785def 100644 --- a/lib/pleroma/gun/conn.ex +++ b/lib/pleroma/gun/conn.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun.Conn do @@ -57,9 +57,7 @@ defmodule Pleroma.Gun.Conn do else error -> Logger.warn( - "Opening proxied connection to #{compose_uri_log(uri)} failed with error #{ - inspect(error) - }" + "Opening proxied connection to #{compose_uri_log(uri)} failed with error #{inspect(error)}" ) error @@ -93,9 +91,7 @@ defmodule Pleroma.Gun.Conn do else error -> Logger.warn( - "Opening socks proxied connection to #{compose_uri_log(uri)} failed with error #{ - inspect(error) - }" + "Opening socks proxied connection to #{compose_uri_log(uri)} failed with error #{inspect(error)}" ) error diff --git a/lib/pleroma/gun/connection_pool.ex b/lib/pleroma/gun/connection_pool.ex index f9fd77ade..2e851de19 100644 --- a/lib/pleroma/gun/connection_pool.ex +++ b/lib/pleroma/gun/connection_pool.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun.ConnectionPool do diff --git a/lib/pleroma/gun/connection_pool/reclaimer.ex b/lib/pleroma/gun/connection_pool/reclaimer.ex index 4c643d7cb..efd5c9fb8 100644 --- a/lib/pleroma/gun/connection_pool/reclaimer.ex +++ b/lib/pleroma/gun/connection_pool/reclaimer.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun.ConnectionPool.Reclaimer do diff --git a/lib/pleroma/gun/connection_pool/worker.ex b/lib/pleroma/gun/connection_pool/worker.ex index a3fa75386..38527ec1d 100644 --- a/lib/pleroma/gun/connection_pool/worker.ex +++ b/lib/pleroma/gun/connection_pool/worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun.ConnectionPool.Worker do diff --git a/lib/pleroma/gun/connection_pool/worker_supervisor.ex b/lib/pleroma/gun/connection_pool/worker_supervisor.ex index 016b675f4..d26a70be3 100644 --- a/lib/pleroma/gun/connection_pool/worker_supervisor.ex +++ b/lib/pleroma/gun/connection_pool/worker_supervisor.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun.ConnectionPool.WorkerSupervisor do diff --git a/lib/pleroma/hashtag.ex b/lib/pleroma/hashtag.ex index 53e2e9c89..a43d88220 100644 --- a/lib/pleroma/hashtag.ex +++ b/lib/pleroma/hashtag.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Hashtag do diff --git a/lib/pleroma/healthcheck.ex b/lib/pleroma/healthcheck.ex index c905bba3f..8e9ab8225 100644 --- a/lib/pleroma/healthcheck.ex +++ b/lib/pleroma/healthcheck.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Healthcheck do diff --git a/lib/pleroma/helpers/auth_helper.ex b/lib/pleroma/helpers/auth_helper.ex index 13e4c8158..61599e71c 100644 --- a/lib/pleroma/helpers/auth_helper.ex +++ b/lib/pleroma/helpers/auth_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Helpers.AuthHelper do diff --git a/lib/pleroma/helpers/inet_helper.ex b/lib/pleroma/helpers/inet_helper.ex index 5acdfaed0..704d37f8a 100644 --- a/lib/pleroma/helpers/inet_helper.ex +++ b/lib/pleroma/helpers/inet_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Helpers.InetHelper do diff --git a/lib/pleroma/helpers/media_helper.ex b/lib/pleroma/helpers/media_helper.ex index 738adfcaa..24c845fcd 100644 --- a/lib/pleroma/helpers/media_helper.ex +++ b/lib/pleroma/helpers/media_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Helpers.MediaHelper do diff --git a/lib/pleroma/helpers/qt_fast_start.ex b/lib/pleroma/helpers/qt_fast_start.ex index c4d11b9dd..5711c7162 100644 --- a/lib/pleroma/helpers/qt_fast_start.ex +++ b/lib/pleroma/helpers/qt_fast_start.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Helpers.QtFastStart do diff --git a/lib/pleroma/helpers/uri_helper.ex b/lib/pleroma/helpers/uri_helper.ex index 8f6a664ad..c8d10d307 100644 --- a/lib/pleroma/helpers/uri_helper.ex +++ b/lib/pleroma/helpers/uri_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Helpers.UriHelper do diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index bee66169d..5bf735c4f 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTML do diff --git a/lib/pleroma/http.ex b/lib/pleroma/http.ex index 07b3ab0ae..d41061538 100644 --- a/lib/pleroma/http.ex +++ b/lib/pleroma/http.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP do @@ -106,5 +106,12 @@ defmodule Pleroma.HTTP do [Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.ConnectionPool] end - defp adapter_middlewares(_), do: [] + defp adapter_middlewares(_) do + if Pleroma.Config.get(:env) == :test do + # Emulate redirects in test env, which are handled by adapters in other environments + [Tesla.Middleware.FollowRedirects] + else + [] + end + end end diff --git a/lib/pleroma/http/adapter_helper.ex b/lib/pleroma/http/adapter_helper.ex index c667afd25..252a6aba5 100644 --- a/lib/pleroma/http/adapter_helper.ex +++ b/lib/pleroma/http/adapter_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.AdapterHelper do diff --git a/lib/pleroma/http/adapter_helper/default.ex b/lib/pleroma/http/adapter_helper/default.ex index a1614b9c5..9c9414738 100644 --- a/lib/pleroma/http/adapter_helper/default.ex +++ b/lib/pleroma/http/adapter_helper/default.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.AdapterHelper.Default do diff --git a/lib/pleroma/http/adapter_helper/gun.ex b/lib/pleroma/http/adapter_helper/gun.ex index 251539f34..74ab9851e 100644 --- a/lib/pleroma/http/adapter_helper/gun.ex +++ b/lib/pleroma/http/adapter_helper/gun.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.AdapterHelper.Gun do diff --git a/lib/pleroma/http/adapter_helper/hackney.ex b/lib/pleroma/http/adapter_helper/hackney.ex index fe3f91a72..f3be1f3d0 100644 --- a/lib/pleroma/http/adapter_helper/hackney.ex +++ b/lib/pleroma/http/adapter_helper/hackney.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.AdapterHelper.Hackney do @@ -24,10 +24,6 @@ defmodule Pleroma.HTTP.AdapterHelper.Hackney do |> Pleroma.HTTP.AdapterHelper.maybe_add_proxy(proxy) end - defp add_scheme_opts(opts, %URI{scheme: "https"}) do - Keyword.put(opts, :ssl_options, versions: [:"tlsv1.2", :"tlsv1.1", :tlsv1]) - end - defp add_scheme_opts(opts, _), do: opts defp maybe_add_with_body(opts) do diff --git a/lib/pleroma/http/ex_aws.ex b/lib/pleroma/http/ex_aws.ex index 283590b18..469c13819 100644 --- a/lib/pleroma/http/ex_aws.ex +++ b/lib/pleroma/http/ex_aws.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.ExAws do diff --git a/lib/pleroma/http/request.ex b/lib/pleroma/http/request.ex index d906024de..01045f8c9 100644 --- a/lib/pleroma/http/request.ex +++ b/lib/pleroma/http/request.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.Request do diff --git a/lib/pleroma/http/request_builder.ex b/lib/pleroma/http/request_builder.ex index 631c927af..f16fb3b35 100644 --- a/lib/pleroma/http/request_builder.ex +++ b/lib/pleroma/http/request_builder.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.RequestBuilder do diff --git a/lib/pleroma/http/tzdata.ex b/lib/pleroma/http/tzdata.ex index 77e1b537e..5d2529c08 100644 --- a/lib/pleroma/http/tzdata.ex +++ b/lib/pleroma/http/tzdata.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.Tzdata do diff --git a/lib/pleroma/http/web_push.ex b/lib/pleroma/http/web_push.ex index 16bbe6e8c..ca399b6c8 100644 --- a/lib/pleroma/http/web_push.ex +++ b/lib/pleroma/http/web_push.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.WebPush do diff --git a/lib/pleroma/instances.ex b/lib/pleroma/instances.ex index 6b57e56da..782948f83 100644 --- a/lib/pleroma/instances.ex +++ b/lib/pleroma/instances.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Instances do diff --git a/lib/pleroma/instances/instance.ex b/lib/pleroma/instances/instance.ex index 4d0e8034d..a5529ad44 100644 --- a/lib/pleroma/instances/instance.ex +++ b/lib/pleroma/instances/instance.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Instances.Instance do @@ -8,6 +8,8 @@ defmodule Pleroma.Instances.Instance do alias Pleroma.Instances alias Pleroma.Instances.Instance alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Workers.BackgroundWorker use Ecto.Schema @@ -195,4 +197,24 @@ defmodule Pleroma.Instances.Instance do nil end end + + @doc """ + Deletes all users from an instance in a background task, thus also deleting + all of those users' activities and notifications. + """ + def delete_users_and_activities(host) when is_binary(host) do + BackgroundWorker.enqueue("delete_instance", %{"host" => host}) + end + + def perform(:delete_instance, host) when is_binary(host) do + User.Query.build(%{nickname: "@#{host}"}) + |> Repo.chunk_stream(100, :batches) + |> Stream.each(fn users -> + users + |> Enum.each(fn user -> + User.perform(:delete, user) + end) + end) + |> Stream.run() + end end diff --git a/lib/pleroma/job_queue_monitor.ex b/lib/pleroma/job_queue_monitor.ex index b5f124923..6233cdcf5 100644 --- a/lib/pleroma/job_queue_monitor.ex +++ b/lib/pleroma/job_queue_monitor.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.JobQueueMonitor do diff --git a/lib/pleroma/jwt.ex b/lib/pleroma/jwt.ex index c75c44bd1..7ec6245d3 100644 --- a/lib/pleroma/jwt.ex +++ b/lib/pleroma/jwt.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.JWT do diff --git a/lib/pleroma/keys.ex b/lib/pleroma/keys.ex index 413861b15..496d20a71 100644 --- a/lib/pleroma/keys.ex +++ b/lib/pleroma/keys.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Keys do diff --git a/lib/pleroma/list.ex b/lib/pleroma/list.ex index fe5721c34..b446b91a0 100644 --- a/lib/pleroma/list.ex +++ b/lib/pleroma/list.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.List do diff --git a/lib/pleroma/logging.ex b/lib/pleroma/logging.ex index 11e1c3bed..ac09c0415 100644 --- a/lib/pleroma/logging.ex +++ b/lib/pleroma/logging.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Logging do diff --git a/lib/pleroma/maintenance.ex b/lib/pleroma/maintenance.ex index f1058b68a..eb5a6ef42 100644 --- a/lib/pleroma/maintenance.ex +++ b/lib/pleroma/maintenance.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Maintenance do @@ -9,7 +9,7 @@ defmodule Pleroma.Maintenance do def vacuum(args) do case args do "analyze" -> - Logger.info("Runnning VACUUM ANALYZE.") + Logger.info("Running VACUUM ANALYZE.") Repo.query!( "vacuum analyze;", @@ -18,7 +18,7 @@ defmodule Pleroma.Maintenance do ) "full" -> - Logger.info("Runnning VACUUM FULL.") + Logger.info("Running VACUUM FULL.") Logger.warn( "Re-packing your entire database may take a while and will consume extra disk space during the process." diff --git a/lib/pleroma/maps.ex b/lib/pleroma/maps.ex index b08b83305..6d586e53e 100644 --- a/lib/pleroma/maps.ex +++ b/lib/pleroma/maps.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Maps do diff --git a/lib/pleroma/marker.ex b/lib/pleroma/marker.ex index 9909de161..68b054e4d 100644 --- a/lib/pleroma/marker.ex +++ b/lib/pleroma/marker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Marker do diff --git a/lib/pleroma/mfa.ex b/lib/pleroma/mfa.ex index 02dce7d49..01b730c76 100644 --- a/lib/pleroma/mfa.ex +++ b/lib/pleroma/mfa.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA do diff --git a/lib/pleroma/mfa/backup_codes.ex b/lib/pleroma/mfa/backup_codes.ex index a7a1fba2e..2f6962c1f 100644 --- a/lib/pleroma/mfa/backup_codes.ex +++ b/lib/pleroma/mfa/backup_codes.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.BackupCodes do diff --git a/lib/pleroma/mfa/changeset.ex b/lib/pleroma/mfa/changeset.ex index 2d46cdf73..3ec3cfe91 100644 --- a/lib/pleroma/mfa/changeset.ex +++ b/lib/pleroma/mfa/changeset.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.Changeset do diff --git a/lib/pleroma/mfa/settings.ex b/lib/pleroma/mfa/settings.ex index 94fbff635..2c7f13e3f 100644 --- a/lib/pleroma/mfa/settings.ex +++ b/lib/pleroma/mfa/settings.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.Settings do diff --git a/lib/pleroma/mfa/token.ex b/lib/pleroma/mfa/token.ex index 76573182a..57bc11ed5 100644 --- a/lib/pleroma/mfa/token.ex +++ b/lib/pleroma/mfa/token.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.Token do diff --git a/lib/pleroma/mfa/totp.ex b/lib/pleroma/mfa/totp.ex index f33e3a379..429c4b700 100644 --- a/lib/pleroma/mfa/totp.ex +++ b/lib/pleroma/mfa/totp.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.TOTP do diff --git a/lib/pleroma/migration_helper/notification_backfill.ex b/lib/pleroma/migration_helper/notification_backfill.ex index 62b710f82..9f4976d9c 100644 --- a/lib/pleroma/migration_helper/notification_backfill.ex +++ b/lib/pleroma/migration_helper/notification_backfill.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MigrationHelper.NotificationBackfill do diff --git a/lib/pleroma/migrators/context_objects_deletion_migrator.ex b/lib/pleroma/migrators/context_objects_deletion_migrator.ex new file mode 100644 index 000000000..fb224795a --- /dev/null +++ b/lib/pleroma/migrators/context_objects_deletion_migrator.ex @@ -0,0 +1,139 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Migrators.ContextObjectsDeletionMigrator do + defmodule State do + use Pleroma.Migrators.Support.BaseMigratorState + + @impl Pleroma.Migrators.Support.BaseMigratorState + defdelegate data_migration(), to: Pleroma.DataMigration, as: :delete_context_objects + end + + use Pleroma.Migrators.Support.BaseMigrator + + alias Pleroma.Migrators.Support.BaseMigrator + alias Pleroma.Object + + @doc "This migration removes objects created exclusively for contexts, containing only an `id` field." + + @impl BaseMigrator + def feature_config_path, do: [:features, :delete_context_objects] + + @impl BaseMigrator + def fault_rate_allowance, do: Config.get([:delete_context_objects, :fault_rate_allowance], 0) + + @impl BaseMigrator + def perform do + data_migration_id = data_migration_id() + max_processed_id = get_stat(:max_processed_id, 0) + + Logger.info("Deleting context objects from `objects` (from oid: #{max_processed_id})...") + + query() + |> where([object], object.id > ^max_processed_id) + |> Repo.chunk_stream(100, :batches, timeout: :infinity) + |> Stream.each(fn objects -> + object_ids = Enum.map(objects, & &1.id) + + results = Enum.map(object_ids, &delete_context_object(&1)) + + failed_ids = + results + |> Enum.filter(&(elem(&1, 0) == :error)) + |> Enum.map(&elem(&1, 1)) + + chunk_affected_count = + results + |> Enum.filter(&(elem(&1, 0) == :ok)) + |> length() + + for failed_id <- failed_ids do + _ = + Repo.query( + "INSERT INTO data_migration_failed_ids(data_migration_id, record_id) " <> + "VALUES ($1, $2) ON CONFLICT DO NOTHING;", + [data_migration_id, failed_id] + ) + end + + _ = + Repo.query( + "DELETE FROM data_migration_failed_ids " <> + "WHERE data_migration_id = $1 AND record_id = ANY($2)", + [data_migration_id, object_ids -- failed_ids] + ) + + max_object_id = Enum.at(object_ids, -1) + + put_stat(:max_processed_id, max_object_id) + increment_stat(:iteration_processed_count, length(object_ids)) + increment_stat(:processed_count, length(object_ids)) + increment_stat(:failed_count, length(failed_ids)) + increment_stat(:affected_count, chunk_affected_count) + put_stat(:records_per_second, records_per_second()) + persist_state() + + # A quick and dirty approach to controlling the load this background migration imposes + sleep_interval = Config.get([:delete_context_objects, :sleep_interval_ms], 0) + Process.sleep(sleep_interval) + end) + |> Stream.run() + end + + @impl BaseMigrator + def query do + # Context objects have no activity type, and only one field, `id`. + # Only those context objects are without types. + from( + object in Object, + where: fragment("(?)->'type' IS NULL", object.data), + select: %{ + id: object.id + } + ) + end + + @spec delete_context_object(integer()) :: {:ok | :error, integer()} + defp delete_context_object(id) do + result = + %Object{id: id} + |> Repo.delete() + |> elem(0) + + {result, id} + end + + @impl BaseMigrator + def retry_failed do + data_migration_id = data_migration_id() + + failed_objects_query() + |> Repo.chunk_stream(100, :one) + |> Stream.each(fn object -> + with {res, _} when res != :error <- delete_context_object(object.id) do + _ = + Repo.query( + "DELETE FROM data_migration_failed_ids " <> + "WHERE data_migration_id = $1 AND record_id = $2", + [data_migration_id, object.id] + ) + end + end) + |> Stream.run() + + put_stat(:failed_count, failures_count()) + persist_state() + + force_continue() + end + + defp failed_objects_query do + from(o in Object) + |> join(:inner, [o], dmf in fragment("SELECT * FROM data_migration_failed_ids"), + on: dmf.record_id == o.id + ) + |> where([_o, dmf], dmf.data_migration_id == ^data_migration_id()) + |> order_by([o], asc: o.id) + end +end diff --git a/lib/pleroma/migrators/hashtags_table_migrator.ex b/lib/pleroma/migrators/hashtags_table_migrator.ex index b84058e11..dca4bfa6f 100644 --- a/lib/pleroma/migrators/hashtags_table_migrator.ex +++ b/lib/pleroma/migrators/hashtags_table_migrator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Migrators.HashtagsTableMigrator do @@ -183,7 +183,7 @@ defmodule Pleroma.Migrators.HashtagsTableMigrator do DELETE FROM hashtags_objects WHERE object_id IN (SELECT DISTINCT objects.id FROM objects JOIN hashtags_objects ON hashtags_objects.object_id = objects.id LEFT JOIN activities - ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = + ON associated_object_id(activities) = (objects.data->>'id') AND activities.data->>'type' = 'Create' WHERE activities.id IS NULL); diff --git a/lib/pleroma/migrators/support/base_migrator.ex b/lib/pleroma/migrators/support/base_migrator.ex index 1f8a5402b..3bcd59fd0 100644 --- a/lib/pleroma/migrators/support/base_migrator.ex +++ b/lib/pleroma/migrators/support/base_migrator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Migrators.Support.BaseMigrator do diff --git a/lib/pleroma/migrators/support/base_migrator_state.ex b/lib/pleroma/migrators/support/base_migrator_state.ex index b698587f2..3d7769fc6 100644 --- a/lib/pleroma/migrators/support/base_migrator_state.ex +++ b/lib/pleroma/migrators/support/base_migrator_state.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Migrators.Support.BaseMigratorState do diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex index 1849cacc8..7203423e2 100644 --- a/lib/pleroma/moderation_log.ex +++ b/lib/pleroma/moderation_log.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ModerationLog do @@ -341,6 +341,26 @@ defmodule Pleroma.ModerationLog do def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, + "action" => "add_suggestion", + "subject" => users + } + }) do + "@#{actor_nickname} added suggested users: #{users_to_nicknames_string(users)}" + end + + def get_log_entry_message(%ModerationLog{ + data: %{ + "actor" => %{"nickname" => actor_nickname}, + "action" => "remove_suggestion", + "subject" => users + } + }) do + "@#{actor_nickname} removed suggested users: #{users_to_nicknames_string(users)}" + end + + def get_log_entry_message(%ModerationLog{ + data: %{ + "actor" => %{"nickname" => actor_nickname}, "nicknames" => nicknames, "tags" => tags, "action" => "tag" @@ -481,9 +501,7 @@ defmodule Pleroma.ModerationLog do "visibility" => visibility } }) do - "@#{actor_nickname} updated status ##{subject_id}, set sensitive: '#{sensitive}', visibility: '#{ - visibility - }'" + "@#{actor_nickname} updated status ##{subject_id}, set sensitive: '#{sensitive}', visibility: '#{visibility}'" end def get_log_entry_message(%ModerationLog{ @@ -523,9 +541,7 @@ defmodule Pleroma.ModerationLog do "subject" => subjects } }) do - "@#{actor_nickname} re-sent confirmation email for users: #{ - users_to_nicknames_string(subjects) - }" + "@#{actor_nickname} re-sent confirmation email for users: #{users_to_nicknames_string(subjects)}" end def get_log_entry_message(%ModerationLog{ diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 71f57e6b2..d661d6ab2 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Notification do @@ -72,6 +72,7 @@ defmodule Pleroma.Notification do pleroma:emoji_reaction pleroma:report reblog + poll } def changeset(%Notification{} = notification, attrs) do @@ -116,9 +117,8 @@ defmodule Pleroma.Notification do |> join(:left, [n, a], object in Object, on: fragment( - "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", + "(?->>'id') = associated_object_id(?)", object.data, - a.data, a.data ) ) @@ -127,6 +127,7 @@ defmodule Pleroma.Notification do |> where([user_actor: user_actor], user_actor.is_active) |> exclude_notification_muted(user, exclude_notification_muted_opts) |> exclude_blocked(user, exclude_blocked_opts) + |> exclude_blockers(user) |> exclude_filtered(user) |> exclude_visibility(opts) end @@ -140,6 +141,17 @@ defmodule Pleroma.Notification do |> FollowingRelationship.keep_following_or_not_domain_blocked(user) end + defp exclude_blockers(query, user) do + if Pleroma.Config.get([:activitypub, :blockers_visible]) == true do + query + else + blocker_ap_ids = User.incoming_relationships_ungrouped_ap_ids(user, [:block]) + + query + |> where([n, a], a.actor not in ^blocker_ap_ids) + end + end + defp exclude_notification_muted(query, _, %{@include_muted_option => true}) do query end @@ -180,13 +192,11 @@ defmodule Pleroma.Notification do |> join(:left, [n, a], mutated_activity in Pleroma.Activity, on: fragment( - "COALESCE((?->'object')->>'id', ?->>'object')", - a.data, + "associated_object_id(?)", a.data ) == fragment( - "COALESCE((?->'object')->>'id', ?->>'object')", - mutated_activity.data, + "associated_object_id(?)", mutated_activity.data ) and fragment("(?->>'type' = 'Like' or ?->>'type' = 'Announce')", a.data, a.data) and @@ -372,7 +382,7 @@ defmodule Pleroma.Notification do end def create_notifications(%Activity{data: %{"type" => type}} = activity, options) - when type in ["Follow", "Like", "Announce", "Move", "EmojiReact", "Flag"] do + when type in ["Follow", "Like", "Announce", "Move", "EmojiReact", "Flag", "Update"] do do_create_notifications(activity, options) end @@ -387,7 +397,7 @@ defmodule Pleroma.Notification do notifications = Enum.map(potential_receivers, fn user -> do_send = do_send && user in enabled_receivers - create_notification(activity, user, do_send) + create_notification(activity, user, do_send: do_send) end) |> Enum.reject(&is_nil/1) @@ -426,6 +436,9 @@ defmodule Pleroma.Notification do activity |> type_from_activity_object() + "Update" -> + "update" + t -> raise "No notification type for activity type #{t}" end @@ -443,15 +456,18 @@ defmodule Pleroma.Notification do end # TODO move to sql, too. - def create_notification(%Activity{} = activity, %User{} = user, do_send \\ true) do - unless skip?(activity, user) do + def create_notification(%Activity{} = activity, %User{} = user, opts \\ []) do + do_send = Keyword.get(opts, :do_send, true) + type = Keyword.get(opts, :type, type_from_activity(activity)) + + unless skip?(activity, user, opts) do {:ok, %{notification: notification}} = Multi.new() |> Multi.insert(:notification, %Notification{ user_id: user.id, activity: activity, seen: mark_as_read?(activity, user), - type: type_from_activity(activity) + type: type }) |> Marker.multi_set_last_read_id(user, "notifications") |> Repo.transaction() @@ -465,6 +481,28 @@ defmodule Pleroma.Notification do end end + def create_poll_notifications(%Activity{} = activity) do + with %Object{data: %{"type" => "Question", "actor" => actor} = data} <- + Object.normalize(activity) do + voters = + case data do + %{"voters" => voters} when is_list(voters) -> voters + _ -> [] + end + + notifications = + Enum.reduce([actor | voters], [], fn ap_id, acc -> + with %User{local: true} = user <- User.get_by_ap_id(ap_id) do + [create_notification(activity, user, type: "poll") | acc] + else + _ -> acc + end + end) + + {:ok, notifications} + end + end + @doc """ Returns a tuple with 2 elements: {notification-enabled receivers, currently disabled receivers (blocking / [thread] muting)} @@ -475,7 +513,16 @@ defmodule Pleroma.Notification do def get_notified_from_activity(activity, local_only \\ true) def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only) - when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact", "Flag"] do + when type in [ + "Create", + "Like", + "Announce", + "Follow", + "Move", + "EmojiReact", + "Flag", + "Update" + ] do potential_receiver_ap_ids = get_potential_receiver_ap_ids(activity) potential_receivers = @@ -512,7 +559,24 @@ defmodule Pleroma.Notification do end def get_potential_receiver_ap_ids(%{data: %{"type" => "Flag", "actor" => actor}}) do - (User.all_superusers() |> Enum.map(fn user -> user.ap_id end)) -- [actor] + (User.all_users_with_privilege(:reports_manage_reports) + |> Enum.map(fn user -> user.ap_id end)) -- + [actor] + end + + # Update activity: notify all who repeated this + def get_potential_receiver_ap_ids(%{data: %{"type" => "Update", "actor" => actor}} = activity) do + with %Object{data: %{"id" => object_id}} <- Object.normalize(activity, fetch: false) do + repeaters = + Activity.Queries.by_type("Announce") + |> Activity.Queries.by_object_id(object_id) + |> Activity.with_joined_user_actor() + |> where([a, u], u.local) + |> select([a, u], u.ap_id) + |> Repo.all() + + repeaters -- [actor] + end end def get_potential_receiver_ap_ids(activity) do @@ -580,8 +644,10 @@ defmodule Pleroma.Notification do Enum.uniq(ap_ids) -- thread_muter_ap_ids end - @spec skip?(Activity.t(), User.t()) :: boolean() - def skip?(%Activity{} = activity, %User{} = user) do + def skip?(activity, user, opts \\ []) + + @spec skip?(Activity.t(), User.t(), Keyword.t()) :: boolean() + def skip?(%Activity{} = activity, %User{} = user, opts) do [ :self, :invisible, @@ -589,17 +655,21 @@ defmodule Pleroma.Notification do :recently_followed, :filtered ] - |> Enum.find(&skip?(&1, activity, user)) + |> Enum.find(&skip?(&1, activity, user, opts)) end - def skip?(_, _), do: false + def skip?(_activity, _user, _opts), do: false - @spec skip?(atom(), Activity.t(), User.t()) :: boolean() - def skip?(:self, %Activity{} = activity, %User{} = user) do - activity.data["actor"] == user.ap_id + @spec skip?(atom(), Activity.t(), User.t(), Keyword.t()) :: boolean() + def skip?(:self, %Activity{} = activity, %User{} = user, opts) do + cond do + opts[:type] == "poll" -> false + activity.data["actor"] == user.ap_id -> true + true -> false + end end - def skip?(:invisible, %Activity{} = activity, _) do + def skip?(:invisible, %Activity{} = activity, _user, _opts) do actor = activity.data["actor"] user = User.get_cached_by_ap_id(actor) User.invisible?(user) @@ -608,15 +678,27 @@ defmodule Pleroma.Notification do def skip?( :block_from_strangers, %Activity{} = activity, - %User{notification_settings: %{block_from_strangers: true}} = user + %User{notification_settings: %{block_from_strangers: true}} = user, + opts ) do actor = activity.data["actor"] follower = User.get_cached_by_ap_id(actor) - !User.following?(follower, user) + + cond do + opts[:type] == "poll" -> false + user.ap_id == actor -> false + !User.following?(follower, user) -> true + true -> false + end end # To do: consider defining recency in hours and checking FollowingRelationship with a single SQL - def skip?(:recently_followed, %Activity{data: %{"type" => "Follow"}} = activity, %User{} = user) do + def skip?( + :recently_followed, + %Activity{data: %{"type" => "Follow"}} = activity, + %User{} = user, + _opts + ) do actor = activity.data["actor"] Notification.for_user(user) @@ -626,9 +708,10 @@ defmodule Pleroma.Notification do end) end - def skip?(:filtered, %{data: %{"type" => type}}, _) when type in ["Follow", "Move"], do: false + def skip?(:filtered, %{data: %{"type" => type}}, _user, _opts) when type in ["Follow", "Move"], + do: false - def skip?(:filtered, activity, user) do + def skip?(:filtered, activity, user, _opts) do object = Object.normalize(activity, fetch: false) cond do @@ -646,7 +729,7 @@ defmodule Pleroma.Notification do end end - def skip?(_, _, _), do: false + def skip?(_type, _activity, _user, _opts), do: false def mark_as_read?(activity, target_user) do user = Activity.user_actor(activity) diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index c3ea1b98b..38accae5d 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Object do @@ -40,8 +40,7 @@ defmodule Pleroma.Object do join(query, join_type, [{object, object_position}], a in Activity, on: fragment( - "COALESCE(?->'object'->>'id', ?->>'object') = (? ->> 'id') AND (?->>'type' = ?) ", - a.data, + "associated_object_id(?) = (? ->> 'id') AND (?->>'type' = ?) ", a.data, object.data, a.data, @@ -145,7 +144,7 @@ defmodule Pleroma.Object do Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}") end - def normalize(_, options \\ [fetch: false]) + def normalize(_, options \\ [fetch: false, id_only: false]) # If we pass an Activity to Object.normalize(), we can try to use the preloaded object. # Use this whenever possible, especially when walking graphs in an O(N) loop! @@ -173,10 +172,15 @@ defmodule Pleroma.Object do def normalize(%{"id" => ap_id}, options), do: normalize(ap_id, options) def normalize(ap_id, options) when is_binary(ap_id) do - if Keyword.get(options, :fetch) do - Fetcher.fetch_object_from_id!(ap_id, options) - else - get_cached_by_ap_id(ap_id) + cond do + Keyword.get(options, :id_only) -> + ap_id + + Keyword.get(options, :fetch) -> + Fetcher.fetch_object_from_id!(ap_id, options) + + true -> + get_cached_by_ap_id(ap_id) end end @@ -208,10 +212,6 @@ defmodule Pleroma.Object do end end - def context_mapping(context) do - Object.change(%Object{}, %{data: %{"id" => context}}) - end - def make_tombstone(%Object{data: %{"id" => id, "type" => type}}, deleted \\ DateTime.utc_now()) do %ObjectTombstone{ id: id, diff --git a/lib/pleroma/object/containment.ex b/lib/pleroma/object/containment.ex index 040537acf..f6106cb3f 100644 --- a/lib/pleroma/object/containment.ex +++ b/lib/pleroma/object/containment.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Object.Containment do diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index 4ca67f0fd..a9a9eeeed 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -1,9 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Object.Fetcher do alias Pleroma.HTTP + alias Pleroma.Instances alias Pleroma.Maps alias Pleroma.Object alias Pleroma.Object.Containment @@ -26,8 +27,42 @@ defmodule Pleroma.Object.Fetcher do end defp maybe_reinject_internal_fields(%{data: %{} = old_data}, new_data) do + has_history? = fn + %{"formerRepresentations" => %{"orderedItems" => list}} when is_list(list) -> true + _ -> false + end + internal_fields = Map.take(old_data, Pleroma.Constants.object_internal_fields()) + remote_history_exists? = has_history?.(new_data) + + # If the remote history exists, we treat that as the only source of truth. + new_data = + if has_history?.(old_data) and not remote_history_exists? do + Map.put(new_data, "formerRepresentations", old_data["formerRepresentations"]) + else + new_data + end + + # If the remote does not have history information, we need to manage it ourselves + new_data = + if not remote_history_exists? do + changed? = + Pleroma.Constants.status_updatable_fields() + |> Enum.any?(fn field -> Map.get(old_data, field) != Map.get(new_data, field) end) + + %{updated_object: updated_object} = + new_data + |> Object.Updater.maybe_update_history(old_data, + updated: changed?, + use_history_in_new_object?: false + ) + + updated_object + else + new_data + end + Map.merge(new_data, internal_fields) end @@ -200,6 +235,10 @@ defmodule Pleroma.Object.Fetcher do {:ok, body} <- get_object(id), {:ok, data} <- safe_json_decode(body), :ok <- Containment.contain_origin_from_id(id, data) do + if not Instances.reachable?(id) do + Instances.set_reachable(id) + end + {:ok, data} else {:scheme, _} -> diff --git a/lib/pleroma/object/updater.ex b/lib/pleroma/object/updater.ex new file mode 100644 index 000000000..ab38d3ed2 --- /dev/null +++ b/lib/pleroma/object/updater.ex @@ -0,0 +1,240 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Object.Updater do + require Pleroma.Constants + + def update_content_fields(orig_object_data, updated_object) do + Pleroma.Constants.status_updatable_fields() + |> Enum.reduce( + %{data: orig_object_data, updated: false}, + fn field, %{data: data, updated: updated} -> + updated = + updated or + (field != "updated" and + Map.get(updated_object, field) != Map.get(orig_object_data, field)) + + data = + if Map.has_key?(updated_object, field) do + Map.put(data, field, updated_object[field]) + else + Map.drop(data, [field]) + end + + %{data: data, updated: updated} + end + ) + end + + def maybe_history(object) do + with history <- Map.get(object, "formerRepresentations"), + true <- is_map(history), + "OrderedCollection" <- Map.get(history, "type"), + true <- is_list(Map.get(history, "orderedItems")), + true <- is_integer(Map.get(history, "totalItems")) do + history + else + _ -> nil + end + end + + def history_for(object) do + with history when not is_nil(history) <- maybe_history(object) do + history + else + _ -> history_skeleton() + end + end + + defp history_skeleton do + %{ + "type" => "OrderedCollection", + "totalItems" => 0, + "orderedItems" => [] + } + end + + def maybe_update_history( + updated_object, + orig_object_data, + opts + ) do + updated = opts[:updated] + use_history_in_new_object? = opts[:use_history_in_new_object?] + + if not updated do + %{updated_object: updated_object, used_history_in_new_object?: false} + else + # Put edit history + # Note that we may have got the edit history by first fetching the object + {new_history, used_history_in_new_object?} = + with true <- use_history_in_new_object?, + updated_history when not is_nil(updated_history) <- maybe_history(opts[:new_data]) do + {updated_history, true} + else + _ -> + history = history_for(orig_object_data) + + latest_history_item = + orig_object_data + |> Map.drop(["id", "formerRepresentations"]) + + updated_history = + history + |> Map.put("orderedItems", [latest_history_item | history["orderedItems"]]) + |> Map.put("totalItems", history["totalItems"] + 1) + + {updated_history, false} + end + + updated_object = + updated_object + |> Map.put("formerRepresentations", new_history) + + %{updated_object: updated_object, used_history_in_new_object?: used_history_in_new_object?} + end + end + + defp maybe_update_poll(to_be_updated, updated_object) do + choice_key = fn data -> + if Map.has_key?(data, "anyOf"), do: "anyOf", else: "oneOf" + end + + with true <- to_be_updated["type"] == "Question", + key <- choice_key.(updated_object), + true <- key == choice_key.(to_be_updated), + orig_choices <- to_be_updated[key] |> Enum.map(&Map.drop(&1, ["replies"])), + new_choices <- updated_object[key] |> Enum.map(&Map.drop(&1, ["replies"])), + true <- orig_choices == new_choices do + # Choices are the same, but counts are different + to_be_updated + |> Map.put(key, updated_object[key]) + else + # Choices (or vote type) have changed, do not allow this + _ -> to_be_updated + end + end + + # This calculates the data to be sent as the object of an Update. + # new_data's formerRepresentations is not considered. + # formerRepresentations is added to the returned data. + def make_update_object_data(original_data, new_data, date) do + %{data: updated_data, updated: updated} = + original_data + |> update_content_fields(new_data) + + if not updated do + updated_data + else + %{updated_object: updated_data} = + updated_data + |> maybe_update_history(original_data, updated: updated, use_history_in_new_object?: false) + + updated_data + |> Map.put("updated", date) + end + end + + # This calculates the data of the new Object from an Update. + # new_data's formerRepresentations is considered. + def make_new_object_data_from_update_object(original_data, new_data) do + update_is_reasonable = + with {_, updated} when not is_nil(updated) <- {:cur_updated, new_data["updated"]}, + {_, {:ok, updated_time, _}} <- {:cur_updated, DateTime.from_iso8601(updated)}, + {_, last_updated} when not is_nil(last_updated) <- + {:last_updated, original_data["updated"] || original_data["published"]}, + {_, {:ok, last_updated_time, _}} <- + {:last_updated, DateTime.from_iso8601(last_updated)}, + :gt <- DateTime.compare(updated_time, last_updated_time) do + :update_everything + else + # only allow poll updates + {:cur_updated, _} -> :no_content_update + :eq -> :no_content_update + # allow all updates + {:last_updated, _} -> :update_everything + # allow no updates + _ -> false + end + + %{ + updated_object: updated_data, + used_history_in_new_object?: used_history_in_new_object?, + updated: updated + } = + if update_is_reasonable == :update_everything do + %{data: updated_data, updated: updated} = + original_data + |> update_content_fields(new_data) + + updated_data + |> maybe_update_history(original_data, + updated: updated, + use_history_in_new_object?: true, + new_data: new_data + ) + |> Map.put(:updated, updated) + else + %{ + updated_object: original_data, + used_history_in_new_object?: false, + updated: false + } + end + + updated_data = + if update_is_reasonable != false do + updated_data + |> maybe_update_poll(new_data) + else + updated_data + end + + %{ + updated_data: updated_data, + updated: updated, + used_history_in_new_object?: used_history_in_new_object? + } + end + + def for_each_history_item(%{"orderedItems" => items} = history, _object, fun) do + new_items = + Enum.map(items, fun) + |> Enum.reduce_while( + {:ok, []}, + fn + {:ok, item}, {:ok, acc} -> {:cont, {:ok, acc ++ [item]}} + e, _acc -> {:halt, e} + end + ) + + case new_items do + {:ok, items} -> {:ok, Map.put(history, "orderedItems", items)} + e -> e + end + end + + def for_each_history_item(history, _, _) do + {:ok, history} + end + + def do_with_history(object, fun) do + with history <- object["formerRepresentations"], + object <- Map.drop(object, ["formerRepresentations"]), + {_, {:ok, object}} <- {:main_body, fun.(object)}, + {_, {:ok, history}} <- {:history_items, for_each_history_item(history, object, fun)} do + object = + if history do + Map.put(object, "formerRepresentations", history) + else + object + end + + {:ok, object} + else + {:main_body, e} -> e + {:history_items, e} -> e + end + end +end diff --git a/lib/pleroma/object_tombstone.ex b/lib/pleroma/object_tombstone.ex index a42d2d9a0..8bdc8f661 100644 --- a/lib/pleroma/object_tombstone.ex +++ b/lib/pleroma/object_tombstone.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ObjectTombstone do diff --git a/lib/pleroma/otp_version.ex b/lib/pleroma/otp_version.ex index a5ac1b072..80b15275a 100644 --- a/lib/pleroma/otp_version.ex +++ b/lib/pleroma/otp_version.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.OTPVersion do diff --git a/lib/pleroma/pagination.ex b/lib/pleroma/pagination.ex index 33e45a0eb..f12ca2819 100644 --- a/lib/pleroma/pagination.ex +++ b/lib/pleroma/pagination.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Pagination do diff --git a/lib/pleroma/password/pbkdf2.ex b/lib/pleroma/password/pbkdf2.ex index 2fd5f4491..92e9e1952 100644 --- a/lib/pleroma/password/pbkdf2.ex +++ b/lib/pleroma/password/pbkdf2.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Password.Pbkdf2 do diff --git a/lib/pleroma/password_reset_token.ex b/lib/pleroma/password_reset_token.ex index edc8ed6a0..42a789ea2 100644 --- a/lib/pleroma/password_reset_token.ex +++ b/lib/pleroma/password_reset_token.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.PasswordResetToken do diff --git a/lib/pleroma/registration.ex b/lib/pleroma/registration.ex index 7b49618e1..b043c37ac 100644 --- a/lib/pleroma/registration.ex +++ b/lib/pleroma/registration.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Registration do diff --git a/lib/pleroma/release_tasks.ex b/lib/pleroma/release_tasks.ex index 1e06aafe4..f9e8d1948 100644 --- a/lib/pleroma/release_tasks.ex +++ b/lib/pleroma/release_tasks.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ReleaseTasks do diff --git a/lib/pleroma/repo.ex b/lib/pleroma/repo.ex index 61b64ed3e..515b0c1ff 100644 --- a/lib/pleroma/repo.ex +++ b/lib/pleroma/repo.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Repo do diff --git a/lib/pleroma/report_note.ex b/lib/pleroma/report_note.ex index f8bab1548..f2ad76fa8 100644 --- a/lib/pleroma/report_note.ex +++ b/lib/pleroma/report_note.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ReportNote do diff --git a/lib/pleroma/reverse_proxy.ex b/lib/pleroma/reverse_proxy.ex index ec69a1779..2248c2713 100644 --- a/lib/pleroma/reverse_proxy.ex +++ b/lib/pleroma/reverse_proxy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ReverseProxy do diff --git a/lib/pleroma/reverse_proxy/client.ex b/lib/pleroma/reverse_proxy/client.ex index 75243d2dc..91f6e5a95 100644 --- a/lib/pleroma/reverse_proxy/client.ex +++ b/lib/pleroma/reverse_proxy/client.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ReverseProxy.Client do diff --git a/lib/pleroma/reverse_proxy/client/hackney.ex b/lib/pleroma/reverse_proxy/client/hackney.ex index dba946308..d3e986912 100644 --- a/lib/pleroma/reverse_proxy/client/hackney.ex +++ b/lib/pleroma/reverse_proxy/client/hackney.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ReverseProxy.Client.Hackney do @@ -7,7 +7,6 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do @impl true def request(method, url, headers, body, opts \\ []) do - opts = Keyword.put(opts, :ssl_options, versions: [:"tlsv1.2", :"tlsv1.1", :tlsv1]) :hackney.request(method, url, headers, body, opts) end diff --git a/lib/pleroma/reverse_proxy/client/tesla.ex b/lib/pleroma/reverse_proxy/client/tesla.ex index 36a0a2060..4596d7a7f 100644 --- a/lib/pleroma/reverse_proxy/client/tesla.ex +++ b/lib/pleroma/reverse_proxy/client/tesla.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ReverseProxy.Client.Tesla do diff --git a/lib/pleroma/reverse_proxy/client/wrapper.ex b/lib/pleroma/reverse_proxy/client/wrapper.ex index 06dd29fea..1ce476927 100644 --- a/lib/pleroma/reverse_proxy/client/wrapper.ex +++ b/lib/pleroma/reverse_proxy/client/wrapper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ReverseProxy.Client.Wrapper do @@ -25,5 +25,6 @@ defmodule Pleroma.ReverseProxy.Client.Wrapper do defp client(Tesla.Adapter.Hackney), do: Pleroma.ReverseProxy.Client.Hackney defp client(Tesla.Adapter.Gun), do: Pleroma.ReverseProxy.Client.Tesla + defp client({Tesla.Adapter.Finch, _}), do: Pleroma.ReverseProxy.Client.Hackney defp client(_), do: Pleroma.Config.get!(Pleroma.ReverseProxy.Client) end diff --git a/lib/pleroma/scheduled_activity.ex b/lib/pleroma/scheduled_activity.ex index 2b156341f..a7be58512 100644 --- a/lib/pleroma/scheduled_activity.ex +++ b/lib/pleroma/scheduled_activity.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ScheduledActivity do diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index a71a504b3..5cfdae051 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Signature do @@ -66,9 +66,8 @@ defmodule Pleroma.Signature do end end - def sign(%User{} = user, headers) do - with {:ok, %{keys: keys}} <- User.ensure_keys_present(user), - {:ok, private_key, _} <- Keys.keys_from_pem(keys) do + def sign(%User{keys: keys} = user, headers) do + with {:ok, private_key, _} <- Keys.keys_from_pem(keys) do HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers) end end diff --git a/lib/pleroma/stats.ex b/lib/pleroma/stats.ex index 3e3f24c2c..47b30b951 100644 --- a/lib/pleroma/stats.ex +++ b/lib/pleroma/stats.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Stats do diff --git a/lib/pleroma/telemetry/logger.ex b/lib/pleroma/telemetry/logger.ex index 44d2f48dc..384c70fbc 100644 --- a/lib/pleroma/telemetry/logger.ex +++ b/lib/pleroma/telemetry/logger.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Telemetry.Logger do @@ -29,9 +29,7 @@ defmodule Pleroma.Telemetry.Logger do _ ) do Logger.debug(fn -> - "Connection pool is exhausted (reached #{max_connections} connections). Starting idle connection cleanup to reclaim as much as #{ - reclaim_max - } connections" + "Connection pool is exhausted (reached #{max_connections} connections). Starting idle connection cleanup to reclaim as much as #{reclaim_max} connections" end) end @@ -73,9 +71,7 @@ defmodule Pleroma.Telemetry.Logger do _ ) do Logger.warn(fn -> - "Pool worker for #{key}: Client #{inspect(client_pid)} died before releasing the connection with #{ - inspect(reason) - }" + "Pool worker for #{key}: Client #{inspect(client_pid)} died before releasing the connection with #{inspect(reason)}" end) end diff --git a/lib/pleroma/tesla/middleware/connection_pool.ex b/lib/pleroma/tesla/middleware/connection_pool.ex index 906706d39..de74270f8 100644 --- a/lib/pleroma/tesla/middleware/connection_pool.ex +++ b/lib/pleroma/tesla/middleware/connection_pool.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Tesla.Middleware.ConnectionPool do diff --git a/lib/pleroma/tests/auth_test_controller.ex b/lib/pleroma/tests/auth_test_controller.ex index 76514948b..d244badf4 100644 --- a/lib/pleroma/tests/auth_test_controller.ex +++ b/lib/pleroma/tests/auth_test_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only # A test controller reachable only in :test env. diff --git a/lib/pleroma/thread_mute.ex b/lib/pleroma/thread_mute.ex index 5d06cf030..8ea4181bd 100644 --- a/lib/pleroma/thread_mute.ex +++ b/lib/pleroma/thread_mute.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ThreadMute do diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index 17822dc5e..4aee9326f 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload do @@ -36,6 +36,7 @@ defmodule Pleroma.Upload do alias Ecto.UUID alias Pleroma.Config alias Pleroma.Maps + alias Pleroma.Web.ActivityPub.Utils require Logger @type source :: @@ -60,12 +61,23 @@ defmodule Pleroma.Upload do width: integer(), height: integer(), blurhash: String.t(), + description: String.t(), path: String.t() } - defstruct [:id, :name, :tempfile, :content_type, :width, :height, :blurhash, :path] - - defp get_description(opts, upload) do - case {opts[:description], Pleroma.Config.get([Pleroma.Upload, :default_description])} do + defstruct [ + :id, + :name, + :tempfile, + :content_type, + :width, + :height, + :blurhash, + :description, + :path + ] + + defp get_description(upload) do + case {upload.description, Pleroma.Config.get([Pleroma.Upload, :default_description])} do {description, _} when is_binary(description) -> description {_, :filename} -> upload.name {_, str} when is_binary(str) -> str @@ -81,13 +93,14 @@ defmodule Pleroma.Upload do with {:ok, upload} <- prepare_upload(upload, opts), upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"}, {:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload), - description = get_description(opts, upload), + description = get_description(upload), {_, true} <- {:description_limit, String.length(description) <= Pleroma.Config.get([:instance, :description_limit])}, {:ok, url_spec} <- Pleroma.Uploaders.Uploader.put_file(opts.uploader, upload) do {:ok, %{ + "id" => Utils.generate_object_id(), "type" => opts.activity_type, "mediaType" => upload.content_type, "url" => [ @@ -152,7 +165,8 @@ defmodule Pleroma.Upload do id: UUID.generate(), name: file.filename, tempfile: file.path, - content_type: file.content_type + content_type: file.content_type, + description: opts.description }} end end @@ -172,7 +186,8 @@ defmodule Pleroma.Upload do id: UUID.generate(), name: hash <> "." <> ext, tempfile: tmp_path, - content_type: content_type + content_type: content_type, + description: opts.description }} end end diff --git a/lib/pleroma/upload/filter.ex b/lib/pleroma/upload/filter.ex index e5db2fb20..717f06621 100644 --- a/lib/pleroma/upload/filter.ex +++ b/lib/pleroma/upload/filter.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter do diff --git a/lib/pleroma/upload/filter/analyze_metadata.ex b/lib/pleroma/upload/filter/analyze_metadata.ex index c89c30fc1..9a76a998b 100644 --- a/lib/pleroma/upload/filter/analyze_metadata.ex +++ b/lib/pleroma/upload/filter/analyze_metadata.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.AnalyzeMetadata do diff --git a/lib/pleroma/upload/filter/anonymize_filename.ex b/lib/pleroma/upload/filter/anonymize_filename.ex index 7e62b3d13..234ccb6bb 100644 --- a/lib/pleroma/upload/filter/anonymize_filename.ex +++ b/lib/pleroma/upload/filter/anonymize_filename.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.AnonymizeFilename do diff --git a/lib/pleroma/upload/filter/dedupe.ex b/lib/pleroma/upload/filter/dedupe.ex index 2bf581b05..ef793d390 100644 --- a/lib/pleroma/upload/filter/dedupe.ex +++ b/lib/pleroma/upload/filter/dedupe.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.Dedupe do diff --git a/lib/pleroma/upload/filter/exiftool/read_description.ex b/lib/pleroma/upload/filter/exiftool/read_description.ex new file mode 100644 index 000000000..03d698a81 --- /dev/null +++ b/lib/pleroma/upload/filter/exiftool/read_description.ex @@ -0,0 +1,49 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Upload.Filter.Exiftool.ReadDescription do + @moduledoc """ + Gets a valid description from the related EXIF tags and provides them in the response if no description is provided yet. + It will first check ImageDescription, when that doesn't probide a valid description, it will check iptc:Caption-Abstract. + A valid description means the fields are filled in and not too long (see `:instance, :description_limit`). + """ + @behaviour Pleroma.Upload.Filter + + @spec filter(Pleroma.Upload.t()) :: {:ok, any()} | {:error, String.t()} + + def filter(%Pleroma.Upload{description: description}) + when is_binary(description), + do: {:ok, :noop} + + def filter(%Pleroma.Upload{tempfile: file} = upload), + do: {:ok, :filtered, upload |> Map.put(:description, read_description_from_exif_data(file))} + + def filter(_, _), do: {:ok, :noop} + + defp read_description_from_exif_data(file) do + nil + |> read_when_empty(file, "-ImageDescription") + |> read_when_empty(file, "-iptc:Caption-Abstract") + end + + defp read_when_empty(current_description, _, _) when is_binary(current_description), + do: current_description + + defp read_when_empty(_, file, tag) do + try do + {tag_content, 0} = + System.cmd("exiftool", ["-b", "-s3", tag, file], stderr_to_stdout: true, parallelism: true) + + tag_content = String.trim(tag_content) + + if tag_content != "" and + String.length(tag_content) <= + Pleroma.Config.get([:instance, :description_limit]), + do: tag_content, + else: nil + rescue + _ in ErlangError -> nil + end + end +end diff --git a/lib/pleroma/upload/filter/exiftool.ex b/lib/pleroma/upload/filter/exiftool/strip_location.ex index a2bfbbf61..6100527d3 100644 --- a/lib/pleroma/upload/filter/exiftool.ex +++ b/lib/pleroma/upload/filter/exiftool/strip_location.ex @@ -1,8 +1,8 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Upload.Filter.Exiftool do +defmodule Pleroma.Upload.Filter.Exiftool.StripLocation do @moduledoc """ Strips GPS related EXIF tags and overwrites the file in place. Also strips or replaces filesystem metadata e.g., timestamps. diff --git a/lib/pleroma/upload/filter/mogrifun.ex b/lib/pleroma/upload/filter/mogrifun.ex index 01126aaeb..a0f247b70 100644 --- a/lib/pleroma/upload/filter/mogrifun.ex +++ b/lib/pleroma/upload/filter/mogrifun.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.Mogrifun do diff --git a/lib/pleroma/upload/filter/mogrify.ex b/lib/pleroma/upload/filter/mogrify.ex index f27aefc22..06efbf321 100644 --- a/lib/pleroma/upload/filter/mogrify.ex +++ b/lib/pleroma/upload/filter/mogrify.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.Mogrify do diff --git a/lib/pleroma/uploaders/local.ex b/lib/pleroma/uploaders/local.ex index 0e1ba4b90..e4a309cea 100644 --- a/lib/pleroma/uploaders/local.ex +++ b/lib/pleroma/uploaders/local.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Uploaders.Local do diff --git a/lib/pleroma/uploaders/s3.ex b/lib/pleroma/uploaders/s3.ex index d85c8cb2f..19287c532 100644 --- a/lib/pleroma/uploaders/s3.ex +++ b/lib/pleroma/uploaders/s3.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Uploaders.S3 do diff --git a/lib/pleroma/uploaders/uploader.ex b/lib/pleroma/uploaders/uploader.ex index deba548b7..77f6f02dd 100644 --- a/lib/pleroma/uploaders/uploader.ex +++ b/lib/pleroma/uploaders/uploader.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Uploaders.Uploader do diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 03c5f91cc..f32907f01 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User do @@ -78,6 +78,10 @@ defmodule Pleroma.User do inverse_subscription: [ subscribee_subscriptions: :subscriber_users, subscriber_subscriptions: :subscribee_users + ], + endorsement: [ + endorser_endorsements: :endorsed_users, + endorsee_endorsements: :endorser_users ] ] @@ -124,7 +128,6 @@ defmodule Pleroma.User do field(:is_moderator, :boolean, default: false) field(:is_admin, :boolean, default: false) field(:show_role, :boolean, default: true) - field(:mastofe_settings, :map, default: nil) field(:uri, ObjectValidators.Uri, default: nil) field(:hide_followers_count, :boolean, default: false) field(:hide_follows_count, :boolean, default: false) @@ -149,6 +152,11 @@ defmodule Pleroma.User do field(:last_active_at, :naive_datetime) field(:disclose_client, :boolean, default: true) field(:pinned_objects, :map, default: %{}) + field(:is_suggested, :boolean, default: false) + field(:last_status_at, :naive_datetime) + field(:birthday, :date) + field(:show_birthday, :boolean, default: false) + field(:language, :string) embeds_one( :notification_settings, @@ -169,25 +177,25 @@ defmodule Pleroma.User do {incoming_relation, incoming_relation_source} ]} <- @user_relationships_config do # Definitions of `has_many` relations: :blocker_blocks, :muter_mutes, :reblog_muter_mutes, - # :notification_muter_mutes, :subscribee_subscriptions + # :notification_muter_mutes, :subscribee_subscriptions, :endorser_endorsements has_many(outgoing_relation, UserRelationship, foreign_key: :source_id, where: [relationship_type: relationship_type] ) # Definitions of `has_many` relations: :blockee_blocks, :mutee_mutes, :reblog_mutee_mutes, - # :notification_mutee_mutes, :subscriber_subscriptions + # :notification_mutee_mutes, :subscriber_subscriptions, :endorsee_endorsements has_many(incoming_relation, UserRelationship, foreign_key: :target_id, where: [relationship_type: relationship_type] ) # Definitions of `has_many` relations: :blocked_users, :muted_users, :reblog_muted_users, - # :notification_muted_users, :subscriber_users + # :notification_muted_users, :subscriber_users, :endorsed_users has_many(outgoing_relation_target, through: [outgoing_relation, :target]) # Definitions of `has_many` relations: :blocker_users, :muter_users, :reblog_muter_users, - # :notification_muter_users, :subscribee_users + # :notification_muter_users, :subscribee_users, :endorser_users has_many(incoming_relation_source, through: [incoming_relation, :source]) end @@ -215,7 +223,7 @@ defmodule Pleroma.User do @user_relationships_config do # `def blocked_users_relation/2`, `def muted_users_relation/2`, # `def reblog_muted_users_relation/2`, `def notification_muted_users/2`, - # `def subscriber_users/2` + # `def subscriber_users/2`, `def endorsed_users_relation/2` def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do target_users_query = assoc(user, unquote(outgoing_relation_target)) @@ -228,7 +236,7 @@ defmodule Pleroma.User do end # `def blocked_users/2`, `def muted_users/2`, `def reblog_muted_users/2`, - # `def notification_muted_users/2`, `def subscriber_users/2` + # `def notification_muted_users/2`, `def subscriber_users/2`, `def endorsed_users/2` def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do __MODULE__ |> apply(unquote(:"#{outgoing_relation_target}_relation"), [ @@ -239,7 +247,8 @@ defmodule Pleroma.User do end # `def blocked_users_ap_ids/2`, `def muted_users_ap_ids/2`, `def reblog_muted_users_ap_ids/2`, - # `def notification_muted_users_ap_ids/2`, `def subscriber_users_ap_ids/2` + # `def notification_muted_users_ap_ids/2`, `def subscriber_users_ap_ids/2`, + # `def endorsed_users_ap_ids/2` def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \\ false) do __MODULE__ |> apply(unquote(:"#{outgoing_relation_target}_relation"), [ @@ -317,7 +326,7 @@ defmodule Pleroma.User do end def visible_for(%User{} = user, for_user) do - if superuser?(for_user) do + if privileged?(for_user, :users_manage_activation_state) do :visible else visible_account_status(user) @@ -344,10 +353,45 @@ defmodule Pleroma.User do end end - @spec superuser?(User.t()) :: boolean() - def superuser?(%User{local: true, is_admin: true}), do: true - def superuser?(%User{local: true, is_moderator: true}), do: true - def superuser?(_), do: false + @spec privileged?(User.t(), atom()) :: boolean() + def privileged?(%User{is_admin: false, is_moderator: false}, _), do: false + + def privileged?( + %User{local: true, is_admin: is_admin, is_moderator: is_moderator}, + privilege_tag + ), + do: + privileged_for?(privilege_tag, is_admin, :admin_privileges) or + privileged_for?(privilege_tag, is_moderator, :moderator_privileges) + + def privileged?(_, _), do: false + + defp privileged_for?(privilege_tag, true, config_role_key), + do: privilege_tag in Config.get([:instance, config_role_key]) + + defp privileged_for?(_, _, _), do: false + + @spec privileges(User.t()) :: [atom()] + def privileges(%User{local: false}) do + [] + end + + def privileges(%User{is_moderator: false, is_admin: false}) do + [] + end + + def privileges(%User{local: true, is_moderator: true, is_admin: true}) do + (Config.get([:instance, :moderator_privileges]) ++ Config.get([:instance, :admin_privileges])) + |> Enum.uniq() + end + + def privileges(%User{local: true, is_moderator: true, is_admin: false}) do + Config.get([:instance, :moderator_privileges]) + end + + def privileges(%User{local: true, is_moderator: false, is_admin: true}) do + Config.get([:instance, :admin_privileges]) + end @spec invisible?(User.t()) :: boolean() def invisible?(%User{invisible: true}), do: true @@ -464,7 +508,9 @@ defmodule Pleroma.User do :actor_type, :also_known_as, :accepts_chat_messages, - :pinned_objects + :pinned_objects, + :birthday, + :show_birthday ] ) |> cast(params, [:name], empty_values: []) @@ -525,9 +571,12 @@ defmodule Pleroma.User do :is_discoverable, :actor_type, :accepts_chat_messages, - :disclose_client + :disclose_client, + :birthday, + :show_birthday ] ) + |> validate_min_age() |> unique_constraint(:nickname) |> validate_format(:nickname, local_nickname_regex()) |> validate_length(:bio, max: bio_limit) @@ -597,7 +646,13 @@ defmodule Pleroma.User do {:ok, new_value} <- value_function.(value) do put_change(changeset, map_field, new_value) else - _ -> changeset + {:error, :file_too_large} -> + Ecto.Changeset.validate_change(changeset, map_field, fn map_field, _value -> + [{map_field, "file is too large"}] + end) + + _ -> + changeset end end @@ -692,11 +747,12 @@ defmodule Pleroma.User do ]) |> validate_required([:name, :nickname]) |> unique_constraint(:nickname) - |> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames])) + |> validate_not_restricted_nickname(:nickname) |> validate_format(:nickname, local_nickname_regex()) |> put_ap_id() |> unique_constraint(:ap_id) |> put_following_and_follower_and_featured_address() + |> put_private_key() end def register_changeset(struct, params \\ %{}, opts \\ []) do @@ -732,32 +788,58 @@ defmodule Pleroma.User do :password_confirmation, :emoji, :accepts_chat_messages, - :registration_reason + :registration_reason, + :birthday, + :language ]) |> validate_required([:name, :nickname, :password, :password_confirmation]) |> validate_confirmation(:password) |> unique_constraint(:email) |> validate_format(:email, @email_regex) - |> validate_change(:email, fn :email, email -> - valid? = - Config.get([User, :email_blacklist]) - |> Enum.all?(fn blacklisted_domain -> - !String.ends_with?(email, ["@" <> blacklisted_domain, "." <> blacklisted_domain]) - end) - - if valid?, do: [], else: [email: "Invalid email"] - end) + |> validate_email_not_in_blacklisted_domain(:email) |> unique_constraint(:nickname) - |> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames])) + |> validate_not_restricted_nickname(:nickname) |> validate_format(:nickname, local_nickname_regex()) |> validate_length(:bio, max: bio_limit) |> validate_length(:name, min: 1, max: name_limit) |> validate_length(:registration_reason, max: reason_limit) |> maybe_validate_required_email(opts[:external]) + |> maybe_validate_required_birthday + |> validate_min_age() |> put_password_hash |> put_ap_id() |> unique_constraint(:ap_id) |> put_following_and_follower_and_featured_address() + |> put_private_key() + end + + def validate_not_restricted_nickname(changeset, field) do + validate_change(changeset, field, fn _, value -> + valid? = + Config.get([User, :restricted_nicknames]) + |> Enum.all?(fn restricted_nickname -> + String.downcase(value) != String.downcase(restricted_nickname) + end) + + if valid?, do: [], else: [nickname: "Invalid nickname"] + end) + end + + def validate_email_not_in_blacklisted_domain(changeset, field) do + validate_change(changeset, field, fn _, value -> + valid? = + Config.get([User, :email_blacklist]) + |> Enum.all?(fn blacklisted_domain -> + blacklisted_domain_downcase = String.downcase(blacklisted_domain) + + !String.ends_with?(String.downcase(value), [ + "@" <> blacklisted_domain_downcase, + "." <> blacklisted_domain_downcase + ]) + end) + + if valid?, do: [], else: [email: "Invalid email"] + end) end def maybe_validate_required_email(changeset, true), do: changeset @@ -770,6 +852,26 @@ defmodule Pleroma.User do end end + defp maybe_validate_required_birthday(changeset) do + if Config.get([:instance, :birthday_required]) do + validate_required(changeset, [:birthday]) + else + changeset + end + end + + defp validate_min_age(changeset) do + changeset + |> validate_change(:birthday, fn :birthday, birthday -> + valid? = + Date.utc_today() + |> Date.diff(birthday) >= + Config.get([:instance, :birthday_min_age]) + + if valid?, do: [], else: [birthday: "Invalid age"] + end) + end + defp put_ap_id(changeset) do ap_id = ap_id(%User{nickname: get_field(changeset, :nickname)}) put_change(changeset, :ap_id, ap_id) @@ -787,6 +889,11 @@ defmodule Pleroma.User do |> put_change(:featured_address, featured) end + defp put_private_key(changeset) do + {:ok, pem} = Keys.generate_rsa_pem() + put_change(changeset, :keys, pem) + end + defp autofollow_users(user) do candidates = Config.get([:instance, :autofollowed_nicknames]) @@ -839,7 +946,7 @@ defmodule Pleroma.User do end end - defp send_user_approval_email(user) do + defp send_user_approval_email(%User{email: email} = user) when is_binary(email) do user |> Pleroma.Emails.UserEmail.approval_pending_email() |> Pleroma.Emails.Mailer.deliver_async() @@ -847,6 +954,10 @@ defmodule Pleroma.User do {:ok, :enqueued} end + defp send_user_approval_email(_user) do + {:ok, :skipped} + end + defp send_admin_approval_emails(user) do all_superusers() |> Enum.filter(fn user -> not is_nil(user.email) end) @@ -1049,6 +1160,10 @@ defmodule Pleroma.User do Repo.get_by(User, ap_id: ap_id) end + def get_by_uri(uri) do + Repo.get_by(User, uri: uri) + end + def get_all_by_ap_id(ap_ids) do from(u in __MODULE__, where: u.ap_id in ^ap_ids @@ -1417,17 +1532,30 @@ defmodule Pleroma.User do {:ok, list(UserRelationship.t())} | {:error, String.t()} def mute(%User{} = muter, %User{} = mutee, params \\ %{}) do notifications? = Map.get(params, :notifications, true) - expires_in = Map.get(params, :expires_in, 0) + duration = Map.get(params, :duration, 0) - with {:ok, user_mute} <- UserRelationship.create_mute(muter, mutee), + expires_at = + if duration > 0 do + DateTime.utc_now() + |> DateTime.add(duration) + else + nil + end + + with {:ok, user_mute} <- UserRelationship.create_mute(muter, mutee, expires_at), {:ok, user_notification_mute} <- - (notifications? && UserRelationship.create_notification_mute(muter, mutee)) || + (notifications? && + UserRelationship.create_notification_mute( + muter, + mutee, + expires_at + )) || {:ok, nil} do - if expires_in > 0 do + if duration > 0 do Pleroma.Workers.MuteExpireWorker.enqueue( "unmute_user", %{"muter_id" => muter.id, "mutee_id" => mutee.id}, - schedule_in: expires_in + scheduled_at: expires_at ) end @@ -1498,13 +1626,19 @@ defmodule Pleroma.User do blocker end - # clear any requested follows as well + # clear any requested follows from both sides as well blocked = case CommonAPI.reject_follow_request(blocked, blocker) do {:ok, %User{} = updated_blocked} -> updated_blocked nil -> blocked end + blocker = + case CommonAPI.reject_follow_request(blocker, blocked) do + {:ok, %User{} = updated_blocker} -> updated_blocker + nil -> blocker + end + unsubscribe(blocked, blocker) unfollowing_blocked = Config.get([:activitypub, :unfollow_blocked], true) @@ -1529,6 +1663,40 @@ defmodule Pleroma.User do unblock(blocker, get_cached_by_ap_id(ap_id)) end + def endorse(%User{} = endorser, %User{} = target) do + with max_endorsed_users <- Pleroma.Config.get([:instance, :max_endorsed_users], 0), + endorsed_users <- + User.endorsed_users_relation(endorser) + |> Repo.aggregate(:count, :id) do + cond do + endorsed_users >= max_endorsed_users -> + {:error, "You have already pinned the maximum number of users"} + + not following?(endorser, target) -> + {:error, "Could not endorse: You are not following #{target.nickname}"} + + true -> + UserRelationship.create_endorsement(endorser, target) + end + end + end + + def endorse(%User{} = endorser, %{ap_id: ap_id}) do + with %User{} = endorsed <- get_cached_by_ap_id(ap_id) do + endorse(endorser, endorsed) + end + end + + def unendorse(%User{} = unendorser, %User{} = target) do + UserRelationship.delete_endorsement(unendorser, target) + end + + def unendorse(%User{} = unendorser, %{ap_id: ap_id}) do + with %User{} = user <- get_cached_by_ap_id(ap_id) do + unendorse(unendorser, user) + end + end + def mutes?(nil, _), do: false def mutes?(%User{} = user, %User{} = target), do: mutes_user?(user, target) @@ -1574,6 +1742,10 @@ defmodule Pleroma.User do end end + def endorses?(%User{} = user, %User{} = target) do + UserRelationship.endorsement_exists?(user, target) + end + @doc """ Returns map of outgoing (blocked, muted etc.) relationships' user AP IDs by relation type. E.g. `outgoing_relationships_ap_ids(user, [:block])` -> `%{block: ["https://some.site/users/userapid"]}` @@ -1691,6 +1863,22 @@ defmodule Pleroma.User do def confirm(%User{} = user), do: {:ok, user} + def set_suggestion(users, is_suggested) when is_list(users) do + Repo.transaction(fn -> + Enum.map(users, fn user -> + with {:ok, user} <- set_suggestion(user, is_suggested), do: user + end) + end) + end + + def set_suggestion(%User{is_suggested: is_suggested} = user, is_suggested), do: {:ok, user} + + def set_suggestion(%User{} = user, is_suggested) when is_boolean(is_suggested) do + user + |> change(is_suggested: is_suggested) + |> update_and_set_cache() + end + def update_notification_settings(%User{} = user, settings) do user |> cast(%{notification_settings: settings}, []) @@ -1727,7 +1915,6 @@ defmodule Pleroma.User do ap_enabled: false, is_moderator: false, is_admin: false, - mastofe_settings: nil, mascot: nil, emoji: %{}, pleroma_settings_store: %{}, @@ -1951,6 +2138,7 @@ defmodule Pleroma.User do follower_address: uri <> "/followers" } |> change + |> put_private_key() |> unique_constraint(:nickname) |> Repo.insert() |> set_cache() @@ -1983,7 +2171,8 @@ defmodule Pleroma.User do @doc "Gets or fetch a user by uri or nickname." @spec get_or_fetch(String.t()) :: {:ok, User.t()} | {:error, String.t()} - def get_or_fetch("http" <> _host = uri), do: get_or_fetch_by_ap_id(uri) + def get_or_fetch("http://" <> _host = uri), do: get_or_fetch_by_ap_id(uri) + def get_or_fetch("https://" <> _host = uri), do: get_or_fetch_by_ap_id(uri) def get_or_fetch(nickname), do: get_or_fetch_by_nickname(nickname) # wait a period of time and return newest version of the User structs @@ -2111,6 +2300,11 @@ defmodule Pleroma.User do |> Repo.all() end + @spec all_users_with_privilege(atom()) :: [User.t()] + def all_users_with_privilege(privilege) do + User.Query.build(%{is_privileged: privilege}) |> Repo.all() + end + def muting_reblogs?(%User{} = user, %User{} = target) do UserRelationship.reblog_mute_exists?(user, target) end @@ -2216,20 +2410,10 @@ defmodule Pleroma.User do } end - def ensure_keys_present(%{keys: keys} = user) when not is_nil(keys), do: {:ok, user} - - def ensure_keys_present(%User{} = user) do - with {:ok, pem} <- Keys.generate_rsa_pem() do - user - |> cast(%{keys: pem}, [:keys]) - |> validate_required([:keys]) - |> update_and_set_cache() - end - end - def get_ap_ids_by_nicknames(nicknames) do from(u in User, where: u.nickname in ^nicknames, + order_by: fragment("array_position(?, ?)", ^nicknames, u.nickname), select: u.ap_id ) |> Repo.all() @@ -2262,12 +2446,44 @@ defmodule Pleroma.User do def change_email(user, email) do user |> cast(%{email: email}, [:email]) - |> validate_required([:email]) + |> maybe_validate_required_email(false) |> unique_constraint(:email) |> validate_format(:email, @email_regex) |> update_and_set_cache() end + def alias_users(user) do + user.also_known_as + |> Enum.map(&User.get_cached_by_ap_id/1) + |> Enum.filter(fn user -> user != nil end) + end + + def add_alias(user, new_alias_user) do + current_aliases = user.also_known_as || [] + new_alias_ap_id = new_alias_user.ap_id + + if new_alias_ap_id in current_aliases do + {:ok, user} + else + user + |> cast(%{also_known_as: current_aliases ++ [new_alias_ap_id]}, [:also_known_as]) + |> update_and_set_cache() + end + end + + def delete_alias(user, alias_user) do + current_aliases = user.also_known_as || [] + alias_ap_id = alias_user.ap_id + + if alias_ap_id in current_aliases do + user + |> cast(%{also_known_as: current_aliases -- [alias_ap_id]}, [:also_known_as]) + |> update_and_set_cache() + else + {:error, :no_such_alias} + end + end + # Internal function; public one is `deactivate/2` defp set_activation_status(user, status) do user @@ -2345,13 +2561,6 @@ defmodule Pleroma.User do |> update_and_set_cache() end - def mastodon_settings_update(user, settings) do - user - |> cast(%{mastofe_settings: settings}, [:mastofe_settings]) - |> validate_required([:mastofe_settings]) - |> update_and_set_cache() - end - @spec confirmation_changeset(User.t(), keyword()) :: Changeset.t() def confirmation_changeset(user, set_confirmation: confirmed?) do params = @@ -2497,12 +2706,33 @@ defmodule Pleroma.User do |> update_and_set_cache() end - def active_user_count(weeks \\ 4) do - active_after = Timex.shift(NaiveDateTime.utc_now(), weeks: -weeks) + def active_user_count(days \\ 30) do + active_after = Timex.shift(NaiveDateTime.utc_now(), days: -days) __MODULE__ |> where([u], u.last_active_at >= ^active_after) |> where([u], u.local == true) |> Repo.aggregate(:count) end + + def update_last_status_at(user) do + User + |> where(id: ^user.id) + |> update([u], set: [last_status_at: fragment("NOW()")]) + |> select([u], u) + |> Repo.update_all([]) + |> case do + {1, [user]} -> set_cache(user) + _ -> {:error, user} + end + end + + def get_friends_birthdays_query(%User{} = user, day, month) do + User.Query.build(%{ + friends: user, + deactivated: false, + birthday_day: day, + birthday_month: month + }) + end end diff --git a/lib/pleroma/user/backup.ex b/lib/pleroma/user/backup.ex index cba94248f..9df010605 100644 --- a/lib/pleroma/user/backup.ex +++ b/lib/pleroma/user/backup.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.Backup do @@ -32,9 +32,7 @@ defmodule Pleroma.User.Backup do end def create(user, admin_id \\ nil) do - with :ok <- validate_email_enabled(), - :ok <- validate_user_email(user), - :ok <- validate_limit(user, admin_id), + with :ok <- validate_limit(user, admin_id), {:ok, backup} <- user |> new() |> Repo.insert() do BackupWorker.process(backup, admin_id) end @@ -86,20 +84,6 @@ defmodule Pleroma.User.Backup do end end - defp validate_email_enabled do - if Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled]) do - :ok - else - {:error, dgettext("errors", "Backups require enabled email")} - end - end - - defp validate_user_email(%User{email: nil}) do - {:error, dgettext("errors", "Email is required")} - end - - defp validate_user_email(%User{email: email}) when is_binary(email), do: :ok - def get_last(user_id) do __MODULE__ |> where(user_id: ^user_id) diff --git a/lib/pleroma/user/import.ex b/lib/pleroma/user/import.ex index 60cd18041..4baa7e3a4 100644 --- a/lib/pleroma/user/import.ex +++ b/lib/pleroma/user/import.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.Import do diff --git a/lib/pleroma/user/notification_setting.ex b/lib/pleroma/user/notification_setting.ex index a7cd61499..3fb845d71 100644 --- a/lib/pleroma/user/notification_setting.ex +++ b/lib/pleroma/user/notification_setting.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.NotificationSetting do diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index ac807fc79..3e090cac0 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.Query do @@ -29,6 +29,7 @@ defmodule Pleroma.User.Query do import Ecto.Query import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1] + alias Pleroma.Config alias Pleroma.FollowingRelationship alias Pleroma.User @@ -46,7 +47,10 @@ defmodule Pleroma.User.Query do unconfirmed: boolean(), is_admin: boolean(), is_moderator: boolean(), + is_suggested: boolean(), + is_discoverable: boolean(), super_users: boolean(), + is_privileged: atom(), invisible: boolean(), internal: boolean(), followers: User.t(), @@ -57,7 +61,9 @@ defmodule Pleroma.User.Query do order_by: term(), select: term(), limit: pos_integer(), - actor_types: [String.t()] + actor_types: [String.t()], + birthday_day: pos_integer(), + birthday_month: pos_integer() } | map() @@ -132,6 +138,43 @@ defmodule Pleroma.User.Query do ) end + defp compose_query({:is_privileged, privilege}, query) do + moderator_privileged = privilege in Config.get([:instance, :moderator_privileges]) + admin_privileged = privilege in Config.get([:instance, :admin_privileges]) + + query = compose_query({:active, true}, query) + query = compose_query({:local, true}, query) + + case {admin_privileged, moderator_privileged} do + {false, false} -> + where( + query, + false + ) + + {true, true} -> + where( + query, + [u], + u.is_admin or u.is_moderator + ) + + {true, false} -> + where( + query, + [u], + u.is_admin + ) + + {false, true} -> + where( + query, + [u], + u.is_moderator + ) + end + end + defp compose_query({:local, _}, query), do: location_query(query, true) defp compose_query({:external, _}, query), do: location_query(query, false) @@ -167,6 +210,14 @@ defmodule Pleroma.User.Query do where(query, [u], u.is_confirmed == false) end + defp compose_query({:is_suggested, bool}, query) do + where(query, [u], u.is_suggested == ^bool) + end + + defp compose_query({:is_discoverable, bool}, query) do + where(query, [u], u.is_discoverable == ^bool) + end + defp compose_query({:followers, %User{id: id}}, query) do query |> where([u], u.id != ^id) @@ -220,6 +271,20 @@ defmodule Pleroma.User.Query do |> where([u], not like(u.nickname, "internal.%")) end + defp compose_query({:birthday_day, day}, query) do + query + |> where([u], u.show_birthday == true) + |> where([u], not is_nil(u.birthday)) + |> where([u], fragment("date_part('day', ?)", u.birthday) == ^day) + end + + defp compose_query({:birthday_month, month}, query) do + query + |> where([u], u.show_birthday == true) + |> where([u], not is_nil(u.birthday)) + |> where([u], fragment("date_part('month', ?)", u.birthday) == ^month) + end + defp compose_query(_unsupported_param, query), do: query defp location_query(query, local) do diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex index a4f6abca2..a7fb8fb83 100644 --- a/lib/pleroma/user/search.ex +++ b/lib/pleroma/user/search.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.Search do @@ -94,6 +94,7 @@ defmodule Pleroma.User.Search do |> subquery() |> order_by(desc: :search_rank) |> maybe_restrict_local(for_user) + |> filter_deactivated_users() end defp select_top_users(query, top_user_ids) do @@ -166,6 +167,10 @@ defmodule Pleroma.User.Search do from(q in query, where: q.actor_type != "Application") end + defp filter_deactivated_users(query) do + from(q in query, where: q.is_active == true) + end + defp filter_blocked_user(query, %User{} = blocker) do query |> join(:left, [u], b in Pleroma.UserRelationship, diff --git a/lib/pleroma/user/welcome_chat_message.ex b/lib/pleroma/user/welcome_chat_message.ex index 0d6690e34..31e0bfaf6 100644 --- a/lib/pleroma/user/welcome_chat_message.ex +++ b/lib/pleroma/user/welcome_chat_message.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.WelcomeChatMessage do diff --git a/lib/pleroma/user/welcome_email.ex b/lib/pleroma/user/welcome_email.ex index 295c1acc7..970975a49 100644 --- a/lib/pleroma/user/welcome_email.ex +++ b/lib/pleroma/user/welcome_email.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.WelcomeEmail do diff --git a/lib/pleroma/user/welcome_message.ex b/lib/pleroma/user/welcome_message.ex index 2cff05549..6010e808c 100644 --- a/lib/pleroma/user/welcome_message.ex +++ b/lib/pleroma/user/welcome_message.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.WelcomeMessage do diff --git a/lib/pleroma/user_invite_token.ex b/lib/pleroma/user_invite_token.ex index 4cff1c515..b242a8848 100644 --- a/lib/pleroma/user_invite_token.ex +++ b/lib/pleroma/user_invite_token.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.UserInviteToken do diff --git a/lib/pleroma/user_note.ex b/lib/pleroma/user_note.ex new file mode 100644 index 000000000..d4b8256b0 --- /dev/null +++ b/lib/pleroma/user_note.ex @@ -0,0 +1,52 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.UserNote do + use Ecto.Schema + + import Ecto.Changeset + import Ecto.Query + + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.UserNote + + schema "user_notes" do + belongs_to(:source, User, type: FlakeId.Ecto.CompatType) + belongs_to(:target, User, type: FlakeId.Ecto.CompatType) + field(:comment, :string) + + timestamps() + end + + def changeset(%UserNote{} = user_note, params \\ %{}) do + user_note + |> cast(params, [:source_id, :target_id, :comment]) + |> validate_required([:source_id, :target_id]) + end + + def show(%User{} = source, %User{} = target) do + with %UserNote{} = note <- + UserNote + |> where(source_id: ^source.id, target_id: ^target.id) + |> Repo.one() do + note.comment + else + _ -> "" + end + end + + def create(%User{} = source, %User{} = target, comment) do + %UserNote{} + |> changeset(%{ + source_id: source.id, + target_id: target.id, + comment: comment + }) + |> Repo.insert( + on_conflict: {:replace, [:comment]}, + conflict_target: [:source_id, :target_id] + ) + end +end diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex index a467e9b65..fbecf3129 100644 --- a/lib/pleroma/user_relationship.ex +++ b/lib/pleroma/user_relationship.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.UserRelationship do @@ -18,25 +18,35 @@ defmodule Pleroma.UserRelationship do belongs_to(:source, User, type: FlakeId.Ecto.CompatType) belongs_to(:target, User, type: FlakeId.Ecto.CompatType) field(:relationship_type, Pleroma.UserRelationship.Type) + field(:expires_at, :utc_datetime) timestamps(updated_at: false) end for relationship_type <- Keyword.keys(Pleroma.UserRelationship.Type.__enum_map__()) do - # `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`, - # `def create_notification_mute/2`, `def create_inverse_subscription/2` - def unquote(:"create_#{relationship_type}")(source, target), - do: create(unquote(relationship_type), source, target) + # `def create_block/3`, `def create_mute/3`, `def create_reblog_mute/3`, + # `def create_notification_mute/3`, `def create_inverse_subscription/3`, + # `def endorsement/3` + def unquote(:"create_#{relationship_type}")(source, target, expires_at \\ nil), + do: create(unquote(relationship_type), source, target, expires_at) # `def delete_block/2`, `def delete_mute/2`, `def delete_reblog_mute/2`, - # `def delete_notification_mute/2`, `def delete_inverse_subscription/2` + # `def delete_notification_mute/2`, `def delete_inverse_subscription/2`, + # `def delete_endorsement/2` def unquote(:"delete_#{relationship_type}")(source, target), do: delete(unquote(relationship_type), source, target) # `def block_exists?/2`, `def mute_exists?/2`, `def reblog_mute_exists?/2`, - # `def notification_mute_exists?/2`, `def inverse_subscription_exists?/2` + # `def notification_mute_exists?/2`, `def inverse_subscription_exists?/2`, + # `def inverse_endorsement_exists?/2` def unquote(:"#{relationship_type}_exists?")(source, target), do: exists?(unquote(relationship_type), source, target) + + # `def get_block_expire_date/2`, `def get_mute_expire_date/2`, + # `def get_reblog_mute_expire_date/2`, `def get_notification_mute_exists?/2`, + # `def get_inverse_subscription_expire_date/2`, `def get_inverse_endorsement_expire_date/2` + def unquote(:"get_#{relationship_type}_expire_date")(source, target), + do: get_expire_date(unquote(relationship_type), source, target) end def user_relationship_types, do: Keyword.keys(user_relationship_mappings()) @@ -45,7 +55,7 @@ defmodule Pleroma.UserRelationship do def changeset(%UserRelationship{} = user_relationship, params \\ %{}) do user_relationship - |> cast(params, [:relationship_type, :source_id, :target_id]) + |> cast(params, [:relationship_type, :source_id, :target_id, :expires_at]) |> validate_required([:relationship_type, :source_id, :target_id]) |> unique_constraint(:relationship_type, name: :user_relationships_source_id_relationship_type_target_id_index @@ -59,16 +69,31 @@ defmodule Pleroma.UserRelationship do |> Repo.exists?() end - def create(relationship_type, %User{} = source, %User{} = target) do + def get_expire_date(relationship_type, %User{} = source, %User{} = target) do + %UserRelationship{expires_at: expires_at} = + UserRelationship + |> where( + relationship_type: ^relationship_type, + source_id: ^source.id, + target_id: ^target.id + ) + |> Repo.one!() + + expires_at + end + + def create(relationship_type, %User{} = source, %User{} = target, expires_at \\ nil) do %UserRelationship{} |> changeset(%{ relationship_type: relationship_type, source_id: source.id, - target_id: target.id + target_id: target.id, + expires_at: expires_at }) |> Repo.insert( - on_conflict: {:replace_all_except, [:id]}, - conflict_target: [:source_id, :relationship_type, :target_id] + on_conflict: {:replace_all_except, [:id, :inserted_at]}, + conflict_target: [:source_id, :relationship_type, :target_id], + returning: true ) end diff --git a/lib/pleroma/utils.ex b/lib/pleroma/utils.ex index a446d3ae6..73001c987 100644 --- a/lib/pleroma/utils.ex +++ b/lib/pleroma/utils.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Utils do diff --git a/lib/pleroma/web.ex b/lib/pleroma/web.ex index 5761e3b38..aee41b0fe 100644 --- a/lib/pleroma/web.ex +++ b/lib/pleroma/web.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web do diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 4c29dda35..b9206b4da 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ActivityPub do @@ -25,6 +25,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do alias Pleroma.Web.Streamer alias Pleroma.Web.WebFinger alias Pleroma.Workers.BackgroundWorker + alias Pleroma.Workers.PollWorker import Ecto.Query import Pleroma.Web.ActivityPub.Utils @@ -80,6 +81,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor} end + def update_last_status_at_if_public(actor, object) do + if is_public?(object), do: User.update_last_status_at(actor), else: {:ok, actor} + end + defp increase_replies_count_if_reply(%{ "object" => %{"inReplyTo" => reply_ap_id} = object, "type" => "Create" @@ -185,7 +190,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do def notify_and_stream(activity) do Notification.create_notifications(activity) - conversation = create_or_bump_conversation(activity, activity.actor) + original_activity = + case activity do + %{data: %{"type" => "Update"}, object: %{data: %{"id" => id}}} -> + Activity.get_create_by_object_ap_id_with_object(id) + + _ -> + activity + end + + conversation = create_or_bump_conversation(original_activity, original_activity.actor) participations = get_participations(conversation) stream_out(activity) stream_out_participations(participations) @@ -251,7 +265,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do @impl true def stream_out(%Activity{data: %{"type" => data_type}} = activity) - when data_type in ["Create", "Announce", "Delete"] do + when data_type in ["Create", "Announce", "Delete", "Update"] do activity |> Topics.get_activity_topics() |> Streamer.stream(activity) @@ -287,7 +301,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do _ <- increase_replies_count_if_reply(create_data), {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity}, {:ok, _actor} <- increase_note_count_if_public(actor, activity), + {:ok, _actor} <- update_last_status_at_if_public(actor, activity), _ <- notify_and_stream(activity), + :ok <- maybe_schedule_poll_notifications(activity), :ok <- maybe_federate(activity) do {:ok, activity} else @@ -302,6 +318,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + defp maybe_schedule_poll_notifications(activity) do + PollWorker.schedule_poll_end(activity) + :ok + end + @spec listen(map()) :: {:ok, Activity.t()} | {:error, any()} def listen(%{to: to, actor: actor, context: context, object: object} = params) do additional = params[:additional] || %{} @@ -380,11 +401,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do _ <- notify_and_stream(activity), :ok <- maybe_federate(stripped_activity) do - User.all_superusers() + User.all_users_with_privilege(:reports_manage_reports) |> Enum.filter(fn user -> user.ap_id != actor end) |> Enum.filter(fn user -> not is_nil(user.email) end) - |> Enum.each(fn superuser -> - superuser + |> Enum.each(fn privileged_user -> + privileged_user |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content) |> Pleroma.Emails.Mailer.deliver_async() end) @@ -401,7 +422,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do "type" => "Move", "actor" => origin.ap_id, "object" => origin.ap_id, - "target" => target.ap_id + "target" => target.ap_id, + "to" => [origin.follower_address] } with true <- origin.ap_id in target.also_known_as, @@ -434,6 +456,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> maybe_preload_bookmarks(opts) |> maybe_set_thread_muted_field(opts) |> restrict_blocked(opts) + |> restrict_blockers_visibility(opts) |> restrict_recipients(recipients, opts[:user]) |> restrict_filtered(opts) |> where( @@ -488,9 +511,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do @spec fetch_public_or_unlisted_activities(map(), Pagination.type()) :: [Activity.t()] def fetch_public_or_unlisted_activities(opts \\ %{}, pagination \\ :keyset) do + includes_local_public = Map.get(opts, :includes_local_public, false) + opts = Map.delete(opts, :user) - [Constants.as_public()] + intended_recipients = + if includes_local_public do + [Constants.as_public(), as_local_public()] + else + [Constants.as_public()] + end + + intended_recipients |> fetch_activities_query(opts) |> restrict_unlisted(opts) |> fetch_paginated_optimized(opts, pagination) @@ -590,9 +622,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do do: query defp restrict_thread_visibility(query, %{user: %User{ap_id: ap_id}}, _) do + local_public = as_local_public() + from( a in query, - where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data) + where: fragment("thread_visibility(?, (?)->>'id', ?) = true", ^ap_id, a.data, ^local_public) ) end @@ -679,8 +713,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp user_activities_recipients(%{godmode: true}), do: [] defp user_activities_recipients(%{reading_user: reading_user}) do - if reading_user do - [Constants.as_public(), reading_user.ap_id | User.following(reading_user)] + if not is_nil(reading_user) and reading_user.local do + [ + Constants.as_public(), + as_local_public(), + reading_user.ap_id | User.following(reading_user) + ] else [Constants.as_public()] end @@ -1021,7 +1059,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do from( [activity, object: o] in query, + # You don't block the author where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids), + + # You don't block any recipients, and didn't author the post where: fragment( "((not (? && ?)) or ? = ?)", @@ -1030,12 +1071,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do activity.actor, ^user.ap_id ), + + # You don't block the domain of any recipients, and didn't author the post where: fragment( - "recipients_contain_blocked_domains(?, ?) = false", + "(recipients_contain_blocked_domains(?, ?) = false) or ? = ?", activity.recipients, - ^domain_blocks + ^domain_blocks, + activity.actor, + ^user.ap_id ), + + # It's not a boost of a user you block where: fragment( "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)", @@ -1043,6 +1090,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do activity.data, ^blocked_ap_ids ), + + # You don't block the author's domain, and also don't follow the author where: fragment( "(not (split_part(?, '/', 3) = ANY(?))) or ? = ANY(?)", @@ -1051,6 +1100,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do activity.actor, ^following_ap_ids ), + + # Same as above, but checks the Object where: fragment( "(not (split_part(?->>'actor', '/', 3) = ANY(?))) or (?->>'actor') = ANY(?)", @@ -1064,6 +1115,31 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_blocked(query, _), do: query + defp restrict_blockers_visibility(query, %{blocking_user: %User{} = user}) do + if Config.get([:activitypub, :blockers_visible]) == true do + query + else + blocker_ap_ids = User.incoming_relationships_ungrouped_ap_ids(user, [:block]) + + from( + activity in query, + # The author doesn't block you + where: fragment("not (? = ANY(?))", activity.actor, ^blocker_ap_ids), + + # It's not a boost of a user that blocks you + where: + fragment( + "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)", + activity.data, + activity.data, + ^blocker_ap_ids + ) + ) + end + end + + defp restrict_blockers_visibility(query, _), do: query + defp restrict_unlisted(query, %{restrict_unlisted: true}) do from( activity in query, @@ -1083,8 +1159,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do [activity, object: o] in query, where: fragment( - "(?)->>'type' = 'Create' and coalesce((?)->'object'->>'id', (?)->>'object') = any (?)", - activity.data, + "(?)->>'type' = 'Create' and associated_object_id((?)) = any (?)", activity.data, activity.data, ^ids @@ -1164,15 +1239,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + defp exclude_invisible_actors(query, %{type: "Flag"}), do: query defp exclude_invisible_actors(query, %{invisible_actors: true}), do: query defp exclude_invisible_actors(query, _opts) do - invisible_ap_ids = - User.Query.build(%{invisible: true, select: [:ap_id]}) - |> Repo.all() - |> Enum.map(fn %{ap_id: ap_id} -> ap_id end) - - from([activity] in query, where: activity.actor not in ^invisible_ap_ids) + query + |> join(:inner, [activity], u in User, + as: :u, + on: activity.actor == u.ap_id and u.invisible == false + ) end defp exclude_id(query, %{exclude_id: id}) when is_binary(id) do @@ -1290,6 +1365,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> restrict_state(opts) |> restrict_favorited_by(opts) |> restrict_blocked(restrict_blocked_opts) + |> restrict_blockers_visibility(opts) |> restrict_muted(restrict_muted_opts) |> restrict_filtered(opts) |> restrict_media(opts) @@ -1301,7 +1377,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> restrict_instance(opts) |> restrict_announce_object_actor(opts) |> restrict_filtered(opts) - |> Activity.restrict_deactivated_users() + |> maybe_restrict_deactivated_users(opts) |> exclude_poll_votes(opts) |> exclude_chat_messages(opts) |> exclude_invisible_actors(opts) @@ -1406,7 +1482,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image() defp normalize_image(_), do: nil - defp object_to_user_data(data) do + defp object_to_user_data(data, additional) do fields = data |> Map.get("attachment", []) @@ -1438,18 +1514,28 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do public_key = if is_map(data["publicKey"]) && is_binary(data["publicKey"]["publicKeyPem"]) do data["publicKey"]["publicKeyPem"] - else - nil end shared_inbox = if is_map(data["endpoints"]) && is_binary(data["endpoints"]["sharedInbox"]) do data["endpoints"]["sharedInbox"] - else - nil end - user_data = %{ + birthday = + if is_binary(data["vcard:bday"]) do + case Date.from_iso8601(data["vcard:bday"]) do + {:ok, date} -> date + {:error, _} -> nil + end + end + + show_birthday = !!birthday + + # if WebFinger request was already done, we probably have acct, otherwise + # we request WebFinger here + nickname = additional[:nickname_from_acct] || generate_nickname(data) + + %{ ap_id: data["id"], uri: get_actor_url(data["url"]), ap_enabled: true, @@ -1471,21 +1557,29 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do inbox: data["inbox"], shared_inbox: shared_inbox, accepts_chat_messages: accepts_chat_messages, - pinned_objects: pinned_objects + birthday: birthday, + show_birthday: show_birthday, + pinned_objects: pinned_objects, + nickname: nickname } + end - # nickname can be nil because of virtual actors - if data["preferredUsername"] do - Map.put( - user_data, - :nickname, - "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}" - ) + defp generate_nickname(%{"preferredUsername" => username} = data) when is_binary(username) do + generated = "#{username}@#{URI.parse(data["id"]).host}" + + if Config.get([WebFinger, :update_nickname_on_user_fetch]) do + case WebFinger.finger(generated) do + {:ok, %{"subject" => "acct:" <> acct}} -> acct + _ -> generated + end else - Map.put(user_data, :nickname, nil) + generated end end + # nickname can be nil because of virtual actors + defp generate_nickname(_), do: nil + def fetch_follow_information_for_user(user) do with {:ok, following_data} <- Fetcher.fetch_and_contain_remote_object_from_id(user.following_address), @@ -1557,17 +1651,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp collection_private(_data), do: {:ok, true} - def user_data_from_user_object(data) do + def user_data_from_user_object(data, additional \\ []) do with {:ok, data} <- MRF.filter(data) do - {:ok, object_to_user_data(data)} + {:ok, object_to_user_data(data, additional)} else e -> {:error, e} end end - def fetch_and_prepare_user_from_ap_id(ap_id) do + def fetch_and_prepare_user_from_ap_id(ap_id, additional \\ []) do with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id), - {:ok, data} <- user_data_from_user_object(data) do + {:ok, data} <- user_data_from_user_object(data, additional) do {:ok, maybe_update_follow_information(data)} else # If this has been deleted, only log a debug and not an error @@ -1590,9 +1684,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do %User{} = old_user <- User.get_by_nickname(nickname), {_, false} <- {:ap_id_comparison, data[:ap_id] == old_user.ap_id} do Logger.info( - "Found an old user for #{nickname}, the old ap id is #{old_user.ap_id}, new one is #{ - data[:ap_id] - }, renaming." + "Found an old user for #{nickname}, the old ap id is #{old_user.ap_id}, new one is #{data[:ap_id]}, renaming." ) old_user @@ -1614,7 +1706,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do "orderedItems" => objects }) when type in ["OrderedCollection", "Collection"] do - Map.new(objects, fn %{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()} end) + Map.new(objects, fn + %{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()} + object_ap_id when is_binary(object_ap_id) -> {object_ap_id, NaiveDateTime.utc_now()} + end) end def fetch_and_prepare_featured_from_ap_id(nil) do @@ -1644,13 +1739,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - def make_user_from_ap_id(ap_id) do + def make_user_from_ap_id(ap_id, additional \\ []) do user = User.get_cached_by_ap_id(ap_id) if user && !User.ap_enabled?(user) do Transmogrifier.upgrade_user_from_ap_id(ap_id) else - with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do + with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id, additional) do {:ok, _pid} = Task.start(fn -> pinned_fetch_task(data) end) if user do @@ -1670,8 +1765,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end def make_user_from_nickname(nickname) do - with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do - make_user_from_ap_id(ap_id) + with {:ok, %{"ap_id" => ap_id, "subject" => "acct:" <> acct}} when not is_nil(ap_id) <- + WebFinger.finger(nickname) do + make_user_from_ap_id(ap_id, nickname_from_acct: acct) else _e -> {:error, "No AP id in WebFinger"} end @@ -1693,4 +1789,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> restrict_visibility(%{visibility: "direct"}) |> order_by([activity], asc: activity.id) end + + defp maybe_restrict_deactivated_users(activity, %{type: "Flag"}), do: activity + + defp maybe_restrict_deactivated_users(activity, _opts), + do: Activity.restrict_deactivated_users(activity) end diff --git a/lib/pleroma/web/activity_pub/activity_pub/persisting.ex b/lib/pleroma/web/activity_pub/activity_pub/persisting.ex index f39cd000a..3dbfdee28 100644 --- a/lib/pleroma/web/activity_pub/activity_pub/persisting.ex +++ b/lib/pleroma/web/activity_pub/activity_pub/persisting.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ActivityPub.Persisting do diff --git a/lib/pleroma/web/activity_pub/activity_pub/streaming.ex b/lib/pleroma/web/activity_pub/activity_pub/streaming.ex index 33c7bf2bc..d7358171d 100644 --- a/lib/pleroma/web/activity_pub/activity_pub/streaming.ex +++ b/lib/pleroma/web/activity_pub/activity_pub/streaming.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ActivityPub.Streaming do diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index d423b1139..1357c379c 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ActivityPubController do @@ -66,8 +66,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end def user(conn, %{"nickname" => nickname}) do - with %User{local: true} = user <- User.get_cached_by_nickname(nickname), - {:ok, user} <- User.ensure_keys_present(user) do + with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do conn |> put_resp_content_type("application/activity+json") |> put_view(UserView) @@ -174,7 +173,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do with %User{} = user <- User.get_cached_by_nickname(nickname), - {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user), {:show_follows, true} <- {:show_follows, (for_user && for_user == user) || !user.hide_follows} do {page, _} = Integer.parse(page) @@ -192,8 +190,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do - with %User{} = user <- User.get_cached_by_nickname(nickname), - {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do + with %User{} = user <- User.get_cached_by_nickname(nickname) do conn |> put_resp_content_type("application/activity+json") |> put_view(UserView) @@ -213,7 +210,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do with %User{} = user <- User.get_cached_by_nickname(nickname), - {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user), {:show_followers, true} <- {:show_followers, (for_user && for_user == user) || !user.hide_followers} do {page, _} = Integer.parse(page) @@ -231,8 +227,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do - with %User{} = user <- User.get_cached_by_nickname(nickname), - {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do + with %User{} = user <- User.get_cached_by_nickname(nickname) do conn |> put_resp_content_type("application/activity+json") |> put_view(UserView) @@ -245,8 +240,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do %{"nickname" => nickname, "page" => page?} = params ) when page? in [true, "true"] do - with %User{} = user <- User.get_cached_by_nickname(nickname), - {:ok, user} <- User.ensure_keys_present(user) do + with %User{} = user <- User.get_cached_by_nickname(nickname) do # "include_poll_votes" is a hack because postgres generates inefficient # queries when filtering by 'Answer', poll votes will be hidden by the # visibility filter in this case anyway @@ -270,8 +264,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end def outbox(conn, %{"nickname" => nickname}) do - with %User{} = user <- User.get_cached_by_nickname(nickname), - {:ok, user} <- User.ensure_keys_present(user) do + with %User{} = user <- User.get_cached_by_nickname(nickname) do conn |> put_resp_content_type("application/activity+json") |> put_view(UserView) @@ -294,15 +287,29 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do json(conn, "ok") end + def inbox(%{assigns: %{valid_signature: false}} = conn, _params) do + conn + |> put_status(:bad_request) + |> json("Invalid HTTP Signature") + end + # POST /relay/inbox -or- POST /internal/fetch/inbox - def inbox(conn, params) do - if params["type"] == "Create" && FederatingPlug.federating?() do + def inbox(conn, %{"type" => "Create"} = params) do + if FederatingPlug.federating?() do post_inbox_relayed_create(conn, params) else - post_inbox_fallback(conn, params) + conn + |> put_status(:bad_request) + |> json("Not federating") end end + def inbox(conn, _params) do + conn + |> put_status(:bad_request) + |> json("error, missing HTTP Signature") + end + defp post_inbox_relayed_create(conn, params) do Logger.debug( "Signature missing or not from author, relayed Create message, fetching object from source" @@ -313,32 +320,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do json(conn, "ok") end - defp post_inbox_fallback(conn, params) do - headers = Enum.into(conn.req_headers, %{}) - - if headers["signature"] && params["actor"] && - String.contains?(headers["signature"], params["actor"]) do - Logger.debug( - "Signature validation error for: #{params["actor"]}, make sure you are forwarding the HTTP Host header!" - ) - - Logger.debug(inspect(conn.req_headers)) - end - - conn - |> put_status(:bad_request) - |> json(dgettext("errors", "error")) - end - defp represent_service_actor(%User{} = user, conn) do - with {:ok, user} <- User.ensure_keys_present(user) do - conn - |> put_resp_content_type("application/activity+json") - |> put_view(UserView) - |> render("user.json", %{user: user}) - else - nil -> {:error, :not_found} - end + conn + |> put_resp_content_type("application/activity+json") + |> put_view(UserView) + |> render("user.json", %{user: user}) end defp represent_service_actor(nil, _), do: {:error, :not_found} @@ -391,12 +377,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do def read_inbox(%{assigns: %{user: %User{nickname: nickname} = user}} = conn, %{ "nickname" => nickname }) do - with {:ok, user} <- User.ensure_keys_present(user) do - conn - |> put_resp_content_type("application/activity+json") - |> put_view(UserView) - |> render("activity_collection.json", %{iri: "#{user.ap_id}/inbox"}) - end + conn + |> put_resp_content_type("application/activity+json") + |> put_view(UserView) + |> render("activity_collection.json", %{iri: "#{user.ap_id}/inbox"}) end def read_inbox(%{assigns: %{user: %User{nickname: as_nickname}}} = conn, %{ @@ -533,19 +517,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do conn end - defp ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do - {:ok, new_user} = User.ensure_keys_present(user) - - for_user = - if new_user != user and match?(%User{}, for_user) do - User.get_cached_by_nickname(for_user.nickname) - else - for_user - end - - {new_user, for_user} - end - def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do with {:ok, object} <- ActivityPub.upload( diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex index cde477710..532047599 100644 --- a/lib/pleroma/web/activity_pub/builder.ex +++ b/lib/pleroma/web/activity_pub/builder.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Builder do @@ -15,6 +15,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Visibility + alias Pleroma.Web.CommonAPI.ActivityDraft require Pleroma.Constants @@ -125,6 +126,37 @@ defmodule Pleroma.Web.ActivityPub.Builder do |> Pleroma.Maps.put_if_present("context", context), []} end + @spec note(ActivityDraft.t()) :: {:ok, map(), keyword()} + def note(%ActivityDraft{} = draft) do + data = + %{ + "type" => "Note", + "to" => draft.to, + "cc" => draft.cc, + "content" => draft.content_html, + "summary" => draft.summary, + "sensitive" => draft.sensitive, + "context" => draft.context, + "attachment" => draft.attachments, + "actor" => draft.user.ap_id, + "tag" => Keyword.values(draft.tags) |> Enum.uniq() + } + |> add_in_reply_to(draft.in_reply_to) + |> Map.merge(draft.extra) + + {:ok, data, []} + end + + defp add_in_reply_to(object, nil), do: object + + defp add_in_reply_to(object, in_reply_to) do + with %Object{} = in_reply_to_object <- Object.normalize(in_reply_to, fetch: false) do + Map.put(object, "inReplyTo", in_reply_to_object.data["id"]) + else + _ -> object + end + end + def chat_message(actor, recipient, content, opts \\ []) do basic = %{ "id" => Utils.generate_object_id(), @@ -186,10 +218,16 @@ defmodule Pleroma.Web.ActivityPub.Builder do end end - # Retricted to user updates for now, always public @spec update(User.t(), Object.t()) :: {:ok, map(), keyword()} def update(actor, object) do - to = [Pleroma.Constants.as_public(), actor.follower_address] + {to, cc} = + if object["type"] in Pleroma.Constants.actor_types() do + # User updates, always public + {[Pleroma.Constants.as_public(), actor.follower_address], []} + else + # Status updates, follow the recipients in the object + {object["to"] || [], object["cc"] || []} + end {:ok, %{ @@ -197,7 +235,8 @@ defmodule Pleroma.Web.ActivityPub.Builder do "type" => "Update", "actor" => actor.ap_id, "object" => object, - "to" => to + "to" => to, + "cc" => cc }, []} end diff --git a/lib/pleroma/web/activity_pub/internal_fetch_actor.ex b/lib/pleroma/web/activity_pub/internal_fetch_actor.ex index ca76071e5..083723894 100644 --- a/lib/pleroma/web/activity_pub/internal_fetch_actor.ex +++ b/lib/pleroma/web/activity_pub/internal_fetch_actor.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.InternalFetchActor do diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex index 41592e71e..ff9f84497 100644 --- a/lib/pleroma/web/activity_pub/mrf.ex +++ b/lib/pleroma/web/activity_pub/mrf.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF do @@ -33,9 +33,11 @@ defmodule Pleroma.Web.ActivityPub.MRF do %{ key: :transparency_exclusions, label: "MRF transparency exclusions", - type: {:list, :string}, + type: {:list, :tuple}, + key_placeholder: "instance", + value_placeholder: "reason", description: - "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.", + "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value. You can also provide a reason for excluding these instance names. The instances and reasons won't be publicly disclosed.", suggestions: [ "exclusion.com" ] @@ -51,10 +53,53 @@ defmodule Pleroma.Web.ActivityPub.MRF do @required_description_keys [:key, :related_policy] + def filter_one(policy, message) do + should_plug_history? = + if function_exported?(policy, :history_awareness, 0) do + policy.history_awareness() + else + :manual + end + |> Kernel.==(:auto) + + if not should_plug_history? do + policy.filter(message) + else + main_result = policy.filter(message) + + with {_, {:ok, main_message}} <- {:main, main_result}, + {_, + %{ + "formerRepresentations" => %{ + "orderedItems" => [_ | _] + } + }} = {_, object} <- {:object, message["object"]}, + {_, {:ok, new_history}} <- + {:history, + Pleroma.Object.Updater.for_each_history_item( + object["formerRepresentations"], + object, + fn item -> + with {:ok, filtered} <- policy.filter(Map.put(message, "object", item)) do + {:ok, filtered["object"]} + else + e -> e + end + end + )} do + {:ok, put_in(main_message, ["object", "formerRepresentations"], new_history)} + else + {:main, _} -> main_result + {:object, _} -> main_result + {:history, e} -> e + end + end + end + def filter(policies, %{} = message) do policies |> Enum.reduce({:ok, message}, fn - policy, {:ok, message} -> policy.filter(message) + policy, {:ok, message} -> filter_one(policy, message) _, error -> error end) end @@ -100,6 +145,11 @@ defmodule Pleroma.Web.ActivityPub.MRF do Enum.any?(domains, fn domain -> Regex.match?(domain, host) end) end + @spec instance_list_from_tuples([{String.t(), String.t()}]) :: [String.t()] + def instance_list_from_tuples(list) do + Enum.map(list, fn {instance, _} -> instance end) + end + def describe(policies) do {:ok, policy_configs} = policies @@ -150,9 +200,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do [description | acc] else Logger.warn( - "#{policy} config description doesn't have one or all required keys #{ - inspect(@required_description_keys) - }" + "#{policy} config description doesn't have one or all required keys #{inspect(@required_description_keys)}" ) acc diff --git a/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex b/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex index e78254280..88f6ca028 100644 --- a/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex index 851e95d22..97d75ecf2 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do @@ -24,7 +24,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do defp score_displayname("fedibot"), do: 1.0 defp score_displayname(_), do: 0.0 - defp determine_if_followbot(%User{nickname: nickname, name: displayname}) do + defp determine_if_followbot(%User{nickname: nickname, name: displayname, actor_type: actor_type}) do # nickname will be a binary string except when following a relay nick_score = if is_binary(nickname) do @@ -45,19 +45,32 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do 0.0 end - nick_score + name_score + # actor_type "Service" is a Bot account + actor_type_score = + if actor_type == "Service" do + 1.0 + else + 0.0 + end + + nick_score + name_score + actor_type_score end defp determine_if_followbot(_), do: 0.0 + defp bot_allowed?(%{"object" => target}, bot_actor) do + %User{} = user = normalize_by_ap_id(target) + + User.following?(user, bot_actor) + end + @impl true def filter(%{"type" => "Follow", "actor" => actor_id} = message) do %User{} = actor = normalize_by_ap_id(actor_id) score = determine_if_followbot(actor) - # TODO: scan biography data for keywords and score it somehow. - if score < 0.8 do + if score < 0.8 || bot_allowed?(message, actor) do {:ok, message} else {:reject, "[AntiFollowbotPolicy] Scored #{actor_id} as #{score}"} diff --git a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex index cdf17fd28..3ec9c52ee 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do @@ -9,6 +9,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do require Logger + @impl true + def history_awareness, do: :auto + # has the user successfully posted before? defp old_user?(%User{} = u) do u.note_count > 0 || u.follower_count > 0 diff --git a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex index b3ff86eed..ad0936839 100644 --- a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex index fad8d873b..a148cc1e7 100644 --- a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex +++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do @@ -10,6 +10,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do @reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless]) + def history_awareness, do: :auto + def filter_by_summary( %{data: %{"summary" => parent_summary}} = _in_reply_to, %{"summary" => child_summary} = child @@ -27,8 +29,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do def filter_by_summary(_in_reply_to, child), do: child - def filter(%{"type" => "Create", "object" => child_object} = object) - when is_map(child_object) do + def filter(%{"type" => type, "object" => child_object} = object) + when type in ["Create", "Update"] and is_map(child_object) do child = child_object["inReplyTo"] |> Object.normalize(fetch: false) diff --git a/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex b/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex index 7cf7de068..5b6adbb4b 100644 --- a/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicy do @behaviour Pleroma.Web.ActivityPub.MRF.Policy alias Pleroma.Config diff --git a/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex b/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex index 11871375e..8cec8eabe 100644 --- a/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy do @@ -10,7 +10,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy do require Pleroma.Constants defp check_by_actor_type(user), do: user.actor_type in ["Application", "Service"] - defp check_by_nickname(user), do: Regex.match?(~r/bot@|ebooks@/i, user.nickname) + defp check_by_nickname(user), do: Regex.match?(~r/.bot@|ebooks@/i, user.nickname) defp check_if_bot(user), do: check_by_actor_type(user) or check_by_nickname(user) diff --git a/lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex b/lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex new file mode 100644 index 000000000..70224561c --- /dev/null +++ b/lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex @@ -0,0 +1,135 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent do + require Pleroma.Constants + + alias Pleroma.Formatter + alias Pleroma.Object + alias Pleroma.User + + @behaviour Pleroma.Web.ActivityPub.MRF.Policy + + @impl true + def history_awareness, do: :auto + + defp do_extract({:a, attrs, _}, acc) do + if Enum.find(attrs, fn {name, value} -> + name == "class" && value in ["mention", "u-url mention", "mention u-url"] + end) do + href = Enum.find(attrs, fn {name, _} -> name == "href" end) |> elem(1) + acc ++ [href] + else + acc + end + end + + defp do_extract({_, _, children}, acc) do + do_extract(children, acc) + end + + defp do_extract(nodes, acc) when is_list(nodes) do + Enum.reduce(nodes, acc, fn node, acc -> do_extract(node, acc) end) + end + + defp do_extract(_, acc), do: acc + + defp extract_mention_uris_from_content(content) do + {:ok, tree} = :fast_html.decode(content, format: [:html_atoms]) + do_extract(tree, []) + end + + defp get_replied_to_user(%{"inReplyTo" => in_reply_to}) do + case Object.normalize(in_reply_to, fetch: false) do + %Object{data: %{"actor" => actor}} -> User.get_cached_by_ap_id(actor) + _ -> nil + end + end + + defp get_replied_to_user(_object), do: nil + + # Ensure the replied-to user is sorted to the left + defp sort_replied_user([%User{id: user_id} | _] = users, %User{id: user_id}), do: users + + defp sort_replied_user(users, %User{id: user_id} = user) do + if Enum.find(users, fn u -> u.id == user_id end) do + users = Enum.reject(users, fn u -> u.id == user_id end) + [user | users] + else + users + end + end + + defp sort_replied_user(users, _), do: users + + # Drop constants and the actor's own AP ID + defp clean_recipients(recipients, object) do + Enum.reject(recipients, fn ap_id -> + ap_id in [ + object["object"]["actor"], + Pleroma.Constants.as_public(), + Pleroma.Web.ActivityPub.Utils.as_local_public() + ] + end) + end + + @impl true + def filter( + %{ + "type" => type, + "object" => %{"type" => "Note", "to" => to, "inReplyTo" => in_reply_to} + } = object + ) + when type in ["Create", "Update"] and is_list(to) and is_binary(in_reply_to) do + # image-only posts from pleroma apparently reach this MRF without the content field + content = object["object"]["content"] || "" + + # Get the replied-to user for sorting + replied_to_user = get_replied_to_user(object["object"]) + + mention_users = + to + |> clean_recipients(object) + |> Enum.map(&User.get_cached_by_ap_id/1) + |> Enum.reject(&is_nil/1) + |> sort_replied_user(replied_to_user) + + explicitly_mentioned_uris = extract_mention_uris_from_content(content) + + added_mentions = + Enum.reduce(mention_users, "", fn %User{ap_id: uri} = user, acc -> + unless uri in explicitly_mentioned_uris do + acc <> Formatter.mention_from_user(user, %{mentions_format: :compact}) <> " " + else + acc + end + end) + + recipients_inline = + if added_mentions != "", + do: "<span class=\"recipients-inline\">#{added_mentions}</span>", + else: "" + + content = + cond do + # For Markdown posts, insert the mentions inside the first <p> tag + recipients_inline != "" && String.starts_with?(content, "<p>") -> + "<p>" <> recipients_inline <> String.trim_leading(content, "<p>") + + recipients_inline != "" -> + recipients_inline <> content + + true -> + content + end + + {:ok, put_in(object["object"]["content"], content)} + end + + @impl true + def filter(object), do: {:ok, object} + + @impl true + def describe, do: {:ok, %{}} +end diff --git a/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex b/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex index b7db4fa3d..b73fd974c 100644 --- a/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do @@ -16,6 +16,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do @behaviour Pleroma.Web.ActivityPub.MRF.Policy + @impl true + def history_awareness, do: :manual + defp check_reject(message, hashtags) do if Enum.any?(Config.get([:mrf_hashtag, :reject]), fn match -> match in hashtags end) do {:reject, "[HashtagPolicy] Matches with rejected keyword"} @@ -47,22 +50,46 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do defp check_ftl_removal(message, _hashtags), do: {:ok, message} - defp check_sensitive(message, hashtags) do - if Enum.any?(Config.get([:mrf_hashtag, :sensitive]), fn match -> match in hashtags end) do - {:ok, Kernel.put_in(message, ["object", "sensitive"], true)} - else - {:ok, message} - end + defp check_sensitive(message) do + {:ok, new_object} = + Object.Updater.do_with_history(message["object"], fn object -> + hashtags = Object.hashtags(%Object{data: object}) + + if Enum.any?(Config.get([:mrf_hashtag, :sensitive]), fn match -> match in hashtags end) do + {:ok, Map.put(object, "sensitive", true)} + else + {:ok, object} + end + end) + + {:ok, Map.put(message, "object", new_object)} end @impl true - def filter(%{"type" => "Create", "object" => object} = message) do - hashtags = Object.hashtags(%Object{data: object}) + def filter(%{"type" => type, "object" => object} = message) when type in ["Create", "Update"] do + history_items = + with %{"formerRepresentations" => %{"orderedItems" => items}} <- object do + items + else + _ -> [] + end + + historical_hashtags = + Enum.reduce(history_items, [], fn item, acc -> + acc ++ Object.hashtags(%Object{data: item}) + end) + + hashtags = Object.hashtags(%Object{data: object}) ++ historical_hashtags if hashtags != [] do with {:ok, message} <- check_reject(message, hashtags), - {:ok, message} <- check_ftl_removal(message, hashtags), - {:ok, message} <- check_sensitive(message, hashtags) do + {:ok, message} <- + (if "type" == "Create" do + check_ftl_removal(message, hashtags) + else + {:ok, message} + end), + {:ok, message} <- check_sensitive(message) do {:ok, message} end else diff --git a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex index 504bd4d57..80e235d6e 100644 --- a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex index 646008dd9..687ec6c2f 100644 --- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do @@ -27,24 +27,46 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do end defp check_reject(%{"object" => %{} = object} = message) do - payload = object_payload(object) - - if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern -> - string_matches?(payload, pattern) - end) do - {:reject, "[KeywordPolicy] Matches with rejected keyword"} - else + with {:ok, _new_object} <- + Pleroma.Object.Updater.do_with_history(object, fn object -> + payload = object_payload(object) + + if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern -> + string_matches?(payload, pattern) + end) do + {:reject, "[KeywordPolicy] Matches with rejected keyword"} + else + {:ok, message} + end + end) do {:ok, message} + else + e -> e end end - defp check_ftl_removal(%{"to" => to, "object" => %{} = object} = message) do - payload = object_payload(object) + defp check_ftl_removal(%{"type" => "Create", "to" => to, "object" => %{} = object} = message) do + check_keyword = fn object -> + payload = object_payload(object) - if Pleroma.Constants.as_public() in to and - Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern -> + if Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern -> string_matches?(payload, pattern) end) do + {:should_delist, nil} + else + {:ok, %{}} + end + end + + should_delist? = fn object -> + with {:ok, _} <- Pleroma.Object.Updater.do_with_history(object, check_keyword) do + false + else + _ -> true + end + end + + if Pleroma.Constants.as_public() in to and should_delist?.(object) do to = List.delete(to, Pleroma.Constants.as_public()) cc = [Pleroma.Constants.as_public() | message["cc"] || []] @@ -59,8 +81,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do end end + defp check_ftl_removal(message) do + {:ok, message} + end + defp check_replace(%{"object" => %{} = object} = message) do - object = + replace_kw = fn object -> ["content", "name", "summary"] |> Enum.filter(fn field -> Map.has_key?(object, field) && object[field] end) |> Enum.reduce(object, fn field, object -> @@ -73,6 +99,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do Map.put(object, field, data) end) + |> (fn object -> {:ok, object} end).() + end + + {:ok, object} = Pleroma.Object.Updater.do_with_history(object, replace_kw) message = Map.put(message, "object", object) @@ -80,7 +110,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do end @impl true - def filter(%{"type" => "Create", "object" => %{"content" => _content}} = message) do + def filter(%{"type" => type, "object" => %{"content" => _content}} = message) + when type in ["Create", "Update"] do with {:ok, message} <- check_reject(message), {:ok, message} <- check_ftl_removal(message), {:ok, message} <- check_replace(message) do @@ -159,6 +190,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do %{ key: :replace, type: {:list, :tuple}, + key_placeholder: "instance", + value_placeholder: "reason", description: """ **Pattern**: a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. diff --git a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex index 25289d3a4..c95d35bb9 100644 --- a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do @@ -16,6 +16,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do recv_timeout: 10_000 ] + @impl true + def history_awareness, do: :auto + defp prefetch(url) do # Fetching only proxiable resources if MediaProxy.enabled?() and MediaProxy.url_proxiable?(url) do @@ -54,10 +57,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do end @impl true - def filter( - %{"type" => "Create", "object" => %{"attachment" => attachments} = _object} = message - ) - when is_list(attachments) and length(attachments) > 0 do + def filter(%{"type" => type, "object" => %{"attachment" => attachments} = _object} = message) + when type in ["Create", "Update"] and is_list(attachments) and length(attachments) > 0 do preload(message) {:ok, message} diff --git a/lib/pleroma/web/activity_pub/mrf/mention_policy.ex b/lib/pleroma/web/activity_pub/mrf/mention_policy.ex index 05b28e4f5..8aa4f347f 100644 --- a/lib/pleroma/web/activity_pub/mrf/mention_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/mention_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex index 80bef591e..855cda3b9 100644 --- a/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do @@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do @impl true def filter(%{"actor" => actor} = object) do with true <- is_local?(actor), + true <- is_eligible_type?(object), true <- is_note?(object), false <- has_attachment?(object), true <- only_mentions?(object) do @@ -32,7 +33,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do end defp has_attachment?(%{ - "type" => "Create", "object" => %{"type" => "Note", "attachment" => attachments} }) when length(attachments) > 0, @@ -40,7 +40,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do defp has_attachment?(_), do: false - defp only_mentions?(%{"type" => "Create", "object" => %{"type" => "Note", "source" => source}}) do + defp only_mentions?(%{"object" => %{"type" => "Note", "source" => source}}) do + source = + case source do + %{"content" => text} -> text + _ -> source + end + non_mentions = source |> String.split() |> Enum.filter(&(not String.starts_with?(&1, "@"))) |> length @@ -53,9 +59,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do defp only_mentions?(_), do: false - defp is_note?(%{"type" => "Create", "object" => %{"type" => "Note"}}), do: true + defp is_note?(%{"object" => %{"type" => "Note"}}), do: true defp is_note?(_), do: false + defp is_eligible_type?(%{"type" => type}) when type in ["Create", "Update"], do: true + defp is_eligible_type?(_), do: false + @impl true def describe, do: {:ok, %{}} end diff --git a/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex index 25031946c..8840c4fac 100644 --- a/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex index 90272766c..f81e9e52a 100644 --- a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do @@ -7,13 +7,16 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do @behaviour Pleroma.Web.ActivityPub.MRF.Policy @impl true + def history_awareness, do: :auto + + @impl true def filter( %{ - "type" => "Create", + "type" => type, "object" => %{"content" => content, "attachment" => _} = _child_object } = object ) - when content in [".", "<p>.</p>"] do + when type in ["Create", "Update"] and content in [".", "<p>.</p>"] do {:ok, put_in(object, ["object", "content"], "")} end diff --git a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex index 0d7146738..2dfc9a901 100644 --- a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex +++ b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do @@ -9,7 +9,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do @behaviour Pleroma.Web.ActivityPub.MRF.Policy @impl true - def filter(%{"type" => "Create", "object" => child_object} = object) do + def history_awareness, do: :auto + + @impl true + def filter(%{"type" => type, "object" => child_object} = object) + when type in ["Create", "Update"] do scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy]) content = diff --git a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex index 02c9b18ed..df1a6dcbb 100644 --- a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do @@ -131,7 +131,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do type: {:list, :atom}, description: "A list of actions to apply to the post. `:delist` removes the post from public timelines; " <> - "`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines; " <> + "`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines, additionally for followers-only it degrades to a direct message; " <> "`:reject` rejects the message entirely", suggestions: [:delist, :strip_followers, :reject] } diff --git a/lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex b/lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex index be95e38ec..b2477fed4 100644 --- a/lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex +++ b/lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.PipelineFiltering do diff --git a/lib/pleroma/web/activity_pub/mrf/policy.ex b/lib/pleroma/web/activity_pub/mrf/policy.ex index a4a960c01..0234de4d5 100644 --- a/lib/pleroma/web/activity_pub/mrf/policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.Policy do @@ -12,5 +12,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.Policy do label: String.t(), description: String.t() } - @optional_callbacks config_description: 0 + @callback history_awareness() :: :auto | :manual + @optional_callbacks config_description: 0, history_awareness: 0 end diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex index b9d3e52c7..9d4a7a405 100644 --- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex +++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do @@ -47,7 +47,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do @impl true def describe, - do: {:ok, %{mrf_rejectnonpublic: Config.get(:mrf_rejectnonpublic) |> Enum.into(%{})}} + do: {:ok, %{mrf_rejectnonpublic: Config.get(:mrf_rejectnonpublic) |> Map.new()}} @impl true def config_description do diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index 30562ac08..829ddeaea 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do @@ -15,7 +15,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_accept(%{host: actor_host} = _actor_info, object) do accepts = - Config.get([:mrf_simple, :accept]) + instance_list(:accept) |> MRF.subdomains_regex() cond do @@ -28,7 +28,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_reject(%{host: actor_host} = _actor_info, object) do rejects = - Config.get([:mrf_simple, :reject]) + instance_list(:reject) |> MRF.subdomains_regex() if MRF.subdomain_match?(rejects, actor_host) do @@ -40,11 +40,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_media_removal( %{host: actor_host} = _actor_info, - %{"type" => "Create", "object" => %{"attachment" => child_attachment}} = object + %{"type" => type, "object" => %{"attachment" => child_attachment}} = object ) - when length(child_attachment) > 0 do + when length(child_attachment) > 0 and type in ["Create", "Update"] do media_removal = - Config.get([:mrf_simple, :media_removal]) + instance_list(:media_removal) |> MRF.subdomains_regex() object = @@ -63,12 +63,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_media_nsfw( %{host: actor_host} = _actor_info, %{ - "type" => "Create", + "type" => type, "object" => %{} = _child_object } = object - ) do + ) + when type in ["Create", "Update"] do media_nsfw = - Config.get([:mrf_simple, :media_nsfw]) + instance_list(:media_nsfw) |> MRF.subdomains_regex() object = @@ -85,7 +86,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do timeline_removal = - Config.get([:mrf_simple, :federated_timeline_removal]) + instance_list(:federated_timeline_removal) |> MRF.subdomains_regex() object = @@ -112,7 +113,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_followers_only(%{host: actor_host} = _actor_info, object) do followers_only = - Config.get([:mrf_simple, :followers_only]) + instance_list(:followers_only) |> MRF.subdomains_regex() object = @@ -137,7 +138,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = object) do report_removal = - Config.get([:mrf_simple, :report_removal]) + instance_list(:report_removal) |> MRF.subdomains_regex() if MRF.subdomain_match?(report_removal, actor_host) do @@ -151,7 +152,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon} = object) do avatar_removal = - Config.get([:mrf_simple, :avatar_removal]) + instance_list(:avatar_removal) |> MRF.subdomains_regex() if MRF.subdomain_match?(avatar_removal, actor_host) do @@ -165,7 +166,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image} = object) do banner_removal = - Config.get([:mrf_simple, :banner_removal]) + instance_list(:banner_removal) |> MRF.subdomains_regex() if MRF.subdomain_match?(banner_removal, actor_host) do @@ -185,12 +186,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_object(object), do: {:ok, object} + defp instance_list(config_key) do + Config.get([:mrf_simple, config_key]) + |> MRF.instance_list_from_tuples() + end + @impl true def filter(%{"type" => "Delete", "actor" => actor} = object) do %{host: actor_host} = URI.parse(actor) reject_deletes = - Config.get([:mrf_simple, :reject_deletes]) + instance_list(:reject_deletes) |> MRF.subdomains_regex() if MRF.subdomain_match?(reject_deletes, actor_host) do @@ -253,14 +259,42 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do @impl true def describe do - exclusions = Config.get([:mrf, :transparency_exclusions]) + exclusions = Config.get([:mrf, :transparency_exclusions]) |> MRF.instance_list_from_tuples() - mrf_simple = + mrf_simple_excluded = Config.get(:mrf_simple) - |> Enum.map(fn {k, v} -> {k, Enum.reject(v, fn v -> v in exclusions end)} end) - |> Enum.into(%{}) + |> Enum.map(fn {rule, instances} -> + {rule, Enum.reject(instances, fn {host, _} -> host in exclusions end)} + end) - {:ok, %{mrf_simple: mrf_simple}} + mrf_simple = + mrf_simple_excluded + |> Enum.map(fn {rule, instances} -> + {rule, Enum.map(instances, fn {host, _} -> host end)} + end) + |> Map.new() + + # This is for backwards compatibility. We originally didn't sent + # extra info like a reason why an instance was rejected/quarantined/etc. + # Because we didn't want to break backwards compatibility it was decided + # to add an extra "info" key. + mrf_simple_info = + mrf_simple_excluded + |> Enum.map(fn {rule, instances} -> + {rule, Enum.reject(instances, fn {_, reason} -> reason == "" end)} + end) + |> Enum.reject(fn {_, instances} -> instances == [] end) + |> Enum.map(fn {rule, instances} -> + instances = + instances + |> Enum.map(fn {host, reason} -> {host, %{"reason" => reason}} end) + |> Map.new() + + {rule, instances} + end) + |> Map.new() + + {:ok, %{mrf_simple: mrf_simple, mrf_simple_info: mrf_simple_info}} end @impl true @@ -270,70 +304,67 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do related_policy: "Pleroma.Web.ActivityPub.MRF.SimplePolicy", label: "MRF Simple", description: "Simple ingress policies", - children: [ - %{ - key: :media_removal, - type: {:list, :string}, - description: "List of instances to strip media attachments from", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :media_nsfw, - label: "Media NSFW", - type: {:list, :string}, - description: "List of instances to tag all media as NSFW (sensitive) from", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :federated_timeline_removal, - type: {:list, :string}, - description: - "List of instances to remove from the Federated (aka The Whole Known Network) Timeline", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :reject, - type: {:list, :string}, - description: "List of instances to reject activities from (except deletes)", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :accept, - type: {:list, :string}, - description: "List of instances to only accept activities from (except deletes)", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :followers_only, - type: {:list, :string}, - description: "Force posts from the given instances to be visible by followers only", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :report_removal, - type: {:list, :string}, - description: "List of instances to reject reports from", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :avatar_removal, - type: {:list, :string}, - description: "List of instances to strip avatars from", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :banner_removal, - type: {:list, :string}, - description: "List of instances to strip banners from", - suggestions: ["example.com", "*.example.com"] - }, - %{ - key: :reject_deletes, - type: {:list, :string}, - description: "List of instances to reject deletions from", - suggestions: ["example.com", "*.example.com"] - } - ] + children: + [ + %{ + key: :media_removal, + description: + "List of instances to strip media attachments from and the reason for doing so" + }, + %{ + key: :media_nsfw, + label: "Media NSFW", + description: + "List of instances to tag all media as NSFW (sensitive) from and the reason for doing so" + }, + %{ + key: :federated_timeline_removal, + description: + "List of instances to remove from the Federated (aka The Whole Known Network) Timeline and the reason for doing so" + }, + %{ + key: :reject, + description: + "List of instances to reject activities from (except deletes) and the reason for doing so" + }, + %{ + key: :accept, + description: + "List of instances to only accept activities from (except deletes) and the reason for doing so" + }, + %{ + key: :followers_only, + description: + "Force posts from the given instances to be visible by followers only and the reason for doing so" + }, + %{ + key: :report_removal, + description: "List of instances to reject reports from and the reason for doing so" + }, + %{ + key: :avatar_removal, + description: "List of instances to strip avatars from and the reason for doing so" + }, + %{ + key: :banner_removal, + description: "List of instances to strip banners from and the reason for doing so" + }, + %{ + key: :reject_deletes, + description: "List of instances to reject deletions from and the reason for doing so" + } + ] + |> Enum.map(fn setting -> + Map.merge( + setting, + %{ + type: {:list, :tuple}, + key_placeholder: "instance", + value_placeholder: "reason", + suggestions: [{"example.com", "Some reason"}, {"*.example.com", "Another reason"}] + } + ) + end) } end end diff --git a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex index fbe9795ac..f66c379b5 100644 --- a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do @@ -12,6 +12,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do defp accept_host?(host), do: host in Config.get([:mrf_steal_emoji, :hosts], []) + defp shortcode_matches?(shortcode, pattern) when is_binary(pattern) do + shortcode == pattern + end + + defp shortcode_matches?(shortcode, pattern) do + String.match?(shortcode, pattern) + end + defp steal_emoji({shortcode, url}, emoji_dir_path) do url = Pleroma.Web.MediaProxy.url(url) @@ -38,9 +46,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do end else Logger.debug( - "MRF.StealEmojiPolicy: :#{shortcode}: at #{url} (#{byte_size(response.body)} B) over size limit (#{ - size_limit - } B)" + "MRF.StealEmojiPolicy: :#{shortcode}: at #{url} (#{byte_size(response.body)} B) over size limit (#{size_limit} B)" ) nil @@ -74,7 +80,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do reject_emoji? = [:mrf_steal_emoji, :rejected_shortcodes] |> Config.get([]) - |> Enum.find(false, fn regex -> String.match?(shortcode, regex) end) + |> Enum.find(false, fn pattern -> shortcode_matches?(shortcode, pattern) end) !reject_emoji? end) @@ -124,8 +130,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do %{ key: :rejected_shortcodes, type: {:list, :string}, - description: "Regex-list of shortcodes to reject", - suggestions: [""] + description: """ + A list of patterns or matches to reject shortcodes with. + + Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. + """, + suggestions: ["foo", ~r/foo/] }, %{ key: :size_limit, diff --git a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex index f84d7cc71..fdb9e5176 100644 --- a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do @@ -23,9 +23,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do def filter(%{"actor" => actor} = message) do with {:ok, match, subchain} <- lookup_subchain(actor) do Logger.debug( - "[SubchainPolicy] Matched #{actor} against #{inspect(match)} with subchain #{ - inspect(subchain) - }" + "[SubchainPolicy] Matched #{actor} against #{inspect(match)} with subchain #{inspect(subchain)}" ) MRF.filter(subchain, message) diff --git a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex index 56ae654f2..73760ca8f 100644 --- a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do @@ -27,22 +27,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do defp process_tag( "mrf_tag:media-force-nsfw", %{ - "type" => "Create", + "type" => type, "object" => %{"attachment" => child_attachment} } = message ) - when length(child_attachment) > 0 do + when length(child_attachment) > 0 and type in ["Create", "Update"] do {:ok, Kernel.put_in(message, ["object", "sensitive"], true)} end defp process_tag( "mrf_tag:media-strip", %{ - "type" => "Create", + "type" => type, "object" => %{"attachment" => child_attachment} = object } = message ) - when length(child_attachment) > 0 do + when length(child_attachment) > 0 and type in ["Create", "Update"] do object = Map.delete(object, "attachment") message = Map.put(message, "object", object) @@ -152,7 +152,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do do: filter_message(target_actor, message) @impl true - def filter(%{"actor" => actor, "type" => "Create"} = message), + def filter(%{"actor" => actor, "type" => type} = message) when type in ["Create", "Update"], do: filter_message(actor, message) @impl true diff --git a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex index 1bcb3688b..e14047d4e 100644 --- a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do @@ -37,7 +37,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do def describe do mrf_user_allowlist = Config.get([:mrf_user_allowlist], []) - |> Enum.into(%{}, fn {k, v} -> {k, length(v)} end) + |> Map.new(fn {k, v} -> {k, length(v)} end) {:ok, %{mrf_user_allowlist: mrf_user_allowlist}} end diff --git a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex index 20f57f609..d9deff35f 100644 --- a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do @@ -39,7 +39,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do @impl true def describe, - do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Enum.into(%{})}} + do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Map.new()}} @impl true def config_description do diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index 6e40d8b72..5bcd6da46 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidator do @@ -103,8 +103,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do meta ) when objtype in ~w[Question Answer Audio Video Event Article Note Page] do - with {:ok, object_data} <- cast_and_apply(object), - meta = Keyword.put(meta, :object_data, object_data |> stringify_keys), + with {:ok, object_data} <- cast_and_apply_and_stringify_with_history(object), + meta = Keyword.put(meta, :object_data, object_data), {:ok, create_activity} <- create_activity |> CreateGenericValidator.cast_and_validate(meta) @@ -128,16 +128,50 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do end with {:ok, object} <- - object - |> validator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) + do_separate_with_history(object, fn object -> + with {:ok, object} <- + object + |> validator.cast_and_validate() + |> Ecto.Changeset.apply_action(:insert) do + object = stringify_keys(object) + + # Insert copy of hashtags as strings for the non-hashtag table indexing + tag = (object["tag"] || []) ++ Object.hashtags(%Object{data: object}) + object = Map.put(object, "tag", tag) + + {:ok, object} + end + end) do + {:ok, object, meta} + end + end - # Insert copy of hashtags as strings for the non-hashtag table indexing - tag = (object["tag"] || []) ++ Object.hashtags(%Object{data: object}) - object = Map.put(object, "tag", tag) + def validate( + %{"type" => "Update", "object" => %{"type" => objtype} = object} = update_activity, + meta + ) + when objtype in ~w[Question Answer Audio Video Event Article Note Page] do + with {_, false} <- {:local, Access.get(meta, :local, false)}, + {_, {:ok, object_data, _}} <- {:object_validation, validate(object, meta)}, + meta = Keyword.put(meta, :object_data, object_data), + {:ok, update_activity} <- + update_activity + |> UpdateValidator.cast_and_validate() + |> Ecto.Changeset.apply_action(:insert) do + update_activity = stringify_keys(update_activity) + {:ok, update_activity, meta} + else + {:local, _} -> + with {:ok, object} <- + update_activity + |> UpdateValidator.cast_and_validate() + |> Ecto.Changeset.apply_action(:insert) do + object = stringify_keys(object) + {:ok, object, meta} + end - {:ok, object, meta} + {:object_validation, e} -> + e end end @@ -178,6 +212,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do def validate(o, m), do: {:error, {:validator_not_set, {o, m}}} + def cast_and_apply_and_stringify_with_history(object) do + do_separate_with_history(object, fn object -> + with {:ok, object_data} <- cast_and_apply(object), + object_data <- object_data |> stringify_keys() do + {:ok, object_data} + end + end) + end + def cast_and_apply(%{"type" => "ChatMessage"} = object) do ChatMessageValidator.cast_and_apply(object) end @@ -204,8 +247,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do def cast_and_apply(o), do: {:error, {:validator_not_set, o}} - # is_struct/1 appears in Elixir 1.11 - def stringify_keys(%{__struct__: _} = object) do + def stringify_keys(object) when is_struct(object) do object |> Map.from_struct() |> stringify_keys @@ -213,6 +255,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do def stringify_keys(object) when is_map(object) do object + |> Enum.filter(fn {_, v} -> v != nil end) |> Map.new(fn {key, val} -> {to_string(key), stringify_keys(val)} end) end @@ -235,4 +278,54 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do Object.normalize(object["object"], fetch: true) :ok end + + defp for_each_history_item( + %{"type" => "OrderedCollection", "orderedItems" => items} = history, + object, + fun + ) do + processed_items = + Enum.map(items, fn item -> + with item <- Map.put(item, "id", object["id"]), + {:ok, item} <- fun.(item) do + item + else + _ -> nil + end + end) + + if Enum.all?(processed_items, &(not is_nil(&1))) do + {:ok, Map.put(history, "orderedItems", processed_items)} + else + {:error, :invalid_history} + end + end + + defp for_each_history_item(nil, _object, _fun) do + {:ok, nil} + end + + defp for_each_history_item(_, _object, _fun) do + {:error, :invalid_history} + end + + # fun is (object -> {:ok, validated_object_with_string_keys}) + defp do_separate_with_history(object, fun) do + with history <- object["formerRepresentations"], + object <- Map.drop(object, ["formerRepresentations"]), + {_, {:ok, object}} <- {:main_body, fun.(object)}, + {_, {:ok, history}} <- {:history_items, for_each_history_item(history, object, fun)} do + object = + if history do + Map.put(object, "formerRepresentations", history) + else + object + end + + {:ok, object} + else + {:main_body, e} -> e + {:history_items, e} -> e + end + end end diff --git a/lib/pleroma/web/activity_pub/object_validator/validating.ex b/lib/pleroma/web/activity_pub/object_validator/validating.ex index 28e8d2498..b695946b4 100644 --- a/lib/pleroma/web/activity_pub/object_validator/validating.ex +++ b/lib/pleroma/web/activity_pub/object_validator/validating.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidator.Validating do diff --git a/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex b/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex index b577a1044..d611da051 100644 --- a/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex @@ -1,12 +1,11 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator do use Ecto.Schema alias Pleroma.Activity - alias Pleroma.EctoType.ActivityPub.ObjectValidators import Ecto.Changeset import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations @@ -14,12 +13,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:object, ObjectValidators.ObjectID) - field(:actor, ObjectValidators.ObjectID) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end end def cast_data(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex b/lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex index f885aabe4..5202db7f1 100644 --- a/lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator do @@ -10,19 +10,20 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator do require Pleroma.Constants - alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.User @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) field(:target) - field(:object, ObjectValidators.ObjectID) - field(:actor, ObjectValidators.ObjectID) - field(:type) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) + + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end end def cast_and_validate(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex b/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex index 4db76f387..c2c7ba1a8 100644 --- a/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do @@ -20,13 +20,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:object, ObjectValidators.ObjectID) - field(:actor, ObjectValidators.ObjectID) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end + field(:context, :string) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) field(:published, ObjectValidators.DateTime) end diff --git a/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex b/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex index 3451e1ff8..2d9b8ba02 100644 --- a/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator do @@ -15,12 +15,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator do @derive Jason.Encoder embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:bto, ObjectValidators.Recipients, default: []) - field(:bcc, ObjectValidators.Recipients, default: []) - field(:type, :string) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + end + end + field(:name, :string) field(:inReplyTo, ObjectValidators.ObjectID) field(:attributedTo, ObjectValidators.ObjectID) diff --git a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex index 825f2082f..2670e3f17 100644 --- a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex @@ -1,15 +1,13 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do use Ecto.Schema alias Pleroma.EctoType.ActivityPub.ObjectValidators - alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations - alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator alias Pleroma.Web.ActivityPub.Transmogrifier import Ecto.Changeset @@ -18,38 +16,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do @derive Jason.Encoder embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:bto, ObjectValidators.Recipients, default: []) - field(:bcc, ObjectValidators.Recipients, default: []) - embeds_many(:tag, TagValidator) - field(:type, :string) - - field(:name, :string) - field(:summary, :string) - field(:content, :string) - - field(:context, :string) - # short identifier for PleromaFE to group statuses by context - field(:context_id, :integer) - - # TODO: Remove actor on objects - field(:actor, ObjectValidators.ObjectID) - - field(:attributedTo, ObjectValidators.ObjectID) - field(:published, ObjectValidators.DateTime) - field(:emoji, ObjectValidators.Emoji, default: %{}) - field(:sensitive, :boolean, default: false) - embeds_many(:attachment, AttachmentValidator) - field(:replies_count, :integer, default: 0) - field(:like_count, :integer, default: 0) - field(:announcement_count, :integer, default: 0) - field(:inReplyTo, ObjectValidators.ObjectID) - field(:url, ObjectValidators.Uri) - - field(:likes, {:array, ObjectValidators.ObjectID}, default: []) - field(:announcements, {:array, ObjectValidators.ObjectID}, default: []) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + object_fields() + status_object_fields() + end + end field(:replies, {:array, ObjectValidators.ObjectID}, default: []) end @@ -75,7 +49,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do defp fix_url(%{"url" => url} = data) when is_map(url), do: Map.put(data, "url", url["href"]) defp fix_url(data), do: data - defp fix_tag(%{"tag" => tag} = data) when is_list(tag), do: data + defp fix_tag(%{"tag" => tag} = data) when is_list(tag) do + Map.put(data, "tag", Enum.filter(tag, &is_map/1)) + end + defp fix_tag(%{"tag" => tag} = data) when is_map(tag), do: Map.put(data, "tag", [tag]) defp fix_tag(data), do: Map.drop(data, ["tag"]) @@ -94,6 +71,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do defp fix_replies(data), do: data + def fix_attachments(%{"attachment" => attachment} = data) when is_map(attachment), + do: Map.put(data, "attachment", [attachment]) + + def fix_attachments(data), do: data + defp fix(data) do data |> CommonFixes.fix_actor() @@ -101,6 +83,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do |> fix_url() |> fix_tag() |> fix_replies() + |> fix_attachments() |> Transmogrifier.fix_emoji() |> Transmogrifier.fix_content_map() end @@ -117,7 +100,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Article", "Note", "Page"]) - |> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id]) + |> validate_required([:id, :actor, :attributedTo, :type, :context]) |> CommonValidations.validate_any_presence([:cc, :to]) |> CommonValidations.validate_fields_match([:actor, :attributedTo]) |> CommonValidations.validate_actor_presence() diff --git a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex index b86ce35fd..398020bff 100644 --- a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do @@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do @primary_key false embedded_schema do + field(:id, :string) field(:type, :string) field(:mediaType, ObjectValidators.MIME, default: "application/octet-stream") field(:name, :string) @@ -43,10 +44,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do |> fix_url() struct - |> cast(data, [:type, :mediaType, :name, :blurhash]) - |> cast_embed(:url, with: &url_changeset/2) + |> cast(data, [:id, :type, :mediaType, :name, :blurhash]) + |> cast_embed(:url, with: &url_changeset/2, required: true) |> validate_inclusion(:type, ~w[Link Document Audio Image Video]) - |> validate_required([:type, :mediaType, :url]) + |> validate_required([:type, :mediaType]) end def url_changeset(struct, data) do @@ -59,15 +60,17 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do end def fix_media_type(data) do - Map.put_new(data, "mediaType", data["mimeType"]) + Map.put_new(data, "mediaType", data["mimeType"] || "application/octet-stream") end - defp handle_href(href, mediaType) do + defp handle_href(href, mediaType, data) do [ %{ "href" => href, "type" => "Link", - "mediaType" => mediaType + "mediaType" => mediaType, + "width" => data["width"], + "height" => data["height"] } ] end @@ -75,10 +78,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do defp fix_url(data) do cond do is_binary(data["url"]) -> - Map.put(data, "url", handle_href(data["url"], data["mediaType"])) + Map.put(data, "url", handle_href(data["url"], data["mediaType"], data)) is_binary(data["href"]) and data["url"] == nil -> - Map.put(data, "url", handle_href(data["href"], data["mediaType"])) + Map.put(data, "url", handle_href(data["href"], data["mediaType"], data)) true -> data @@ -88,6 +91,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do defp validate_data(cng) do cng |> validate_inclusion(:type, ~w[Document Audio Image Video]) - |> validate_required([:mediaType, :url, :type]) + |> validate_required([:mediaType, :type]) end end diff --git a/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex b/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex index 572687deb..671a7ef0c 100644 --- a/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex @@ -1,15 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do use Ecto.Schema - alias Pleroma.EctoType.ActivityPub.ObjectValidators - alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations - alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator alias Pleroma.Web.ActivityPub.Transmogrifier import Ecto.Changeset @@ -18,38 +15,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do @derive Jason.Encoder embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:bto, ObjectValidators.Recipients, default: []) - field(:bcc, ObjectValidators.Recipients, default: []) - embeds_many(:tag, TagValidator) - field(:type, :string) - - field(:name, :string) - field(:summary, :string) - field(:content, :string) - - field(:context, :string) - # short identifier for PleromaFE to group statuses by context - field(:context_id, :integer) - - # TODO: Remove actor on objects - field(:actor, ObjectValidators.ObjectID) - - field(:attributedTo, ObjectValidators.ObjectID) - field(:published, ObjectValidators.DateTime) - field(:emoji, ObjectValidators.Emoji, default: %{}) - field(:sensitive, :boolean, default: false) - embeds_many(:attachment, AttachmentValidator) - field(:replies_count, :integer, default: 0) - field(:like_count, :integer, default: 0) - field(:announcement_count, :integer, default: 0) - field(:inReplyTo, ObjectValidators.ObjectID) - field(:url, ObjectValidators.Uri) - - field(:likes, {:array, ObjectValidators.ObjectID}, default: []) - field(:announcements, {:array, ObjectValidators.ObjectID}, default: []) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + object_fields() + status_object_fields() + end + end end def cast_and_apply(data) do @@ -131,14 +104,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do struct |> cast(data, __schema__(:fields) -- [:attachment, :tag]) - |> cast_embed(:attachment) + |> cast_embed(:attachment, required: true) |> cast_embed(:tag) end defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Audio", "Video"]) - |> validate_required([:id, :actor, :attributedTo, :type, :context, :attachment]) + |> validate_required([:id, :actor, :attributedTo, :type, :context]) |> CommonValidations.validate_any_presence([:cc, :to]) |> CommonValidations.validate_fields_match([:actor, :attributedTo]) |> CommonValidations.validate_actor_presence() diff --git a/lib/pleroma/web/activity_pub/object_validators/block_validator.ex b/lib/pleroma/web/activity_pub/object_validators/block_validator.ex index 88948135f..0de87a27e 100644 --- a/lib/pleroma/web/activity_pub/object_validators/block_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/block_validator.ex @@ -1,24 +1,25 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator do use Ecto.Schema - alias Pleroma.EctoType.ActivityPub.ObjectValidators + alias Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations import Ecto.Changeset - import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations @primary_key false + @derive Jason.Encoder embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:actor, ObjectValidators.ObjectID) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:object, ObjectValidators.ObjectID) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end end def cast_data(data) do @@ -30,8 +31,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator do cng |> validate_required([:id, :type, :actor, :to, :cc, :object]) |> validate_inclusion(:type, ["Block"]) - |> validate_actor_presence() - |> validate_actor_presence(field_name: :object) + |> CommonValidations.validate_actor_presence() + |> CommonValidations.validate_actor_presence(field_name: :object) end def cast_and_validate(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex b/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex index b153156b0..efae48cae 100644 --- a/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator do diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex new file mode 100644 index 000000000..7b60c139a --- /dev/null +++ b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex @@ -0,0 +1,67 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do + alias Pleroma.EctoType.ActivityPub.ObjectValidators + alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator + + # Activities and Objects, except (Create)ChatMessage + defmacro message_fields do + quote bind_quoted: binding() do + field(:type, :string) + field(:id, ObjectValidators.ObjectID, primary_key: true) + + field(:to, ObjectValidators.Recipients, default: []) + field(:cc, ObjectValidators.Recipients, default: []) + field(:bto, ObjectValidators.Recipients, default: []) + field(:bcc, ObjectValidators.Recipients, default: []) + end + end + + defmacro activity_fields do + quote bind_quoted: binding() do + field(:object, ObjectValidators.ObjectID) + field(:actor, ObjectValidators.ObjectID) + end + end + + # All objects except Answer and CHatMessage + defmacro object_fields do + quote bind_quoted: binding() do + field(:content, :string) + + field(:published, ObjectValidators.DateTime) + field(:updated, ObjectValidators.DateTime) + field(:emoji, ObjectValidators.Emoji, default: %{}) + embeds_many(:attachment, AttachmentValidator) + end + end + + # Basically objects that aren't ChatMessage and Answer + defmacro status_object_fields do + quote bind_quoted: binding() do + # TODO: Remove actor on objects + field(:actor, ObjectValidators.ObjectID) + field(:attributedTo, ObjectValidators.ObjectID) + + embeds_many(:tag, TagValidator) + + field(:name, :string) + field(:summary, :string) + + field(:context, :string) + + field(:sensitive, :boolean, default: false) + field(:replies_count, :integer, default: 0) + field(:like_count, :integer, default: 0) + field(:announcement_count, :integer, default: 0) + field(:inReplyTo, ObjectValidators.ObjectID) + field(:url, ObjectValidators.Uri) + + field(:likes, {:array, ObjectValidators.ObjectID}, default: []) + field(:announcements, {:array, ObjectValidators.ObjectID}, default: []) + end + end +end diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex index 9631013a7..add46d561 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do @@ -22,14 +22,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do end def fix_object_defaults(data) do - %{data: %{"id" => context}, id: context_id} = - Utils.create_context(data["context"] || data["conversation"]) + context = + Utils.maybe_create_context( + data["context"] || data["conversation"] || data["inReplyTo"] || data["id"] + ) %User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["attributedTo"]) data |> Map.put("context", context) - |> Map.put("context_id", context_id) |> cast_and_filter_recipients("to", follower_collection) |> cast_and_filter_recipients("cc", follower_collection) |> cast_and_filter_recipients("bto", follower_collection) diff --git a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex index be5074348..1c5b1a059 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do @@ -136,11 +136,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do # This figures out if a user is able to create, delete or modify something # based on the domain and superuser status - @spec validate_modification_rights(Ecto.Changeset.t()) :: Ecto.Changeset.t() - def validate_modification_rights(cng) do + @spec validate_modification_rights(Ecto.Changeset.t(), atom()) :: Ecto.Changeset.t() + def validate_modification_rights(cng, privilege) do actor = User.get_cached_by_ap_id(get_field(cng, :actor)) - if User.superuser?(actor) || same_domain?(cng) do + if User.privileged?(actor, privilege) || same_domain?(cng) do cng else cng diff --git a/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex index 7a31a99bf..b299647a1 100644 --- a/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only # NOTES @@ -17,11 +17,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator do @primary_key false embedded_schema do + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + activity_fields() + end + end + field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:actor, ObjectValidators.ObjectID) field(:type, :string) field(:to, ObjectValidators.Recipients, default: []) - field(:object, ObjectValidators.ObjectID) end def cast_and_apply(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex index d2de53049..2395abfd4 100644 --- a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only # Code based on CreateChatMessageValidator @@ -20,14 +20,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:actor, ObjectValidators.ObjectID) - field(:type, :string) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:bto, ObjectValidators.Recipients, default: []) - field(:bcc, ObjectValidators.Recipients, default: []) - field(:object, ObjectValidators.ObjectID) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end + field(:expires_at, ObjectValidators.DateTime) # Should be moved to object, done for CommonAPI.Utils.make_context @@ -75,7 +75,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do data |> CommonFixes.fix_actor() - |> Map.put_new("context", object["context"]) + |> Map.put("context", object["context"]) |> fix_addressing(object) end diff --git a/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex b/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex index 05f93da82..4d8502ada 100644 --- a/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do @@ -15,13 +15,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:actor, ObjectValidators.ObjectID) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end + field(:deleted_activity_id, ObjectValidators.ObjectID) - field(:object, ObjectValidators.ObjectID) end def cast_data(data) do @@ -59,7 +61,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do |> validate_required([:id, :type, :actor, :to, :cc, :object]) |> validate_inclusion(:type, ["Delete"]) |> validate_delete_actor(:actor) - |> validate_modification_rights() + |> validate_modification_rights(:messages_delete) |> validate_object_or_user_presence(allowed_types: @deletable_types) |> add_deleted_activity_id() end diff --git a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex index a18bd7540..0858281e5 100644 --- a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex @@ -1,11 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do use Ecto.Schema - alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Object alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes @@ -15,14 +14,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:object, ObjectValidators.ObjectID) - field(:actor, ObjectValidators.ObjectID) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end + field(:context, :string) field(:content, :string) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) end def cast_and_validate(data) do @@ -48,6 +49,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do defp fix(data) do data = data + |> fix_emoji_qualification() |> CommonFixes.fix_actor() |> CommonFixes.fix_activity_addressing() @@ -60,6 +62,23 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do end end + defp fix_emoji_qualification(%{"content" => emoji} = data) do + new_emoji = Pleroma.Emoji.fully_qualify_emoji(emoji) + + cond do + Pleroma.Emoji.is_unicode_emoji?(emoji) -> + data + + Pleroma.Emoji.is_unicode_emoji?(new_emoji) -> + data |> Map.put("content", new_emoji) + + true -> + data + end + end + + defp fix_emoji_qualification(data), do: data + defp validate_emoji(cng) do content = get_field(cng, :content) diff --git a/lib/pleroma/web/activity_pub/object_validators/event_validator.ex b/lib/pleroma/web/activity_pub/object_validators/event_validator.ex index fee2e997a..ab204f69a 100644 --- a/lib/pleroma/web/activity_pub/object_validators/event_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/event_validator.ex @@ -1,15 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do use Ecto.Schema - alias Pleroma.EctoType.ActivityPub.ObjectValidators - alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations - alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator alias Pleroma.Web.ActivityPub.Transmogrifier import Ecto.Changeset @@ -19,38 +16,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do # Extends from NoteValidator embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:bto, ObjectValidators.Recipients, default: []) - field(:bcc, ObjectValidators.Recipients, default: []) - embeds_many(:tag, TagValidator) - field(:type, :string) - - field(:name, :string) - field(:summary, :string) - field(:content, :string) - - field(:context, :string) - # short identifier for PleromaFE to group statuses by context - field(:context_id, :integer) - - # TODO: Remove actor on objects - field(:actor, ObjectValidators.ObjectID) - - field(:attributedTo, ObjectValidators.ObjectID) - field(:published, ObjectValidators.DateTime) - field(:emoji, ObjectValidators.Emoji, default: %{}) - field(:sensitive, :boolean, default: false) - embeds_many(:attachment, AttachmentValidator) - field(:replies_count, :integer, default: 0) - field(:like_count, :integer, default: 0) - field(:announcement_count, :integer, default: 0) - field(:inReplyTo, ObjectValidators.ObjectID) - field(:url, ObjectValidators.Uri) - - field(:likes, {:array, ObjectValidators.ObjectID}, default: []) - field(:announcements, {:array, ObjectValidators.ObjectID}, default: []) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + object_fields() + status_object_fields() + end + end end def cast_and_apply(data) do @@ -89,7 +62,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Event"]) - |> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id]) + |> validate_required([:id, :actor, :attributedTo, :type, :context]) |> CommonValidations.validate_any_presence([:cc, :to]) |> CommonValidations.validate_fields_match([:actor, :attributedTo]) |> CommonValidations.validate_actor_presence() diff --git a/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex b/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex index 239cee5e7..b3ca5b691 100644 --- a/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex @@ -1,24 +1,24 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.FollowValidator do use Ecto.Schema - alias Pleroma.EctoType.ActivityPub.ObjectValidators - import Ecto.Changeset import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:actor, ObjectValidators.ObjectID) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:object, ObjectValidators.ObjectID) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end + field(:state, :string, default: "pending") end diff --git a/lib/pleroma/web/activity_pub/object_validators/like_validator.ex b/lib/pleroma/web/activity_pub/object_validators/like_validator.ex index 8b99c89b9..bdc4d7181 100644 --- a/lib/pleroma/web/activity_pub/object_validators/like_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/like_validator.ex @@ -1,11 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do use Ecto.Schema - alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Object alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.Utils @@ -16,13 +15,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:object, ObjectValidators.ObjectID) - field(:actor, ObjectValidators.ObjectID) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end + field(:context, :string) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) end def cast_and_validate(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/question_options_validator.ex b/lib/pleroma/web/activity_pub/object_validators/question_options_validator.ex index ddcd1be7c..541945fa4 100644 --- a/lib/pleroma/web/activity_pub/object_validators/question_options_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/question_options_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionOptionsValidator do diff --git a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex index 083d08ec4..ce3305142 100644 --- a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex @@ -1,16 +1,14 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do use Ecto.Schema alias Pleroma.EctoType.ActivityPub.ObjectValidators - alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionOptionsValidator - alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator alias Pleroma.Web.ActivityPub.Transmogrifier import Ecto.Changeset @@ -20,35 +18,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do # Extends from NoteValidator embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:bto, ObjectValidators.Recipients, default: []) - field(:bcc, ObjectValidators.Recipients, default: []) - embeds_many(:tag, TagValidator) - field(:type, :string) - field(:content, :string) - field(:context, :string) - - # TODO: Remove actor on objects - field(:actor, ObjectValidators.ObjectID) - - field(:attributedTo, ObjectValidators.ObjectID) - field(:summary, :string) - field(:published, ObjectValidators.DateTime) - field(:emoji, ObjectValidators.Emoji, default: %{}) - field(:sensitive, :boolean, default: false) - embeds_many(:attachment, AttachmentValidator) - field(:replies_count, :integer, default: 0) - field(:like_count, :integer, default: 0) - field(:announcement_count, :integer, default: 0) - field(:inReplyTo, ObjectValidators.ObjectID) - field(:url, ObjectValidators.Uri) - # short identifier for PleromaFE to group statuses by context - field(:context_id, :integer) - - field(:likes, {:array, ObjectValidators.ObjectID}, default: []) - field(:announcements, {:array, ObjectValidators.ObjectID}, default: []) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + object_fields() + status_object_fields() + end + end field(:closed, ObjectValidators.DateTime) field(:voters, {:array, ObjectValidators.ObjectID}, default: []) @@ -103,7 +80,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Question"]) - |> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id]) + |> validate_required([:id, :actor, :attributedTo, :type, :context]) |> CommonValidations.validate_any_presence([:cc, :to]) |> CommonValidations.validate_fields_match([:actor, :attributedTo]) |> CommonValidations.validate_actor_presence() diff --git a/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex b/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex index 751021585..9f15f1981 100644 --- a/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.TagValidator do diff --git a/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex b/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex index 6ff648c84..f03051491 100644 --- a/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex @@ -1,12 +1,11 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do use Ecto.Schema alias Pleroma.Activity - alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.User import Ecto.Changeset @@ -15,12 +14,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:object, ObjectValidators.ObjectID) - field(:actor, ObjectValidators.ObjectID) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end end def cast_and_validate(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/update_validator.ex b/lib/pleroma/web/activity_pub/object_validators/update_validator.ex index 6bb1dc7fa..1e940a400 100644 --- a/lib/pleroma/web/activity_pub/object_validators/update_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/update_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do @@ -13,11 +13,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + end + end + field(:actor, ObjectValidators.ObjectID) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) # In this case, we save the full object in this activity instead of just a # reference, so we can always see what was actually changed by this. field(:object, :map) @@ -48,7 +51,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do with actor = get_field(cng, :actor), object = get_field(cng, :object), {:ok, object_id} <- ObjectValidators.ObjectID.cast(object), - true <- actor == object_id do + actor_uri <- URI.parse(actor), + object_uri <- URI.parse(object_id), + true <- actor_uri.host == object_uri.host do cng else _e -> diff --git a/lib/pleroma/web/activity_pub/pipeline.ex b/lib/pleroma/web/activity_pub/pipeline.ex index 0d6e8aad2..ca8653ab1 100644 --- a/lib/pleroma/web/activity_pub/pipeline.ex +++ b/lib/pleroma/web/activity_pub/pipeline.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Pipeline do diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index c8cdd429e..6c1ba76a3 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Publisher do @@ -111,6 +111,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do quarantined_instances = Config.get([:instance, :quarantined_instances], []) + |> Pleroma.Web.ActivityPub.MRF.instance_list_from_tuples() |> Pleroma.Web.ActivityPub.MRF.subdomains_regex() !Pleroma.Web.ActivityPub.MRF.subdomain_match?(quarantined_instances, host) diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index 6d60a074f..2010351d1 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Relay do diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index b82a89896..a2152b945 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.SideEffects do @@ -23,7 +23,9 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.Push alias Pleroma.Web.Streamer + alias Pleroma.Workers.PollWorker + require Pleroma.Constants require Logger @cachex Pleroma.Config.get([:cachex, :provider], Cachex) @@ -152,23 +154,26 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do # Tasks this handles: # - Update the user + # - Update a non-user object (Note, Question, etc.) # # For a local user, we also get a changeset with the full information, so we # can update non-federating, non-activitypub settings as well. @impl true def handle(%{data: %{"type" => "Update", "object" => updated_object}} = object, meta) do - if changeset = Keyword.get(meta, :user_update_changeset) do - changeset - |> User.update_and_set_cache() + updated_object_id = updated_object["id"] + + with {_, true} <- {:has_id, is_binary(updated_object_id)}, + %{"type" => type} <- updated_object, + {_, is_user} <- {:is_user, type in Pleroma.Constants.actor_types()} do + if is_user do + handle_update_user(object, meta) + else + handle_update_object(object, meta) + end else - {:ok, new_user_data} = ActivityPub.user_data_from_user_object(updated_object) - - User.get_by_ap_id(updated_object["id"]) - |> User.remote_user_changeset(new_user_data) - |> User.update_and_set_cache() + _ -> + {:ok, object, meta} end - - {:ok, object, meta} end # Tasks this handles: @@ -194,12 +199,13 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do # - Set up notifications @impl true def handle(%{data: %{"type" => "Create"}} = activity, meta) do - with {:ok, object, meta} <- handle_object_creation(meta[:object_data], meta), + with {:ok, object, meta} <- handle_object_creation(meta[:object_data], activity, meta), %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do {:ok, notifications} = Notification.create_notifications(activity, do_send: false) {:ok, _user} = ActivityPub.increase_note_count_if_public(user, object) + {:ok, _user} = ActivityPub.update_last_status_at_if_public(user, object) - if in_reply_to = object.data["inReplyTo"] && object.data["type"] != "Answer" do + if in_reply_to = object.data["type"] != "Answer" && object.data["inReplyTo"] do Object.increase_replies_count(in_reply_to) end @@ -276,7 +282,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do # Tasks this handles: # - Delete and unpins the create activity # - Replace object with Tombstone - # - Set up notification # - Reduce the user note count # - Reduce the reply count # - Stream out the activity @@ -318,7 +323,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do end if result == :ok do - Notification.create_notifications(object) {:ok, object, meta} else {:error, result} @@ -388,7 +392,80 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do {:ok, object, meta} end - def handle_object_creation(%{"type" => "ChatMessage"} = object, meta) do + defp handle_update_user( + %{data: %{"type" => "Update", "object" => updated_object}} = object, + meta + ) do + if changeset = Keyword.get(meta, :user_update_changeset) do + changeset + |> User.update_and_set_cache() + else + {:ok, new_user_data} = ActivityPub.user_data_from_user_object(updated_object) + + User.get_by_ap_id(updated_object["id"]) + |> User.remote_user_changeset(new_user_data) + |> User.update_and_set_cache() + end + + {:ok, object, meta} + end + + defp handle_update_object( + %{data: %{"type" => "Update", "object" => updated_object}} = object, + meta + ) do + orig_object_ap_id = updated_object["id"] + orig_object = Object.get_by_ap_id(orig_object_ap_id) + orig_object_data = orig_object.data + + updated_object = + if meta[:local] do + # If this is a local Update, we don't process it by transmogrifier, + # so we use the embedded object as-is. + updated_object + else + meta[:object_data] + end + + if orig_object_data["type"] in Pleroma.Constants.updatable_object_types() do + %{ + updated_data: updated_object_data, + updated: updated, + used_history_in_new_object?: used_history_in_new_object? + } = Object.Updater.make_new_object_data_from_update_object(orig_object_data, updated_object) + + changeset = + orig_object + |> Repo.preload(:hashtags) + |> Object.change(%{data: updated_object_data}) + + with {:ok, new_object} <- Repo.update(changeset), + {:ok, _} <- Object.invalid_object_cache(new_object), + {:ok, _} <- Object.set_cache(new_object), + # The metadata/utils.ex uses the object id for the cache. + {:ok, _} <- Pleroma.Activity.HTML.invalidate_cache_for(new_object.id) do + if used_history_in_new_object? do + with create_activity when not is_nil(create_activity) <- + Pleroma.Activity.get_create_by_object_ap_id(orig_object_ap_id), + {:ok, _} <- Pleroma.Activity.HTML.invalidate_cache_for(create_activity.id) do + nil + else + _ -> nil + end + end + + if updated do + object + |> Activity.normalize() + |> ActivityPub.notify_and_stream() + end + end + end + + {:ok, object, meta} + end + + def handle_object_creation(%{"type" => "ChatMessage"} = object, _activity, meta) do with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do actor = User.get_cached_by_ap_id(object.data["actor"]) recipient = User.get_cached_by_ap_id(hd(object.data["to"])) @@ -423,7 +500,14 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do end end - def handle_object_creation(%{"type" => "Answer"} = object_map, meta) do + def handle_object_creation(%{"type" => "Question"} = object, activity, meta) do + with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do + PollWorker.schedule_poll_end(activity) + {:ok, object, meta} + end + end + + def handle_object_creation(%{"type" => "Answer"} = object_map, _activity, meta) do with {:ok, object, meta} <- Pipeline.common_pipeline(object_map, meta) do Object.increase_vote_count( object.data["inReplyTo"], @@ -435,15 +519,15 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do end end - def handle_object_creation(%{"type" => objtype} = object, meta) - when objtype in ~w[Audio Video Question Event Article Note Page] do + def handle_object_creation(%{"type" => objtype} = object, _activity, meta) + when objtype in ~w[Audio Video Event Article Note Page] do with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do {:ok, object, meta} end end # Nothing to do - def handle_object_creation(object, meta) do + def handle_object_creation(object, _activity, meta) do {:ok, object, meta} end diff --git a/lib/pleroma/web/activity_pub/side_effects/handling.ex b/lib/pleroma/web/activity_pub/side_effects/handling.ex index a82305155..eb012f576 100644 --- a/lib/pleroma/web/activity_pub/side_effects/handling.ex +++ b/lib/pleroma/web/activity_pub/side_effects/handling.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.SideEffects.Handling do diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 5766489be..e4c04da0d 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier do @@ -687,6 +687,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> strip_internal_fields |> strip_internal_tags |> set_type + |> maybe_process_history + end + + defp maybe_process_history(%{"formerRepresentations" => %{"orderedItems" => history}} = object) do + processed_history = + Enum.map( + history, + fn + item when is_map(item) -> prepare_object(item) + item -> item + end + ) + + put_in(object, ["formerRepresentations", "orderedItems"], processed_history) + end + + defp maybe_process_history(object) do + object end # @doc @@ -711,6 +729,21 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do {:ok, data} end + def prepare_outgoing(%{"type" => "Update", "object" => %{"type" => objtype} = object} = data) + when objtype in Pleroma.Constants.updatable_object_types() do + object = + object + |> prepare_object + + data = + data + |> Map.put("object", object) + |> Map.merge(Utils.make_json_ld_header()) + |> Map.delete("bcc") + + {:ok, data} + end + def prepare_outgoing(%{"type" => "Announce", "actor" => ap_id, "object" => object_id} = data) do object = object_id diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 1df53f79a..b898d6fe8 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Utils do @@ -154,22 +154,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do Notification.get_notified_from_activity(%Activity{data: object}, false) end - def create_context(context) do - context = context || generate_id("contexts") - - # Ecto has problems accessing the constraint inside the jsonb, - # so we explicitly check for the existed object before insert - object = Object.get_cached_by_ap_id(context) - - with true <- is_nil(object), - changeset <- Object.context_mapping(context), - {:ok, inserted_object} <- Repo.insert(changeset) do - inserted_object - else - _ -> - object - end - end + def maybe_create_context(context), do: context || generate_id("contexts") @doc """ Enqueues an activity for federation if it's local @@ -201,18 +186,16 @@ defmodule Pleroma.Web.ActivityPub.Utils do |> Map.put_new("id", "pleroma:fakeid") |> Map.put_new_lazy("published", &make_date/0) |> Map.put_new("context", "pleroma:fakecontext") - |> Map.put_new("context_id", -1) |> lazy_put_object_defaults(true) end def lazy_put_activity_defaults(map, _fake?) do - %{data: %{"id" => context}, id: context_id} = create_context(map["context"]) + context = maybe_create_context(map["context"]) map |> Map.put_new_lazy("id", &generate_activity_id/0) |> Map.put_new_lazy("published", &make_date/0) |> Map.put_new("context", context) - |> Map.put_new("context_id", context_id) |> lazy_put_object_defaults(false) end @@ -226,7 +209,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do |> Map.put_new("id", "pleroma:fake_object_id") |> Map.put_new_lazy("published", &make_date/0) |> Map.put_new("context", activity["context"]) - |> Map.put_new("context_id", activity["context_id"]) |> Map.put_new("fake", true) %{activity | "object" => object} @@ -239,7 +221,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do |> Map.put_new_lazy("id", &generate_object_id/0) |> Map.put_new_lazy("published", &make_date/0) |> Map.put_new("context", activity["context"]) - |> Map.put_new("context_id", activity["context_id"]) %{activity | "object" => object} end @@ -446,7 +427,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do |> Activity.Queries.by_type() |> Activity.Queries.by_actor(actor) |> Activity.Queries.by_object_id(object) - |> where(fragment("data->>'state' = 'pending'")) + |> where(fragment("data->>'state' = 'pending'") or fragment("data->>'state' = 'accept'")) |> update(set: [data: fragment("jsonb_set(data, '{state}', ?)", ^state)]) |> Repo.update_all([]) @@ -714,20 +695,24 @@ defmodule Pleroma.Web.ActivityPub.Utils do Enum.map(statuses || [], &build_flag_object/1) end - defp build_flag_object(%Activity{data: %{"id" => id}, object: %{data: data}}) do - activity_actor = User.get_by_ap_id(data["actor"]) + defp build_flag_object(%Activity{} = activity) do + object = Object.normalize(activity, fetch: false) - %{ - "type" => "Note", - "id" => id, - "content" => data["content"], - "published" => data["published"], - "actor" => - AccountView.render( - "show.json", - %{user: activity_actor, skip_visibility_check: true} - ) - } + # Do not allow people to report Creates. Instead, report the Object that is Created. + if activity.data["type"] != "Create" do + build_flag_object_with_actor_and_id( + object, + User.get_by_ap_id(activity.data["actor"]), + activity.data["id"] + ) + else + build_flag_object(object) + end + end + + defp build_flag_object(%Object{} = object) do + actor = User.get_by_ap_id(object.data["actor"]) + build_flag_object_with_actor_and_id(object, actor, object.data["id"]) end defp build_flag_object(act) when is_map(act) or is_binary(act) do @@ -739,12 +724,12 @@ defmodule Pleroma.Web.ActivityPub.Utils do end case Activity.get_by_ap_id_with_object(id) do - %Activity{} = activity -> - build_flag_object(activity) + %Activity{object: object} = _ -> + build_flag_object(object) nil -> - if activity = Activity.get_by_object_ap_id_with_object(id) do - build_flag_object(activity) + if %Object{} = object = Object.get_by_ap_id(id) do + build_flag_object(object) else %{"id" => id, "deleted" => true} end @@ -753,6 +738,20 @@ defmodule Pleroma.Web.ActivityPub.Utils do defp build_flag_object(_), do: [] + defp build_flag_object_with_actor_and_id(%Object{data: data}, actor, id) do + %{ + "type" => "Note", + "id" => id, + "content" => data["content"], + "published" => data["published"], + "actor" => + AccountView.render( + "show.json", + %{user: actor, skip_visibility_check: true} + ) + } + end + #### Report-related helpers def get_reports(params, page, page_size) do params = @@ -767,22 +766,21 @@ defmodule Pleroma.Web.ActivityPub.Utils do ActivityPub.fetch_activities([], params, :offset) end - def update_report_state(%Activity{} = activity, state) - when state in @strip_status_report_states do - {:ok, stripped_activity} = strip_report_status_data(activity) + defp maybe_strip_report_status(data, state) do + with true <- Config.get([:instance, :report_strip_status]), + true <- state in @strip_status_report_states, + {:ok, stripped_activity} = strip_report_status_data(%Activity{data: data}) do + data |> Map.put("object", stripped_activity.data["object"]) + else + _ -> data + end + end + def update_report_state(%Activity{} = activity, state) when state in @supported_report_states do new_data = activity.data |> Map.put("state", state) - |> Map.put("object", stripped_activity.data["object"]) - - activity - |> Changeset.change(data: new_data) - |> Repo.update() - end - - def update_report_state(%Activity{} = activity, state) when state in @supported_report_states do - new_data = Map.put(activity.data, "state", state) + |> maybe_strip_report_status(state) activity |> Changeset.change(data: new_data) diff --git a/lib/pleroma/web/activity_pub/views/object_view.ex b/lib/pleroma/web/activity_pub/views/object_view.ex index 8a3e4d77b..63caa915c 100644 --- a/lib/pleroma/web/activity_pub/views/object_view.ex +++ b/lib/pleroma/web/activity_pub/views/object_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectView do @@ -29,11 +29,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do def render("object.json", %{object: %Activity{} = activity}) do base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header() - object = Object.normalize(activity, fetch: false) + object_id = Object.normalize(activity, id_only: true) additional = Transmogrifier.prepare_object(activity.data) - |> Map.put("object", object.data["id"]) + |> Map.put("object", object_id) Map.merge(base, additional) end diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 344da19d3..f69fca075 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.UserView do @@ -34,7 +34,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do def render("endpoints.json", _), do: %{} def render("service.json", %{user: user}) do - {:ok, user} = User.ensure_keys_present(user) {:ok, _, public_key} = Keys.keys_from_pem(user.keys) public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key) public_key = :public_key.pem_encode([public_key]) @@ -71,7 +70,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do do: render("service.json", %{user: user}) |> Map.put("preferredUsername", user.nickname) def render("user.json", %{user: user}) do - {:ok, user} = User.ensure_keys_present(user) {:ok, _, public_key} = Keys.keys_from_pem(user.keys) public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key) public_key = :public_key.pem_encode([public_key]) @@ -92,6 +90,11 @@ defmodule Pleroma.Web.ActivityPub.UserView do %{} end + birthday = + if user.show_birthday && user.birthday, + do: Date.to_iso8601(user.birthday), + else: nil + %{ "id" => user.ap_id, "type" => user.actor_type, @@ -116,7 +119,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do # Note: key name is indeed "discoverable" (not an error) "discoverable" => user.is_discoverable, "capabilities" => capabilities, - "alsoKnownAs" => user.also_known_as + "alsoKnownAs" => user.also_known_as, + "vcard:bday" => birthday } |> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user)) |> Map.merge(maybe_make_image(&User.banner_url/2, "image", user)) diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex index 986fa3a08..7c57f88f9 100644 --- a/lib/pleroma/web/activity_pub/visibility.ex +++ b/lib/pleroma/web/activity_pub/visibility.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Visibility do @@ -84,7 +84,10 @@ defmodule Pleroma.Web.ActivityPub.Visibility do when module in [Activity, Object] do x = [user.ap_id | User.following(user)] y = [message.data["actor"]] ++ message.data["to"] ++ (message.data["cc"] || []) - is_public?(message) || Enum.any?(x, &(&1 in y)) + + user_is_local = user.local + federatable = not is_local_public?(message) + (is_public?(message) || Enum.any?(x, &(&1 in y))) and (user_is_local || federatable) end def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do diff --git a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex index 839ac1a8d..1894000ff 100644 --- a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.AdminAPIController do @@ -49,7 +49,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, %{scopes: ["admin:read:statuses"]} - when action in [:list_user_statuses, :list_instance_statuses] + when action in [:list_user_statuses] ) plug( @@ -81,24 +81,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do action_fallback(AdminAPI.FallbackController) - def list_instance_statuses(conn, %{"instance" => instance} = params) do - with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true - {page, page_size} = page_params(params) - - result = - ActivityPub.fetch_statuses(nil, %{ - instance: instance, - limit: page_size, - offset: (page - 1) * page_size, - exclude_reblogs: not with_reblogs, - total: true - }) - - conn - |> put_view(AdminAPI.StatusView) - |> render("index.json", %{total: result[:total], activities: result[:items], as: :activity}) - end - def list_user_statuses(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = params) do with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true godmode = params["godmode"] == "true" || params["godmode"] == true diff --git a/lib/pleroma/web/admin_api/controllers/announcement_controller.ex b/lib/pleroma/web/admin_api/controllers/announcement_controller.ex new file mode 100644 index 000000000..6ad5fc12c --- /dev/null +++ b/lib/pleroma/web/admin_api/controllers/announcement_controller.ex @@ -0,0 +1,83 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.AnnouncementController do + use Pleroma.Web, :controller + + alias Pleroma.Announcement + alias Pleroma.Web.ControllerHelper + alias Pleroma.Web.Plugs.OAuthScopesPlug + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action in [:create, :delete, :change]) + plug(OAuthScopesPlug, %{scopes: ["admin:read"]} when action in [:index, :show]) + action_fallback(Pleroma.Web.AdminAPI.FallbackController) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.AnnouncementOperation + + defp default_limit, do: 20 + + def index(conn, params) do + limit = Map.get(params, :limit, default_limit()) + offset = Map.get(params, :offset, 0) + + announcements = Announcement.list_paginated(%{limit: limit, offset: offset}) + + render(conn, "index.json", announcements: announcements) + end + + def show(conn, %{id: id} = _params) do + announcement = Announcement.get_by_id(id) + + if is_nil(announcement) do + {:error, :not_found} + else + render(conn, "show.json", announcement: announcement) + end + end + + def create(%{body_params: params} = conn, _params) do + with {:ok, announcement} <- Announcement.add(change_params(params)) do + render(conn, "show.json", announcement: announcement) + else + _ -> + {:error, 400} + end + end + + def change_params(orig_params) do + data = + %{} + |> Pleroma.Maps.put_if_present("content", orig_params, &Map.fetch(&1, :content)) + |> Pleroma.Maps.put_if_present("all_day", orig_params, &Map.fetch(&1, :all_day)) + + orig_params + |> Map.merge(%{data: data}) + end + + def change(%{body_params: params} = conn, %{id: id} = _params) do + with announcement <- Announcement.get_by_id(id), + {:exists, true} <- {:exists, not is_nil(announcement)}, + {:ok, announcement} <- Announcement.update(announcement, change_params(params)) do + render(conn, "show.json", announcement: announcement) + else + {:exists, false} -> + {:error, :not_found} + + _ -> + {:error, 400} + end + end + + def delete(conn, %{id: id} = _params) do + case Announcement.delete_by_id(id) do + :ok -> + conn + |> ControllerHelper.json_response(:ok, %{}) + + _ -> + {:error, :not_found} + end + end +end diff --git a/lib/pleroma/web/admin_api/controllers/chat_controller.ex b/lib/pleroma/web/admin_api/controllers/chat_controller.ex index ff20c8604..298543fcf 100644 --- a/lib/pleroma/web/admin_api/controllers/chat_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/chat_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ChatController do @@ -8,7 +8,6 @@ defmodule Pleroma.Web.AdminAPI.ChatController do alias Pleroma.Activity alias Pleroma.Chat alias Pleroma.Chat.MessageReference - alias Pleroma.ModerationLog alias Pleroma.Pagination alias Pleroma.Web.AdminAPI alias Pleroma.Web.CommonAPI @@ -42,12 +41,6 @@ defmodule Pleroma.Web.AdminAPI.ChatController do ^chat_id <- to_string(cm_ref.chat_id), %Activity{id: activity_id} <- Activity.get_create_by_object_ap_id(object_ap_id), {:ok, _} <- CommonAPI.delete(activity_id, user) do - ModerationLog.insert_log(%{ - action: "chat_message_delete", - actor: user, - subject_id: message_id - }) - conn |> put_view(MessageReferenceView) |> render("show.json", chat_message_reference: cm_ref) diff --git a/lib/pleroma/web/admin_api/controllers/config_controller.ex b/lib/pleroma/web/admin_api/controllers/config_controller.ex index a718d7b8d..a03318c0e 100644 --- a/lib/pleroma/web/admin_api/controllers/config_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/config_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ConfigController do @@ -22,10 +22,58 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ConfigOperation + defp translate_descriptions(descriptions, path \\ []) do + Enum.map(descriptions, fn desc -> translate_item(desc, path) end) + end + + defp translate_string(str, path, type) do + Gettext.dpgettext( + Pleroma.Web.Gettext, + "config_descriptions", + Pleroma.Docs.Translator.Compiler.msgctxt_for(path, type), + str + ) + end + + defp maybe_put_translated(item, key, path) do + if item[key] do + Map.put( + item, + key, + translate_string( + item[key], + path ++ [Pleroma.Docs.Translator.Compiler.key_for(item)], + to_string(key) + ) + ) + else + item + end + end + + defp translate_item(item, path) do + item + |> maybe_put_translated(:label, path) + |> maybe_put_translated(:description, path) + |> translate_children(path) + end + + defp translate_children(%{children: children} = item, path) when is_list(children) do + item + |> Map.put( + :children, + translate_descriptions(children, path ++ [Pleroma.Docs.Translator.Compiler.key_for(item)]) + ) + end + + defp translate_children(item, _path) do + item + end + def descriptions(conn, _params) do descriptions = Enum.filter(Pleroma.Docs.JSON.compiled_descriptions(), &whitelisted_config?/1) - json(conn, descriptions) + json(conn, translate_descriptions(descriptions)) end def show(conn, %{only_db: true}) do diff --git a/lib/pleroma/web/admin_api/controllers/fallback_controller.ex b/lib/pleroma/web/admin_api/controllers/fallback_controller.ex index 45d8815b5..e72f45c21 100644 --- a/lib/pleroma/web/admin_api/controllers/fallback_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/fallback_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.FallbackController do diff --git a/lib/pleroma/web/admin_api/controllers/frontend_controller.ex b/lib/pleroma/web/admin_api/controllers/frontend_controller.ex index 442e6a5a0..b4dbb82fe 100644 --- a/lib/pleroma/web/admin_api/controllers/frontend_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/frontend_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.FrontendController do diff --git a/lib/pleroma/web/admin_api/controllers/instance_controller.ex b/lib/pleroma/web/admin_api/controllers/instance_controller.ex new file mode 100644 index 000000000..117a72280 --- /dev/null +++ b/lib/pleroma/web/admin_api/controllers/instance_controller.ex @@ -0,0 +1,63 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.InstanceController do + use Pleroma.Web, :controller + + import Pleroma.Web.ControllerHelper, only: [fetch_integer_param: 3] + + alias Pleroma.Instances.Instance + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.AdminAPI + alias Pleroma.Web.Plugs.OAuthScopesPlug + + require Logger + + @default_page_size 50 + + plug( + OAuthScopesPlug, + %{scopes: ["admin:read:statuses"]} + when action in [:list_statuses] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["admin:write:accounts", "admin:write:statuses"]} + when action in [:delete] + ) + + action_fallback(AdminAPI.FallbackController) + + def list_statuses(conn, %{"instance" => instance} = params) do + with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true + {page, page_size} = page_params(params) + + result = + ActivityPub.fetch_statuses(nil, %{ + instance: instance, + limit: page_size, + offset: (page - 1) * page_size, + exclude_reblogs: not with_reblogs, + total: true + }) + + conn + |> put_view(AdminAPI.StatusView) + |> render("index.json", %{total: result[:total], activities: result[:items], as: :activity}) + end + + def delete(conn, %{"instance" => instance}) do + with {:ok, _job} <- Instance.delete_users_and_activities(instance) do + json(conn, instance) + end + end + + defp page_params(params) do + { + fetch_integer_param(params, "page", 1), + fetch_integer_param(params, "page_size", @default_page_size) + } + end +end diff --git a/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex b/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex index a55857a0e..990a94313 100644 --- a/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do diff --git a/lib/pleroma/web/admin_api/controllers/invite_controller.ex b/lib/pleroma/web/admin_api/controllers/invite_controller.ex index 727ebd846..c5d759bb5 100644 --- a/lib/pleroma/web/admin_api/controllers/invite_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/invite_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.InviteController do diff --git a/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex b/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex index a6d7aaf54..4d53f5451 100644 --- a/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do diff --git a/lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex b/lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex index 51b17d392..879e8b2b4 100644 --- a/lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.OAuthAppController do diff --git a/lib/pleroma/web/admin_api/controllers/relay_controller.ex b/lib/pleroma/web/admin_api/controllers/relay_controller.ex index c6bd43fea..2e83fe139 100644 --- a/lib/pleroma/web/admin_api/controllers/relay_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/relay_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.RelayController do diff --git a/lib/pleroma/web/admin_api/controllers/report_controller.ex b/lib/pleroma/web/admin_api/controllers/report_controller.ex index d4a4935ee..15cbbcc3e 100644 --- a/lib/pleroma/web/admin_api/controllers/report_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/report_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ReportController do diff --git a/lib/pleroma/web/admin_api/controllers/status_controller.ex b/lib/pleroma/web/admin_api/controllers/status_controller.ex index 7058def82..9a3d49b57 100644 --- a/lib/pleroma/web/admin_api/controllers/status_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/status_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.StatusController do @@ -65,12 +65,6 @@ defmodule Pleroma.Web.AdminAPI.StatusController do def delete(%{assigns: %{user: user}} = conn, %{id: id}) do with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do - ModerationLog.insert_log(%{ - action: "status_delete", - actor: user, - subject_id: id - }) - json(conn, %{}) end end diff --git a/lib/pleroma/web/admin_api/controllers/user_controller.ex b/lib/pleroma/web/admin_api/controllers/user_controller.ex index 637a0e702..7b4ee46a4 100644 --- a/lib/pleroma/web/admin_api/controllers/user_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/user_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.UserController do @@ -35,7 +35,9 @@ defmodule Pleroma.Web.AdminAPI.UserController do :toggle_activation, :activate, :deactivate, - :approve + :approve, + :suggest, + :unsuggest ] ) @@ -239,6 +241,32 @@ defmodule Pleroma.Web.AdminAPI.UserController do render(conn, "index.json", users: updated_users) end + def suggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do + users = Enum.map(nicknames, &User.get_cached_by_nickname/1) + {:ok, updated_users} = User.set_suggestion(users, true) + + ModerationLog.insert_log(%{ + actor: admin, + subject: users, + action: "add_suggestion" + }) + + render(conn, "index.json", users: updated_users) + end + + def unsuggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do + users = Enum.map(nicknames, &User.get_cached_by_nickname/1) + {:ok, updated_users} = User.set_suggestion(users, false) + + ModerationLog.insert_log(%{ + actor: admin, + subject: users, + action: "remove_suggestion" + }) + + render(conn, "index.json", users: updated_users) + end + def index(conn, params) do {page, page_size} = page_params(params) filters = maybe_parse_filters(params[:filters]) diff --git a/lib/pleroma/web/admin_api/report.ex b/lib/pleroma/web/admin_api/report.ex index 345bc1e87..c79bee27e 100644 --- a/lib/pleroma/web/admin_api/report.ex +++ b/lib/pleroma/web/admin_api/report.ex @@ -1,9 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.Report do alias Pleroma.Activity + alias Pleroma.Object alias Pleroma.User def extract_report_info( @@ -16,10 +17,44 @@ defmodule Pleroma.Web.AdminAPI.Report do status_ap_ids |> Enum.reject(&is_nil(&1)) |> Enum.map(fn - act when is_map(act) -> Activity.get_by_ap_id_with_object(act["id"]) - act when is_binary(act) -> Activity.get_by_ap_id_with_object(act) + act when is_map(act) -> + Activity.get_create_by_object_ap_id_with_object(act["id"]) || + Activity.get_by_ap_id_with_object(act["id"]) || make_fake_activity(act, user) + + act when is_binary(act) -> + Activity.get_create_by_object_ap_id_with_object(act) || + Activity.get_by_ap_id_with_object(act) end) %{report: report, user: user, account: account, statuses: statuses} end + + defp make_fake_activity(act, user) do + %Activity{ + id: "pleroma:fake", + data: %{ + "actor" => user.ap_id, + "type" => "Create", + "to" => [], + "cc" => [], + "object" => act["id"], + "published" => act["published"], + "id" => act["id"], + "context" => "pleroma:fake" + }, + recipients: [user.ap_id], + object: %Object{ + data: %{ + "actor" => user.ap_id, + "type" => "Note", + "content" => act["content"], + "published" => act["published"], + "to" => [], + "cc" => [], + "id" => act["id"], + "context" => "pleroma:fake" + } + } + } + end end diff --git a/lib/pleroma/web/admin_api/search.ex b/lib/pleroma/web/admin_api/search.ex index da38fab56..f5195acde 100644 --- a/lib/pleroma/web/admin_api/search.ex +++ b/lib/pleroma/web/admin_api/search.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.Search do diff --git a/lib/pleroma/web/admin_api/views/account_view.ex b/lib/pleroma/web/admin_api/views/account_view.ex index fae0c07f0..280152241 100644 --- a/lib/pleroma/web/admin_api/views/account_view.ex +++ b/lib/pleroma/web/admin_api/views/account_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.AccountView do @@ -80,6 +80,7 @@ defmodule Pleroma.Web.AdminAPI.AccountView do "tags" => user.tags || [], "is_confirmed" => user.is_confirmed, "is_approved" => user.is_approved, + "is_suggested" => user.is_suggested, "url" => user.uri || user.ap_id, "registration_reason" => user.registration_reason, "actor_type" => user.actor_type, diff --git a/lib/pleroma/web/admin_api/views/announcement_view.ex b/lib/pleroma/web/admin_api/views/announcement_view.ex new file mode 100644 index 000000000..a35bd60cf --- /dev/null +++ b/lib/pleroma/web/admin_api/views/announcement_view.ex @@ -0,0 +1,15 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.AnnouncementView do + use Pleroma.Web, :view + + def render("index.json", %{announcements: announcements}) do + render_many(announcements, __MODULE__, "show.json") + end + + def render("show.json", %{announcement: announcement}) do + Pleroma.Announcement.render_json(announcement, admin: true) + end +end diff --git a/lib/pleroma/web/admin_api/views/chat_view.ex b/lib/pleroma/web/admin_api/views/chat_view.ex index 2a2015ad1..d58bf8eda 100644 --- a/lib/pleroma/web/admin_api/views/chat_view.ex +++ b/lib/pleroma/web/admin_api/views/chat_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ChatView do diff --git a/lib/pleroma/web/admin_api/views/config_view.ex b/lib/pleroma/web/admin_api/views/config_view.ex index d29b4963d..f582ad42e 100644 --- a/lib/pleroma/web/admin_api/views/config_view.ex +++ b/lib/pleroma/web/admin_api/views/config_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ConfigView do diff --git a/lib/pleroma/web/admin_api/views/frontend_view.ex b/lib/pleroma/web/admin_api/views/frontend_view.ex index a3933a57d..0ca3d67cb 100644 --- a/lib/pleroma/web/admin_api/views/frontend_view.ex +++ b/lib/pleroma/web/admin_api/views/frontend_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.FrontendView do diff --git a/lib/pleroma/web/admin_api/views/invite_view.ex b/lib/pleroma/web/admin_api/views/invite_view.ex index c7e307bda..76cee3bc5 100644 --- a/lib/pleroma/web/admin_api/views/invite_view.ex +++ b/lib/pleroma/web/admin_api/views/invite_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.InviteView do diff --git a/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex b/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex index 1ec123048..b46f54efe 100644 --- a/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex +++ b/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.MediaProxyCacheView do diff --git a/lib/pleroma/web/admin_api/views/moderation_log_view.ex b/lib/pleroma/web/admin_api/views/moderation_log_view.ex index b3a9efff3..1f25f194f 100644 --- a/lib/pleroma/web/admin_api/views/moderation_log_view.ex +++ b/lib/pleroma/web/admin_api/views/moderation_log_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ModerationLogView do diff --git a/lib/pleroma/web/admin_api/views/o_auth_app_view.ex b/lib/pleroma/web/admin_api/views/o_auth_app_view.ex index af046f343..d1aef0e10 100644 --- a/lib/pleroma/web/admin_api/views/o_auth_app_view.ex +++ b/lib/pleroma/web/admin_api/views/o_auth_app_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.OAuthAppView do diff --git a/lib/pleroma/web/admin_api/views/report_view.ex b/lib/pleroma/web/admin_api/views/report_view.ex index 1c67b2458..b761dbb22 100644 --- a/lib/pleroma/web/admin_api/views/report_view.ex +++ b/lib/pleroma/web/admin_api/views/report_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ReportView do diff --git a/lib/pleroma/web/admin_api/views/status_view.ex b/lib/pleroma/web/admin_api/views/status_view.ex index 48d639b41..03b5c440a 100644 --- a/lib/pleroma/web/admin_api/views/status_view.ex +++ b/lib/pleroma/web/admin_api/views/status_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.StatusView do diff --git a/lib/pleroma/web/admin_api/views/user_view.ex b/lib/pleroma/web/admin_api/views/user_view.ex index e91265ffe..f198921ae 100644 --- a/lib/pleroma/web/admin_api/views/user_view.ex +++ b/lib/pleroma/web/admin_api/views/user_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.UserView do diff --git a/lib/pleroma/web/api_spec.ex b/lib/pleroma/web/api_spec.ex index 528cd9cf4..cae4241ff 100644 --- a/lib/pleroma/web/api_spec.ex +++ b/lib/pleroma/web/api_spec.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec do diff --git a/lib/pleroma/web/api_spec/cast_and_validate.ex b/lib/pleroma/web/api_spec/cast_and_validate.ex index d23a7dcb6..add59eb88 100644 --- a/lib/pleroma/web/api_spec/cast_and_validate.ex +++ b/lib/pleroma/web/api_spec/cast_and_validate.ex @@ -1,6 +1,6 @@ # Pleroma: A lightweight social networking server # Copyright © 2019-2020 Moxley Stratton, Mike Buhot <https://github.com/open-api-spex/open_api_spex>, MPL-2.0 -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.CastAndValidate do diff --git a/lib/pleroma/web/api_spec/helpers.ex b/lib/pleroma/web/api_spec/helpers.ex index 6f67339e6..f20a9163d 100644 --- a/lib/pleroma/web/api_spec/helpers.ex +++ b/lib/pleroma/web/api_spec/helpers.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Helpers do diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 54e5ebc76..aabe988f7 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.AccountOperation do @@ -64,7 +64,8 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do requestBody: request_body("Parameters", update_credentials_request(), required: true), responses: %{ 200 => Operation.response("Account", "application/json", Account), - 403 => Operation.response("Error", "application/json", ApiError) + 403 => Operation.response("Error", "application/json", ApiError), + 413 => Operation.response("Error", "application/json", ApiError) } } end @@ -223,9 +224,15 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do type: :object, properties: %{ reblogs: %Schema{ - type: :boolean, + allOf: [BooleanLike], description: "Receive this account's reblogs in home timeline? Defaults to true.", default: true + }, + notify: %Schema{ + allOf: [BooleanLike], + description: + "Receive notifications for all statuses posted by the account? Defaults to false.", + default: false } } }, @@ -273,10 +280,16 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "Mute notifications in addition to statuses? Defaults to `true`." ), Operation.parameter( + :duration, + :query, + %Schema{type: :integer}, + "Expire the mute in `duration` seconds. Default 0 for infinity" + ), + Operation.parameter( :expires_in, :query, %Schema{type: :integer, default: 0}, - "Expire the mute in `expires_in` seconds. Default 0 for infinity" + "Deprecated, use `duration` instead" ) ], responses: %{ @@ -328,6 +341,81 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end + def endorse_operation do + %Operation{ + tags: ["Account actions"], + summary: "Endorse", + operationId: "AccountController.endorse", + security: [%{"oAuth" => ["follow", "write:accounts"]}], + description: "Addds the given account to endorsed accounts list.", + parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}], + responses: %{ + 200 => Operation.response("Relationship", "application/json", AccountRelationship), + 400 => + Operation.response("Bad Request", "application/json", %Schema{ + allOf: [ApiError], + title: "Unprocessable Entity", + example: %{ + "error" => "You have already pinned the maximum number of users" + } + }) + } + } + end + + def unendorse_operation do + %Operation{ + tags: ["Account actions"], + summary: "Unendorse", + operationId: "AccountController.unendorse", + security: [%{"oAuth" => ["follow", "write:accounts"]}], + description: "Removes the given account from endorsed accounts list.", + parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}], + responses: %{ + 200 => Operation.response("Relationship", "application/json", AccountRelationship) + } + } + end + + def remove_from_followers_operation do + %Operation{ + tags: ["Account actions"], + summary: "Remove from followers", + operationId: "AccountController.remove_from_followers", + security: [%{"oAuth" => ["follow", "write:follows"]}], + description: "Remove the given account from followers", + parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}], + responses: %{ + 200 => Operation.response("Relationship", "application/json", AccountRelationship), + 400 => Operation.response("Error", "application/json", ApiError), + 404 => Operation.response("Error", "application/json", ApiError) + } + } + end + + def note_operation do + %Operation{ + tags: ["Account actions"], + summary: "Set a private note about a user.", + operationId: "AccountController.note", + security: [%{"oAuth" => ["follow", "write:accounts"]}], + requestBody: request_body("Parameters", note_request()), + description: "Create a note for the given account.", + parameters: [ + %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, + Operation.parameter( + :comment, + :query, + %Schema{type: :string}, + "Account note body" + ) + ], + responses: %{ + 200 => Operation.response("Relationship", "application/json", AccountRelationship) + } + } + end + def follow_by_uri_operation do %Operation{ tags: ["Account actions"], @@ -371,15 +459,35 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end + def lookup_operation do + %Operation{ + tags: ["Account lookup"], + summary: "Find a user by nickname", + operationId: "AccountController.lookup", + parameters: [ + Operation.parameter( + :acct, + :query, + :string, + "User nickname" + ) + ], + responses: %{ + 200 => Operation.response("Account", "application/json", Account), + 404 => Operation.response("Error", "application/json", ApiError) + } + } + end + def endorsements_operation do %Operation{ tags: ["Retrieve account information"], summary: "Endorsements", operationId: "AccountController.endorsements", - description: "Not implemented", + description: "Returns endorsed accounts", security: [%{"oAuth" => ["read:accounts"]}], responses: %{ - 200 => empty_array_response() + 200 => Operation.response("Array of Accounts", "application/json", array_of_accounts()) } } end @@ -458,6 +566,25 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do type: :string, nullable: true, description: "Invite token required when the registrations aren't public" + }, + birthday: %Schema{ + nullable: true, + description: "User's birthday", + anyOf: [ + %Schema{ + type: :string, + format: :date + }, + %Schema{ + type: :string, + maxLength: 0 + } + ] + }, + language: %Schema{ + type: :string, + nullable: true, + description: "User's preferred language for emails" } }, example: %{ @@ -635,7 +762,26 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do description: "Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed." }, - actor_type: ActorType + actor_type: ActorType, + birthday: %Schema{ + nullable: true, + description: "User's birthday", + anyOf: [ + %Schema{ + type: :string, + format: :date + }, + %Schema{ + type: :string, + maxLength: 0 + } + ] + }, + show_birthday: %Schema{ + allOf: [BooleanLike], + nullable: true, + description: "User's birthday will be visible" + } }, example: %{ bot: false, @@ -655,7 +801,9 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do allow_following_move: false, also_known_as: ["https://foo.bar/users/foo"], discoverable: false, - actor_type: "Person" + actor_type: "Person", + show_birthday: false, + birthday: "2001-02-12" } } end @@ -685,9 +833,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "blocked_by" => true, "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "domain_blocking" => false, "subscribing" => false, + "notifying" => false, "endorsed" => true }, %{ @@ -699,9 +849,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "blocked_by" => true, "muting" => true, "muting_notifications" => false, + "note" => "", "requested" => true, "domain_blocking" => false, "subscribing" => false, + "notifying" => false, "endorsed" => false }, %{ @@ -713,9 +865,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "blocked_by" => false, "muting" => true, "muting_notifications" => false, + "note" => "", "requested" => false, "domain_blocking" => true, "subscribing" => true, + "notifying" => true, "endorsed" => false } ] @@ -746,10 +900,15 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do description: "Mute notifications in addition to statuses? Defaults to true.", default: true }, + duration: %Schema{ + type: :integer, + nullable: true, + description: "Expire the mute in `expires_in` seconds. Default 0 for infinity" + }, expires_in: %Schema{ type: :integer, nullable: true, - description: "Expire the mute in `expires_in` seconds. Default 0 for infinity", + description: "Deprecated, use `duration` instead", default: 0 } }, @@ -760,6 +919,23 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end + defp note_request do + %Schema{ + title: "AccountNoteRequest", + description: "POST body for adding a note for an account", + type: :object, + properties: %{ + comment: %Schema{ + type: :string, + description: "Account note body" + } + }, + example: %{ + "comment" => "Example note" + } + } + end + defp array_of_lists do %Schema{ title: "ArrayOfLists", diff --git a/lib/pleroma/web/api_spec/operations/admin/announcement_operation.ex b/lib/pleroma/web/api_spec/operations/admin/announcement_operation.ex new file mode 100644 index 000000000..58a039e72 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/admin/announcement_operation.ex @@ -0,0 +1,165 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Admin.AnnouncementOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.Announcement + alias Pleroma.Web.ApiSpec.Schemas.ApiError + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def index_operation do + %Operation{ + tags: ["Announcement managment"], + summary: "Retrieve a list of announcements", + operationId: "AdminAPI.AnnouncementController.index", + security: [%{"oAuth" => ["admin:read"]}], + parameters: [ + Operation.parameter( + :limit, + :query, + %Schema{type: :integer, minimum: 1}, + "the maximum number of announcements to return" + ), + Operation.parameter( + :offset, + :query, + %Schema{type: :integer, minimum: 0}, + "the offset of the first announcement to return" + ) + | admin_api_params() + ], + responses: %{ + 200 => Operation.response("Response", "application/json", list_of_announcements()), + 400 => Operation.response("Forbidden", "application/json", ApiError), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + def show_operation do + %Operation{ + tags: ["Announcement managment"], + summary: "Display one announcement", + operationId: "AdminAPI.AnnouncementController.show", + security: [%{"oAuth" => ["admin:read"]}], + parameters: [ + Operation.parameter( + :id, + :path, + :string, + "announcement id" + ) + | admin_api_params() + ], + responses: %{ + 200 => Operation.response("Response", "application/json", Announcement), + 403 => Operation.response("Forbidden", "application/json", ApiError), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def delete_operation do + %Operation{ + tags: ["Announcement managment"], + summary: "Delete one announcement", + operationId: "AdminAPI.AnnouncementController.delete", + security: [%{"oAuth" => ["admin:write"]}], + parameters: [ + Operation.parameter( + :id, + :path, + :string, + "announcement id" + ) + | admin_api_params() + ], + responses: %{ + 200 => Operation.response("Response", "application/json", %Schema{type: :object}), + 403 => Operation.response("Forbidden", "application/json", ApiError), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def create_operation do + %Operation{ + tags: ["Announcement managment"], + summary: "Create one announcement", + operationId: "AdminAPI.AnnouncementController.create", + security: [%{"oAuth" => ["admin:write"]}], + requestBody: request_body("Parameters", create_request(), required: true), + responses: %{ + 200 => Operation.response("Response", "application/json", Announcement), + 400 => Operation.response("Bad Request", "application/json", ApiError), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + def change_operation do + %Operation{ + tags: ["Announcement managment"], + summary: "Change one announcement", + operationId: "AdminAPI.AnnouncementController.change", + security: [%{"oAuth" => ["admin:write"]}], + parameters: [ + Operation.parameter( + :id, + :path, + :string, + "announcement id" + ) + | admin_api_params() + ], + requestBody: request_body("Parameters", change_request(), required: true), + responses: %{ + 200 => Operation.response("Response", "application/json", Announcement), + 400 => Operation.response("Bad Request", "application/json", ApiError), + 403 => Operation.response("Forbidden", "application/json", ApiError), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + defp create_or_change_props do + %{ + content: %Schema{type: :string}, + starts_at: %Schema{type: :string, format: "date-time", nullable: true}, + ends_at: %Schema{type: :string, format: "date-time", nullable: true}, + all_day: %Schema{type: :boolean} + } + end + + def create_request do + %Schema{ + title: "AnnouncementCreateRequest", + type: :object, + required: [:content], + properties: create_or_change_props() + } + end + + def change_request do + %Schema{ + title: "AnnouncementChangeRequest", + type: :object, + properties: create_or_change_props() + } + end + + def list_of_announcements do + %Schema{ + type: :array, + items: Announcement + } + end +end diff --git a/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex b/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex index 57906445e..2a274e080 100644 --- a/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.ChatOperation do diff --git a/lib/pleroma/web/api_spec/operations/admin/config_operation.ex b/lib/pleroma/web/api_spec/operations/admin/config_operation.ex index 30c3433b7..487dd5cda 100644 --- a/lib/pleroma/web/api_spec/operations/admin/config_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/config_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.ConfigOperation do diff --git a/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex b/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex index 566f1eeb1..4bfe5ac5a 100644 --- a/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.FrontendOperation do diff --git a/lib/pleroma/web/api_spec/operations/admin/instance_document_operation.ex b/lib/pleroma/web/api_spec/operations/admin/instance_document_operation.ex index 79ceae970..fc0de499b 100644 --- a/lib/pleroma/web/api_spec/operations/admin/instance_document_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/instance_document_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.InstanceDocumentOperation do diff --git a/lib/pleroma/web/api_spec/operations/admin/invite_operation.ex b/lib/pleroma/web/api_spec/operations/admin/invite_operation.ex index 704f082ba..e4a9ffaeb 100644 --- a/lib/pleroma/web/api_spec/operations/admin/invite_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/invite_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.InviteOperation do diff --git a/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex b/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex index 8f85ebf2d..0b1eb3946 100644 --- a/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.MediaProxyCacheOperation do diff --git a/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex b/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex index 35b029b19..1a05aff6a 100644 --- a/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do diff --git a/lib/pleroma/web/api_spec/operations/admin/relay_operation.ex b/lib/pleroma/web/api_spec/operations/admin/relay_operation.ex index c55c84fee..8b241bd49 100644 --- a/lib/pleroma/web/api_spec/operations/admin/relay_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/relay_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.RelayOperation do diff --git a/lib/pleroma/web/api_spec/operations/admin/report_operation.ex b/lib/pleroma/web/api_spec/operations/admin/report_operation.ex index 8d7577505..312e091a5 100644 --- a/lib/pleroma/web/api_spec/operations/admin/report_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/report_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do diff --git a/lib/pleroma/web/api_spec/operations/admin/status_operation.ex b/lib/pleroma/web/api_spec/operations/admin/status_operation.ex index d25ab5247..229912dd7 100644 --- a/lib/pleroma/web/api_spec/operations/admin/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/status_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do diff --git a/lib/pleroma/web/api_spec/operations/admin/user_operation.ex b/lib/pleroma/web/api_spec/operations/admin/user_operation.ex index c9d0bfd7c..a5179ac39 100644 --- a/lib/pleroma/web/api_spec/operations/admin/user_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/user_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.UserOperation do @@ -216,7 +216,71 @@ defmodule Pleroma.Web.ApiSpec.Admin.UserOperation do request_body( "Parameters", %Schema{ - description: "POST body for deleting multiple users", + description: "POST body for approving multiple users", + type: :object, + properties: %{ + nicknames: %Schema{ + type: :array, + items: %Schema{type: :string} + } + } + } + ), + responses: %{ + 200 => + Operation.response("Response", "application/json", %Schema{ + type: :object, + properties: %{user: %Schema{type: :array, items: user()}} + }), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + def suggest_operation do + %Operation{ + tags: ["User administration"], + summary: "Suggest multiple users", + operationId: "AdminAPI.UserController.suggest", + security: [%{"oAuth" => ["admin:write:accounts"]}], + parameters: admin_api_params(), + requestBody: + request_body( + "Parameters", + %Schema{ + description: "POST body for adding multiple suggested users", + type: :object, + properties: %{ + nicknames: %Schema{ + type: :array, + items: %Schema{type: :string} + } + } + } + ), + responses: %{ + 200 => + Operation.response("Response", "application/json", %Schema{ + type: :object, + properties: %{user: %Schema{type: :array, items: user()}} + }), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + def unsuggest_operation do + %Operation{ + tags: ["User administration"], + summary: "Unsuggest multiple users", + operationId: "AdminAPI.UserController.unsuggest", + security: [%{"oAuth" => ["admin:write:accounts"]}], + parameters: admin_api_params(), + requestBody: + request_body( + "Parameters", + %Schema{ + description: "POST body for removing multiple suggested users", type: :object, properties: %{ nicknames: %Schema{ diff --git a/lib/pleroma/web/api_spec/operations/announcement_operation.ex b/lib/pleroma/web/api_spec/operations/announcement_operation.ex new file mode 100644 index 000000000..71be0002a --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/announcement_operation.ex @@ -0,0 +1,57 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.AnnouncementOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.Announcement + alias Pleroma.Web.ApiSpec.Schemas.ApiError + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def index_operation do + %Operation{ + tags: ["Announcement"], + summary: "Retrieve a list of announcements", + operationId: "MastodonAPI.AnnouncementController.index", + security: [%{"oAuth" => []}], + responses: %{ + 200 => Operation.response("Response", "application/json", list_of_announcements()), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + def mark_read_operation do + %Operation{ + tags: ["Announcement"], + summary: "Mark one announcement as read", + operationId: "MastodonAPI.AnnouncementController.mark_read", + security: [%{"oAuth" => ["write:accounts"]}], + parameters: [ + Operation.parameter( + :id, + :path, + :string, + "announcement id" + ) + ], + responses: %{ + 200 => Operation.response("Response", "application/json", %Schema{type: :object}), + 403 => Operation.response("Forbidden", "application/json", ApiError), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def list_of_announcements do + %Schema{ + type: :array, + items: Announcement + } + end +end diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index dfb1c7170..dfa2237c0 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -1,11 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.AppOperation do alias OpenApiSpex.Operation alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Helpers + alias Pleroma.Web.ApiSpec.Schemas.App @spec open_api_operation(atom) :: Operation.t() def open_api_operation(action) do @@ -22,7 +23,7 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do operationId: "AppController.create", requestBody: Helpers.request_body("Parameters", create_request(), required: true), responses: %{ - 200 => Operation.response("App", "application/json", create_response()), + 200 => Operation.response("App", "application/json", App), 422 => Operation.response( "Unprocessable Entity", @@ -119,30 +120,4 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do } } end - - defp create_response do - %Schema{ - title: "AppCreateResponse", - description: "Response schema for an app", - type: :object, - properties: %{ - id: %Schema{type: :string}, - name: %Schema{type: :string}, - client_id: %Schema{type: :string}, - client_secret: %Schema{type: :string}, - redirect_uri: %Schema{type: :string}, - vapid_key: %Schema{type: :string}, - website: %Schema{type: :string, nullable: true} - }, - example: %{ - "id" => "123", - "name" => "My App", - "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM", - "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw", - "vapid_key" => - "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", - "website" => "https://myapp.com/" - } - } - end end diff --git a/lib/pleroma/web/api_spec/operations/chat_operation.ex b/lib/pleroma/web/api_spec/operations/chat_operation.ex index 23cb66392..cf6a055fc 100644 --- a/lib/pleroma/web/api_spec/operations/chat_operation.ex +++ b/lib/pleroma/web/api_spec/operations/chat_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.ChatOperation do diff --git a/lib/pleroma/web/api_spec/operations/conversation_operation.ex b/lib/pleroma/web/api_spec/operations/conversation_operation.ex index 17ed1af5e..82ccf41f9 100644 --- a/lib/pleroma/web/api_spec/operations/conversation_operation.ex +++ b/lib/pleroma/web/api_spec/operations/conversation_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.ConversationOperation do diff --git a/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex b/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex index 98da1a6de..77823f13e 100644 --- a/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex +++ b/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.CustomEmojiOperation do diff --git a/lib/pleroma/web/api_spec/operations/directory_operation.ex b/lib/pleroma/web/api_spec/operations/directory_operation.ex new file mode 100644 index 000000000..55752fa62 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/directory_operation.ex @@ -0,0 +1,41 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.DirectoryOperation do + alias OpenApiSpex.Operation + alias Pleroma.Web.ApiSpec.AccountOperation + alias Pleroma.Web.ApiSpec.Schemas.ApiError + alias Pleroma.Web.ApiSpec.Schemas.BooleanLike + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def index_operation do + %Operation{ + tags: ["Directory"], + summary: "Profile directory", + operationId: "DirectoryController.index", + parameters: + [ + Operation.parameter( + :order, + :query, + :string, + "Order by recent activity or account creation", + required: nil + ), + Operation.parameter(:local, :query, BooleanLike, "Include local users only") + ] ++ pagination_params(), + responses: %{ + 200 => + Operation.response("Accounts", "application/json", AccountOperation.array_of_accounts()), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end +end diff --git a/lib/pleroma/web/api_spec/operations/domain_block_operation.ex b/lib/pleroma/web/api_spec/operations/domain_block_operation.ex index f124e7fe5..2340fd914 100644 --- a/lib/pleroma/web/api_spec/operations/domain_block_operation.ex +++ b/lib/pleroma/web/api_spec/operations/domain_block_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do diff --git a/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex b/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex index a7b306a30..74341d64f 100644 --- a/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex +++ b/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.EmojiReactionOperation do diff --git a/lib/pleroma/web/api_spec/operations/filter_operation.ex b/lib/pleroma/web/api_spec/operations/filter_operation.ex index 5102921bc..a1700b7c9 100644 --- a/lib/pleroma/web/api_spec/operations/filter_operation.ex +++ b/lib/pleroma/web/api_spec/operations/filter_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.FilterOperation do diff --git a/lib/pleroma/web/api_spec/operations/follow_request_operation.ex b/lib/pleroma/web/api_spec/operations/follow_request_operation.ex index 784019699..72dc8b5fa 100644 --- a/lib/pleroma/web/api_spec/operations/follow_request_operation.ex +++ b/lib/pleroma/web/api_spec/operations/follow_request_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.FollowRequestOperation do diff --git a/lib/pleroma/web/api_spec/operations/instance_operation.ex b/lib/pleroma/web/api_spec/operations/instance_operation.ex index 9384acc32..3c4b504fe 100644 --- a/lib/pleroma/web/api_spec/operations/instance_operation.ex +++ b/lib/pleroma/web/api_spec/operations/instance_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.InstanceOperation do diff --git a/lib/pleroma/web/api_spec/operations/list_operation.ex b/lib/pleroma/web/api_spec/operations/list_operation.ex index 8a6e92b99..7d876ae2d 100644 --- a/lib/pleroma/web/api_spec/operations/list_operation.ex +++ b/lib/pleroma/web/api_spec/operations/list_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.ListOperation do diff --git a/lib/pleroma/web/api_spec/operations/marker_operation.ex b/lib/pleroma/web/api_spec/operations/marker_operation.ex index c5ff5984b..4dfdeb448 100644 --- a/lib/pleroma/web/api_spec/operations/marker_operation.ex +++ b/lib/pleroma/web/api_spec/operations/marker_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.MarkerOperation do diff --git a/lib/pleroma/web/api_spec/operations/media_operation.ex b/lib/pleroma/web/api_spec/operations/media_operation.ex index 451b6510f..e6df21246 100644 --- a/lib/pleroma/web/api_spec/operations/media_operation.ex +++ b/lib/pleroma/web/api_spec/operations/media_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.MediaOperation do diff --git a/lib/pleroma/web/api_spec/operations/notification_operation.ex b/lib/pleroma/web/api_spec/operations/notification_operation.ex index ec88eabe1..56aa129d2 100644 --- a/lib/pleroma/web/api_spec/operations/notification_operation.ex +++ b/lib/pleroma/web/api_spec/operations/notification_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.NotificationOperation do @@ -51,6 +51,12 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do :include_types, :query, %Schema{type: :array, items: notification_type()}, + "Deprecated, use `types` instead" + ), + Operation.parameter( + :types, + :query, + %Schema{type: :array, items: notification_type()}, "Include the notifications for activities with the given types" ), Operation.parameter( @@ -195,7 +201,8 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do "pleroma:chat_mention", "pleroma:report", "move", - "follow_request" + "follow_request", + "poll" ], description: """ The type of event that resulted in the notification. diff --git a/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex index ad49f6426..5375c5b15 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex @@ -1,9 +1,11 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.AccountOperation alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship alias Pleroma.Web.ApiSpec.Schemas.ApiError alias Pleroma.Web.ApiSpec.Schemas.FlakeID @@ -62,6 +64,25 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do } end + def endorsements_operation do + %Operation{ + tags: ["Retrieve account information"], + summary: "Endorsements", + description: "Returns endorsed accounts", + operationId: "PleromaAPI.AccountController.endorsements", + parameters: [with_relationships_param(), id_param()], + responses: %{ + 200 => + Operation.response( + "Array of Accounts", + "application/json", + AccountOperation.array_of_accounts() + ), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + def subscribe_operation do %Operation{ tags: ["Account actions"], @@ -92,6 +113,34 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do } end + def birthdays_operation do + %Operation{ + tags: ["Retrieve account information"], + summary: "Birthday reminders", + description: "Birthday reminders about users you follow.", + operationId: "PleromaAPI.AccountController.birthdays", + parameters: [ + Operation.parameter( + :day, + :query, + %Schema{type: :integer}, + "Day of users' birthdays" + ), + Operation.parameter( + :month, + :query, + %Schema{type: :integer}, + "Month of users' birthdays" + ) + ], + security: [%{"oAuth" => ["read:accounts"]}], + responses: %{ + 200 => + Operation.response("Accounts", "application/json", AccountOperation.array_of_accounts()) + } + } + end + defp id_param do Operation.parameter(:id, :path, FlakeID, "Account ID", example: "9umDrYheeY451cQnEe", diff --git a/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex new file mode 100644 index 000000000..9f8df6c5c --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.PleromaAppOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.App + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + @spec index_operation() :: Operation.t() + def index_operation do + %Operation{ + tags: ["Applications"], + summary: "List applications", + description: "List the OAuth applications for the current user", + operationId: "AppController.index", + responses: %{ + 200 => Operation.response("Array of App", "application/json", array_of_apps()) + } + } + end + + defp array_of_apps do + %Schema{type: :array, items: App, example: [App.schema().example]} + end +end diff --git a/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex index c78e9780f..45fa2b058 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaBackupOperation do @@ -16,7 +16,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaBackupOperation do %Operation{ tags: ["Backups"], summary: "List backups", - security: [%{"oAuth" => ["read:account"]}], + security: [%{"oAuth" => ["read:backups"]}], operationId: "PleromaAPI.BackupController.index", responses: %{ 200 => @@ -37,7 +37,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaBackupOperation do %Operation{ tags: ["Backups"], summary: "Create a backup", - security: [%{"oAuth" => ["read:account"]}], + security: [%{"oAuth" => ["read:backups"]}], operationId: "PleromaAPI.BackupController.create", responses: %{ 200 => diff --git a/lib/pleroma/web/api_spec/operations/pleroma_conversation_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_conversation_operation.ex index 12fb8ed36..89f0e13e1 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_conversation_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_conversation_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaConversationOperation do diff --git a/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex index 8c76096b5..d09c1c10e 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaEmojiFileOperation do diff --git a/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex index 49247d9b6..6add3ff33 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaEmojiPackOperation do diff --git a/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex index 612113147..82db4e1a8 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaInstancesOperation do diff --git a/lib/pleroma/web/api_spec/operations/pleroma_mascot_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_mascot_operation.ex index 6191cb97d..775e27219 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_mascot_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_mascot_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaMascotOperation do diff --git a/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex index 1dda39240..a994345db 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaNotificationOperation do diff --git a/lib/pleroma/web/api_spec/operations/pleroma_report_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_report_operation.ex index ee8870dc2..9bc1877b1 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_report_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_report_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaReportOperation do diff --git a/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex index 6a909fc85..b6273bfcf 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do diff --git a/lib/pleroma/web/api_spec/operations/pleroma_settings_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_settings_operation.ex new file mode 100644 index 000000000..e2cef4f67 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/pleroma_settings_operation.ex @@ -0,0 +1,72 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.PleromaSettingsOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def show_operation do + %Operation{ + tags: ["Settings"], + summary: "Get settings for an application", + description: "Get synchronized settings for an application", + operationId: "SettingsController.show", + parameters: [app_name_param()], + security: [%{"oAuth" => ["read:accounts"]}], + responses: %{ + 200 => Operation.response("object", "application/json", object()) + } + } + end + + def update_operation do + %Operation{ + tags: ["Settings"], + summary: "Update settings for an application", + description: "Update synchronized settings for an application", + operationId: "SettingsController.update", + parameters: [app_name_param()], + security: [%{"oAuth" => ["write:accounts"]}], + requestBody: request_body("Parameters", update_request(), required: true), + responses: %{ + 200 => Operation.response("object", "application/json", object()) + } + } + end + + def app_name_param do + Operation.parameter(:app, :path, %Schema{type: :string}, "Application name", + example: "pleroma-fe", + required: true + ) + end + + def object do + %Schema{ + title: "Settings object", + description: "The object that contains settings for the application.", + type: :object + } + end + + def update_request do + %Schema{ + title: "SettingsUpdateRequest", + type: :object, + description: + "The settings object to be merged with the current settings. To remove a field, set it to null.", + example: %{ + "config1" => true, + "config2_to_unset" => nil + } + } + end +end diff --git a/lib/pleroma/web/api_spec/operations/poll_operation.ex b/lib/pleroma/web/api_spec/operations/poll_operation.ex index 0d1c8d099..efd784f03 100644 --- a/lib/pleroma/web/api_spec/operations/poll_operation.ex +++ b/lib/pleroma/web/api_spec/operations/poll_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PollOperation do diff --git a/lib/pleroma/web/api_spec/operations/report_operation.ex b/lib/pleroma/web/api_spec/operations/report_operation.ex index b744efa60..c74ac7d5f 100644 --- a/lib/pleroma/web/api_spec/operations/report_operation.ex +++ b/lib/pleroma/web/api_spec/operations/report_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.ReportOperation do diff --git a/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex b/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex index b9c5b35c1..802d3b6dd 100644 --- a/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex +++ b/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.ScheduledActivityOperation do diff --git a/lib/pleroma/web/api_spec/operations/search_operation.ex b/lib/pleroma/web/api_spec/operations/search_operation.ex index ff4fd0027..1a7e49be4 100644 --- a/lib/pleroma/web/api_spec/operations/search_operation.ex +++ b/lib/pleroma/web/api_spec/operations/search_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.SearchOperation do diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index 802fbef3e..e921128c7 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -1,14 +1,18 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.StatusOperation do alias OpenApiSpex.Operation alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.AccountOperation + alias Pleroma.Web.ApiSpec.Schemas.Account alias Pleroma.Web.ApiSpec.Schemas.ApiError + alias Pleroma.Web.ApiSpec.Schemas.Attachment alias Pleroma.Web.ApiSpec.Schemas.BooleanLike + alias Pleroma.Web.ApiSpec.Schemas.Emoji alias Pleroma.Web.ApiSpec.Schemas.FlakeID + alias Pleroma.Web.ApiSpec.Schemas.Poll alias Pleroma.Web.ApiSpec.Schemas.ScheduledStatus alias Pleroma.Web.ApiSpec.Schemas.Status alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope @@ -434,6 +438,59 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do } end + def show_history_operation do + %Operation{ + tags: ["Retrieve status history"], + summary: "Status history", + description: "View history of a status", + operationId: "StatusController.show_history", + security: [%{"oAuth" => ["read:statuses"]}], + parameters: [ + id_param() + ], + responses: %{ + 200 => status_history_response(), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def show_source_operation do + %Operation{ + tags: ["Retrieve status source"], + summary: "Status source", + description: "View source of a status", + operationId: "StatusController.show_source", + security: [%{"oAuth" => ["read:statuses"]}], + parameters: [ + id_param() + ], + responses: %{ + 200 => status_source_response(), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def update_operation do + %Operation{ + tags: ["Update status"], + summary: "Update status", + description: "Change the content of a status", + operationId: "StatusController.update", + security: [%{"oAuth" => ["write:statuses"]}], + parameters: [ + id_param() + ], + requestBody: request_body("Parameters", update_request(), required: true), + responses: %{ + 200 => status_response(), + 403 => Operation.response("Forbidden", "application/json", ApiError), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + def array_of_statuses do %Schema{type: :array, items: Status, example: [Status.schema().example]} end @@ -537,6 +594,60 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do } end + defp update_request do + %Schema{ + title: "StatusUpdateRequest", + type: :object, + properties: %{ + status: %Schema{ + type: :string, + nullable: true, + description: + "Text content of the status. If `media_ids` is provided, this becomes optional. Attaching a `poll` is optional while `status` is provided." + }, + media_ids: %Schema{ + nullable: true, + type: :array, + items: %Schema{type: :string}, + description: "Array of Attachment ids to be attached as media." + }, + poll: poll_params(), + sensitive: %Schema{ + allOf: [BooleanLike], + nullable: true, + description: "Mark status and attached media as sensitive?" + }, + spoiler_text: %Schema{ + type: :string, + nullable: true, + description: + "Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field." + }, + content_type: %Schema{ + type: :string, + nullable: true, + description: + "The MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint." + }, + to: %Schema{ + type: :array, + nullable: true, + items: %Schema{type: :string}, + description: + "A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for for post visibility are not affected by this and will still apply" + } + }, + example: %{ + "status" => "What time is it?", + "sensitive" => "false", + "poll" => %{ + "options" => ["Cofe", "Adventure"], + "expires_in" => 420 + } + } + } + end + def poll_params do %Schema{ nullable: true, @@ -579,6 +690,87 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do Operation.response("Status", "application/json", Status) end + defp status_history_response do + Operation.response( + "Status History", + "application/json", + %Schema{ + title: "Status history", + description: "Response schema for history of a status", + type: :array, + items: %Schema{ + type: :object, + properties: %{ + account: %Schema{ + allOf: [Account], + description: "The account that authored this status" + }, + content: %Schema{ + type: :string, + format: :html, + description: "HTML-encoded status content" + }, + sensitive: %Schema{ + type: :boolean, + description: "Is this status marked as sensitive content?" + }, + spoiler_text: %Schema{ + type: :string, + description: + "Subject or summary line, below which status content is collapsed until expanded" + }, + created_at: %Schema{ + type: :string, + format: "date-time", + description: "The date when this status was created" + }, + media_attachments: %Schema{ + type: :array, + items: Attachment, + description: "Media that is attached to this status" + }, + emojis: %Schema{ + type: :array, + items: Emoji, + description: "Custom emoji to be used when rendering status content" + }, + poll: %Schema{ + allOf: [Poll], + nullable: true, + description: "The poll attached to the status" + } + } + } + } + ) + end + + defp status_source_response do + Operation.response( + "Status Source", + "application/json", + %Schema{ + type: :object, + properties: %{ + id: FlakeID, + text: %Schema{ + type: :string, + description: "Raw source of status content" + }, + spoiler_text: %Schema{ + type: :string, + description: + "Subject or summary line, below which status content is collapsed until expanded" + }, + content_type: %Schema{ + type: :string, + description: "The content type of the source" + } + } + } + ) + end + defp context do %Schema{ title: "StatusContext", diff --git a/lib/pleroma/web/api_spec/operations/subscription_operation.ex b/lib/pleroma/web/api_spec/operations/subscription_operation.ex index 60a7fb3b0..c53ec2956 100644 --- a/lib/pleroma/web/api_spec/operations/subscription_operation.ex +++ b/lib/pleroma/web/api_spec/operations/subscription_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.SubscriptionOperation do diff --git a/lib/pleroma/web/api_spec/operations/timeline_operation.ex b/lib/pleroma/web/api_spec/operations/timeline_operation.ex index 24d792916..fbe3f763a 100644 --- a/lib/pleroma/web/api_spec/operations/timeline_operation.ex +++ b/lib/pleroma/web/api_spec/operations/timeline_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.TimelineOperation do diff --git a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex index 879b2227e..29df03e34 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do @@ -121,7 +121,10 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do type: :object, required: [:email, :password], properties: %{ - email: %Schema{type: :string, description: "New email"}, + email: %Schema{ + type: :string, + description: "New email. Set to blank to remove the user's email." + }, password: %Schema{type: :string, description: "Current password"} } } @@ -188,6 +191,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do parameters: [ Operation.parameter(:password, :query, :string, "Password") ], + requestBody: request_body("Parameters", delete_account_request(), required: false), responses: %{ 200 => Operation.response("Success", "application/json", %Schema{ @@ -210,6 +214,146 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do } end + def move_account_operation do + %Operation{ + tags: ["Account credentials"], + summary: "Move account", + security: [%{"oAuth" => ["write:accounts"]}], + operationId: "UtilController.move_account", + requestBody: request_body("Parameters", move_account_request(), required: true), + responses: %{ + 200 => + Operation.response("Success", "application/json", %Schema{ + type: :object, + properties: %{status: %Schema{type: :string, example: "success"}} + }), + 400 => Operation.response("Error", "application/json", ApiError), + 403 => Operation.response("Error", "application/json", ApiError), + 404 => Operation.response("Error", "application/json", ApiError) + } + } + end + + defp move_account_request do + %Schema{ + title: "MoveAccountRequest", + description: "POST body for moving the account", + type: :object, + required: [:password, :target_account], + properties: %{ + password: %Schema{type: :string, description: "Current password"}, + target_account: %Schema{ + type: :string, + description: "The nickname of the target account to move to" + } + } + } + end + + def list_aliases_operation do + %Operation{ + tags: ["Account credentials"], + summary: "List account aliases", + security: [%{"oAuth" => ["read:accounts"]}], + operationId: "UtilController.list_aliases", + responses: %{ + 200 => + Operation.response("Success", "application/json", %Schema{ + type: :object, + properties: %{ + aliases: %Schema{ + type: :array, + items: %Schema{type: :string}, + example: ["foo@example.org"] + } + } + }), + 400 => Operation.response("Error", "application/json", ApiError), + 403 => Operation.response("Error", "application/json", ApiError) + } + } + end + + def add_alias_operation do + %Operation{ + tags: ["Account credentials"], + summary: "Add an alias to this account", + security: [%{"oAuth" => ["write:accounts"]}], + operationId: "UtilController.add_alias", + requestBody: request_body("Parameters", add_alias_request(), required: true), + responses: %{ + 200 => + Operation.response("Success", "application/json", %Schema{ + type: :object, + properties: %{ + status: %Schema{ + type: :string, + example: "success" + } + } + }), + 400 => Operation.response("Error", "application/json", ApiError), + 403 => Operation.response("Error", "application/json", ApiError), + 404 => Operation.response("Error", "application/json", ApiError) + } + } + end + + defp add_alias_request do + %Schema{ + title: "AddAliasRequest", + description: "PUT body for adding aliases", + type: :object, + required: [:alias], + properties: %{ + alias: %Schema{ + type: :string, + description: "The nickname of the account to add to aliases" + } + } + } + end + + def delete_alias_operation do + %Operation{ + tags: ["Account credentials"], + summary: "Delete an alias from this account", + security: [%{"oAuth" => ["write:accounts"]}], + operationId: "UtilController.delete_alias", + requestBody: request_body("Parameters", delete_alias_request(), required: true), + responses: %{ + 200 => + Operation.response("Success", "application/json", %Schema{ + type: :object, + properties: %{ + status: %Schema{ + type: :string, + example: "success" + } + } + }), + 400 => Operation.response("Error", "application/json", ApiError), + 403 => Operation.response("Error", "application/json", ApiError), + 404 => Operation.response("Error", "application/json", ApiError) + } + } + end + + defp delete_alias_request do + %Schema{ + title: "DeleteAliasRequest", + description: "PUT body for deleting aliases", + type: :object, + required: [:alias], + properties: %{ + alias: %Schema{ + type: :string, + description: "The nickname of the account to delete from aliases" + } + } + } + end + def healthcheck_operation do %Operation{ tags: ["Accounts"], @@ -234,4 +378,58 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})} } end + + def remote_interaction_operation do + %Operation{ + tags: ["Accounts"], + summary: "Remote interaction", + operationId: "UtilController.remote_interaction", + requestBody: request_body("Parameters", remote_interaction_request(), required: true), + responses: %{ + 200 => + Operation.response("Remote interaction URL", "application/json", %Schema{type: :object}) + } + } + end + + defp remote_interaction_request do + %Schema{ + title: "RemoteInteractionRequest", + description: "POST body for remote interaction", + type: :object, + required: [:ap_id, :profile], + properties: %{ + ap_id: %Schema{type: :string, description: "Profile or status ActivityPub ID"}, + profile: %Schema{type: :string, description: "Remote profile webfinger"} + } + } + end + + def show_subscribe_form_operation do + %Operation{ + tags: ["Accounts"], + summary: "Show remote subscribe form", + operationId: "UtilController.show_subscribe_form", + parameters: [], + responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})} + } + end + + defp delete_account_request do + %Schema{ + title: "AccountDeleteRequest", + description: "POST body for deleting one's own account", + type: :object, + properties: %{ + password: %Schema{ + type: :string, + description: "The user's own password for confirmation.", + format: :password + } + }, + example: %{ + "password" => "prettyp0ony1313" + } + } + end end diff --git a/lib/pleroma/web/api_spec/operations/user_import_operation.ex b/lib/pleroma/web/api_spec/operations/user_import_operation.ex index 8df19f1fc..e99e6e648 100644 --- a/lib/pleroma/web/api_spec/operations/user_import_operation.ex +++ b/lib/pleroma/web/api_spec/operations/user_import_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.UserImportOperation do diff --git a/lib/pleroma/web/api_spec/render_error.ex b/lib/pleroma/web/api_spec/render_error.ex index e501a6be4..3539af6e4 100644 --- a/lib/pleroma/web/api_spec/render_error.ex +++ b/lib/pleroma/web/api_spec/render_error.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.RenderError do diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex index bd7143ab9..8aeb821a8 100644 --- a/lib/pleroma/web/api_spec/schemas/account.ex +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Account do @@ -33,6 +33,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do header: %Schema{type: :string, format: :uri}, id: FlakeID, locked: %Schema{type: :boolean}, + mute_expires_at: %Schema{type: :string, format: "date-time", nullable: true}, note: %Schema{type: :string, format: :html}, statuses_count: %Schema{type: :integer}, url: %Schema{type: :string, format: :uri}, @@ -47,12 +48,14 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do description: "whether the user allows automatically follow moved following accounts" }, background_image: %Schema{type: :string, nullable: true, format: :uri}, + birthday: %Schema{type: :string, nullable: true, format: :date}, chat_token: %Schema{type: :string}, is_confirmed: %Schema{ type: :boolean, description: "whether the user account is waiting on email confirmation to be activated" }, + show_birthday: %Schema{type: :boolean, nullable: true}, hide_favorites: %Schema{type: :boolean}, hide_followers_count: %Schema{ type: :boolean, @@ -194,13 +197,16 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do "id" => "9tKi3esbG7OQgZ2920", "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "showing_reblogs" => true, - "subscribing" => false + "subscribing" => false, + "notifying" => false }, "settings_store" => %{ "pleroma-fe" => %{} - } + }, + "birthday" => "2001-02-12" }, "source" => %{ "fields" => [], diff --git a/lib/pleroma/web/api_spec/schemas/account_field.ex b/lib/pleroma/web/api_spec/schemas/account_field.ex index 7c4f94001..93ba1b5a5 100644 --- a/lib/pleroma/web/api_spec/schemas/account_field.ex +++ b/lib/pleroma/web/api_spec/schemas/account_field.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.AccountField do diff --git a/lib/pleroma/web/api_spec/schemas/account_relationship.ex b/lib/pleroma/web/api_spec/schemas/account_relationship.ex index 16b73ebb4..68219a099 100644 --- a/lib/pleroma/web/api_spec/schemas/account_relationship.ex +++ b/lib/pleroma/web/api_spec/schemas/account_relationship.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do @@ -22,9 +22,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do id: FlakeID, muting: %Schema{type: :boolean}, muting_notifications: %Schema{type: :boolean}, + note: %Schema{type: :string}, requested: %Schema{type: :boolean}, showing_reblogs: %Schema{type: :boolean}, - subscribing: %Schema{type: :boolean} + subscribing: %Schema{type: :boolean}, + notifying: %Schema{type: :boolean} }, example: %{ "blocked_by" => false, @@ -36,9 +38,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do "id" => "9tKi3esbG7OQgZ2920", "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "showing_reblogs" => true, - "subscribing" => false + "subscribing" => false, + "notifying" => false } }) end diff --git a/lib/pleroma/web/api_spec/schemas/actor_type.ex b/lib/pleroma/web/api_spec/schemas/actor_type.ex index 1336640a1..13b6b476b 100644 --- a/lib/pleroma/web/api_spec/schemas/actor_type.ex +++ b/lib/pleroma/web/api_spec/schemas/actor_type.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.ActorType do diff --git a/lib/pleroma/web/api_spec/schemas/announcement.ex b/lib/pleroma/web/api_spec/schemas/announcement.ex new file mode 100644 index 000000000..67d129ef6 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/announcement.ex @@ -0,0 +1,45 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.Announcement do + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.FlakeID + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "Announcement", + description: "Response schema for an announcement", + type: :object, + properties: %{ + id: FlakeID, + content: %Schema{type: :string}, + starts_at: %Schema{ + type: :string, + format: "date-time", + nullable: true + }, + ends_at: %Schema{ + type: :string, + format: "date-time", + nullable: true + }, + all_day: %Schema{type: :boolean}, + published_at: %Schema{type: :string, format: "date-time"}, + updated_at: %Schema{type: :string, format: "date-time"}, + read: %Schema{type: :boolean}, + mentions: %Schema{type: :array}, + statuses: %Schema{type: :array}, + tags: %Schema{type: :array}, + emojis: %Schema{type: :array}, + reactions: %Schema{type: :array}, + pleroma: %Schema{ + type: :object, + properties: %{ + raw_content: %Schema{type: :string} + } + } + } + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/api_error.ex b/lib/pleroma/web/api_spec/schemas/api_error.ex index 0d6d0b75c..58a710761 100644 --- a/lib/pleroma/web/api_spec/schemas/api_error.ex +++ b/lib/pleroma/web/api_spec/schemas/api_error.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.ApiError do diff --git a/lib/pleroma/web/api_spec/schemas/app.ex b/lib/pleroma/web/api_spec/schemas/app.ex new file mode 100644 index 000000000..742413b33 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/app.ex @@ -0,0 +1,33 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.App do + alias OpenApiSpex.Schema + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "App", + description: "Response schema for an app", + type: :object, + properties: %{ + id: %Schema{type: :string}, + name: %Schema{type: :string}, + client_id: %Schema{type: :string}, + client_secret: %Schema{type: :string}, + redirect_uri: %Schema{type: :string}, + vapid_key: %Schema{type: :string}, + website: %Schema{type: :string, nullable: true} + }, + example: %{ + "id" => "123", + "name" => "My App", + "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM", + "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw", + "vapid_key" => + "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", + "website" => "https://myapp.com/" + } + }) +end diff --git a/lib/pleroma/web/api_spec/schemas/attachment.ex b/lib/pleroma/web/api_spec/schemas/attachment.ex index ca3659c93..48634a14f 100644 --- a/lib/pleroma/web/api_spec/schemas/attachment.ex +++ b/lib/pleroma/web/api_spec/schemas/attachment.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Attachment do diff --git a/lib/pleroma/web/api_spec/schemas/boolean_like.ex b/lib/pleroma/web/api_spec/schemas/boolean_like.ex index 94c5020ca..14f728eef 100644 --- a/lib/pleroma/web/api_spec/schemas/boolean_like.ex +++ b/lib/pleroma/web/api_spec/schemas/boolean_like.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.BooleanLike do diff --git a/lib/pleroma/web/api_spec/schemas/chat.ex b/lib/pleroma/web/api_spec/schemas/chat.ex index 4afed910d..a07d12865 100644 --- a/lib/pleroma/web/api_spec/schemas/chat.ex +++ b/lib/pleroma/web/api_spec/schemas/chat.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Chat do diff --git a/lib/pleroma/web/api_spec/schemas/chat_message.ex b/lib/pleroma/web/api_spec/schemas/chat_message.ex index 348fe95f8..57f7890e5 100644 --- a/lib/pleroma/web/api_spec/schemas/chat_message.ex +++ b/lib/pleroma/web/api_spec/schemas/chat_message.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.ChatMessage do diff --git a/lib/pleroma/web/api_spec/schemas/conversation.ex b/lib/pleroma/web/api_spec/schemas/conversation.ex index 7c609965f..f00a9733f 100644 --- a/lib/pleroma/web/api_spec/schemas/conversation.ex +++ b/lib/pleroma/web/api_spec/schemas/conversation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Conversation do diff --git a/lib/pleroma/web/api_spec/schemas/emoji.ex b/lib/pleroma/web/api_spec/schemas/emoji.ex index ceb3c7186..936bbe1da 100644 --- a/lib/pleroma/web/api_spec/schemas/emoji.ex +++ b/lib/pleroma/web/api_spec/schemas/emoji.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Emoji do diff --git a/lib/pleroma/web/api_spec/schemas/flake_id.ex b/lib/pleroma/web/api_spec/schemas/flake_id.ex index 45314d53a..4c3ec01e7 100644 --- a/lib/pleroma/web/api_spec/schemas/flake_id.ex +++ b/lib/pleroma/web/api_spec/schemas/flake_id.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.FlakeID do diff --git a/lib/pleroma/web/api_spec/schemas/list.ex b/lib/pleroma/web/api_spec/schemas/list.ex index 90f5ec987..e57de7917 100644 --- a/lib/pleroma/web/api_spec/schemas/list.ex +++ b/lib/pleroma/web/api_spec/schemas/list.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.List do diff --git a/lib/pleroma/web/api_spec/schemas/poll.ex b/lib/pleroma/web/api_spec/schemas/poll.ex index 943ad8bd4..91570582b 100644 --- a/lib/pleroma/web/api_spec/schemas/poll.ex +++ b/lib/pleroma/web/api_spec/schemas/poll.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Poll do diff --git a/lib/pleroma/web/api_spec/schemas/push_subscription.ex b/lib/pleroma/web/api_spec/schemas/push_subscription.ex index 20fe9f304..a5466630b 100644 --- a/lib/pleroma/web/api_spec/schemas/push_subscription.ex +++ b/lib/pleroma/web/api_spec/schemas/push_subscription.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.PushSubscription do diff --git a/lib/pleroma/web/api_spec/schemas/scheduled_status.ex b/lib/pleroma/web/api_spec/schemas/scheduled_status.ex index 607586e32..a1acda1e8 100644 --- a/lib/pleroma/web/api_spec/schemas/scheduled_status.ex +++ b/lib/pleroma/web/api_spec/schemas/scheduled_status.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.ScheduledStatus do diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 3d042dc19..698f11794 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Status do @@ -73,6 +73,12 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do format: "date-time", description: "The date when this status was created" }, + edited_at: %Schema{ + type: :string, + format: "date-time", + nullable: true, + description: "The date when this status was last edited" + }, emojis: %Schema{ type: :array, items: Emoji, @@ -142,9 +148,15 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do description: "A map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`" }, + context: %Schema{ + type: :string, + description: "The thread identifier the status is associated with" + }, conversation_id: %Schema{ type: :integer, - description: "The ID of the AP context the status is associated with (if any)" + deprecated: true, + description: + "The ID of the AP context the status is associated with (if any); deprecated, please use `context` instead" }, direct_conversation_id: %Schema{ type: :integer, @@ -282,9 +294,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do "id" => "9toJCsKN7SmSf3aj5c", "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "showing_reblogs" => true, - "subscribing" => false + "subscribing" => false, + "notifying" => false }, "skip_thread_containment" => false, "tags" => [] @@ -317,6 +331,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do "pinned" => false, "pleroma" => %{ "content" => %{"text/plain" => "foobar"}, + "context" => "http://localhost:4001/objects/8b4c0c80-6a37-4d2a-b1b9-05a19e3875aa", "conversation_id" => 345_972, "direct_conversation_id" => nil, "emoji_reactions" => [], diff --git a/lib/pleroma/web/api_spec/schemas/tag.ex b/lib/pleroma/web/api_spec/schemas/tag.ex index 657b675e5..66bf0ca71 100644 --- a/lib/pleroma/web/api_spec/schemas/tag.ex +++ b/lib/pleroma/web/api_spec/schemas/tag.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Tag do diff --git a/lib/pleroma/web/api_spec/schemas/visibility_scope.ex b/lib/pleroma/web/api_spec/schemas/visibility_scope.ex index 25a08a0b2..ecd247ba4 100644 --- a/lib/pleroma/web/api_spec/schemas/visibility_scope.ex +++ b/lib/pleroma/web/api_spec/schemas/visibility_scope.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.VisibilityScope do diff --git a/lib/pleroma/web/auth/authenticator.ex b/lib/pleroma/web/auth/authenticator.ex index 3fe9718c4..a0bd154db 100644 --- a/lib/pleroma/web/auth/authenticator.ex +++ b/lib/pleroma/web/auth/authenticator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.Authenticator do diff --git a/lib/pleroma/web/auth/helpers.ex b/lib/pleroma/web/auth/helpers.ex index c566de8d4..02e1f39ab 100644 --- a/lib/pleroma/web/auth/helpers.ex +++ b/lib/pleroma/web/auth/helpers.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.Helpers do diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex index f77e8d203..e8cd4491c 100644 --- a/lib/pleroma/web/auth/ldap_authenticator.ex +++ b/lib/pleroma/web/auth/ldap_authenticator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.LDAPAuthenticator do diff --git a/lib/pleroma/web/auth/pleroma_authenticator.ex b/lib/pleroma/web/auth/pleroma_authenticator.ex index 68472e75f..09a58eb66 100644 --- a/lib/pleroma/web/auth/pleroma_authenticator.ex +++ b/lib/pleroma/web/auth/pleroma_authenticator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.PleromaAuthenticator do diff --git a/lib/pleroma/web/auth/totp_authenticator.ex b/lib/pleroma/web/auth/totp_authenticator.ex index 5947cd8c9..4be3641fd 100644 --- a/lib/pleroma/web/auth/totp_authenticator.ex +++ b/lib/pleroma/web/auth/totp_authenticator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.TOTPAuthenticator do diff --git a/lib/pleroma/web/auth/wrapper_authenticator.ex b/lib/pleroma/web/auth/wrapper_authenticator.ex index c67082f7b..a077cfa41 100644 --- a/lib/pleroma/web/auth/wrapper_authenticator.ex +++ b/lib/pleroma/web/auth/wrapper_authenticator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.WrapperAuthenticator do diff --git a/lib/pleroma/web/channels/user_socket.ex b/lib/pleroma/web/channels/user_socket.ex index 043206835..0f61b80c3 100644 --- a/lib/pleroma/web/channels/user_socket.ex +++ b/lib/pleroma/web/channels/user_socket.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.UserSocket do diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 1b5f8491e..89cc0d6fe 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -1,11 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.CommonAPI do alias Pleroma.Activity alias Pleroma.Conversation.Participation alias Pleroma.Formatter + alias Pleroma.ModerationLog alias Pleroma.Object alias Pleroma.ThreadMute alias Pleroma.User @@ -117,7 +118,8 @@ defmodule Pleroma.Web.CommonAPI do def unfollow(follower, unfollowed) do with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed), {:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed), - {:ok, _subscription} <- User.unsubscribe(follower, unfollowed) do + {:ok, _subscription} <- User.unsubscribe(follower, unfollowed), + {:ok, _endorsement} <- User.unendorse(follower, unfollowed) do {:ok, follower} end end @@ -143,9 +145,24 @@ defmodule Pleroma.Web.CommonAPI do {:find_activity, Activity.get_by_id(activity_id)}, {_, %Object{} = object, _} <- {:find_object, Object.normalize(activity, fetch: false), activity}, - true <- User.superuser?(user) || user.ap_id == object.data["actor"], + true <- User.privileged?(user, :messages_delete) || user.ap_id == object.data["actor"], {:ok, delete_data, _} <- Builder.delete(user, object.data["id"]), {:ok, delete, _} <- Pipeline.common_pipeline(delete_data, local: true) do + if User.privileged?(user, :messages_delete) and user.ap_id != object.data["actor"] do + action = + if object.data["type"] == "ChatMessage" do + "chat_message_delete" + else + "status_delete" + end + + ModerationLog.insert_log(%{ + action: action, + actor: user, + subject_id: activity_id + }) + end + {:ok, delete} else {:find_activity, _} -> @@ -401,6 +418,41 @@ defmodule Pleroma.Web.CommonAPI do end end + def update(user, orig_activity, changes) do + with orig_object <- Object.normalize(orig_activity), + {:ok, new_object} <- make_update_data(user, orig_object, changes), + {:ok, update_data, _} <- Builder.update(user, new_object), + {:ok, update, _} <- Pipeline.common_pipeline(update_data, local: true) do + {:ok, update} + else + _ -> {:error, nil} + end + end + + defp make_update_data(user, orig_object, changes) do + kept_params = %{ + visibility: Visibility.get_visibility(orig_object), + in_reply_to_id: + with replied_id when is_binary(replied_id) <- orig_object.data["inReplyTo"], + %Activity{id: activity_id} <- Activity.get_create_by_object_ap_id(replied_id) do + activity_id + else + _ -> nil + end + } + + params = Map.merge(changes, kept_params) + + with {:ok, draft} <- ActivityDraft.create(user, params) do + change = + Object.Updater.make_update_object_data(orig_object.data, draft.object, Utils.make_date()) + + {:ok, change} + else + _ -> {:error, nil} + end + end + @spec pin(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, term()} def pin(id, %User{} = user) do with %Activity{} = activity <- create_activity_by_id(id), @@ -487,9 +539,7 @@ defmodule Pleroma.Web.CommonAPI do else {what, result} = error -> Logger.warn( - "CommonAPI.remove_mute/2 failed. #{what}: #{result}, user_id: #{user_id}, activity_id: #{ - activity_id - }" + "CommonAPI.remove_mute/2 failed. #{what}: #{result}, user_id: #{user_id}, activity_id: #{activity_id}" ) {:error, error} diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index c691d71d2..9af635da8 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -1,11 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.CommonAPI.ActivityDraft do alias Pleroma.Activity alias Pleroma.Conversation.Participation alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils @@ -111,7 +112,12 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do defp attachments(%{params: params} = draft) do attachments = Utils.attachments_from_ids(params) - %__MODULE__{draft | attachments: attachments} + draft = %__MODULE__{draft | attachments: attachments} + + case Utils.validate_attachments_count(attachments) do + :ok -> draft + {:error, message} -> add_error(draft, message) + end end defp in_reply_to(%{params: %{in_reply_to_status_id: ""}} = draft), do: draft @@ -213,10 +219,15 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do emoji = Map.merge(emoji, summary_emoji) + {:ok, note_data, _meta} = Builder.note(draft) + object = - Utils.make_note_data(draft) + note_data |> Map.put("emoji", emoji) - |> Map.put("source", draft.status) + |> Map.put("source", %{ + "content" => draft.status, + "mediaType" => Utils.get_content_type(draft.params[:content_type]) + }) |> Map.put("generator", draft.params[:generator]) %__MODULE__{draft | object: object} diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 10eb48250..ff0814329 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.CommonAPI.Utils do @@ -37,7 +37,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do def attachments_from_ids_no_descs(ids) do Enum.map(ids, fn media_id -> - case Repo.get(Object, media_id) do + case get_attachment(media_id) do %Object{data: data} -> data _ -> nil end @@ -51,13 +51,17 @@ defmodule Pleroma.Web.CommonAPI.Utils do {_, descs} = Jason.decode(descs_str) Enum.map(ids, fn media_id -> - with %Object{data: data} <- Repo.get(Object, media_id) do + with %Object{data: data} <- get_attachment(media_id) do Map.put(data, "name", descs[media_id]) end end) |> Enum.reject(&is_nil/1) end + defp get_attachment(media_id) do + Repo.get(Object, media_id) + end + @spec get_to_and_cc(ActivityDraft.t()) :: {list(String.t()), list(String.t())} def get_to_and_cc(%{in_reply_to_conversation: %Participation{} = participation}) do @@ -219,7 +223,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do |> maybe_add_attachments(draft.attachments, attachment_links) end - defp get_content_type(content_type) do + def get_content_type(content_type) do if Enum.member?(Config.get([:instance, :allowed_post_formats]), content_type) do content_type else @@ -291,33 +295,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do |> Formatter.html_escape("text/html") end - def make_note_data(%ActivityDraft{} = draft) do - %{ - "type" => "Note", - "to" => draft.to, - "cc" => draft.cc, - "content" => draft.content_html, - "summary" => draft.summary, - "sensitive" => draft.sensitive, - "context" => draft.context, - "attachment" => draft.attachments, - "actor" => draft.user.ap_id, - "tag" => Keyword.values(draft.tags) |> Enum.uniq() - } - |> add_in_reply_to(draft.in_reply_to) - |> Map.merge(draft.extra) - end - - defp add_in_reply_to(object, nil), do: object - - defp add_in_reply_to(object, in_reply_to) do - with %Object{} = in_reply_to_object <- Object.normalize(in_reply_to, fetch: false) do - Map.put(object, "inReplyTo", in_reply_to_object.data["id"]) - else - _ -> object - end - end - def format_naive_asctime(date) do date |> DateTime.from_naive!("Etc/UTC") |> format_asctime end @@ -476,35 +453,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do def get_report_statuses(_, _), do: {:ok, nil} - # DEPRECATED mostly, context objects are now created at insertion time. - def context_to_conversation_id(context) do - with %Object{id: id} <- Object.get_cached_by_ap_id(context) do - id - else - _e -> - changeset = Object.context_mapping(context) - - case Repo.insert(changeset) do - {:ok, %{id: id}} -> - id - - # This should be solved by an upsert, but it seems ecto - # has problems accessing the constraint inside the jsonb. - {:error, _} -> - Object.get_cached_by_ap_id(context).id - end - end - end - - def conversation_id_to_context(id) do - with %Object{data: %{"id" => context}} <- Repo.get(Object, id) do - context - else - _e -> - {:error, dgettext("errors", "No such conversation")} - end - end - def validate_character_limit("" = _full_payload, [] = _attachments) do {:error, dgettext("errors", "Cannot post an empty status without attachments")} end @@ -519,4 +467,19 @@ defmodule Pleroma.Web.CommonAPI.Utils do {:error, dgettext("errors", "The status is over the character limit")} end end + + def validate_attachments_count([] = _attachments) do + :ok + end + + def validate_attachments_count(attachments) do + limit = Config.get([:instance, :max_media_attachments]) + count = length(attachments) + + if count <= limit do + :ok + else + {:error, dgettext("errors", "Too many attachments")} + end + end end diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex index 7b84b43e4..0c7fc17f4 100644 --- a/lib/pleroma/web/controller_helper.ex +++ b/lib/pleroma/web/controller_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ControllerHelper do diff --git a/lib/pleroma/web/embed_controller.ex b/lib/pleroma/web/embed_controller.ex index c7912bb1f..8b9f0a051 100644 --- a/lib/pleroma/web/embed_controller.ex +++ b/lib/pleroma/web/embed_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.EmbedController do diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index 8e274de88..d8d40cceb 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Endpoint do @@ -10,6 +10,7 @@ defmodule Pleroma.Web.Endpoint do alias Pleroma.Config socket("/socket", Pleroma.Web.UserSocket) + socket("/live", Phoenix.LiveView.Socket) plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint]) diff --git a/lib/pleroma/web/fallback/legacy_pleroma_api_rerouter_plug.ex b/lib/pleroma/web/fallback/legacy_pleroma_api_rerouter_plug.ex index f86d6b52b..6176f3d90 100644 --- a/lib/pleroma/web/fallback/legacy_pleroma_api_rerouter_plug.ex +++ b/lib/pleroma/web/fallback/legacy_pleroma_api_rerouter_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Fallback.LegacyPleromaApiRerouterPlug do diff --git a/lib/pleroma/web/fallback/redirect_controller.ex b/lib/pleroma/web/fallback/redirect_controller.ex index 5fca290e5..1a86f7a53 100644 --- a/lib/pleroma/web/fallback/redirect_controller.ex +++ b/lib/pleroma/web/fallback/redirect_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Fallback.RedirectController do diff --git a/lib/pleroma/web/federator.ex b/lib/pleroma/web/federator.ex index 69cfc2d52..318b6cb11 100644 --- a/lib/pleroma/web/federator.ex +++ b/lib/pleroma/web/federator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Federator do @@ -47,10 +47,15 @@ defmodule Pleroma.Web.Federator do end @impl true - def publish(activity) do - PublisherWorker.enqueue("publish", %{"activity_id" => activity.id}) + def publish(%Pleroma.Activity{data: %{"type" => type}} = activity) do + PublisherWorker.enqueue("publish", %{"activity_id" => activity.id}, + priority: publish_priority(type) + ) end + defp publish_priority("Delete"), do: 3 + defp publish_priority(_), do: 0 + # Job Worker Callbacks @spec perform(atom(), module(), any()) :: {:ok, any()} | {:error, any()} @@ -61,10 +66,8 @@ defmodule Pleroma.Web.Federator do def perform(:publish, activity) do Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end) - with %User{} = actor <- User.get_cached_by_ap_id(activity.data["actor"]), - {:ok, actor} <- User.ensure_keys_present(actor) do - Publisher.publish(actor, activity) - end + %User{} = actor = User.get_cached_by_ap_id(activity.data["actor"]) + Publisher.publish(actor, activity) end def perform(:incoming_ap_doc, params) do diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex index b7ee56803..a45796e9d 100644 --- a/lib/pleroma/web/federator/publisher.ex +++ b/lib/pleroma/web/federator/publisher.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Federator.Publisher do diff --git a/lib/pleroma/web/federator/publishing.ex b/lib/pleroma/web/federator/publishing.ex index fe7805be9..3a242b8dc 100644 --- a/lib/pleroma/web/federator/publishing.ex +++ b/lib/pleroma/web/federator/publishing.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Federator.Publishing do diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex index c0fb35e01..449659f4b 100644 --- a/lib/pleroma/web/feed/feed_view.ex +++ b/lib/pleroma/web/feed/feed_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Feed.FeedView do @@ -9,18 +9,13 @@ defmodule Pleroma.Web.Feed.FeedView do alias Pleroma.Formatter alias Pleroma.Object alias Pleroma.User + alias Pleroma.Web.Gettext alias Pleroma.Web.MediaProxy require Pleroma.Constants - @spec pub_date(String.t() | DateTime.t()) :: String.t() - def pub_date(date) when is_binary(date) do - date - |> Timex.parse!("{ISO:Extended}") - |> pub_date - end - - def pub_date(%DateTime{} = date), do: Timex.format!(date, "{RFC822}") + @days ~w(Mon Tue Wed Thu Fri Sat Sun) + @months ~w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec) def prepare_activity(activity, opts \\ []) do object = Object.normalize(activity, fetch: false) @@ -40,13 +35,18 @@ defmodule Pleroma.Web.Feed.FeedView do def most_recent_update(activities) do with %{updated_at: updated_at} <- List.first(activities) do - NaiveDateTime.to_iso8601(updated_at) + to_rfc3339(updated_at) end end - def most_recent_update(activities, user) do + def most_recent_update(activities, user, :atom) do (List.first(activities) || user).updated_at - |> NaiveDateTime.to_iso8601() + |> to_rfc3339() + end + + def most_recent_update(activities, user, :rss) do + (List.first(activities) || user).updated_at + |> to_rfc2822() end def feed_logo do @@ -60,6 +60,10 @@ defmodule Pleroma.Web.Feed.FeedView do |> MediaProxy.url() end + def email(user) do + user.nickname <> "@" <> Pleroma.Web.Endpoint.host() + end + def logo(user) do user |> User.avatar_url() @@ -68,18 +72,34 @@ defmodule Pleroma.Web.Feed.FeedView do def last_activity(activities), do: List.last(activities) - def activity_title(%{"content" => content}, opts \\ %{}) do - content + def activity_title(%{"content" => content, "summary" => summary} = data, opts \\ %{}) do + title = + cond do + summary != "" -> summary + content != "" -> activity_content(data) + true -> "a post" + end + + title |> Pleroma.Web.Metadata.Utils.scrub_html() |> Pleroma.Emoji.Formatter.demojify() |> Formatter.truncate(opts[:max_length], opts[:omission]) - |> escape() + end + + def activity_description(data) do + content = activity_content(data) + summary = data["summary"] + + cond do + content != "" -> escape(content) + summary != "" -> escape(summary) + true -> escape(data["type"]) + end end def activity_content(%{"content" => content}) do content |> String.replace(~r/[\n\r]/, "") - |> escape() end def activity_content(_), do: "" @@ -111,4 +131,60 @@ defmodule Pleroma.Web.Feed.FeedView do |> html_escape() |> safe_to_string() end + + @spec to_rfc3339(String.t() | NativeDateTime.t()) :: String.t() + def to_rfc3339(date) when is_binary(date) do + date + |> Timex.parse!("{ISO:Extended}") + |> to_rfc3339() + end + + def to_rfc3339(nd) do + nd + |> Timex.to_datetime() + |> Timex.format!("{RFC3339}") + end + + @spec to_rfc2822(String.t() | DateTime.t() | NativeDateTime.t()) :: String.t() + def to_rfc2822(datestr) when is_binary(datestr) do + datestr + |> Timex.parse!("{ISO:Extended}") + |> to_rfc2822() + end + + def to_rfc2822(%DateTime{} = date) do + date + |> DateTime.to_naive() + |> NaiveDateTime.to_erl() + |> rfc2822_from_erl() + end + + def to_rfc2822(nd) do + nd + |> Timex.to_datetime() + |> DateTime.to_naive() + |> NaiveDateTime.to_erl() + |> rfc2822_from_erl() + end + + @doc """ + Builds a RFC2822 timestamp from an Erlang timestamp + [RFC2822 3.3 - Date and Time Specification](https://tools.ietf.org/html/rfc2822#section-3.3) + This function always assumes the Erlang timestamp is in Universal time, not Local time + """ + def rfc2822_from_erl({{year, month, day} = date, {hour, minute, second}}) do + day_name = Enum.at(@days, :calendar.day_of_the_week(date) - 1) + month_name = Enum.at(@months, month - 1) + + date_part = "#{day_name}, #{day} #{month_name} #{year}" + time_part = "#{pad(hour)}:#{pad(minute)}:#{pad(second)}" + + date_part <> " " <> time_part <> " +0000" + end + + defp pad(num) do + num + |> Integer.to_string() + |> String.pad_leading(2, "0") + end end diff --git a/lib/pleroma/web/feed/tag_controller.ex b/lib/pleroma/web/feed/tag_controller.ex index ef9293a55..e60767327 100644 --- a/lib/pleroma/web/feed/tag_controller.ex +++ b/lib/pleroma/web/feed/tag_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Feed.TagController do diff --git a/lib/pleroma/web/feed/user_controller.ex b/lib/pleroma/web/feed/user_controller.ex index fa7879caf..6657c2b3e 100644 --- a/lib/pleroma/web/feed/user_controller.ex +++ b/lib/pleroma/web/feed/user_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Feed.UserController do @@ -18,6 +18,8 @@ defmodule Pleroma.Web.Feed.UserController do def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do Pleroma.Web.Fallback.RedirectController.redirector_with_meta(conn, %{user: user}) + else + _ -> Pleroma.Web.Fallback.RedirectController.redirector(conn, nil) end end diff --git a/lib/pleroma/web/gettext.ex b/lib/pleroma/web/gettext.ex index c0ca4d0e9..5ef49d841 100644 --- a/lib/pleroma/web/gettext.ex +++ b/lib/pleroma/web/gettext.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Gettext do @@ -25,4 +25,196 @@ defmodule Pleroma.Web.Gettext do See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage. """ use Gettext, otp_app: :pleroma + + def language_tag do + # Naive implementation: HTML lang attribute uses BCP 47, which + # uses - as a separator. + # https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang + + Gettext.get_locale() + |> String.replace("_", "-", global: true) + end + + def normalize_locale(locale) do + if is_binary(locale) do + String.replace(locale, "-", "_", global: true) + else + nil + end + end + + def supports_locale?(locale) do + Pleroma.Web.Gettext + |> Gettext.known_locales() + |> Enum.member?(locale) + end + + def variant?(locale), do: String.contains?(locale, "_") + + def language_for_variant(locale) do + Enum.at(String.split(locale, "_"), 0) + end + + def ensure_fallbacks(locales) do + locales + |> Enum.flat_map(fn locale -> + others = + other_supported_variants_of_locale(locale) + |> Enum.filter(fn l -> not Enum.member?(locales, l) end) + + [locale] ++ others + end) + end + + def other_supported_variants_of_locale(locale) do + cond do + supports_locale?(locale) -> + [] + + variant?(locale) -> + lang = language_for_variant(locale) + if supports_locale?(lang), do: [lang], else: [] + + true -> + Gettext.known_locales(Pleroma.Web.Gettext) + |> Enum.filter(fn l -> String.starts_with?(l, locale <> "_") end) + end + end + + def get_locales do + Process.get({Pleroma.Web.Gettext, :locales}, []) + end + + def is_locale_list(locales) do + Enum.all?(locales, &is_binary/1) + end + + def put_locales(locales) do + if is_locale_list(locales) do + Process.put({Pleroma.Web.Gettext, :locales}, Enum.uniq(locales)) + Gettext.put_locale(Enum.at(locales, 0, Gettext.get_locale())) + :ok + else + {:error, :not_locale_list} + end + end + + def locale_or_default(locale) do + if supports_locale?(locale) do + locale + else + Gettext.get_locale() + end + end + + def with_locales_func(locales, fun) do + prev_locales = Process.get({Pleroma.Web.Gettext, :locales}) + put_locales(locales) + + try do + fun.() + after + if prev_locales do + put_locales(prev_locales) + else + Process.delete({Pleroma.Web.Gettext, :locales}) + Process.delete(Gettext) + end + end + end + + defmacro with_locales(locales, do: fun) do + quote do + Pleroma.Web.Gettext.with_locales_func(unquote(locales), fn -> + unquote(fun) + end) + end + end + + def to_locale_list(locale) when is_binary(locale) do + locale + |> String.split(",") + |> Enum.filter(&supports_locale?/1) + end + + def to_locale_list(_), do: [] + + defmacro with_locale_or_default(locale, do: fun) do + quote do + Pleroma.Web.Gettext.with_locales_func( + Pleroma.Web.Gettext.to_locale_list(unquote(locale)) + |> Enum.concat(Pleroma.Web.Gettext.get_locales()), + fn -> + unquote(fun) + end + ) + end + end + + defp next_locale(locale, list) do + index = Enum.find_index(list, fn item -> item == locale end) + + if not is_nil(index) do + Enum.at(list, index + 1) + else + nil + end + end + + # We do not yet have a proper English translation. The "English" + # version is currently but the fallback msgid. However, this + # will not work if the user puts English as the first language, + # and at the same time specifies other languages, as gettext will + # think the English translation is missing, and call + # handle_missing_translation functions. This may result in + # text in other languages being shown even if English is preferred + # by the user. + # + # To prevent this, we do not allow fallbacking when the current + # locale missing a translation is English. + defp should_fallback?(locale) do + locale != "en" + end + + def handle_missing_translation(locale, domain, msgctxt, msgid, bindings) do + next = next_locale(locale, get_locales()) + + if is_nil(next) or not should_fallback?(locale) do + super(locale, domain, msgctxt, msgid, bindings) + else + {:ok, + Gettext.with_locale(next, fn -> + Gettext.dpgettext(Pleroma.Web.Gettext, domain, msgctxt, msgid, bindings) + end)} + end + end + + def handle_missing_plural_translation( + locale, + domain, + msgctxt, + msgid, + msgid_plural, + n, + bindings + ) do + next = next_locale(locale, get_locales()) + + if is_nil(next) or not should_fallback?(locale) do + super(locale, domain, msgctxt, msgid, msgid_plural, n, bindings) + else + {:ok, + Gettext.with_locale(next, fn -> + Gettext.dpngettext( + Pleroma.Web.Gettext, + domain, + msgctxt, + msgid, + msgid_plural, + n, + bindings + ) + end)} + end + end end diff --git a/lib/pleroma/web/instance_document.ex b/lib/pleroma/web/instance_document.ex index a33bf605b..9da3c5008 100644 --- a/lib/pleroma/web/instance_document.ex +++ b/lib/pleroma/web/instance_document.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.InstanceDocument do diff --git a/lib/pleroma/web/mailer/subscription_controller.ex b/lib/pleroma/web/mailer/subscription_controller.ex index f89abe46a..f2fc8fbc8 100644 --- a/lib/pleroma/web/mailer/subscription_controller.ex +++ b/lib/pleroma/web/mailer/subscription_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Mailer.SubscriptionController do diff --git a/lib/pleroma/web/manifest_controller.ex b/lib/pleroma/web/manifest_controller.ex new file mode 100644 index 000000000..3b02e4bc0 --- /dev/null +++ b/lib/pleroma/web/manifest_controller.ex @@ -0,0 +1,14 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ManifestController do + use Pleroma.Web, :controller + + plug(:skip_auth when action == :show) + + @doc "GET /manifest.json" + def show(conn, _params) do + render(conn, "manifest.json") + end +end diff --git a/lib/pleroma/web/masto_fe_controller.ex b/lib/pleroma/web/masto_fe_controller.ex deleted file mode 100644 index d2460f51d..000000000 --- a/lib/pleroma/web/masto_fe_controller.ex +++ /dev/null @@ -1,61 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.MastoFEController do - use Pleroma.Web, :controller - - alias Pleroma.User - alias Pleroma.Web.MastodonAPI.AuthController - alias Pleroma.Web.OAuth.Token - alias Pleroma.Web.Plugs.OAuthScopesPlug - - plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :put_settings) - - # Note: :index action handles attempt of unauthenticated access to private instance with redirect - plug(:skip_public_check when action == :index) - - plug( - OAuthScopesPlug, - %{scopes: ["read"], fallback: :proceed_unauthenticated} - when action == :index - ) - - plug(:skip_auth when action == :manifest) - - @doc "GET /web/*path" - def index(conn, _params) do - with %{assigns: %{user: %User{} = user, token: %Token{app_id: token_app_id} = token}} <- conn, - {:ok, %{id: ^token_app_id}} <- AuthController.local_mastofe_app() do - conn - |> put_layout(false) - |> render("index.html", - token: token.token, - user: user, - custom_emojis: Pleroma.Emoji.get_all() - ) - else - _ -> - conn - |> put_session(:return_to, conn.request_path) - |> redirect(to: "/web/login") - end - end - - @doc "GET /web/manifest.json" - def manifest(conn, _params) do - render(conn, "manifest.json") - end - - @doc "PUT /api/web/settings: Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere" - def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do - with {:ok, _} <- User.mastodon_settings_update(user, settings) do - json(conn, %{}) - else - e -> - conn - |> put_status(:internal_server_error) - |> json(%{error: inspect(e)}) - end - end -end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 0a4a72bb4..ea6e593d9 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AccountController do @@ -15,6 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do alias Pleroma.Maps alias Pleroma.User + alias Pleroma.UserNote alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.Pipeline @@ -31,7 +32,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(:skip_auth when action == :create) + plug(:skip_auth when action in [:create, :lookup]) plug(:skip_public_check when action in [:show, :statuses]) @@ -53,7 +54,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do when action in [:verify_credentials, :endorsements, :identity_proofs] ) - plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :update_credentials) + plug( + OAuthScopesPlug, + %{scopes: ["write:accounts"]} + when action in [:update_credentials, :note, :endorse, :unendorse] + ) plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists) @@ -71,15 +76,19 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug( OAuthScopesPlug, - %{scopes: ["follow", "write:follows"]} when action in [:follow_by_uri, :follow, :unfollow] + %{scopes: ["follow", "write:follows"]} + when action in [:follow_by_uri, :follow, :unfollow, :remove_from_followers] ) plug(OAuthScopesPlug, %{scopes: ["follow", "read:mutes"]} when action == :mutes) plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute]) - @relationship_actions [:follow, :unfollow] - @needs_account ~W(followers following lists follow unfollow mute unmute block unblock)a + @relationship_actions [:follow, :unfollow, :remove_from_followers] + @needs_account ~W( + followers following lists follow unfollow mute unmute block unblock + note endorse unendorse remove_from_followers + )a plug( RateLimiter, @@ -184,7 +193,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do :skip_thread_containment, :allow_following_move, :also_known_as, - :accepts_chat_messages + :accepts_chat_messages, + :show_birthday ] |> Enum.reduce(%{}, fn key, acc -> Maps.put_if_present(acc, key, params[key], &{:ok, Params.truthy_param?(&1)}) @@ -212,6 +222,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do |> Maps.put_if_present(:is_locked, params[:locked]) # Note: param name is indeed :discoverable (not an error) |> Maps.put_if_present(:is_discoverable, params[:discoverable]) + |> Maps.put_if_present(:birthday, params[:birthday]) + |> Maps.put_if_present(:language, Pleroma.Web.Gettext.normalize_locale(params[:language])) # What happens here: # @@ -242,7 +254,17 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do with_pleroma_settings: true ) else - _e -> render_error(conn, :forbidden, "Invalid request") + {:error, %Ecto.Changeset{errors: [avatar: {"file is too large", _}]}} -> + render_error(conn, :request_entity_too_large, "File is too large") + + {:error, %Ecto.Changeset{errors: [banner: {"file is too large", _}]}} -> + render_error(conn, :request_entity_too_large, "File is too large") + + {:error, %Ecto.Changeset{errors: [background: {"file is too large", _}]}} -> + render_error(conn, :request_entity_too_large, "File is too large") + + _e -> + render_error(conn, :forbidden, "Invalid request") end end @@ -401,6 +423,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "POST /api/v1/accounts/:id/mute" def mute(%{assigns: %{user: muter, account: muted}, body_params: params} = conn, _params) do + params = + params + |> Map.put_new(:duration, Map.get(params, :expires_in, 0)) + with {:ok, _user_relationships} <- User.mute(muter, muted, params) do render(conn, "relationship.json", user: muter, target: muted) else @@ -435,6 +461,48 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end end + @doc "POST /api/v1/accounts/:id/note" + def note( + %{assigns: %{user: noter, account: target}, body_params: %{comment: comment}} = conn, + _params + ) do + with {:ok, _user_note} <- UserNote.create(noter, target, comment) do + render(conn, "relationship.json", user: noter, target: target) + end + end + + @doc "POST /api/v1/accounts/:id/pin" + def endorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) do + with {:ok, _user_relationships} <- User.endorse(endorser, endorsed) do + render(conn, "relationship.json", user: endorser, target: endorsed) + else + {:error, message} -> json_response(conn, :bad_request, %{error: message}) + end + end + + @doc "POST /api/v1/accounts/:id/unpin" + def unendorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) do + with {:ok, _user_relationships} <- User.unendorse(endorser, endorsed) do + render(conn, "relationship.json", user: endorser, target: endorsed) + else + {:error, message} -> json_response(conn, :forbidden, %{error: message}) + end + end + + @doc "POST /api/v1/accounts/:id/remove_from_followers" + def remove_from_followers(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do + {:error, "Can not unfollow yourself"} + end + + def remove_from_followers(%{assigns: %{user: followed, account: follower}} = conn, _params) do + with {:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do + render(conn, "relationship.json", user: followed, target: follower) + else + nil -> + render_error(conn, :not_found, "Record not found") + end + end + @doc "POST /api/v1/follows" def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do case User.get_cached_by_nickname(uri) do @@ -461,7 +529,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do users: users, for: user, as: :user, - embed_relationships: embed_relationships?(params) + embed_relationships: embed_relationships?(params), + mutes: true ) end @@ -477,8 +546,33 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do |> render("index.json", users: users, for: user, as: :user) end + @doc "GET /api/v1/accounts/lookup" + def lookup(conn, %{acct: nickname} = _params) do + with %User{} = user <- User.get_by_nickname(nickname) do + render(conn, "show.json", + user: user, + skip_visibility_check: true + ) + else + error -> user_visibility_error(conn, error) + end + end + @doc "GET /api/v1/endorsements" - def endorsements(conn, params), do: MastodonAPIController.empty_array(conn, params) + def endorsements(%{assigns: %{user: user}} = conn, params) do + users = + user + |> User.endorsed_users_relation(_restrict_deactivated = true) + |> Pleroma.Repo.all() + + conn + |> render("index.json", + users: users, + for: user, + as: :user, + embed_relationships: embed_relationships?(params) + ) + end @doc "GET /api/v1/identity_proofs" def identity_proofs(conn, params), do: MastodonAPIController.empty_array(conn, params) diff --git a/lib/pleroma/web/mastodon_api/controllers/announcement_controller.ex b/lib/pleroma/web/mastodon_api/controllers/announcement_controller.ex new file mode 100644 index 000000000..080af96d5 --- /dev/null +++ b/lib/pleroma/web/mastodon_api/controllers/announcement_controller.ex @@ -0,0 +1,60 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.AnnouncementController do + use Pleroma.Web, :controller + + import Pleroma.Web.ControllerHelper, + only: [ + json_response: 3 + ] + + alias Pleroma.Announcement + alias Pleroma.Web.Plugs.OAuthScopesPlug + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + # Mastodon docs say this only requires a user token, no scopes needed + # As the op `|` requires at least one scope to be present, we use `&` here. + plug( + OAuthScopesPlug, + %{scopes: [], op: :&} + when action in [:index] + ) + + # Same as in MastodonAPI specs + plug( + OAuthScopesPlug, + %{scopes: ["write:accounts"]} + when action in [:mark_read] + ) + + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AnnouncementOperation + + @doc "GET /api/v1/announcements" + def index(%{assigns: %{user: user}} = conn, _params) do + render(conn, "index.json", announcements: all_visible(), user: user) + end + + def index(conn, _params) do + render(conn, "index.json", announcements: all_visible(), user: nil) + end + + defp all_visible do + Announcement.list_all_visible() + end + + @doc "POST /api/v1/announcements/:id/dismiss" + def mark_read(%{assigns: %{user: user}} = conn, %{id: id} = _params) do + with announcement when not is_nil(announcement) <- Announcement.get_by_id(id), + {:ok, _} <- Announcement.mark_read_by(announcement, user) do + json_response(conn, :ok, %{}) + else + _ -> + {:error, :not_found} + end + end +end diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index a95cc52fd..844673ae0 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AppController do @@ -10,7 +10,9 @@ defmodule Pleroma.Web.MastodonAPI.AppController do use Pleroma.Web, :controller + alias Pleroma.Maps alias Pleroma.Repo + alias Pleroma.User alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Scopes alias Pleroma.Web.OAuth.Token @@ -21,26 +23,28 @@ defmodule Pleroma.Web.MastodonAPI.AppController do plug(Pleroma.Web.ApiSpec.CastAndValidate) - @local_mastodon_name "Mastodon-Local" - defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AppOperation @doc "POST /api/v1/apps" def create(%{body_params: params} = conn, _params) do scopes = Scopes.fetch_scopes(params, ["read"]) + user_id = get_user_id(conn) app_attrs = params |> Map.take([:client_name, :redirect_uris, :website]) |> Map.put(:scopes, scopes) + |> Maps.put_if_present(:user_id, user_id) with cs <- App.register_changeset(%App{}, app_attrs), - false <- cs.changes[:client_name] == @local_mastodon_name, {:ok, app} <- Repo.insert(cs) do render(conn, "show.json", app: app) end end + defp get_user_id(%{assigns: %{user: %User{id: user_id}}}), do: user_id + defp get_user_id(_conn), do: nil + @doc """ GET /api/v1/apps/verify_credentials Gets compact non-secret representation of the app. Supports app tokens and user tokens. diff --git a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex index 4920d65da..fbb54a171 100644 --- a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AuthController do @@ -7,77 +7,12 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do import Pleroma.Web.ControllerHelper, only: [json_response: 3] - alias Pleroma.Helpers.AuthHelper - alias Pleroma.Helpers.UriHelper - alias Pleroma.User - alias Pleroma.Web.OAuth.App - alias Pleroma.Web.OAuth.Authorization - alias Pleroma.Web.OAuth.Token - alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken alias Pleroma.Web.TwitterAPI.TwitterAPI action_fallback(Pleroma.Web.MastodonAPI.FallbackController) plug(Pleroma.Web.Plugs.RateLimiter, [name: :password_reset] when action == :password_reset) - @local_mastodon_name "Mastodon-Local" - - @doc "GET /web/login" - # Local Mastodon FE login callback action - def login(conn, %{"code" => auth_token} = params) do - with {:ok, app} <- local_mastofe_app(), - {:ok, auth} <- Authorization.get_by_token(app, auth_token), - {:ok, oauth_token} <- Token.exchange_token(app, auth) do - redirect_to = - conn - |> local_mastodon_post_login_path() - |> UriHelper.modify_uri_params(%{"access_token" => oauth_token.token}) - - conn - |> AuthHelper.put_session_token(oauth_token.token) - |> redirect(to: redirect_to) - else - _ -> redirect_to_oauth_form(conn, params) - end - end - - def login(conn, params) do - with %{assigns: %{user: %User{}, token: %Token{app_id: app_id}}} <- conn, - {:ok, %{id: ^app_id}} <- local_mastofe_app() do - redirect(conn, to: local_mastodon_post_login_path(conn)) - else - _ -> redirect_to_oauth_form(conn, params) - end - end - - defp redirect_to_oauth_form(conn, _params) do - with {:ok, app} <- local_mastofe_app() do - path = - Routes.o_auth_path(conn, :authorize, - response_type: "code", - client_id: app.client_id, - redirect_uri: ".", - scope: Enum.join(app.scopes, " ") - ) - - redirect(conn, to: path) - end - end - - @doc "DELETE /auth/sign_out" - def logout(conn, _) do - conn = - with %{assigns: %{token: %Token{} = oauth_token}} <- conn, - session_token = AuthHelper.get_session_token(conn), - {:ok, %Token{token: ^session_token}} <- RevokeToken.revoke(oauth_token) do - AuthHelper.delete_session_token(conn) - else - _ -> conn - end - - redirect(conn, to: "/") - end - @doc "POST /auth/password" def password_reset(conn, params) do nickname_or_email = params["email"] || params["nickname"] @@ -86,23 +21,4 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do json_response(conn, :no_content, "") end - - defp local_mastodon_post_login_path(conn) do - case get_session(conn, :return_to) do - nil -> - Routes.masto_fe_path(conn, :index, ["getting-started"]) - - return_to -> - delete_session(conn, :return_to) - return_to - end - end - - @spec local_mastofe_app() :: {:ok, App.t()} | {:error, Ecto.Changeset.t()} - def local_mastofe_app do - App.get_or_make( - %{client_name: @local_mastodon_name, redirect_uris: "."}, - ["read", "write", "follow", "push", "admin"] - ) - end end diff --git a/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex b/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex index f2a0949e8..9cc6225c6 100644 --- a/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ConversationController do diff --git a/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex b/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex index 31b647755..8b27b0b02 100644 --- a/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.CustomEmojiController do diff --git a/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex b/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex new file mode 100644 index 000000000..253f06cfb --- /dev/null +++ b/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex @@ -0,0 +1,82 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.DirectoryController do + use Pleroma.Web, :controller + + import Ecto.Query + alias Pleroma.Pagination + alias Pleroma.User + alias Pleroma.UserRelationship + alias Pleroma.Web.MastodonAPI.AccountView + + require Logger + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + plug(:skip_auth when action == "index") + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DirectoryOperation + + @doc "GET /api/v1/directory" + def index(%{assigns: %{user: user}} = conn, params) do + with true <- Pleroma.Config.get([:instance, :profile_directory]) do + limit = Map.get(params, :limit, 20) |> min(80) + + users = + User.Query.build(%{is_discoverable: true, invisible: false, limit: limit}) + |> order_by_creation_date(params) + |> exclude_remote(params) + |> exclude_user(user) + |> exclude_relationships(user, [:block, :mute]) + |> Pagination.fetch_paginated(params, :offset) + + conn + |> put_view(AccountView) + |> render("index.json", for: user, users: users, as: :user) + else + _ -> json(conn, []) + end + end + + defp order_by_creation_date(query, %{order: "new"}) do + query + end + + defp order_by_creation_date(query, _params) do + query + |> order_by([u], desc_nulls_last: u.last_status_at) + end + + defp exclude_remote(query, %{local: true}) do + where(query, [u], u.local == true) + end + + defp exclude_remote(query, _params) do + query + end + + defp exclude_user(query, %User{id: user_id}) do + where(query, [u], u.id != ^user_id) + end + + defp exclude_user(query, _user) do + query + end + + defp exclude_relationships(query, %User{id: user_id}, relationship_types) do + query + |> join(:left, [u], r in UserRelationship, + as: :user_relationships, + on: + r.target_id == u.id and r.source_id == ^user_id and + r.relationship_type in ^relationship_types + ) + |> where([user_relationships: r], is_nil(r.target_id)) + end + + defp exclude_relationships(query, _user, _relationship_types) do + query + end +end diff --git a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex index 30300307d..b2e347ed9 100644 --- a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.DomainBlockController do diff --git a/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex b/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex index 84621500e..1c650eb21 100644 --- a/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.FallbackController do diff --git a/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex index 9b1ae809d..0959b4bcc 100644 --- a/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.FilterController do diff --git a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex index d915298f1..ba6d074cc 100644 --- a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.FollowRequestController do diff --git a/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex b/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex index 5376e4594..6410e872c 100644 --- a/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.InstanceController do diff --git a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex index b7b41f449..2117aae3a 100644 --- a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ListController do diff --git a/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex b/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex index c745f3493..4ad30f330 100644 --- a/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MarkerController do diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index a0f79f377..0aa7b379f 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do diff --git a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex index 5918b288d..7d9a63cf4 100644 --- a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MediaController do diff --git a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex index 647ba661e..a490e8319 100644 --- a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.NotificationController do @@ -50,11 +50,13 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do favourite move pleroma:emoji_reaction + poll + update } def index(%{assigns: %{user: user}} = conn, params) do params = Map.new(params, fn {k, v} -> {to_string(k), v} end) - |> Map.put_new("include_types", @default_notification_types) + |> Map.put_new("types", Map.get(params, :include_types, @default_notification_types)) notifications = MastodonAPI.get_notifications(user, params) diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex index f44ff997d..002c210d2 100644 --- a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.PollController do diff --git a/lib/pleroma/web/mastodon_api/controllers/report_controller.ex b/lib/pleroma/web/mastodon_api/controllers/report_controller.ex index 03d9a4f4f..3db80d728 100644 --- a/lib/pleroma/web/mastodon_api/controllers/report_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/report_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ReportController do diff --git a/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex index 3b7a0c788..0392fcef1 100644 --- a/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex index 64b177eb3..5e6e04734 100644 --- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.SearchController do @@ -17,6 +17,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do require Logger + @search_limit 40 + plug(Pleroma.Web.ApiSpec.CastAndValidate) # Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search) @@ -77,7 +79,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do [ resolve: params[:resolve], following: params[:following], - limit: params[:limit], + limit: min(params[:limit], @search_limit), offset: params[:offset], type: params[:type], author: get_author(params), diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 2eff4d9d0..e594ea491 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.StatusController do @@ -38,7 +38,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do :index, :show, :card, - :context + :context, + :show_history, + :show_source ] ) @@ -49,7 +51,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do :create, :delete, :reblog, - :unreblog + :unreblog, + :update ] ) @@ -191,6 +194,59 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do create(%Plug.Conn{conn | body_params: params}, %{}) end + @doc "GET /api/v1/statuses/:id/history" + def show_history(%{assigns: assigns} = conn, %{id: id} = params) do + with user = assigns[:user], + %Activity{} = activity <- Activity.get_by_id_with_object(id), + true <- Visibility.visible_for_user?(activity, user) do + try_render(conn, "history.json", + activity: activity, + for: user, + with_direct_conversation_id: true, + with_muted: Map.get(params, :with_muted, false) + ) + else + _ -> {:error, :not_found} + end + end + + @doc "GET /api/v1/statuses/:id/source" + def show_source(%{assigns: assigns} = conn, %{id: id} = _params) do + with user = assigns[:user], + %Activity{} = activity <- Activity.get_by_id_with_object(id), + true <- Visibility.visible_for_user?(activity, user) do + try_render(conn, "source.json", + activity: activity, + for: user + ) + else + _ -> {:error, :not_found} + end + end + + @doc "PUT /api/v1/statuses/:id" + def update(%{assigns: %{user: user}, body_params: body_params} = conn, %{id: id} = params) do + with {_, %Activity{}} = {_, activity} <- {:activity, Activity.get_by_id_with_object(id)}, + {_, true} <- {:visible, Visibility.visible_for_user?(activity, user)}, + {_, true} <- {:is_create, activity.data["type"] == "Create"}, + actor <- Activity.user_actor(activity), + {_, true} <- {:own_status, actor.id == user.id}, + changes <- body_params |> put_application(conn), + {_, {:ok, _update_activity}} <- {:pipeline, CommonAPI.update(user, activity, changes)}, + {_, %Activity{}} = {_, activity} <- {:refetched, Activity.get_by_id_with_object(id)} do + try_render(conn, "show.json", + activity: activity, + for: user, + with_direct_conversation_id: true, + with_muted: Map.get(params, :with_muted, false) + ) + else + {:own_status, _} -> {:error, :forbidden} + {:pipeline, _} -> {:error, :internal_server_error} + _ -> {:error, :not_found} + end + end + @doc "GET /api/v1/statuses/:id" def show(%{assigns: %{user: user}} = conn, %{id: id} = params) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), diff --git a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex index fcb3d4829..9cc0071f6 100644 --- a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.SubscriptionController do diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index 01e122dd9..69ae70ad4 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -1,14 +1,19 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.SuggestionController do use Pleroma.Web, :controller + import Ecto.Query + alias Pleroma.FollowingRelationship + alias Pleroma.User + alias Pleroma.UserRelationship require Logger plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action == :index) + plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action in [:index, :index2]) + plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["write"]} when action in [:dismiss]) def open_api_operation(action) do operation = String.to_existing_atom("#{action}_operation") @@ -26,7 +31,90 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do } end + def index2_operation do + %OpenApiSpex.Operation{ + tags: ["Suggestions"], + summary: "Follow suggestions", + operationId: "SuggestionController.index2", + responses: %{ + 200 => Pleroma.Web.ApiSpec.Helpers.empty_array_response() + } + } + end + + def dismiss_operation do + %OpenApiSpex.Operation{ + tags: ["Suggestions"], + summary: "Remove a suggestion", + operationId: "SuggestionController.dismiss", + parameters: [ + OpenApiSpex.Operation.parameter( + :account_id, + :path, + %OpenApiSpex.Schema{type: :string}, + "Account to dismiss", + required: true + ) + ], + responses: %{ + 200 => Pleroma.Web.ApiSpec.Helpers.empty_object_response() + } + } + end + @doc "GET /api/v1/suggestions" def index(conn, params), do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params) + + @doc "GET /api/v2/suggestions" + def index2(%{assigns: %{user: user}} = conn, params) do + limit = Map.get(params, :limit, 40) |> min(80) + + users = + %{is_suggested: true, invisible: false, limit: limit} + |> User.Query.build() + |> exclude_user(user) + |> exclude_relationships(user, [:block, :mute, :suggestion_dismiss]) + |> exclude_following(user) + |> Pleroma.Repo.all() + + render(conn, "index.json", %{ + users: users, + source: :staff, + for: user, + skip_visibility_check: true + }) + end + + defp exclude_user(query, %User{id: user_id}) do + where(query, [u], u.id != ^user_id) + end + + defp exclude_relationships(query, %User{id: user_id}, relationship_types) do + query + |> join(:left, [u], r in UserRelationship, + as: :user_relationships, + on: + r.target_id == u.id and r.source_id == ^user_id and + r.relationship_type in ^relationship_types + ) + |> where([user_relationships: r], is_nil(r.target_id)) + end + + defp exclude_following(query, %User{id: user_id}) do + query + |> join(:left, [u], r in FollowingRelationship, + as: :following_relationships, + on: r.following_id == u.id and r.follower_id == ^user_id and r.state == :follow_accept + ) + |> where([following_relationships: r], is_nil(r.following_id)) + end + + @doc "DELETE /api/v1/suggestions/:account_id" + def dismiss(%{assigns: %{user: source}} = conn, %{account_id: user_id}) do + with %User{} = target <- User.get_cached_by_id(user_id), + {:ok, _} <- UserRelationship.create(:suggestion_dismiss, source, target) do + json(conn, %{}) + end + end end diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index 10c279893..293c61b41 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.TimelineController do @@ -112,6 +112,8 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do |> Map.put(:muting_user, user) |> Map.put(:reply_filtering_user, user) |> Map.put(:instance, params[:instance]) + # Restricts unfederated content to authenticated users + |> Map.put(:includes_local_public, not is_nil(user)) |> ActivityPub.fetch_public_activities() conn diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index 71479550e..467dc2fac 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MastodonAPI do @@ -24,6 +24,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do with {:ok, follower, _followed, _} <- result do options = cast_params(params) set_reblogs_visibility(options[:reblogs], result) + set_subscription(options[:notify], result) {:ok, follower} end end @@ -36,6 +37,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do CommonAPI.show_reblogs(follower, followed) end + defp set_subscription(true, {:ok, follower, followed, _}) do + User.subscribe(follower, followed) + end + + defp set_subscription(false, {:ok, follower, followed, _}) do + User.unsubscribe(follower, followed) + end + + defp set_subscription(_, _), do: {:ok, nil} + @spec get_followers(User.t(), map()) :: list(User.t()) def get_followers(user, params \\ %{}) do user @@ -50,11 +61,24 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do end def get_notifications(user, params \\ %{}) do - options = cast_params(params) + options = + cast_params(params) |> Map.update(:include_types, [], fn include_types -> include_types end) + + options = + if ("pleroma:report" not in options.include_types and + User.privileged?(user, :reports_manage_reports)) or + User.privileged?(user, :reports_manage_reports) do + options + else + options + |> Map.update(:exclude_types, ["pleroma:report"], fn current_exclude_types -> + current_exclude_types ++ ["pleroma:report"] + end) + end user |> Notification.for_user_query(options) - |> restrict(:include_types, options) + |> restrict(:types, options) |> restrict(:exclude_types, options) |> restrict(:account_ap_id, options) |> Pagination.fetch_paginated(params) @@ -69,18 +93,19 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do defp cast_params(params) do param_types = %{ exclude_types: {:array, :string}, - include_types: {:array, :string}, + types: {:array, :string}, exclude_visibilities: {:array, :string}, reblogs: :boolean, with_muted: :boolean, - account_ap_id: :string + account_ap_id: :string, + notify: :boolean } changeset = cast({%{}, param_types}, params, Map.keys(param_types)) changeset.changes end - defp restrict(query, :include_types, %{include_types: mastodon_types = [_ | _]}) do + defp restrict(query, :types, %{types: mastodon_types = [_ | _]}) do where(query, [n], n.type in ^mastodon_types) end diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 9e9de33f6..cc3e3582f 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AccountView do @@ -7,6 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do alias Pleroma.FollowingRelationship alias Pleroma.User + alias Pleroma.UserNote alias Pleroma.UserRelationship alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.MastodonAPI.AccountView @@ -101,6 +102,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do User.following?(target, reading_user) end + subscribing = + UserRelationship.exists?( + user_relationships, + :inverse_subscription, + target, + reading_user, + &User.subscribed_to?(&2, &1) + ) + # NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags %{ id: to_string(target.id), @@ -138,14 +148,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do target, &User.muted_notifications?(&1, &2) ), - subscribing: - UserRelationship.exists?( - user_relationships, - :inverse_subscription, - target, - reading_user, - &User.subscribed_to?(&2, &1) - ), + subscribing: subscribing, + notifying: subscribing, requested: follow_state == :follow_pending, domain_blocking: User.blocks_domain?(reading_user, target), showing_reblogs: @@ -156,7 +160,19 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do target, &User.muting_reblogs?(&1, &2) ), - endorsed: false + note: + UserNote.show( + reading_user, + target + ), + endorsed: + UserRelationship.exists?( + user_relationships, + :endorsement, + target, + reading_user, + &User.endorses?(&2, &1) + ) } end @@ -261,6 +277,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do actor_type: user.actor_type } }, + last_status_at: user.last_status_at, # Pleroma extensions # Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub @@ -269,6 +286,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do ap_id: user.ap_id, also_known_as: user.also_known_as, is_confirmed: user.is_confirmed, + is_suggested: user.is_suggested, tags: user.tags, hide_followers_count: user.hide_followers_count, hide_follows_count: user.hide_follows_count, @@ -293,6 +311,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do |> maybe_put_unread_conversation_count(user, opts[:for]) |> maybe_put_unread_notification_count(user, opts[:for]) |> maybe_put_email_address(user, opts[:for]) + |> maybe_put_mute_expires_at(user, opts[:for], opts) + |> maybe_show_birthday(user, opts[:for]) end defp username_from_nickname(string) when is_binary(string) do @@ -326,6 +346,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do |> Kernel.put_in([:source, :privacy], user.default_scope) |> Kernel.put_in([:source, :pleroma, :show_role], user.show_role) |> Kernel.put_in([:source, :pleroma, :no_rich_text], user.no_rich_text) + |> Kernel.put_in([:source, :pleroma, :show_birthday], user.show_birthday) end defp maybe_put_settings(data, _, _, _), do: data @@ -349,19 +370,22 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do defp maybe_put_chat_token(data, _, _, _), do: data defp maybe_put_role(data, %User{show_role: true} = user, _) do - data - |> Kernel.put_in([:pleroma, :is_admin], user.is_admin) - |> Kernel.put_in([:pleroma, :is_moderator], user.is_moderator) + put_role(data, user) end defp maybe_put_role(data, %User{id: user_id} = user, %User{id: user_id}) do + put_role(data, user) + end + + defp maybe_put_role(data, _, _), do: data + + defp put_role(data, user) do data |> Kernel.put_in([:pleroma, :is_admin], user.is_admin) |> Kernel.put_in([:pleroma, :is_moderator], user.is_moderator) + |> Kernel.put_in([:pleroma, :privileges], User.privileges(user)) end - defp maybe_put_role(data, _, _), do: data - defp maybe_put_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do Kernel.put_in( data, @@ -378,12 +402,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do defp maybe_put_allow_following_move(data, _, _), do: data - defp maybe_put_activation_status(data, user, %User{is_admin: true}) do - Kernel.put_in(data, [:pleroma, :deactivated], !user.is_active) + defp maybe_put_activation_status(data, user, user_for) do + if User.privileged?(user_for, :users_manage_activation_state), + do: Kernel.put_in(data, [:pleroma, :deactivated], !user.is_active), + else: data end - defp maybe_put_activation_status(data, _, _), do: data - defp maybe_put_unread_conversation_count(data, %User{id: user_id} = user, %User{id: user_id}) do data |> Kernel.put_in( @@ -414,6 +438,30 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do defp maybe_put_email_address(data, _, _), do: data + defp maybe_put_mute_expires_at(data, %User{} = user, target, %{mutes: true}) do + Map.put( + data, + :mute_expires_at, + UserRelationship.get_mute_expire_date(target, user) + ) + end + + defp maybe_put_mute_expires_at(data, _, _, _), do: data + + defp maybe_show_birthday(data, %User{id: user_id} = user, %User{id: user_id}) do + data + |> Kernel.put_in([:pleroma, :birthday], user.birthday) + end + + defp maybe_show_birthday(data, %User{show_birthday: true} = user, _) do + data + |> Kernel.put_in([:pleroma, :birthday], user.birthday) + end + + defp maybe_show_birthday(data, _, _) do + data + end + defp image_url(%{"url" => [%{"href" => href} | _]}), do: href defp image_url(_), do: nil end diff --git a/lib/pleroma/web/mastodon_api/views/announcement_view.ex b/lib/pleroma/web/mastodon_api/views/announcement_view.ex new file mode 100644 index 000000000..93fdfb1f1 --- /dev/null +++ b/lib/pleroma/web/mastodon_api/views/announcement_view.ex @@ -0,0 +1,15 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.AnnouncementView do + use Pleroma.Web, :view + + def render("index.json", %{announcements: announcements, user: user}) do + render_many(announcements, __MODULE__, "show.json", user: user) + end + + def render("show.json", %{announcement: announcement, user: user}) do + Pleroma.Announcement.render_json(announcement, for: user) + end +end diff --git a/lib/pleroma/web/mastodon_api/views/app_view.ex b/lib/pleroma/web/mastodon_api/views/app_view.ex index c406b5a27..92cccd452 100644 --- a/lib/pleroma/web/mastodon_api/views/app_view.ex +++ b/lib/pleroma/web/mastodon_api/views/app_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AppView do diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex index 46b63b54b..f6577cd2f 100644 --- a/lib/pleroma/web/mastodon_api/views/conversation_view.ex +++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ConversationView do diff --git a/lib/pleroma/web/mastodon_api/views/custom_emoji_view.ex b/lib/pleroma/web/mastodon_api/views/custom_emoji_view.ex index 7d2d605e9..cd59ab946 100644 --- a/lib/pleroma/web/mastodon_api/views/custom_emoji_view.ex +++ b/lib/pleroma/web/mastodon_api/views/custom_emoji_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.CustomEmojiView do diff --git a/lib/pleroma/web/mastodon_api/views/filter_view.ex b/lib/pleroma/web/mastodon_api/views/filter_view.ex index 8e8798c1e..0c9706166 100644 --- a/lib/pleroma/web/mastodon_api/views/filter_view.ex +++ b/lib/pleroma/web/mastodon_api/views/filter_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.FilterView do diff --git a/lib/pleroma/web/mastodon_api/views/follow_request_view.ex b/lib/pleroma/web/mastodon_api/views/follow_request_view.ex index 4c7d9fc65..5e50bc2b6 100644 --- a/lib/pleroma/web/mastodon_api/views/follow_request_view.ex +++ b/lib/pleroma/web/mastodon_api/views/follow_request_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.FollowRequestView do diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index 3528185d5..917725839 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.InstanceView do @@ -17,6 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do uri: Pleroma.Web.Endpoint.url(), title: Keyword.get(instance, :name), description: Keyword.get(instance, :description), + short_description: Keyword.get(instance, :short_description), version: "#{@mastodon_api_level} (compatible; #{Pleroma.Application.named_version()})", email: Keyword.get(instance, :email), urls: %{ @@ -31,6 +32,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do approval_required: Keyword.get(instance, :account_approval_required), # Extra (not present in Mastodon): max_toot_chars: Keyword.get(instance, :limit), + max_media_attachments: Keyword.get(instance, :max_media_attachments), poll_limits: Keyword.get(instance, :poll_limits), upload_limit: Keyword.get(instance, :upload_limit), avatar_upload_limit: Keyword.get(instance, :avatar_upload_limit), @@ -45,7 +47,9 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do features: features(), federation: federation(), fields_limits: fields_limits(), - post_formats: Config.get([:instance, :allowed_post_formats]) + post_formats: Config.get([:instance, :allowed_post_formats]), + birthday_required: Config.get([:instance, :birthday_required]), + birthday_min_age: Config.get([:instance, :birthday_min_age]) }, stats: %{mau: Pleroma.User.active_user_count()}, vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key) @@ -59,10 +63,15 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do "mastodon_api", "mastodon_api_streaming", "polls", + "v2_suggestions", "pleroma_explicit_addressing", "shareable_emoji_packs", "multifetch", "pleroma:api/v1/notifications:include_types_filter", + "editing", + if Config.get([:activitypub, :blockers_visible]) do + "blockers_visible" + end, if Config.get([:media_proxy, :enabled]) do "media_proxy" end, @@ -83,7 +92,14 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do "safe_dm_mentions" end, "pleroma_emoji_reactions", - "pleroma_chat_messages" + "pleroma_chat_messages", + if Config.get([:instance, :show_reactions]) do + "exposable_reactions" + end, + if Config.get([:instance, :profile_directory]) do + "profile_directory" + end, + "pleroma:get:main/ostatus" ] |> Enum.filter(& &1) end @@ -95,7 +111,20 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do {:ok, data} = MRF.describe() data - |> Map.merge(%{quarantined_instances: quarantined}) + |> Map.put( + :quarantined_instances, + Enum.map(quarantined, fn {instance, _reason} -> instance end) + ) + # This is for backwards compatibility. We originally didn't sent + # extra info like a reason why an instance was rejected/quarantined/etc. + # Because we didn't want to break backwards compatibility it was decided + # to add an extra "info" key. + |> Map.put(:quarantined_instances_info, %{ + "quarantined_instances" => + quarantined + |> Enum.map(fn {instance, reason} -> {instance, %{"reason" => reason}} end) + |> Map.new() + }) else %{} end diff --git a/lib/pleroma/web/mastodon_api/views/list_view.ex b/lib/pleroma/web/mastodon_api/views/list_view.ex index 931e77769..a7ae7c5f7 100644 --- a/lib/pleroma/web/mastodon_api/views/list_view.ex +++ b/lib/pleroma/web/mastodon_api/views/list_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ListView do diff --git a/lib/pleroma/web/mastodon_api/views/marker_view.ex b/lib/pleroma/web/mastodon_api/views/marker_view.ex index 0c1880935..944769b1a 100644 --- a/lib/pleroma/web/mastodon_api/views/marker_view.ex +++ b/lib/pleroma/web/mastodon_api/views/marker_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MarkerView do diff --git a/lib/pleroma/web/mastodon_api/views/media_view.ex b/lib/pleroma/web/mastodon_api/views/media_view.ex index cf521887e..4db72de8f 100644 --- a/lib/pleroma/web/mastodon_api/views/media_view.ex +++ b/lib/pleroma/web/mastodon_api/views/media_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MediaView do diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex index df9bedfed..b5b5b2376 100644 --- a/lib/pleroma/web/mastodon_api/views/notification_view.ex +++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.NotificationView do @@ -19,7 +19,11 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView - @parent_types ~w{Like Announce EmojiReact} + defp object_id_for(%{data: %{"object" => %{"id" => id}}}) when is_binary(id), do: id + + defp object_id_for(%{data: %{"object" => id}}) when is_binary(id), do: id + + @parent_types ~w{Like Announce EmojiReact Update} def render("index.json", %{notifications: notifications, for: reading_user} = opts) do activities = Enum.map(notifications, & &1.activity) @@ -30,7 +34,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do %{data: %{"type" => type}} -> type in @parent_types end) - |> Enum.map(& &1.data["object"]) + |> Enum.map(&object_id_for/1) |> Activity.create_by_object_ap_id() |> Activity.with_preloaded_object(:left) |> Pleroma.Repo.all() @@ -78,9 +82,9 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do parent_activity_fn = fn -> if opts[:parent_activities] do - Activity.Queries.find_by_object_ap_id(opts[:parent_activities], activity.data["object"]) + Activity.Queries.find_by_object_ap_id(opts[:parent_activities], object_id_for(activity)) else - Activity.get_create_by_object_ap_id(activity.data["object"]) + Activity.get_create_by_object_ap_id(object_id_for(activity)) end end @@ -109,9 +113,15 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do "reblog" -> put_status(response, parent_activity_fn.(), reading_user, status_render_opts) + "update" -> + put_status(response, parent_activity_fn.(), reading_user, status_render_opts) + "move" -> put_target(response, activity, reading_user, %{}) + "poll" -> + put_status(response, activity, reading_user, status_render_opts) + "pleroma:emoji_reaction" -> response |> put_status(parent_activity_fn.(), reading_user, status_render_opts) diff --git a/lib/pleroma/web/mastodon_api/views/poll_view.ex b/lib/pleroma/web/mastodon_api/views/poll_view.ex index 71bc8b949..34e23873e 100644 --- a/lib/pleroma/web/mastodon_api/views/poll_view.ex +++ b/lib/pleroma/web/mastodon_api/views/poll_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.PollView do diff --git a/lib/pleroma/web/mastodon_api/views/report_view.ex b/lib/pleroma/web/mastodon_api/views/report_view.ex index 0ff347ade..983f7bd24 100644 --- a/lib/pleroma/web/mastodon_api/views/report_view.ex +++ b/lib/pleroma/web/mastodon_api/views/report_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ReportView do diff --git a/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex b/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex index 453221f41..772d22f0c 100644 --- a/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex +++ b/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ScheduledActivityView do diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 463f34198..0a8c98b44 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.StatusView do @@ -57,11 +57,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do end) end - defp get_context_id(%{data: %{"context_id" => context_id}}) when not is_nil(context_id), - do: context_id - - defp get_context_id(%{data: %{"context" => context}}) when is_binary(context), - do: Utils.context_to_conversation_id(context) + # DEPRECATED This field seems to be a left-over from the StatusNet era. + # If your application uses `pleroma.conversation_id`: this field is deprecated. + # It is currently stubbed instead by doing a CRC32 of the context, and + # clearing the MSB to avoid overflow exceptions with signed integers on the + # different clients using this field (Java/Kotlin code, mostly; see Husky.) + # This should be removed in a future version of Pleroma. Pleroma-FE currently + # depends on this field, as well. + defp get_context_id(%{data: %{"context" => context}}) when is_binary(context) do + import Bitwise + + :erlang.crc32(context) + |> band(bnot(0x8000_0000)) + end defp get_context_id(_), do: nil @@ -258,10 +266,30 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do created_at = Utils.to_masto_date(object.data["published"]) + edited_at = + with %{"updated" => updated} <- object.data, + date <- Utils.to_masto_date(updated), + true <- date != "" do + date + else + _ -> + nil + end + reply_to = get_reply_to(activity, opts) reply_to_user = reply_to && CommonAPI.get_user(reply_to.data["actor"]) + history_len = + 1 + + (Object.Updater.history_for(object.data) + |> Map.get("orderedItems") + |> length()) + + # See render("history.json", ...) for more details + # Here the implicit index of the current content is 0 + chrono_order = history_len - 1 + content = object |> render_content() @@ -271,14 +299,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do |> Activity.HTML.get_cached_scrubbed_html_for_activity( User.html_filter_policy(opts[:for]), activity, - "mastoapi:content" + "mastoapi:content:#{chrono_order}" ) content_plaintext = content |> Activity.HTML.get_cached_stripped_html_for_activity( activity, - "mastoapi:content" + "mastoapi:content:#{chrono_order}" ) summary = object.data["summary"] || "" @@ -344,8 +372,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do reblog: nil, card: card, content: content_html, - text: opts[:with_source] && object.data["source"], + text: opts[:with_source] && get_source_text(object.data["source"]), created_at: created_at, + edited_at: edited_at, reblogs_count: announcement_count, replies_count: object.data["repliesCount"] || 0, favourites_count: like_count, @@ -367,6 +396,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do pleroma: %{ local: activity.local, conversation_id: get_context_id(activity), + context: object.data["context"], in_reply_to_account_acct: reply_to_user && reply_to_user.nickname, content: %{"text/plain" => content_plaintext}, spoiler_text: %{"text/plain" => summary}, @@ -384,6 +414,100 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do nil end + def render("history.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do + object = Object.normalize(activity, fetch: false) + + hashtags = Object.hashtags(object) + + user = CommonAPI.get_user(activity.data["actor"]) + + past_history = + Object.Updater.history_for(object.data) + |> Map.get("orderedItems") + |> Enum.map(&Map.put(&1, "id", object.data["id"])) + |> Enum.map(&%Object{data: &1, id: object.id}) + + history = + [object | past_history] + # Mastodon expects the original to be at the first + |> Enum.reverse() + |> Enum.with_index() + |> Enum.map(fn {object, chrono_order} -> + %{ + # The history is prepended every time there is a new edit. + # In chrono_order, the oldest item is always at 0, and so on. + # The chrono_order is an invariant kept between edits. + chrono_order: chrono_order, + object: object + } + end) + + individual_opts = + opts + |> Map.put(:as, :item) + |> Map.put(:user, user) + |> Map.put(:hashtags, hashtags) + + render_many(history, StatusView, "history_item.json", individual_opts) + end + + def render( + "history_item.json", + %{ + activity: activity, + user: user, + item: %{object: object, chrono_order: chrono_order}, + hashtags: hashtags + } = opts + ) do + sensitive = object.data["sensitive"] || Enum.member?(hashtags, "nsfw") + + attachment_data = object.data["attachment"] || [] + attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment) + + created_at = Utils.to_masto_date(object.data["updated"] || object.data["published"]) + + content = + object + |> render_content() + + content_html = + content + |> Activity.HTML.get_cached_scrubbed_html_for_activity( + User.html_filter_policy(opts[:for]), + activity, + "mastoapi:content:#{chrono_order}" + ) + + summary = object.data["summary"] || "" + + %{ + account: + AccountView.render("show.json", %{ + user: user, + for: opts[:for] + }), + content: content_html, + sensitive: sensitive, + spoiler_text: summary, + created_at: created_at, + media_attachments: attachments, + emojis: build_emojis(object.data["emoji"]), + poll: render(PollView, "show.json", object: object, for: opts[:for]) + } + end + + def render("source.json", %{activity: %{data: %{"object" => _object}} = activity} = _opts) do + object = Object.normalize(activity, fetch: false) + + %{ + id: activity.id, + text: get_source_text(Map.get(object.data, "source", "")), + spoiler_text: Map.get(object.data, "summary", ""), + content_type: get_source_content_type(object.data["source"]) + } + end + def render("card.json", %{rich_media: rich_media, page_url: page_url}) do page_url_data = URI.parse(page_url) @@ -436,10 +560,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do true -> "unknown" end - <<hash_id::signed-32, _rest::binary>> = :crypto.hash(:md5, href) + attachment_id = + with {_, ap_id} when is_binary(ap_id) <- {:ap_id, attachment["id"]}, + {_, %Object{data: _object_data, id: object_id}} <- + {:object, Object.get_by_ap_id(ap_id)} do + to_string(object_id) + else + _ -> + <<hash_id::signed-32, _rest::binary>> = :crypto.hash(:md5, href) + to_string(attachment["id"] || hash_id) + end %{ - id: to_string(attachment["id"] || hash_id), + id: attachment_id, url: href, remote_url: href, preview_url: href_preview, @@ -601,4 +734,24 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do end defp build_image_url(_, _), do: nil + + defp get_source_text(%{"content" => content} = _source) do + content + end + + defp get_source_text(source) when is_binary(source) do + source + end + + defp get_source_text(_) do + "" + end + + defp get_source_content_type(%{"mediaType" => type} = _source) do + type + end + + defp get_source_content_type(_source) do + Utils.get_content_type(nil) + end end diff --git a/lib/pleroma/web/mastodon_api/views/subscription_view.ex b/lib/pleroma/web/mastodon_api/views/subscription_view.ex index a07d23512..baa1e03bd 100644 --- a/lib/pleroma/web/mastodon_api/views/subscription_view.ex +++ b/lib/pleroma/web/mastodon_api/views/subscription_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.SubscriptionView do diff --git a/lib/pleroma/web/mastodon_api/views/suggestion_view.ex b/lib/pleroma/web/mastodon_api/views/suggestion_view.ex new file mode 100644 index 000000000..d3df4ef3f --- /dev/null +++ b/lib/pleroma/web/mastodon_api/views/suggestion_view.ex @@ -0,0 +1,28 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.SuggestionView do + use Pleroma.Web, :view + alias Pleroma.Web.MastodonAPI.AccountView + + @source_types [:staff, :global, :past_interactions] + + def render("index.json", %{users: users} = opts) do + Enum.map(users, fn user -> + opts = + opts + |> Map.put(:user, user) + |> Map.delete(:users) + + render("show.json", opts) + end) + end + + def render("show.json", %{source: source, user: _user} = opts) when source in @source_types do + %{ + source: source, + account: AccountView.render("show.json", opts) + } + end +end diff --git a/lib/pleroma/web/mastodon_api/views/timeline_view.ex b/lib/pleroma/web/mastodon_api/views/timeline_view.ex index 91226d78e..702eb7eeb 100644 --- a/lib/pleroma/web/mastodon_api/views/timeline_view.ex +++ b/lib/pleroma/web/mastodon_api/views/timeline_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.TimelineView do diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index 930e9eb29..88444106d 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do @@ -50,9 +50,7 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do def websocket_init(state) do Logger.debug( - "#{__MODULE__} accepted websocket connection for user #{ - (state.user || %{id: "anonymous"}).id - }, topic #{state.topic}" + "#{__MODULE__} accepted websocket connection for user #{(state.user || %{id: "anonymous"}).id}, topic #{state.topic}" ) Streamer.add_socket(state.topic, state.oauth_token) @@ -111,9 +109,7 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do def terminate(reason, _req, state) do Logger.debug( - "#{__MODULE__} terminating websocket connection for user #{ - (state.user || %{id: "anonymous"}).id - }, topic #{state.topic || "?"}: #{inspect(reason)}" + "#{__MODULE__} terminating websocket connection for user #{(state.user || %{id: "anonymous"}).id}, topic #{state.topic || "?"}: #{inspect(reason)}" ) Streamer.remove_socket(state.topic) diff --git a/lib/pleroma/web/media_proxy.ex b/lib/pleroma/web/media_proxy.ex index 0b232f14b..d64760fc2 100644 --- a/lib/pleroma/web/media_proxy.ex +++ b/lib/pleroma/web/media_proxy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MediaProxy do diff --git a/lib/pleroma/web/media_proxy/invalidation.ex b/lib/pleroma/web/media_proxy/invalidation.ex index cb2db5ce9..ea927fe41 100644 --- a/lib/pleroma/web/media_proxy/invalidation.ex +++ b/lib/pleroma/web/media_proxy/invalidation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MediaProxy.Invalidation do diff --git a/lib/pleroma/web/media_proxy/invalidation/http.ex b/lib/pleroma/web/media_proxy/invalidation/http.ex index 0b2a45518..28ea74991 100644 --- a/lib/pleroma/web/media_proxy/invalidation/http.ex +++ b/lib/pleroma/web/media_proxy/invalidation/http.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MediaProxy.Invalidation.Http do diff --git a/lib/pleroma/web/media_proxy/invalidation/script.ex b/lib/pleroma/web/media_proxy/invalidation/script.ex index 87a21166c..784178f1c 100644 --- a/lib/pleroma/web/media_proxy/invalidation/script.ex +++ b/lib/pleroma/web/media_proxy/invalidation/script.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MediaProxy.Invalidation.Script do diff --git a/lib/pleroma/web/media_proxy/media_proxy_controller.ex b/lib/pleroma/web/media_proxy/media_proxy_controller.ex index c74eaaf93..d2ad62c13 100644 --- a/lib/pleroma/web/media_proxy/media_proxy_controller.ex +++ b/lib/pleroma/web/media_proxy/media_proxy_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MediaProxy.MediaProxyController do @@ -54,7 +54,7 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do media_proxy_url = MediaProxy.url(url) with {:ok, %{status: status} = head_response} when status in 200..299 <- - Pleroma.HTTP.request("head", media_proxy_url, [], [], pool: :media) do + Pleroma.HTTP.request("HEAD", media_proxy_url, [], [], pool: :media) do content_type = Tesla.get_header(head_response, "content-type") content_length = Tesla.get_header(head_response, "content-length") content_length = content_length && String.to_integer(content_length) diff --git a/lib/pleroma/web/metadata.ex b/lib/pleroma/web/metadata.ex index 46ef00c08..59d018730 100644 --- a/lib/pleroma/web/metadata.ex +++ b/lib/pleroma/web/metadata.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata do diff --git a/lib/pleroma/web/metadata/player_view.ex b/lib/pleroma/web/metadata/player_view.ex index 9be5e433d..59c56a236 100644 --- a/lib/pleroma/web/metadata/player_view.ex +++ b/lib/pleroma/web/metadata/player_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.PlayerView do diff --git a/lib/pleroma/web/metadata/providers/feed.ex b/lib/pleroma/web/metadata/providers/feed.ex index d0ab5c19e..e97d6a54f 100644 --- a/lib/pleroma/web/metadata/providers/feed.ex +++ b/lib/pleroma/web/metadata/providers/feed.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.Feed do diff --git a/lib/pleroma/web/metadata/providers/open_graph.ex b/lib/pleroma/web/metadata/providers/open_graph.ex index df0cca74a..97d3865ed 100644 --- a/lib/pleroma/web/metadata/providers/open_graph.ex +++ b/lib/pleroma/web/metadata/providers/open_graph.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.OpenGraph do diff --git a/lib/pleroma/web/metadata/providers/provider.ex b/lib/pleroma/web/metadata/providers/provider.ex index c91d87c6d..bb31c4d83 100644 --- a/lib/pleroma/web/metadata/providers/provider.ex +++ b/lib/pleroma/web/metadata/providers/provider.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.Provider do diff --git a/lib/pleroma/web/metadata/providers/rel_me.ex b/lib/pleroma/web/metadata/providers/rel_me.ex index f013def51..f0bee85c8 100644 --- a/lib/pleroma/web/metadata/providers/rel_me.ex +++ b/lib/pleroma/web/metadata/providers/rel_me.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.RelMe do diff --git a/lib/pleroma/web/metadata/providers/restrict_indexing.ex b/lib/pleroma/web/metadata/providers/restrict_indexing.ex index aa6511610..a43a7c92e 100644 --- a/lib/pleroma/web/metadata/providers/restrict_indexing.ex +++ b/lib/pleroma/web/metadata/providers/restrict_indexing.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.RestrictIndexing do diff --git a/lib/pleroma/web/metadata/providers/twitter_card.ex b/lib/pleroma/web/metadata/providers/twitter_card.ex index 79183df86..2dac22ee2 100644 --- a/lib/pleroma/web/metadata/providers/twitter_card.ex +++ b/lib/pleroma/web/metadata/providers/twitter_card.ex @@ -1,6 +1,6 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.TwitterCard do @@ -20,12 +20,12 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do [ title_tag(user), - {:meta, [property: "twitter:description", content: scrubbed_content], []} + {:meta, [name: "twitter:description", content: scrubbed_content], []} ] ++ if attachments == [] or Metadata.activity_nsfw?(object) do [ image_tag(user), - {:meta, [property: "twitter:card", content: "summary"], []} + {:meta, [name: "twitter:card", content: "summary"], []} ] else attachments @@ -37,20 +37,19 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do with truncated_bio = Utils.scrub_html_and_truncate(user.bio) do [ title_tag(user), - {:meta, [property: "twitter:description", content: truncated_bio], []}, + {:meta, [name: "twitter:description", content: truncated_bio], []}, image_tag(user), - {:meta, [property: "twitter:card", content: "summary"], []} + {:meta, [name: "twitter:card", content: "summary"], []} ] end end defp title_tag(user) do - {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []} + {:meta, [name: "twitter:title", content: Utils.user_name_string(user)], []} end def image_tag(user) do - {:meta, [property: "twitter:image", content: MediaProxy.preview_url(User.avatar_url(user))], - []} + {:meta, [name: "twitter:image", content: MediaProxy.preview_url(User.avatar_url(user))], []} end defp build_attachments(id, %{data: %{"attachment" => attachments}}) do @@ -60,10 +59,10 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do case Utils.fetch_media_type(@media_types, url["mediaType"]) do "audio" -> [ - {:meta, [property: "twitter:card", content: "player"], []}, - {:meta, [property: "twitter:player:width", content: "480"], []}, - {:meta, [property: "twitter:player:height", content: "80"], []}, - {:meta, [property: "twitter:player", content: player_url(id)], []} + {:meta, [name: "twitter:card", content: "player"], []}, + {:meta, [name: "twitter:player:width", content: "480"], []}, + {:meta, [name: "twitter:player:height", content: "80"], []}, + {:meta, [name: "twitter:player", content: player_url(id)], []} | acc ] @@ -74,10 +73,10 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do # workaround. "image" -> [ - {:meta, [property: "twitter:card", content: "summary_large_image"], []}, + {:meta, [name: "twitter:card", content: "summary_large_image"], []}, {:meta, [ - property: "twitter:player", + name: "twitter:player", content: MediaProxy.url(url["href"]) ], []} | acc @@ -90,14 +89,14 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do width = url["width"] || 480 [ - {:meta, [property: "twitter:card", content: "player"], []}, - {:meta, [property: "twitter:player", content: player_url(id)], []}, - {:meta, [property: "twitter:player:width", content: "#{width}"], []}, - {:meta, [property: "twitter:player:height", content: "#{height}"], []}, - {:meta, [property: "twitter:player:stream", content: MediaProxy.url(url["href"])], + {:meta, [name: "twitter:card", content: "player"], []}, + {:meta, [name: "twitter:player", content: player_url(id)], []}, + {:meta, [name: "twitter:player:width", content: "#{width}"], []}, + {:meta, [name: "twitter:player:height", content: "#{height}"], []}, + {:meta, [name: "twitter:player:stream", content: MediaProxy.url(url["href"])], []}, - {:meta, - [property: "twitter:player:stream:content_type", content: url["mediaType"]], []} + {:meta, [name: "twitter:player:stream:content_type", content: url["mediaType"]], + []} | acc ] @@ -123,8 +122,8 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do !is_nil(url["height"]) && !is_nil(url["width"]) -> metadata ++ [ - {:meta, [property: "twitter:player:width", content: "#{url["width"]}"], []}, - {:meta, [property: "twitter:player:height", content: "#{url["height"]}"], []} + {:meta, [name: "twitter:player:width", content: "#{url["width"]}"], []}, + {:meta, [name: "twitter:player:height", content: "#{url["height"]}"], []} ] true -> diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex index caca42934..15414a988 100644 --- a/lib/pleroma/web/metadata/utils.ex +++ b/lib/pleroma/web/metadata/utils.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Utils do @@ -8,8 +8,8 @@ defmodule Pleroma.Web.Metadata.Utils do alias Pleroma.Formatter alias Pleroma.HTML - def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do - content + defp scrub_html_and_truncate_object_field(field, object) do + field # html content comes from DB already encoded, decode first and scrub after |> HtmlEntities.decode() |> String.replace(~r/<br\s?\/?>/, " ") @@ -19,6 +19,17 @@ defmodule Pleroma.Web.Metadata.Utils do |> Formatter.truncate() end + def scrub_html_and_truncate(%{data: %{"summary" => summary}} = object) + when is_binary(summary) and summary != "" do + summary + |> scrub_html_and_truncate_object_field(object) + end + + def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do + content + |> scrub_html_and_truncate_object_field(object) + end + def scrub_html_and_truncate(content, max_length \\ 200) when is_binary(content) do content |> scrub_html diff --git a/lib/pleroma/web/mongoose_im/mongoose_im_controller.ex b/lib/pleroma/web/mongoose_im/mongoose_im_controller.ex index 6ace3e0b5..0945ebb12 100644 --- a/lib/pleroma/web/mongoose_im/mongoose_im_controller.ex +++ b/lib/pleroma/web/mongoose_im/mongoose_im_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MongooseIM.MongooseIMController do diff --git a/lib/pleroma/web/nodeinfo/nodeinfo.ex b/lib/pleroma/web/nodeinfo/nodeinfo.ex index 6a0112d2a..9e27ac26c 100644 --- a/lib/pleroma/web/nodeinfo/nodeinfo.ex +++ b/lib/pleroma/web/nodeinfo/nodeinfo.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Nodeinfo.Nodeinfo do @@ -35,7 +35,9 @@ defmodule Pleroma.Web.Nodeinfo.Nodeinfo do openRegistrations: Config.get([:instance, :registrations_open]), usage: %{ users: %{ - total: Map.get(stats, :user_count, 0) + total: Map.get(stats, :user_count, 0), + activeMonth: Pleroma.User.active_user_count(30), + activeHalfyear: Pleroma.User.active_user_count(180) }, localPosts: Map.get(stats, :status_count, 0) }, @@ -47,6 +49,10 @@ defmodule Pleroma.Web.Nodeinfo.Nodeinfo do enabled: false }, staffAccounts: staff_accounts, + roles: %{ + admin: Config.get([:instance, :admin_privileges]), + moderator: Config.get([:instance, :moderator_privileges]) + }, federation: federation, pollLimits: Config.get([:instance, :poll_limits]), postFormats: Config.get([:instance, :allowed_post_formats]), diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex index 69ec27ba0..85c2393ff 100644 --- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex +++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Nodeinfo.NodeinfoController do diff --git a/lib/pleroma/web/o_auth.ex b/lib/pleroma/web/o_auth.ex index 3bc1a6ad4..d8c68df66 100644 --- a/lib/pleroma/web/o_auth.ex +++ b/lib/pleroma/web/o_auth.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth do diff --git a/lib/pleroma/web/o_auth/app.ex b/lib/pleroma/web/o_auth/app.ex index 382750010..0aa655381 100644 --- a/lib/pleroma/web/o_auth/app.ex +++ b/lib/pleroma/web/o_auth/app.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.App do @@ -7,6 +7,7 @@ defmodule Pleroma.Web.OAuth.App do import Ecto.Changeset import Ecto.Query alias Pleroma.Repo + alias Pleroma.User @type t :: %__MODULE__{} @@ -19,6 +20,8 @@ defmodule Pleroma.Web.OAuth.App do field(:client_secret, :string) field(:trusted, :boolean, default: false) + belongs_to(:user, User, type: FlakeId.Ecto.CompatType) + has_many(:oauth_authorizations, Pleroma.Web.OAuth.Authorization, on_delete: :delete_all) has_many(:oauth_tokens, Pleroma.Web.OAuth.Token, on_delete: :delete_all) @@ -27,7 +30,7 @@ defmodule Pleroma.Web.OAuth.App do @spec changeset(t(), map()) :: Ecto.Changeset.t() def changeset(struct, params) do - cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted]) + cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted, :user_id]) end @spec register_changeset(t(), map()) :: Ecto.Changeset.t() @@ -129,6 +132,12 @@ defmodule Pleroma.Web.OAuth.App do {:ok, Repo.all(query), count} end + @spec get_user_apps(User.t()) :: {:ok, [t()], non_neg_integer()} + def get_user_apps(%User{id: user_id}) do + from(a in __MODULE__, where: a.user_id == ^user_id) + |> Repo.all() + end + @spec destroy(pos_integer()) :: {:ok, t()} | {:error, Ecto.Changeset.t()} def destroy(id) do with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do diff --git a/lib/pleroma/web/o_auth/authorization.ex b/lib/pleroma/web/o_auth/authorization.ex index e0ecb0f4f..593d2d66f 100644 --- a/lib/pleroma/web/o_auth/authorization.ex +++ b/lib/pleroma/web/o_auth/authorization.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Authorization do diff --git a/lib/pleroma/web/o_auth/fallback_controller.ex b/lib/pleroma/web/o_auth/fallback_controller.ex index df68cbfc1..684a52c81 100644 --- a/lib/pleroma/web/o_auth/fallback_controller.ex +++ b/lib/pleroma/web/o_auth/fallback_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.FallbackController do diff --git a/lib/pleroma/web/o_auth/mfa_controller.ex b/lib/pleroma/web/o_auth/mfa_controller.ex index b38b00213..c4bc4a879 100644 --- a/lib/pleroma/web/o_auth/mfa_controller.ex +++ b/lib/pleroma/web/o_auth/mfa_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.MFAController do diff --git a/lib/pleroma/web/o_auth/mfa_view.ex b/lib/pleroma/web/o_auth/mfa_view.ex index 3d473f29c..bcadafe57 100644 --- a/lib/pleroma/web/o_auth/mfa_view.ex +++ b/lib/pleroma/web/o_auth/mfa_view.ex @@ -1,11 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.MFAView do use Pleroma.Web, :view import Phoenix.HTML.Form alias Pleroma.MFA + alias Pleroma.Web.Gettext def render("mfa_response.json", %{token: token, user: user}) do %{ diff --git a/lib/pleroma/web/o_auth/o_auth_controller.ex b/lib/pleroma/web/o_auth/o_auth_controller.ex index 247d8399c..c1fb4f378 100644 --- a/lib/pleroma/web/o_auth/o_auth_controller.ex +++ b/lib/pleroma/web/o_auth/o_auth_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.OAuthController do @@ -597,9 +597,6 @@ defmodule Pleroma.Web.OAuth.OAuthController do end end - # Special case: Local MastodonFE - defp redirect_uri(%Plug.Conn{} = conn, "."), do: Routes.auth_url(conn, :login) - defp redirect_uri(%Plug.Conn{}, redirect_uri), do: redirect_uri defp get_session_registration_id(%Plug.Conn{} = conn), do: get_session(conn, :registration_id) diff --git a/lib/pleroma/web/o_auth/o_auth_view.ex b/lib/pleroma/web/o_auth/o_auth_view.ex index 1419c96a2..108102ce2 100644 --- a/lib/pleroma/web/o_auth/o_auth_view.ex +++ b/lib/pleroma/web/o_auth/o_auth_view.ex @@ -1,10 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.OAuthView do use Pleroma.Web, :view import Phoenix.HTML.Form + import Phoenix.HTML + alias Pleroma.Web.Gettext alias Pleroma.Web.OAuth.Token.Utils diff --git a/lib/pleroma/web/o_auth/scopes.ex b/lib/pleroma/web/o_auth/scopes.ex index ada43eae9..f33fc21c4 100644 --- a/lib/pleroma/web/o_auth/scopes.ex +++ b/lib/pleroma/web/o_auth/scopes.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Scopes do diff --git a/lib/pleroma/web/o_auth/token.ex b/lib/pleroma/web/o_auth/token.ex index 9d69e9db4..26de7bb10 100644 --- a/lib/pleroma/web/o_auth/token.ex +++ b/lib/pleroma/web/o_auth/token.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Token do diff --git a/lib/pleroma/web/o_auth/token/query.ex b/lib/pleroma/web/o_auth/token/query.ex index d16a759d8..4a4d2d3ef 100644 --- a/lib/pleroma/web/o_auth/token/query.ex +++ b/lib/pleroma/web/o_auth/token/query.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Token.Query do diff --git a/lib/pleroma/web/o_auth/token/strategy/refresh_token.ex b/lib/pleroma/web/o_auth/token/strategy/refresh_token.ex index f5a0ed272..6b0d9503c 100644 --- a/lib/pleroma/web/o_auth/token/strategy/refresh_token.ex +++ b/lib/pleroma/web/o_auth/token/strategy/refresh_token.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Token.Strategy.RefreshToken do diff --git a/lib/pleroma/web/o_auth/token/strategy/revoke.ex b/lib/pleroma/web/o_auth/token/strategy/revoke.ex index de99bc137..3b265b339 100644 --- a/lib/pleroma/web/o_auth/token/strategy/revoke.ex +++ b/lib/pleroma/web/o_auth/token/strategy/revoke.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Token.Strategy.Revoke do diff --git a/lib/pleroma/web/o_auth/token/utils.ex b/lib/pleroma/web/o_auth/token/utils.ex index b572dc9cf..773cd9792 100644 --- a/lib/pleroma/web/o_auth/token/utils.ex +++ b/lib/pleroma/web/o_auth/token/utils.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Token.Utils do diff --git a/lib/pleroma/web/o_status/o_status_controller.ex b/lib/pleroma/web/o_status/o_status_controller.ex index da3264149..ea4994bd0 100644 --- a/lib/pleroma/web/o_status/o_status_controller.ex +++ b/lib/pleroma/web/o_status/o_status_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OStatus.OStatusController do diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex index 8e4d3e7f7..591391b60 100644 --- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex @@ -1,12 +1,17 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.AccountController do use Pleroma.Web, :controller import Pleroma.Web.ControllerHelper, - only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2] + only: [ + json_response: 3, + add_link_headers: 2, + embed_relationships?: 1, + assign_account_by_id: 2 + ] alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub @@ -40,9 +45,23 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do %{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites ) + plug( + OAuthScopesPlug, + %{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]} + when action == :endorsements + ) + + plug( + OAuthScopesPlug, + %{scopes: ["read:accounts"]} when action == :birthdays + ) + plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend) - plug(:assign_account_by_id when action in [:favourites, :subscribe, :unsubscribe]) + plug( + :assign_account_by_id + when action in [:favourites, :endorsements, :subscribe, :unsubscribe] + ) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAccountOperation @@ -90,6 +109,22 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do ) end + @doc "GET /api/v1/pleroma/accounts/:id/endorsements" + def endorsements(%{assigns: %{user: for_user, account: user}} = conn, params) do + users = + user + |> User.endorsed_users_relation(_restrict_deactivated = true) + |> Pleroma.Repo.all() + + conn + |> render("index.json", + for: for_user, + users: users, + as: :user, + embed_relationships: embed_relationships?(params) + ) + end + @doc "POST /api/v1/pleroma/accounts/:id/subscribe" def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do with {:ok, _subscription} <- User.subscribe(user, subscription_target) do @@ -107,4 +142,18 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do {:error, message} -> json_response(conn, :forbidden, %{error: message}) end end + + @doc "GET /api/v1/pleroma/birthdays" + def birthdays(%{assigns: %{user: %User{} = user}} = conn, %{day: day, month: month} = _params) do + birthdays = + User.get_friends_birthdays_query(user, day, month) + |> Pleroma.Repo.all() + + conn + |> render("index.json", + for: user, + users: birthdays, + as: :user + ) + end end diff --git a/lib/pleroma/web/pleroma_api/controllers/app_controller.ex b/lib/pleroma/web/pleroma_api/controllers/app_controller.ex new file mode 100644 index 000000000..3e84f75a4 --- /dev/null +++ b/lib/pleroma/web/pleroma_api/controllers/app_controller.ex @@ -0,0 +1,23 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.AppController do + use Pleroma.Web, :controller + + alias Pleroma.Web.OAuth.App + alias Pleroma.Web.Plugs.OAuthScopesPlug + + plug(OAuthScopesPlug, %{scopes: ["follow", "read"]} when action in [:index]) + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAppOperation + + @doc "GET /api/v1/pleroma/apps" + def index(%{assigns: %{user: user}} = conn, _params) do + with apps <- App.get_user_apps(user) do + render(conn, "index.json", %{apps: apps}) + end + end +end diff --git a/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex b/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex index fc5d16771..b9daed22b 100644 --- a/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.BackupController do @@ -9,7 +9,7 @@ defmodule Pleroma.Web.PleromaAPI.BackupController do alias Pleroma.Web.Plugs.OAuthScopesPlug action_fallback(Pleroma.Web.MastodonAPI.FallbackController) - plug(OAuthScopesPlug, %{scopes: ["read:accounts"]} when action in [:index, :create]) + plug(OAuthScopesPlug, %{scopes: ["read:backups"]} when action in [:index, :create]) plug(Pleroma.Web.ApiSpec.CastAndValidate) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaBackupOperation diff --git a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex index dcd54b1af..3d7b6a4a7 100644 --- a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ChatController do use Pleroma.Web, :controller @@ -151,7 +151,9 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do index_query(user, params) |> Pagination.fetch_paginated(params) - render(conn, "index.json", chats: chats) + conn + |> add_link_headers(chats) + |> render("index.json", chats: chats) end defp index_query(%{id: user_id} = user, params) do diff --git a/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex b/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex index be2f4617d..37990db54 100644 --- a/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ConversationController do diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex index 204e81311..f854cf9c1 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.EmojiFileController do diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex index 1ea44f347..420fea12c 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.EmojiPackController do diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex index da5f2474f..78fd0b219 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do diff --git a/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex b/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex index 01424c6ba..6257e3153 100644 --- a/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.InstancesController do diff --git a/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex b/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex index 429ef5112..66e9d8481 100644 --- a/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.MascotController do diff --git a/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex b/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex index bcb3a9ae1..87ea81cef 100644 --- a/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.NotificationController do diff --git a/lib/pleroma/web/pleroma_api/controllers/report_controller.ex b/lib/pleroma/web/pleroma_api/controllers/report_controller.ex index d93d7570a..1f0a82cee 100644 --- a/lib/pleroma/web/pleroma_api/controllers/report_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/report_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ReportController do diff --git a/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex b/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex index ca26d80ef..bf6dc500c 100644 --- a/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ScrobbleController do diff --git a/lib/pleroma/web/pleroma_api/controllers/settings_controller.ex b/lib/pleroma/web/pleroma_api/controllers/settings_controller.ex new file mode 100644 index 000000000..1136575b6 --- /dev/null +++ b/lib/pleroma/web/pleroma_api/controllers/settings_controller.ex @@ -0,0 +1,79 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.SettingsController do + use Pleroma.Web, :controller + + alias Pleroma.Web.Plugs.OAuthScopesPlug + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + plug( + OAuthScopesPlug, + %{scopes: ["write:accounts"]} when action in [:update] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["read:accounts"]} when action in [:show] + ) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaSettingsOperation + + @doc "GET /api/v1/pleroma/settings/:app" + def show(%{assigns: %{user: user}} = conn, %{app: app} = _params) do + conn + |> json(get_settings(user, app)) + end + + @doc "PATCH /api/v1/pleroma/settings/:app" + def update(%{assigns: %{user: user}, body_params: body_params} = conn, %{app: app} = _params) do + settings = + get_settings(user, app) + |> merge_recursively(body_params) + + with changeset <- + Pleroma.User.update_changeset( + user, + %{pleroma_settings_store: %{app => settings}} + ), + {:ok, _} <- Pleroma.Repo.update(changeset) do + conn + |> json(settings) + end + end + + defp merge_recursively(old, %{} = new) do + old = ensure_object(old) + + Enum.reduce( + new, + old, + fn + {k, nil}, acc -> + Map.drop(acc, [k]) + + {k, %{} = new_child}, acc -> + Map.put(acc, k, merge_recursively(acc[k], new_child)) + + {k, v}, acc -> + Map.put(acc, k, v) + end + ) + end + + defp get_settings(user, app) do + user.pleroma_settings_store + |> Map.get(app, %{}) + |> ensure_object() + end + + defp ensure_object(%{} = object) do + object + end + + defp ensure_object(_) do + %{} + end +end diff --git a/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex b/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex index 3940ad581..e69847b00 100644 --- a/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationController do diff --git a/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex b/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex index 078d470d9..90428a532 100644 --- a/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.UserImportController do diff --git a/lib/pleroma/web/pleroma_api/views/account_view.ex b/lib/pleroma/web/pleroma_api/views/account_view.ex index 28941f471..910bcee16 100644 --- a/lib/pleroma/web/pleroma_api/views/account_view.ex +++ b/lib/pleroma/web/pleroma_api/views/account_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.AccountView do diff --git a/lib/pleroma/web/pleroma_api/views/app_view.ex b/lib/pleroma/web/pleroma_api/views/app_view.ex new file mode 100644 index 000000000..1fdc3c224 --- /dev/null +++ b/lib/pleroma/web/pleroma_api/views/app_view.ex @@ -0,0 +1,11 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.AppView do + use Pleroma.Web, :view + + def render("index.json", %{apps: apps}) do + render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json") + end +end diff --git a/lib/pleroma/web/pleroma_api/views/backup_view.ex b/lib/pleroma/web/pleroma_api/views/backup_view.ex index 944600c86..d778590f0 100644 --- a/lib/pleroma/web/pleroma_api/views/backup_view.ex +++ b/lib/pleroma/web/pleroma_api/views/backup_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.BackupView do diff --git a/lib/pleroma/web/pleroma_api/views/chat/message_reference_view.ex b/lib/pleroma/web/pleroma_api/views/chat/message_reference_view.ex index 2e4355992..241bf0010 100644 --- a/lib/pleroma/web/pleroma_api/views/chat/message_reference_view.ex +++ b/lib/pleroma/web/pleroma_api/views/chat/message_reference_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do diff --git a/lib/pleroma/web/pleroma_api/views/chat_view.ex b/lib/pleroma/web/pleroma_api/views/chat_view.ex index 3794818a7..db6c13c05 100644 --- a/lib/pleroma/web/pleroma_api/views/chat_view.ex +++ b/lib/pleroma/web/pleroma_api/views/chat_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ChatView do diff --git a/lib/pleroma/web/pleroma_api/views/conversation_view.ex b/lib/pleroma/web/pleroma_api/views/conversation_view.ex index 173006360..2c9c8d1c6 100644 --- a/lib/pleroma/web/pleroma_api/views/conversation_view.ex +++ b/lib/pleroma/web/pleroma_api/views/conversation_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ConversationView do diff --git a/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex b/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex index c94527e6d..68ebd8292 100644 --- a/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex +++ b/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.EmojiReactionView do diff --git a/lib/pleroma/web/pleroma_api/views/notification_view.ex b/lib/pleroma/web/pleroma_api/views/notification_view.ex index 36b2fdfe8..db7f94803 100644 --- a/lib/pleroma/web/pleroma_api/views/notification_view.ex +++ b/lib/pleroma/web/pleroma_api/views/notification_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.NotificationView do diff --git a/lib/pleroma/web/pleroma_api/views/report_view.ex b/lib/pleroma/web/pleroma_api/views/report_view.ex index a0b3f085c..127e8f0cb 100644 --- a/lib/pleroma/web/pleroma_api/views/report_view.ex +++ b/lib/pleroma/web/pleroma_api/views/report_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ReportView do diff --git a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex index 2bc069529..a5985fb2a 100644 --- a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex +++ b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ScrobbleView do diff --git a/lib/pleroma/web/plug.ex b/lib/pleroma/web/plug.ex index dffad3a06..29ab52e55 100644 --- a/lib/pleroma/web/plug.ex +++ b/lib/pleroma/web/plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plug do diff --git a/lib/pleroma/web/plugs/admin_secret_authentication_plug.ex b/lib/pleroma/web/plugs/admin_secret_authentication_plug.ex index 976e5cd92..2e4702fa1 100644 --- a/lib/pleroma/web/plugs/admin_secret_authentication_plug.ex +++ b/lib/pleroma/web/plugs/admin_secret_authentication_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlug do diff --git a/lib/pleroma/web/plugs/authentication_plug.ex b/lib/pleroma/web/plugs/authentication_plug.ex index 8d58169cf..a7fd697b5 100644 --- a/lib/pleroma/web/plugs/authentication_plug.ex +++ b/lib/pleroma/web/plugs/authentication_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.AuthenticationPlug do diff --git a/lib/pleroma/web/plugs/basic_auth_decoder_plug.ex b/lib/pleroma/web/plugs/basic_auth_decoder_plug.ex index 397f26de5..3eb13f955 100644 --- a/lib/pleroma/web/plugs/basic_auth_decoder_plug.ex +++ b/lib/pleroma/web/plugs/basic_auth_decoder_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.BasicAuthDecoderPlug do diff --git a/lib/pleroma/web/plugs/cache.ex b/lib/pleroma/web/plugs/cache.ex index 935b2d834..667477857 100644 --- a/lib/pleroma/web/plugs/cache.ex +++ b/lib/pleroma/web/plugs/cache.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.Cache do diff --git a/lib/pleroma/web/plugs/digest_plug.ex b/lib/pleroma/web/plugs/digest_plug.ex index d72f8073c..20e265f8f 100644 --- a/lib/pleroma/web/plugs/digest_plug.ex +++ b/lib/pleroma/web/plugs/digest_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.DigestPlug do diff --git a/lib/pleroma/web/plugs/ensure_authenticated_plug.ex b/lib/pleroma/web/plugs/ensure_authenticated_plug.ex index 31e7410d6..8044a6bb2 100644 --- a/lib/pleroma/web/plugs/ensure_authenticated_plug.ex +++ b/lib/pleroma/web/plugs/ensure_authenticated_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.EnsureAuthenticatedPlug do diff --git a/lib/pleroma/web/plugs/ensure_privileged_plug.ex b/lib/pleroma/web/plugs/ensure_privileged_plug.ex new file mode 100644 index 000000000..f886c87ea --- /dev/null +++ b/lib/pleroma/web/plugs/ensure_privileged_plug.ex @@ -0,0 +1,44 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.EnsurePrivilegedPlug do + @moduledoc """ + Ensures staff are privileged enough to do certain tasks. + """ + import Pleroma.Web.TranslationHelpers + import Plug.Conn + + alias Pleroma.Config + alias Pleroma.User + + def init(options) do + options + end + + def call(%{assigns: %{user: %User{is_admin: false, is_moderator: false}}} = conn, _) do + conn + |> render_error(:forbidden, "User isn't privileged.") + |> halt() + end + + def call( + %{assigns: %{user: %User{is_admin: is_admin, is_moderator: is_moderator}}} = conn, + privilege + ) do + if (is_admin and privilege in Config.get([:instance, :admin_privileges])) or + (is_moderator and privilege in Config.get([:instance, :moderator_privileges])) do + conn + else + conn + |> render_error(:forbidden, "User isn't privileged.") + |> halt() + end + end + + def call(conn, _) do + conn + |> render_error(:forbidden, "User isn't privileged.") + |> halt() + end +end diff --git a/lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex b/lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex index 8a8532f41..e98a3b661 100644 --- a/lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex +++ b/lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug do diff --git a/lib/pleroma/web/plugs/ensure_user_token_assigns_plug.ex b/lib/pleroma/web/plugs/ensure_user_token_assigns_plug.ex index 534b0cff1..5c57d2707 100644 --- a/lib/pleroma/web/plugs/ensure_user_token_assigns_plug.ex +++ b/lib/pleroma/web/plugs/ensure_user_token_assigns_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.EnsureUserTokenAssignsPlug do diff --git a/lib/pleroma/web/plugs/expect_authenticated_check_plug.ex b/lib/pleroma/web/plugs/expect_authenticated_check_plug.ex index f09cffe95..d1403e2bd 100644 --- a/lib/pleroma/web/plugs/expect_authenticated_check_plug.ex +++ b/lib/pleroma/web/plugs/expect_authenticated_check_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug do diff --git a/lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex b/lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex index e227d5150..a74c76395 100644 --- a/lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex +++ b/lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.ExpectPublicOrAuthenticatedCheckPlug do diff --git a/lib/pleroma/web/plugs/federating_plug.ex b/lib/pleroma/web/plugs/federating_plug.ex index eeef7e45b..d5b8ef3ed 100644 --- a/lib/pleroma/web/plugs/federating_plug.ex +++ b/lib/pleroma/web/plugs/federating_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.FederatingPlug do diff --git a/lib/pleroma/web/plugs/frontend_static.ex b/lib/pleroma/web/plugs/frontend_static.ex index ebe7eaf86..6ab8e4667 100644 --- a/lib/pleroma/web/plugs/frontend_static.ex +++ b/lib/pleroma/web/plugs/frontend_static.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.FrontendStatic do diff --git a/lib/pleroma/web/plugs/http_security_plug.ex b/lib/pleroma/web/plugs/http_security_plug.ex index d1e6cc9d3..34895c8d5 100644 --- a/lib/pleroma/web/plugs/http_security_plug.ex +++ b/lib/pleroma/web/plugs/http_security_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do @@ -68,7 +68,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do ] } - [{"reply-to", Jason.encode!(report_group)} | headers] + [{"report-to", Jason.encode!(report_group)} | headers] else headers end @@ -117,7 +117,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do if Config.get(:env) == :dev do "script-src 'self' 'unsafe-eval'" else - "script-src 'self'" + "script-src 'self' 'wasm-unsafe-eval'" end report = if report_uri, do: ["report-uri ", report_uri, ";report-to csp-endpoint"] diff --git a/lib/pleroma/web/plugs/http_signature_plug.ex b/lib/pleroma/web/plugs/http_signature_plug.ex index 0f7550516..4bf325218 100644 --- a/lib/pleroma/web/plugs/http_signature_plug.ex +++ b/lib/pleroma/web/plugs/http_signature_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do @@ -25,21 +25,58 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do end end + defp validate_signature(conn, request_target) do + # Newer drafts for HTTP signatures now use @request-target instead of the + # old (request-target). We'll now support both for incoming signatures. + conn = + conn + |> put_req_header("(request-target)", request_target) + |> put_req_header("@request-target", request_target) + + HTTPSignatures.validate_conn(conn) + end + + defp validate_signature(conn) do + # This (request-target) is non-standard, but many implementations do it + # this way due to a misinterpretation of + # https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-06 + # "path" was interpreted as not having the query, though later examples + # show that it must be the absolute path + query. This behavior is kept to + # make sure most software (Pleroma itself, Mastodon, and probably others) + # do not break. + request_target = String.downcase("#{conn.method}") <> " #{conn.request_path}" + + # This is the proper way to build the @request-target, as expected by + # many HTTP signature libraries, clarified in the following draft: + # https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-11.html#section-2.2.6 + # It is the same as before, but containing the query part as well. + proper_target = request_target <> "?#{conn.query_string}" + + cond do + # Normal, non-standard behavior but expected by Pleroma and more. + validate_signature(conn, request_target) -> + true + + # Has query string and the previous one failed: let's try the standard. + conn.query_string != "" -> + validate_signature(conn, proper_target) + + # If there's no query string and signature fails, it's rotten. + true -> + false + end + end + defp maybe_assign_valid_signature(conn) do if has_signature_header?(conn) do - # set (request-target) header to the appropriate value - # we also replace the digest header with the one we computed - request_target = String.downcase("#{conn.method}") <> " #{conn.request_path}" - + # we replace the digest header with the one we computed in DigestPlug conn = - conn - |> put_req_header("(request-target)", request_target) - |> case do + case conn do %{assigns: %{digest: digest}} = conn -> put_req_header(conn, "digest", digest) conn -> conn end - assign(conn, :valid_signature, HTTPSignatures.validate_conn(conn)) + assign(conn, :valid_signature, validate_signature(conn)) else Logger.debug("No signature header!") conn diff --git a/lib/pleroma/web/plugs/idempotency_plug.ex b/lib/pleroma/web/plugs/idempotency_plug.ex index 9ac8f3647..a3b7af869 100644 --- a/lib/pleroma/web/plugs/idempotency_plug.ex +++ b/lib/pleroma/web/plugs/idempotency_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.IdempotencyPlug do diff --git a/lib/pleroma/web/plugs/instance_static.ex b/lib/pleroma/web/plugs/instance_static.ex index 723b25679..75bfdd65b 100644 --- a/lib/pleroma/web/plugs/instance_static.ex +++ b/lib/pleroma/web/plugs/instance_static.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.InstanceStatic do diff --git a/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex b/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex index 58cb0316a..c6d531086 100644 --- a/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex +++ b/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do diff --git a/lib/pleroma/web/plugs/o_auth_plug.ex b/lib/pleroma/web/plugs/o_auth_plug.ex index 5e06ac3f6..ba04ddb72 100644 --- a/lib/pleroma/web/plugs/o_auth_plug.ex +++ b/lib/pleroma/web/plugs/o_auth_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.OAuthPlug do @@ -47,15 +47,17 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do # @spec fetch_user_and_token(String.t()) :: {:ok, User.t(), Token.t()} | nil defp fetch_user_and_token(token) do - query = + token_query = from(t in Token, - where: t.token == ^token, - join: user in assoc(t, :user), - preload: [user: user] + where: t.token == ^token ) - with %Token{user: user} = token_record <- Repo.one(query) do + with %Token{user_id: user_id} = token_record <- Repo.one(token_query), + false <- is_nil(user_id), + %User{} = user <- User.get_cached_by_id(user_id) do {:ok, user, token_record} + else + _ -> nil end end diff --git a/lib/pleroma/web/plugs/o_auth_scopes_plug.ex b/lib/pleroma/web/plugs/o_auth_scopes_plug.ex index f017c8bc7..faf0fd8c6 100644 --- a/lib/pleroma/web/plugs/o_auth_scopes_plug.ex +++ b/lib/pleroma/web/plugs/o_auth_scopes_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.OAuthScopesPlug do diff --git a/lib/pleroma/web/plugs/plug_helper.ex b/lib/pleroma/web/plugs/plug_helper.ex index d73021bf7..21bf03523 100644 --- a/lib/pleroma/web/plugs/plug_helper.ex +++ b/lib/pleroma/web/plugs/plug_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.PlugHelper do diff --git a/lib/pleroma/web/plugs/rate_limiter.ex b/lib/pleroma/web/plugs/rate_limiter.ex index 5bebe0ad5..2080b06bd 100644 --- a/lib/pleroma/web/plugs/rate_limiter.ex +++ b/lib/pleroma/web/plugs/rate_limiter.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.RateLimiter do diff --git a/lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex b/lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex index 3db59bf17..a96be670a 100644 --- a/lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex +++ b/lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.RateLimiter.LimiterSupervisor do diff --git a/lib/pleroma/web/plugs/rate_limiter/supervisor.ex b/lib/pleroma/web/plugs/rate_limiter/supervisor.ex index 0dc2aa71b..f00f3d95e 100644 --- a/lib/pleroma/web/plugs/rate_limiter/supervisor.ex +++ b/lib/pleroma/web/plugs/rate_limiter/supervisor.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.RateLimiter.Supervisor do diff --git a/lib/pleroma/web/plugs/remote_ip.ex b/lib/pleroma/web/plugs/remote_ip.ex index 4d7daca56..f207d9fef 100644 --- a/lib/pleroma/web/plugs/remote_ip.ex +++ b/lib/pleroma/web/plugs/remote_ip.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.RemoteIp do diff --git a/lib/pleroma/web/plugs/set_format_plug.ex b/lib/pleroma/web/plugs/set_format_plug.ex index 7ef88f305..84c67e7f0 100644 --- a/lib/pleroma/web/plugs/set_format_plug.ex +++ b/lib/pleroma/web/plugs/set_format_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.SetFormatPlug do diff --git a/lib/pleroma/web/plugs/set_locale_plug.ex b/lib/pleroma/web/plugs/set_locale_plug.ex index d77191cff..271912ace 100644 --- a/lib/pleroma/web/plugs/set_locale_plug.ex +++ b/lib/pleroma/web/plugs/set_locale_plug.ex @@ -1,23 +1,61 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only # NOTE: this module is based on https://github.com/smeevil/set_locale defmodule Pleroma.Web.Plugs.SetLocalePlug do import Plug.Conn, only: [get_req_header: 2, assign: 3] + def frontend_language_cookie_name, do: "userLanguage" + def init(_), do: nil def call(conn, _) do - locale = get_locale_from_header(conn) || Gettext.get_locale() - Gettext.put_locale(locale) - assign(conn, :locale, locale) + locales = get_locales_from_header(conn) + first_locale = Enum.at(locales, 0, Gettext.get_locale()) + + Pleroma.Web.Gettext.put_locales(locales) + + conn + |> assign(:locale, first_locale) + |> assign(:locales, locales) end - defp get_locale_from_header(conn) do + defp get_locales_from_header(conn) do conn - |> extract_accept_language() - |> Enum.find(&supported_locale?/1) + |> extract_preferred_language() + |> normalize_language_codes() + |> all_supported() + |> Enum.uniq() + end + + defp all_supported(locales) do + locales + |> Pleroma.Web.Gettext.ensure_fallbacks() + |> Enum.filter(&supported_locale?/1) + end + + defp normalize_language_codes(codes) do + codes + |> Enum.map(fn code -> Pleroma.Web.Gettext.normalize_locale(code) end) + end + + defp extract_preferred_language(conn) do + extract_frontend_language(conn) ++ extract_accept_language(conn) + end + + defp extract_frontend_language(conn) do + %{req_cookies: cookies} = + conn + |> Plug.Conn.fetch_cookies() + + case cookies[frontend_language_cookie_name()] do + nil -> + [] + + fe_lang -> + String.split(fe_lang, ",") + end end defp extract_accept_language(conn) do @@ -29,7 +67,6 @@ defmodule Pleroma.Web.Plugs.SetLocalePlug do |> Enum.sort(&(&1.quality > &2.quality)) |> Enum.map(& &1.tag) |> Enum.reject(&is_nil/1) - |> ensure_language_fallbacks() _ -> [] @@ -37,9 +74,7 @@ defmodule Pleroma.Web.Plugs.SetLocalePlug do end defp supported_locale?(locale) do - Pleroma.Web.Gettext - |> Gettext.known_locales() - |> Enum.member?(locale) + Pleroma.Web.Gettext.supports_locale?(locale) end defp parse_language_option(string) do @@ -53,11 +88,4 @@ defmodule Pleroma.Web.Plugs.SetLocalePlug do %{tag: captures["tag"], quality: quality} end - - defp ensure_language_fallbacks(tags) do - Enum.flat_map(tags, fn tag -> - [language | _] = String.split(tag, "-") - if Enum.member?(tags, language), do: [tag], else: [tag, language] - end) - end end diff --git a/lib/pleroma/web/plugs/set_user_session_id_plug.ex b/lib/pleroma/web/plugs/set_user_session_id_plug.ex index a1cfa0915..c37214573 100644 --- a/lib/pleroma/web/plugs/set_user_session_id_plug.ex +++ b/lib/pleroma/web/plugs/set_user_session_id_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.SetUserSessionIdPlug do diff --git a/lib/pleroma/web/plugs/static_fe_plug.ex b/lib/pleroma/web/plugs/static_fe_plug.ex index 9ba9dc5ff..9a364fdbc 100644 --- a/lib/pleroma/web/plugs/static_fe_plug.ex +++ b/lib/pleroma/web/plugs/static_fe_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.StaticFEPlug do diff --git a/lib/pleroma/web/plugs/trailing_format_plug.ex b/lib/pleroma/web/plugs/trailing_format_plug.ex index c5069ae0e..a883ba541 100644 --- a/lib/pleroma/web/plugs/trailing_format_plug.ex +++ b/lib/pleroma/web/plugs/trailing_format_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.TrailingFormatPlug do diff --git a/lib/pleroma/web/plugs/uploaded_media.ex b/lib/pleroma/web/plugs/uploaded_media.ex index 2378e98d2..ad8143234 100644 --- a/lib/pleroma/web/plugs/uploaded_media.ex +++ b/lib/pleroma/web/plugs/uploaded_media.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.UploadedMedia do diff --git a/lib/pleroma/web/plugs/user_enabled_plug.ex b/lib/pleroma/web/plugs/user_enabled_plug.ex index 1142a8dbc..ca104166a 100644 --- a/lib/pleroma/web/plugs/user_enabled_plug.ex +++ b/lib/pleroma/web/plugs/user_enabled_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.UserEnabledPlug do diff --git a/lib/pleroma/web/plugs/user_fetcher_plug.ex b/lib/pleroma/web/plugs/user_fetcher_plug.ex index 707df9bfd..87bc27393 100644 --- a/lib/pleroma/web/plugs/user_fetcher_plug.ex +++ b/lib/pleroma/web/plugs/user_fetcher_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.UserFetcherPlug do diff --git a/lib/pleroma/web/plugs/user_is_admin_plug.ex b/lib/pleroma/web/plugs/user_is_admin_plug.ex index 7649912ba..548eb9b9a 100644 --- a/lib/pleroma/web/plugs/user_is_admin_plug.ex +++ b/lib/pleroma/web/plugs/user_is_admin_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.UserIsAdminPlug do diff --git a/lib/pleroma/web/plugs/user_is_staff_plug.ex b/lib/pleroma/web/plugs/user_is_staff_plug.ex new file mode 100644 index 000000000..951e14674 --- /dev/null +++ b/lib/pleroma/web/plugs/user_is_staff_plug.ex @@ -0,0 +1,23 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.UserIsStaffPlug do + import Pleroma.Web.TranslationHelpers + import Plug.Conn + + alias Pleroma.User + + def init(options) do + options + end + + def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _), do: conn + def call(%{assigns: %{user: %User{is_moderator: true}}} = conn, _), do: conn + + def call(conn, _) do + conn + |> render_error(:forbidden, "User is not a staff member.") + |> halt() + end +end diff --git a/lib/pleroma/web/plugs/user_tracking_plug.ex b/lib/pleroma/web/plugs/user_tracking_plug.ex index c9a988f00..9b52fd505 100644 --- a/lib/pleroma/web/plugs/user_tracking_plug.ex +++ b/lib/pleroma/web/plugs/user_tracking_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.UserTrackingPlug do diff --git a/lib/pleroma/web/preload.ex b/lib/pleroma/web/preload.ex index e8588bcc9..4485383f9 100644 --- a/lib/pleroma/web/preload.ex +++ b/lib/pleroma/web/preload.ex @@ -1,10 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Preload do alias Phoenix.HTML - require Logger def build_tags(_conn, params) do preload_data = diff --git a/lib/pleroma/web/preload/providers/instance.ex b/lib/pleroma/web/preload/providers/instance.ex index eb0254c74..6183f7b70 100644 --- a/lib/pleroma/web/preload/providers/instance.ex +++ b/lib/pleroma/web/preload/providers/instance.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Preload.Providers.Instance do diff --git a/lib/pleroma/web/preload/providers/provider.ex b/lib/pleroma/web/preload/providers/provider.ex index 60f304f2c..85e2badaa 100644 --- a/lib/pleroma/web/preload/providers/provider.ex +++ b/lib/pleroma/web/preload/providers/provider.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Preload.Providers.Provider do diff --git a/lib/pleroma/web/preload/providers/timelines.ex b/lib/pleroma/web/preload/providers/timelines.ex index c1704ccdc..1031f48d2 100644 --- a/lib/pleroma/web/preload/providers/timelines.ex +++ b/lib/pleroma/web/preload/providers/timelines.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Preload.Providers.Timelines do diff --git a/lib/pleroma/web/preload/providers/user.ex b/lib/pleroma/web/preload/providers/user.ex index 504f79ba0..74550cd56 100644 --- a/lib/pleroma/web/preload/providers/user.ex +++ b/lib/pleroma/web/preload/providers/user.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Preload.Providers.User do diff --git a/lib/pleroma/web/push.ex b/lib/pleroma/web/push.ex index 154dae614..9665b0b4a 100644 --- a/lib/pleroma/web/push.ex +++ b/lib/pleroma/web/push.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Push do diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index 83cbdc870..3c5f00764 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Push.Impl do @@ -16,7 +16,7 @@ defmodule Pleroma.Web.Push.Impl do require Logger import Ecto.Query - @types ["Create", "Follow", "Announce", "Like", "Move", "EmojiReact"] + @types ["Create", "Follow", "Announce", "Like", "Move", "EmojiReact", "Update"] @doc "Performs sending notifications for user subscriptions" @spec perform(Notification.t()) :: list(any) | :error | {:error, :unknown_type} @@ -124,8 +124,8 @@ defmodule Pleroma.Web.Push.Impl do def format_body(activity, actor, object, mastodon_type \\ nil) - def format_body(_activity, actor, %{data: %{"type" => "ChatMessage", "content" => content}}, _) do - case content do + def format_body(_activity, actor, %{data: %{"type" => "ChatMessage"} = data}, _) do + case data["content"] do nil -> "@#{actor.nickname}: (Attachment)" content -> "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}" end @@ -174,6 +174,15 @@ defmodule Pleroma.Web.Push.Impl do end end + def format_body( + %{activity: %{data: %{"type" => "Update"}}}, + actor, + _object, + _mastodon_type + ) do + "@#{actor.nickname} edited a status" + end + def format_title(activity, mastodon_type \\ nil) def format_title(%{activity: %{data: %{"directMessage" => true}}}, _mastodon_type) do @@ -187,6 +196,7 @@ defmodule Pleroma.Web.Push.Impl do "follow_request" -> "New Follow Request" "reblog" -> "New Repeat" "favourite" -> "New Favorite" + "update" -> "New Update" "pleroma:chat_mention" -> "New Chat Message" "pleroma:emoji_reaction" -> "New Reaction" type -> "New #{String.capitalize(type || "event")}" diff --git a/lib/pleroma/web/push/subscription.ex b/lib/pleroma/web/push/subscription.ex index 4f6c9bc9f..6fc45bd61 100644 --- a/lib/pleroma/web/push/subscription.ex +++ b/lib/pleroma/web/push/subscription.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Push.Subscription do @@ -26,7 +26,7 @@ defmodule Pleroma.Web.Push.Subscription do end # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength - @supported_alert_types ~w[follow favourite mention reblog pleroma:chat_mention pleroma:emoji_reaction]a + @supported_alert_types ~w[follow favourite mention reblog poll pleroma:chat_mention pleroma:emoji_reaction]a defp alerts(%{data: %{alerts: alerts}}) do alerts = Map.take(alerts, @supported_alert_types) diff --git a/lib/pleroma/web/rel_me.ex b/lib/pleroma/web/rel_me.ex index 7e745d07e..98fbc1c59 100644 --- a/lib/pleroma/web/rel_me.ex +++ b/lib/pleroma/web/rel_me.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RelMe do diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex index 566fc8c8a..0488df30e 100644 --- a/lib/pleroma/web/rich_media/helpers.ex +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright _ 2017-2020 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Helpers do diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex index d6b54943b..dbe81eabb 100644 --- a/lib/pleroma/web/rich_media/parser.ex +++ b/lib/pleroma/web/rich_media/parser.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parser do diff --git a/lib/pleroma/web/rich_media/parser/ttl.ex b/lib/pleroma/web/rich_media/parser/ttl.ex index 0b7f14fb2..59d7f87ab 100644 --- a/lib/pleroma/web/rich_media/parser/ttl.ex +++ b/lib/pleroma/web/rich_media/parser/ttl.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parser.TTL do diff --git a/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex b/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex index c7eb267f3..fa41c160d 100644 --- a/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex +++ b/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl do diff --git a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex index 31c3d1e33..320a5f515 100644 --- a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex +++ b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do diff --git a/lib/pleroma/web/rich_media/parsers/o_embed.ex b/lib/pleroma/web/rich_media/parsers/o_embed.ex index 09eabec56..75318d9c7 100644 --- a/lib/pleroma/web/rich_media/parsers/o_embed.ex +++ b/lib/pleroma/web/rich_media/parsers/o_embed.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do diff --git a/lib/pleroma/web/rich_media/parsers/ogp.ex b/lib/pleroma/web/rich_media/parsers/ogp.ex index d0edf1c88..b7f2b4216 100644 --- a/lib/pleroma/web/rich_media/parsers/ogp.ex +++ b/lib/pleroma/web/rich_media/parsers/ogp.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parsers.OGP do diff --git a/lib/pleroma/web/rich_media/parsers/twitter_card.ex b/lib/pleroma/web/rich_media/parsers/twitter_card.ex index 0adf84159..cc653729d 100644 --- a/lib/pleroma/web/rich_media/parsers/twitter_card.ex +++ b/lib/pleroma/web/rich_media/parsers/twitter_card.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parsers.TwitterCard do diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index efca7078a..ba1d64ab2 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -1,9 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Router do use Pleroma.Web, :router + import Phoenix.LiveDashboard.Router pipeline :accepts_html do plug(:accepts, ["html"]) @@ -96,14 +97,82 @@ defmodule Pleroma.Web.Router do plug(Pleroma.Web.Plugs.AdminSecretAuthenticationPlug) plug(:after_auth) plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug) - plug(Pleroma.Web.Plugs.UserIsAdminPlug) + plug(Pleroma.Web.Plugs.UserIsStaffPlug) plug(Pleroma.Web.Plugs.IdempotencyPlug) end - pipeline :mastodon_html do - plug(:browser) - plug(:authenticate) - plug(:after_auth) + pipeline :require_admin do + plug(Pleroma.Web.Plugs.UserIsAdminPlug) + end + + pipeline :require_privileged_role_users_delete do + plug(:admin_api) + plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_delete) + end + + pipeline :require_privileged_role_users_manage_credentials do + plug(:admin_api) + plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_manage_credentials) + end + + pipeline :require_privileged_role_messages_read do + plug(:admin_api) + plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :messages_read) + end + + pipeline :require_privileged_role_users_manage_tags do + plug(:admin_api) + plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_manage_tags) + end + + pipeline :require_privileged_role_users_manage_activation_state do + plug(:admin_api) + plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_manage_activation_state) + end + + pipeline :require_privileged_role_users_manage_invites do + plug(:admin_api) + plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_manage_invites) + end + + pipeline :require_privileged_role_reports_manage_reports do + plug(:admin_api) + plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :reports_manage_reports) + end + + pipeline :require_privileged_role_users_read do + plug(:admin_api) + plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_read) + end + + pipeline :require_privileged_role_messages_delete do + plug(:admin_api) + plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :messages_delete) + end + + pipeline :require_privileged_role_emoji_manage_emoji do + plug(:admin_api) + plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :emoji_manage_emoji) + end + + pipeline :require_privileged_role_instances_delete do + plug(:admin_api) + plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :instances_delete) + end + + pipeline :require_privileged_role_moderation_log_read do + plug(:admin_api) + plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :moderation_log_read) + end + + pipeline :require_privileged_role_statistics_read do + plug(:admin_api) + plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :statistics_read) + end + + pipeline :require_privileged_role_announcements_manage_announcements do + plug(:admin_api) + plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :announcements_manage_announcements) end pipeline :pleroma_html do @@ -152,6 +221,7 @@ defmodule Pleroma.Web.Router do get("/emoji", UtilController, :emoji) get("/captcha", UtilController, :captcha) get("/healthcheck", UtilController, :healthcheck) + post("/remote_interaction", UtilController, :remote_interaction) end scope "/api/v1/pleroma", Pleroma.Web do @@ -159,12 +229,9 @@ defmodule Pleroma.Web.Router do post("/uploader_callback/:upload_path", UploaderController, :callback) end + # AdminAPI: only admins can perform these actions scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do - pipe_through(:admin_api) - - put("/users/disable_mfa", AdminAPIController, :disable_mfa) - put("/users/tag", AdminAPIController, :tag_users) - delete("/users/tag", AdminAPIController, :untag_users) + pipe_through([:admin_api, :require_admin]) get("/users/:nickname/permission_group", AdminAPIController, :right_get) get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get) @@ -187,85 +254,176 @@ defmodule Pleroma.Web.Router do post("/users/follow", UserController, :follow) post("/users/unfollow", UserController, :unfollow) - delete("/users", UserController, :delete) post("/users", UserController, :create) - patch("/users/:nickname/toggle_activation", UserController, :toggle_activation) - patch("/users/activate", UserController, :activate) - patch("/users/deactivate", UserController, :deactivate) - patch("/users/approve", UserController, :approve) + + patch("/users/suggest", UserController, :suggest) + patch("/users/unsuggest", UserController, :unsuggest) get("/relay", RelayController, :index) post("/relay", RelayController, :follow) delete("/relay", RelayController, :unfollow) - post("/users/invite_token", InviteController, :create) - get("/users/invites", InviteController, :index) - post("/users/revoke_invite", InviteController, :revoke) - post("/users/email_invite", InviteController, :email) + get("/instance_document/:name", InstanceDocumentController, :show) + patch("/instance_document/:name", InstanceDocumentController, :update) + delete("/instance_document/:name", InstanceDocumentController, :delete) + + get("/config", ConfigController, :show) + post("/config", ConfigController, :update) + get("/config/descriptions", ConfigController, :descriptions) + get("/need_reboot", AdminAPIController, :need_reboot) + get("/restart", AdminAPIController, :restart) + + get("/oauth_app", OAuthAppController, :index) + post("/oauth_app", OAuthAppController, :create) + patch("/oauth_app/:id", OAuthAppController, :update) + delete("/oauth_app/:id", OAuthAppController, :delete) + + get("/media_proxy_caches", MediaProxyCacheController, :index) + post("/media_proxy_caches/delete", MediaProxyCacheController, :delete) + post("/media_proxy_caches/purge", MediaProxyCacheController, :purge) + + get("/frontends", FrontendController, :index) + post("/frontends/install", FrontendController, :install) + + post("/backups", AdminAPIController, :create_backup) + end + + # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role) + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through(:require_privileged_role_announcements_manage_announcements) + + get("/announcements", AnnouncementController, :index) + post("/announcements", AnnouncementController, :create) + get("/announcements/:id", AnnouncementController, :show) + patch("/announcements/:id", AnnouncementController, :change) + delete("/announcements/:id", AnnouncementController, :delete) + end + + # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role) + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through(:require_privileged_role_users_delete) + + delete("/users", UserController, :delete) + end + + # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role) + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through(:require_privileged_role_users_manage_credentials) get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset) - patch("/users/force_password_reset", AdminAPIController, :force_password_reset) get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials) patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials) + put("/users/disable_mfa", AdminAPIController, :disable_mfa) + patch("/users/force_password_reset", AdminAPIController, :force_password_reset) + patch("/users/confirm_email", AdminAPIController, :confirm_email) + patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email) + end + + # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role) + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through(:require_privileged_role_messages_read) - get("/users", UserController, :index) - get("/users/:nickname", UserController, :show) get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses) get("/users/:nickname/chats", AdminAPIController, :list_user_chats) - get("/instances/:instance/statuses", AdminAPIController, :list_instance_statuses) + get("/statuses", StatusController, :index) - get("/instance_document/:name", InstanceDocumentController, :show) - patch("/instance_document/:name", InstanceDocumentController, :update) - delete("/instance_document/:name", InstanceDocumentController, :delete) + get("/chats/:id", ChatController, :show) + get("/chats/:id/messages", ChatController, :messages) - patch("/users/confirm_email", AdminAPIController, :confirm_email) - patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email) + get("/instances/:instance/statuses", InstanceController, :list_statuses) + + get("/statuses/:id", StatusController, :show) + end + + # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role) + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through(:require_privileged_role_users_manage_tags) + + put("/users/tag", AdminAPIController, :tag_users) + delete("/users/tag", AdminAPIController, :untag_users) + end + + # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role) + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through(:require_privileged_role_users_manage_activation_state) + + patch("/users/:nickname/toggle_activation", UserController, :toggle_activation) + patch("/users/activate", UserController, :activate) + patch("/users/deactivate", UserController, :deactivate) + end + + # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role) + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through(:require_privileged_role_users_manage_invites) + + patch("/users/approve", UserController, :approve) + post("/users/invite_token", InviteController, :create) + get("/users/invites", InviteController, :index) + post("/users/revoke_invite", InviteController, :revoke) + post("/users/email_invite", InviteController, :email) + end + + # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role) + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through(:require_privileged_role_reports_manage_reports) get("/reports", ReportController, :index) get("/reports/:id", ReportController, :show) patch("/reports", ReportController, :update) post("/reports/:id/notes", ReportController, :notes_create) delete("/reports/:report_id/notes/:id", ReportController, :notes_delete) + end + + # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role) + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through(:require_privileged_role_users_read) + + get("/users", UserController, :index) + get("/users/:nickname", UserController, :show) + end + + # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role) + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through(:require_privileged_role_messages_delete) - get("/statuses/:id", StatusController, :show) put("/statuses/:id", StatusController, :update) delete("/statuses/:id", StatusController, :delete) - get("/statuses", StatusController, :index) - get("/config", ConfigController, :show) - post("/config", ConfigController, :update) - get("/config/descriptions", ConfigController, :descriptions) - get("/need_reboot", AdminAPIController, :need_reboot) - get("/restart", AdminAPIController, :restart) + delete("/chats/:id/messages/:message_id", ChatController, :delete_message) + end - get("/moderation_log", AdminAPIController, :list_log) + # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role) + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through(:require_privileged_role_emoji_manage_emoji) post("/reload_emoji", AdminAPIController, :reload_emoji) - get("/stats", AdminAPIController, :stats) + end - get("/oauth_app", OAuthAppController, :index) - post("/oauth_app", OAuthAppController, :create) - patch("/oauth_app/:id", OAuthAppController, :update) - delete("/oauth_app/:id", OAuthAppController, :delete) + # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role) + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through(:require_privileged_role_instances_delete) - get("/media_proxy_caches", MediaProxyCacheController, :index) - post("/media_proxy_caches/delete", MediaProxyCacheController, :delete) - post("/media_proxy_caches/purge", MediaProxyCacheController, :purge) + delete("/instances/:instance", InstanceController, :delete) + end - get("/chats/:id", ChatController, :show) - get("/chats/:id/messages", ChatController, :messages) - delete("/chats/:id/messages/:message_id", ChatController, :delete_message) + # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role) + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through(:require_privileged_role_moderation_log_read) - get("/frontends", FrontendController, :index) - post("/frontends/install", FrontendController, :install) + get("/moderation_log", AdminAPIController, :list_log) + end - post("/backups", AdminAPIController, :create_backup) + # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role) + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do + pipe_through(:require_privileged_role_statistics_read) + + get("/stats", AdminAPIController, :stats) end scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do scope "/pack" do - pipe_through(:admin_api) + pipe_through(:require_privileged_role_emoji_manage_emoji) post("/", EmojiPackController, :create) patch("/", EmojiPackController, :update) @@ -280,7 +438,7 @@ defmodule Pleroma.Web.Router do # Modifying packs scope "/packs" do - pipe_through(:admin_api) + pipe_through(:require_privileged_role_emoji_manage_emoji) get("/import", EmojiPackController, :import_from_filesystem) get("/remote", EmojiPackController, :remote) @@ -304,6 +462,7 @@ defmodule Pleroma.Web.Router do pipe_through(:pleroma_html) post("/main/ostatus", UtilController, :remote_subscribe) + get("/main/ostatus", UtilController, :show_subscribe_form) get("/ostatus_subscribe", RemoteFollowController, :follow) post("/ostatus_subscribe", RemoteFollowController, :do_follow) end @@ -316,6 +475,11 @@ defmodule Pleroma.Web.Router do post("/delete_account", UtilController, :delete_account) put("/notification_settings", UtilController, :update_notificaton_settings) post("/disable_account", UtilController, :disable_account) + post("/move_account", UtilController, :move_account) + + put("/aliases", UtilController, :add_alias) + get("/aliases", UtilController, :list_aliases) + delete("/aliases", UtilController, :delete_alias) end scope "/api/pleroma", Pleroma.Web.PleromaAPI do @@ -368,6 +532,7 @@ defmodule Pleroma.Web.Router do scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do pipe_through(:api) + get("/apps", AppController, :index) get("/statuses/:id/reactions/:emoji", EmojiReactionController, :index) get("/statuses/:id/reactions", EmojiReactionController, :index) end @@ -412,6 +577,7 @@ defmodule Pleroma.Web.Router do scope [] do pipe_through(:api) get("/accounts/:id/favourites", AccountController, :favourites) + get("/accounts/:id/endorsements", AccountController, :endorsements) end scope [] do @@ -419,6 +585,15 @@ defmodule Pleroma.Web.Router do post("/accounts/:id/subscribe", AccountController, :subscribe) post("/accounts/:id/unsubscribe", AccountController, :unsubscribe) + + get("/birthdays", AccountController, :birthdays) + end + + scope [] do + pipe_through(:authenticated_api) + + get("/settings/:app", SettingsController, :show) + patch("/settings/:app", SettingsController, :update) end post("/accounts/confirmation_resend", AccountController, :confirmation_resend) @@ -457,6 +632,10 @@ defmodule Pleroma.Web.Router do post("/accounts/:id/unblock", AccountController, :unblock) post("/accounts/:id/mute", AccountController, :mute) post("/accounts/:id/unmute", AccountController, :unmute) + post("/accounts/:id/note", AccountController, :note) + post("/accounts/:id/pin", AccountController, :endorse) + post("/accounts/:id/unpin", AccountController, :unendorse) + post("/accounts/:id/remove_from_followers", AccountController, :remove_from_followers) get("/conversations", ConversationController, :index) post("/conversations/:id/read", ConversationController, :mark_as_read) @@ -518,6 +697,7 @@ defmodule Pleroma.Web.Router do get("/bookmarks", StatusController, :bookmarks) post("/statuses", StatusController, :create) + put("/statuses/:id", StatusController, :update) delete("/statuses/:id", StatusController, :delete) post("/statuses/:id/reblog", StatusController, :reblog) post("/statuses/:id/unreblog", StatusController, :unreblog) @@ -536,17 +716,14 @@ defmodule Pleroma.Web.Router do delete("/push/subscription", SubscriptionController, :delete) get("/suggestions", SuggestionController, :index) + delete("/suggestions/:account_id", SuggestionController, :dismiss) get("/timelines/home", TimelineController, :home) get("/timelines/direct", TimelineController, :direct) get("/timelines/list/:list_id", TimelineController, :list) - end - scope "/api/web", Pleroma.Web do - pipe_through(:authenticated_api) - - # Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere - put("/settings", MastoFEController, :put_settings) + get("/announcements", AnnouncementController, :index) + post("/announcements/:id/dismiss", AnnouncementController, :mark_read) end scope "/api/v1", Pleroma.Web.MastodonAPI do @@ -562,6 +739,8 @@ defmodule Pleroma.Web.Router do get("/accounts/search", SearchController, :account_search) get("/search", SearchController, :search) + get("/accounts/lookup", AccountController, :lookup) + get("/accounts/:id/statuses", AccountController, :statuses) get("/accounts/:id/followers", AccountController, :followers) get("/accounts/:id/following", AccountController, :following) @@ -578,6 +757,8 @@ defmodule Pleroma.Web.Router do get("/statuses/:id/card", StatusController, :card) get("/statuses/:id/favourited_by", StatusController, :favourited_by) get("/statuses/:id/reblogged_by", StatusController, :reblogged_by) + get("/statuses/:id/history", StatusController, :show_history) + get("/statuses/:id/source", StatusController, :show_source) get("/custom_emojis", CustomEmojiController, :index) @@ -587,6 +768,8 @@ defmodule Pleroma.Web.Router do get("/timelines/tag/:tag", TimelineController, :hashtag) get("/polls/:id", PollController, :show) + + get("/directory", DirectoryController, :index) end scope "/api/v2", Pleroma.Web.MastodonAPI do @@ -594,6 +777,8 @@ defmodule Pleroma.Web.Router do get("/search", SearchController, :search2) post("/media", MediaController, :create2) + + get("/suggestions", SuggestionController, :index2) end scope "/api", Pleroma.Web do @@ -747,20 +932,13 @@ defmodule Pleroma.Web.Router do scope "/", Pleroma.Web do pipe_through(:api) - get("/web/manifest.json", MastoFEController, :manifest) + get("/manifest.json", ManifestController, :show) end scope "/", Pleroma.Web do - pipe_through(:mastodon_html) - - get("/web/login", MastodonAPI.AuthController, :login) - delete("/auth/sign_out", MastodonAPI.AuthController, :logout) - - post("/auth/password", MastodonAPI.AuthController, :password_reset) - - get("/web/*path", MastoFEController, :index) + pipe_through(:pleroma_html) - get("/embed/:id", EmbedController, :show) + post("/auth/password", TwitterAPI.PasswordController, :request) end scope "/proxy/", Pleroma.Web do @@ -778,6 +956,11 @@ defmodule Pleroma.Web.Router do end end + scope "/" do + pipe_through([:pleroma_html, :authenticate, :require_admin]) + live_dashboard("/phoenix/live_dashboard") + end + # Test-only routes needed to test action dispatching and plug chain execution if Pleroma.Config.get(:env) == :test do @test_actions [ diff --git a/lib/pleroma/web/shout_channel.ex b/lib/pleroma/web/shout_channel.ex index 17caecb1a..928f0a1dd 100644 --- a/lib/pleroma/web/shout_channel.ex +++ b/lib/pleroma/web/shout_channel.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ShoutChannel do diff --git a/lib/pleroma/web/static_fe/static_fe_controller.ex b/lib/pleroma/web/static_fe/static_fe_controller.ex index 50f0927a3..97c41c6f9 100644 --- a/lib/pleroma/web/static_fe/static_fe_controller.ex +++ b/lib/pleroma/web/static_fe/static_fe_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.StaticFE.StaticFEController do diff --git a/lib/pleroma/web/static_fe/static_fe_view.ex b/lib/pleroma/web/static_fe/static_fe_view.ex index c04715337..8e23a79a3 100644 --- a/lib/pleroma/web/static_fe/static_fe_view.ex +++ b/lib/pleroma/web/static_fe/static_fe_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.StaticFE.StaticFEView do diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index 8bf70d99b..3c0da5c27 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Streamer do @@ -296,6 +296,24 @@ defmodule Pleroma.Web.Streamer do defp push_to_socket(_topic, %Activity{data: %{"type" => "Delete"}}), do: :noop + defp push_to_socket(topic, %Activity{data: %{"type" => "Update"}} = item) do + create_activity = + Pleroma.Activity.get_create_by_object_ap_id(item.object.data["id"]) + |> Map.put(:object, item.object) + + anon_render = StreamerView.render("status_update.json", create_activity) + + Registry.dispatch(@registry, topic, fn list -> + Enum.each(list, fn {pid, auth?} -> + if auth? do + send(pid, {:render_with_user, StreamerView, "status_update.json", create_activity}) + else + send(pid, {:text, anon_render}) + end + end) + end) + end + defp push_to_socket(topic, item) do anon_render = StreamerView.render("update.json", item) diff --git a/lib/pleroma/web/templates/email/digest.html.eex b/lib/pleroma/web/templates/email/digest.html.eex index 60eceff22..1efc76e1a 100644 --- a/lib/pleroma/web/templates/email/digest.html.eex +++ b/lib/pleroma/web/templates/email/digest.html.eex @@ -160,7 +160,7 @@ <div style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height: 14px; color: <%= @styling.header_color %>;"> <p style="line-height: 36px; text-align: center; margin: 0;"><span - style="font-size: 30px; color: <%= @styling.header_color %>;">Hey <%= @user.nickname %>, here is what you've missed!</span></p> + style="font-size: 30px; color: <%= @styling.header_color %>;"><%= Gettext.dpgettext("static_pages", "digest email header line", "Hey %{nickname}, here is what you've missed!", nickname: @user.nickname) %></span></p> </div> </div> <!--[if mso]></td></tr></table><![endif]--> @@ -382,7 +382,7 @@ <div style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_color %>;"> <p style="font-size: 12px; line-height: 24px; text-align: center; margin: 0;"><span - style="font-size: 20px;"><%= length(@followers) %> New Followers</span><span + style="font-size: 20px;"><%= Gettext.dpngettext("static_pages", "new followers count header", "%{count} New Follower", "%{count} New Followers", length(@followers), count: length(@followers)) %></span><span style="font-size: 20px; line-height: 24px;"></span></p> </div> </div> @@ -535,16 +535,16 @@ style="color:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;"> <p style="font-size: 12px; line-height: 16px; text-align: center; color: <%= @styling.text_color %>; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0;"> - <span style="font-size: 14px;">You have received this email because you have signed up to receive digest emails from <b><%= @instance %></b> Pleroma instance.</span></p> + <span style="font-size: 14px;"><%= raw Gettext.dpgettext("static_pages", "digest email sending reason", "You have received this email because you have signed up to receive digest emails from <b>%{instance}</b> Pleroma instance.", instance: safe_to_string(html_escape(@instance))) %></span></p> <p style="font-size: 12px; line-height: 14px; text-align: center; color: <%= @styling.text_color %>; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0;"> </p> <p style="font-size: 12px; line-height: 16px; text-align: center; color: <%= @styling.text_color %>; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0;"> - <span style="font-size: 14px;">The email address you are subscribed as is <a href="mailto:<%= @user.email %>" style="color: <%= @styling.link_color %>;text-decoration: none;"><%= @user.email %></a>. </span></p> + <span style="font-size: 14px;"><%= raw Gettext.dpgettext("static_pages", "digest email receiver address", "The email address you are subscribed as is <a href='mailto:%{@user.email}' style='color: %{color};text-decoration: none;'>%{email}</a>. ", color: safe_to_string(html_escape(@styling.link_color)), email: safe_to_string(html_escape(@user.email))) %></span></p> <p style="font-size: 12px; line-height: 16px; text-align: center; color: <%= @styling.text_color %>; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0;"> - <span style="font-size: 14px;">To unsubscribe, please go <%= link "here", style: "color: #{@styling.link_color};text-decoration: none;", to: @unsubscribe_link %>.</span></p> + <span style="font-size: 14px;"><%= raw Gettext.dpgettext("static_pages", "digest email unsubscribe action", "To unsubscribe, please go %{here}.", here: safe_to_string link(Gettext.dpgettext("static_pages", "digest email unsubscribe action link text", "here"), style: "color: #{@styling.link_color};text-decoration: none;", to: @unsubscribe_link)) %></span></p> </div> <!--[if mso]></td></tr></table><![endif]--> <!--[if (!mso)&(!IE)]><!--> diff --git a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex index 57bd92468..260338772 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex @@ -3,15 +3,15 @@ <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> <id><%= @data["id"] %></id> <title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title> - <content type="html"><%= activity_content(@data) %></content> - <published><%= @activity.data["published"] %></published> - <updated><%= @activity.data["published"] %></updated> + <content type="html"><%= activity_description(@data) %></content> + <published><%= to_rfc3339(@activity.data["published"]) %></published> + <updated><%= to_rfc3339(@activity.data["published"]) %></updated> <ostatus:conversation ref="<%= activity_context(@activity) %>"> <%= activity_context(@activity) %> </ostatus:conversation> <link href="<%= activity_context(@activity) %>" rel="ostatus:conversation"/> - <%= if @data["summary"] do %> + <%= if @data["summary"] != "" do %> <summary><%= escape(@data["summary"]) %></summary> <% end %> diff --git a/lib/pleroma/web/templates/feed/feed/_activity.rss.eex b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex index 279f2171d..5c8f35fe4 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex @@ -3,17 +3,12 @@ <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> <guid><%= @data["id"] %></guid> <title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title> - <description><%= activity_content(@data) %></description> - <pubDate><%= @activity.data["published"] %></pubDate> - <updated><%= @activity.data["published"] %></updated> + <description><%= activity_description(@data) %></description> + <pubDate><%= to_rfc2822(@activity.data["published"]) %></pubDate> <ostatus:conversation ref="<%= activity_context(@activity) %>"> <%= activity_context(@activity) %> </ostatus:conversation> - <%= if @data["summary"] do %> - <description><%= escape(@data["summary"]) %></description> - <% end %> - <%= if @activity.local do %> <link><%= @data["id"] %></link> <% else %> @@ -27,7 +22,7 @@ <% end %> <%= for attachment <- @data["attachment"] || [] do %> - <link type="<%= attachment_type(attachment) %>"><%= attachment_href(attachment) %></link> + <enclosure url="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>" /> <% end %> <%= if @data["inReplyTo"] do %> diff --git a/lib/pleroma/web/templates/feed/feed/_author.atom.eex b/lib/pleroma/web/templates/feed/feed/_author.atom.eex index 25cbffada..90be8a559 100644 --- a/lib/pleroma/web/templates/feed/feed/_author.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_author.atom.eex @@ -1,17 +1,14 @@ <author> - <id><%= @user.ap_id %></id> - <activity:object>http://activitystrea.ms/schema/1.0/person</activity:object> <uri><%= @user.ap_id %></uri> + <name><%= @user.nickname %></name> + <activity:object>http://activitystrea.ms/schema/1.0/person</activity:object> + <activity:displayName><%= @user.name %></activity:displayName> + <activity:image><%= User.avatar_url(@user) %></activity:image> + <activity:id><%= @user.ap_id %></activity:id> + <activity:published><%= to_rfc3339(@user.inserted_at) %></activity:published> + <activity:updated><%= to_rfc3339(@user.updated_at) %></activity:updated> + <activity:url><%= @user.ap_id %></activity:url> <poco:preferredUsername><%= @user.nickname %></poco:preferredUsername> <poco:displayName><%= @user.name %></poco:displayName> <poco:note><%= escape(@user.bio) %></poco:note> - <summary><%= escape(@user.bio) %></summary> - <name><%= @user.nickname %></name> - <link rel="avatar" href="<%= User.avatar_url(@user) %>"/> - <%= if User.banner_url(@user) do %> - <link rel="header" href="<%= User.banner_url(@user) %>"/> - <% end %> - <%= if @user.local do %> - <ap_enabled>true</ap_enabled> - <% end %> </author> diff --git a/lib/pleroma/web/templates/feed/feed/_author.rss.eex b/lib/pleroma/web/templates/feed/feed/_author.rss.eex index 526aeddcf..22477e6b1 100644 --- a/lib/pleroma/web/templates/feed/feed/_author.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/_author.rss.eex @@ -1,17 +1,10 @@ -<managingEditor> - <guid><%= @user.ap_id %></guid> - <activity:object>http://activitystrea.ms/schema/1.0/person</activity:object> - <uri><%= @user.ap_id %></uri> - <poco:preferredUsername><%= @user.nickname %></poco:preferredUsername> - <poco:displayName><%= @user.name %></poco:displayName> - <poco:note><%= escape(@user.bio) %></poco:note> - <description><%= escape(@user.bio) %></description> - <name><%= @user.nickname %></name> - <link rel="avatar"><%= User.avatar_url(@user) %></link> - <%= if User.banner_url(@user) do %> - <link rel="header"><%= User.banner_url(@user) %></link> - <% end %> - <%= if @user.local do %> - <ap_enabled>true</ap_enabled> - <% end %> -</managingEditor> +<managingEditor><%= "#{email(@user)} (#{escape(@user.name)})" %></managingEditor> +<activity:object>http://activitystrea.ms/schema/1.0/person</activity:object> +<activity:displayName><%= @user.name %></activity:displayName> +<activity:image><%= User.avatar_url(@user) %></activity:image> +<activity:id><%= @user.ap_id %></activity:id> +<activity:published><%= to_rfc3339(@user.inserted_at) %></activity:published> +<activity:updated><%= to_rfc3339(@user.updated_at) %></activity:updated> +<poco:preferredUsername><%= @user.nickname %></poco:preferredUsername> +<poco:displayName><%= @user.name %></poco:displayName> +<poco:note><%= escape(@user.bio) %></poco:note> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex index aa3035bca..25980c1e4 100644 --- a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex @@ -1,12 +1,22 @@ <entry> - <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> - <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> - - <%= render @view_module, "_tag_author.atom", assigns %> - - <id><%= @data["id"] %></id> - <title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title> - <content type="html"><%= activity_content(@data) %></content> + <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + + <%= render Phoenix.Controller.view_module(@conn), "_tag_author.atom", assigns %> + + <id><%= @data["id"] %></id> + <title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title> + <content type="html"><%= activity_description(@data) %></content> + <published><%= to_rfc3339(@activity.data["published"]) %></published> + <updated><%= to_rfc3339(@activity.data["published"]) %></updated> + <ostatus:conversation ref="<%= activity_context(@activity) %>"> + <%= activity_context(@activity) %> + </ostatus:conversation> + <link href="<%= activity_context(@activity) %>" rel="ostatus:conversation"/> + + <%= if @data["summary"] != "" do %> + <summary><%= @data["summary"] %></summary> + <% end %> <%= if @activity.local do %> <link type="application/atom+xml" href='<%= @data["id"] %>' rel="self"/> @@ -15,37 +25,25 @@ <link type="text/html" href='<%= @data["external_url"] %>' rel="alternate"/> <% end %> - <published><%= @activity.data["published"] %></published> - <updated><%= @activity.data["published"] %></updated> - - <ostatus:conversation ref="<%= activity_context(@activity) %>"> - <%= activity_context(@activity) %> - </ostatus:conversation> - <link href="<%= activity_context(@activity) %>" rel="ostatus:conversation"/> - - <%= if @data["summary"] do %> - <summary><%= @data["summary"] %></summary> - <% end %> - - <%= for id <- @activity.recipients do %> - <%= if id == Pleroma.Constants.as_public() do %> + <%= for id <- @activity.recipients do %> + <%= if id == Pleroma.Constants.as_public() do %> + <link rel="mentioned" + ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" + href="http://activityschema.org/collection/public"/> + <% else %> + <%= unless Regex.match?(~r/^#{Pleroma.Web.Endpoint.url()}.+followers$/, id) do %> <link rel="mentioned" - ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" - href="http://activityschema.org/collection/public"/> - <% else %> - <%= unless Regex.match?(~r/^#{Pleroma.Web.Endpoint.url()}.+followers$/, id) do %> - <link rel="mentioned" - ostatus:object-type="http://activitystrea.ms/schema/1.0/person" - href="<%= id %>" /> - <% end %> + ostatus:object-type="http://activitystrea.ms/schema/1.0/person" + href="<%= id %>" /> <% end %> <% end %> + <% end %> - <%= for tag <- Pleroma.Object.hashtags(@object) do %> - <category term="<%= tag %>"></category> - <% end %> + <%= for tag <- Pleroma.Object.hashtags(@object) do %> + <category term="<%= tag %>"></category> + <% end %> - <%= for {emoji, file} <- @data["emoji"] || %{} do %> - <link name="<%= emoji %>" rel="emoji" href="<%= file %>"/> - <% end %> + <%= for {emoji, file} <- @data["emoji"] || %{} do %> + <link name="<%= emoji %>" rel="emoji" href="<%= file %>"/> + <% end %> </entry> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex index 2334e24a2..d582c83e8 100644 --- a/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex @@ -4,9 +4,9 @@ <guid isPermalink="true"><%= activity_context(@activity) %></guid> <link><%= activity_context(@activity) %></link> - <pubDate><%= pub_date(@activity.data["published"]) %></pubDate> + <pubDate><%= to_rfc2822(@activity.data["published"]) %></pubDate> - <description><%= activity_content(@data) %></description> + <description><%= activity_description(@data) %></description> <%= for attachment <- @data["attachment"] || [] do %> <enclosure url="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>"/> <% end %> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_author.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_author.atom.eex index 997c4936e..71c696832 100644 --- a/lib/pleroma/web/templates/feed/feed/_tag_author.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_tag_author.atom.eex @@ -1,18 +1,14 @@ <author> - <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type> - <id><%= @actor.ap_id %></id> - <uri><%= @actor.ap_id %></uri> - <name><%= @actor.nickname %></name> - <summary><%= escape(@actor.bio) %></summary> - <link rel="avatar" href="<%= User.avatar_url(@actor) %>"/> - <%= if User.banner_url(@actor) do %> - <link rel="header" href="<%= User.banner_url(@actor) %>"/> - <% end %> - <%= if @actor.local do %> - <ap_enabled>true</ap_enabled> - <% end %> - - <poco:preferredUsername><%= @actor.nickname %></poco:preferredUsername> - <poco:displayName><%= @actor.name %></poco:displayName> - <poco:note><%= escape(@actor.bio) %></poco:note> + <uri><%= @actor.ap_id %></uri> + <name><%= @actor.nickname %></name> + <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type> + <activity:displayName><%= @actor.name %></activity:displayName> + <activity:image><%= User.avatar_url(@actor) %></activity:image> + <activity:id><%= @actor.ap_id %></activity:id> + <activity:published><%= to_rfc3339(@actor.inserted_at) %></activity:published> + <activity:updated><%= to_rfc3339(@actor.updated_at) %></activity:updated> + <activity:url><%= @actor.ap_id %></activity:url> + <poco:preferredUsername><%= @actor.nickname %></poco:preferredUsername> + <poco:displayName><%= @actor.name %></poco:displayName> + <poco:note><%= escape(@actor.bio) %></poco:note> </author> diff --git a/lib/pleroma/web/templates/feed/feed/tag.atom.eex b/lib/pleroma/web/templates/feed/feed/tag.atom.eex index de0731085..14b0ee594 100644 --- a/lib/pleroma/web/templates/feed/feed/tag.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/tag.atom.eex @@ -1,22 +1,20 @@ <?xml version="1.0" encoding="UTF-8"?> +<feed + xmlns="http://www.w3.org/2005/Atom" + xmlns:thr="http://purl.org/syndication/thread/1.0" + xmlns:activity="http://activitystrea.ms/spec/1.0/" + xmlns:poco="http://portablecontacts.net/spec/1.0" + xmlns:ostatus="http://ostatus.org/schema/1.0" + xmlns:statusnet="http://status.net/schema/api/1/"> -<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom" - xmlns:thr="http://purl.org/syndication/thread/1.0" - xmlns:georss="http://www.georss.org/georss" - xmlns:activity="http://activitystrea.ms/spec/1.0/" - xmlns:media="http://purl.org/syndication/atommedia" - xmlns:poco="http://portablecontacts.net/spec/1.0" - xmlns:ostatus="http://ostatus.org/schema/1.0" - xmlns:statusnet="http://status.net/schema/api/1/"> + <id><%= Routes.tag_feed_url(@conn, :feed, @tag) <> ".atom" %></id> + <title>#<%= @tag %></title> + <subtitle><%= Gettext.dpgettext("static_pages", "tag feed description", "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse.", tag: @tag) %></subtitle> + <logo><%= feed_logo() %></logo> + <updated><%= most_recent_update(@activities) %></updated> + <link rel="self" href="<%= '#{Routes.tag_feed_url(@conn, :feed, @tag)}.atom' %>" type="application/atom+xml"/> - <id><%= '#{Routes.tag_feed_url(@conn, :feed, @tag)}.rss' %></id> - <title>#<%= @tag %></title> - - <subtitle>These are public toots tagged with #<%= @tag %>. You can interact with them if you have an account anywhere in the fediverse.</subtitle> - <logo><%= feed_logo() %></logo> - <updated><%= most_recent_update(@activities) %></updated> - <link rel="self" href="<%= '#{Routes.tag_feed_url(@conn, :feed, @tag)}.atom' %>" type="application/atom+xml"/> - <%= for activity <- @activities do %> - <%= render @view_module, "_tag_activity.atom", Map.merge(assigns, prepare_activity(activity, actor: true)) %> - <% end %> + <%= for activity <- @activities do %> + <%= render Phoenix.Controller.view_module(@conn), "_tag_activity.atom", Map.merge(assigns, prepare_activity(activity, actor: true)) %> + <% end %> </feed> diff --git a/lib/pleroma/web/templates/feed/feed/tag.rss.eex b/lib/pleroma/web/templates/feed/feed/tag.rss.eex index 9c3613feb..27dde5627 100644 --- a/lib/pleroma/web/templates/feed/feed/tag.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/tag.rss.eex @@ -1,15 +1,16 @@ <?xml version="1.0" encoding="UTF-8"?> -<rss version="2.0" xmlns:webfeeds="http://webfeeds.org/rss/1.0"> +<rss version="2.0" + xmlns:webfeeds="http://webfeeds.org/rss/1.0" + xmlns:thr="http://purl.org/syndication/thread/1.0"> <channel> - <title>#<%= @tag %></title> - <description>These are public toots tagged with #<%= @tag %>. You can interact with them if you have an account anywhere in the fediverse.</description> + <description><%= Gettext.dpgettext("static_pages", "tag feed description", "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse.", tag: @tag) %></description> <link><%= '#{Routes.tag_feed_url(@conn, :feed, @tag)}.rss' %></link> <webfeeds:logo><%= feed_logo() %></webfeeds:logo> <webfeeds:accentColor>2b90d9</webfeeds:accentColor> <%= for activity <- @activities do %> - <%= render @view_module, "_tag_activity.xml", Map.merge(assigns, prepare_activity(activity)) %> + <%= render Phoenix.Controller.view_module(@conn), "_tag_activity.xml", Map.merge(assigns, prepare_activity(activity)) %> <% end %> </channel> </rss> diff --git a/lib/pleroma/web/templates/feed/feed/user.atom.eex b/lib/pleroma/web/templates/feed/feed/user.atom.eex index 5c1f0ecbc..e36bfc66c 100644 --- a/lib/pleroma/web/templates/feed/feed/user.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/user.atom.eex @@ -8,17 +8,18 @@ <id><%= Routes.user_feed_url(@conn, :feed, @user.nickname) <> ".atom" %></id> <title><%= @user.nickname <> "'s timeline" %></title> - <updated><%= most_recent_update(@activities, @user) %></updated> + <subtitle><%= escape(@user.bio) %></subtitle> + <updated><%= most_recent_update(@activities, @user, :atom) %></updated> <logo><%= logo(@user) %></logo> <link rel="self" href="<%= '#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.atom' %>" type="application/atom+xml"/> - <%= render @view_module, "_author.atom", assigns %> + <%= render Phoenix.Controller.view_module(@conn), "_author.atom", assigns %> <%= if last_activity(@activities) do %> <link rel="next" href="<%= '#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.atom?max_id=#{last_activity(@activities).id}' %>" type="application/atom+xml"/> <% end %> <%= for activity <- @activities do %> - <%= render @view_module, "_activity.atom", Map.merge(assigns, prepare_activity(activity)) %> + <%= render Phoenix.Controller.view_module(@conn), "_activity.atom", Map.merge(assigns, prepare_activity(activity)) %> <% end %> </feed> diff --git a/lib/pleroma/web/templates/feed/feed/user.rss.eex b/lib/pleroma/web/templates/feed/feed/user.rss.eex index 6b842a085..fae3fcf3d 100644 --- a/lib/pleroma/web/templates/feed/feed/user.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/user.rss.eex @@ -1,20 +1,30 @@ <?xml version="1.0" encoding="UTF-8" ?> -<rss version="2.0"> +<rss version="2.0" + xmlns:atom="http://www.w3.org/2005/Atom" + xmlns:thr="http://purl.org/syndication/thread/1.0" + xmlns:activity="http://activitystrea.ms/spec/1.0/" + xmlns:ostatus="http://ostatus.org/schema/1.0" + xmlns:poco="http://portablecontacts.net/spec/1.0"> <channel> - <guid><%= Routes.user_feed_url(@conn, :feed, @user.nickname) <> ".rss" %></guid> <title><%= @user.nickname <> "'s timeline" %></title> - <updated><%= most_recent_update(@activities, @user) %></updated> - <image><%= logo(@user) %></image> <link><%= '#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss' %></link> + <atom:link href="<%= Routes.user_feed_url(@conn, :feed, @user.nickname) <> ".atom" %>" + rel="self" type="application/rss+xml" /> + <description><%= escape(@user.bio) %></description> + <image> + <url><%= logo(@user) %></url> + <title><%= @user.nickname <> "'s timeline" %></title> + <link><%= '#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss' %></link> + </image> - <%= render @view_module, "_author.rss", assigns %> + <%= render Phoenix.Controller.view_module(@conn), "_author.rss", assigns %> <%= if last_activity(@activities) do %> <link rel="next"><%= '#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss?max_id=#{last_activity(@activities).id}' %></link> <% end %> <%= for activity <- @activities do %> - <%= render @view_module, "_activity.rss", Map.merge(assigns, prepare_activity(activity)) %> + <%= render Phoenix.Controller.view_module(@conn), "_activity.rss", Map.merge(assigns, prepare_activity(activity)) %> <% end %> </channel> </rss> diff --git a/lib/pleroma/web/templates/layout/app.html.eex b/lib/pleroma/web/templates/layout/app.html.eex index 1ede59fd8..e33bada85 100644 --- a/lib/pleroma/web/templates/layout/app.html.eex +++ b/lib/pleroma/web/templates/layout/app.html.eex @@ -1,5 +1,5 @@ <!DOCTYPE html> -<html lang="en"> +<html lang="<%= Pleroma.Web.Gettext.language_tag() %>"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,minimal-ui"> diff --git a/lib/pleroma/web/templates/layout/email.html.eex b/lib/pleroma/web/templates/layout/email.html.eex index f6dcd7f0f..5858e48b4 100644 --- a/lib/pleroma/web/templates/layout/email.html.eex +++ b/lib/pleroma/web/templates/layout/email.html.eex @@ -1,10 +1,10 @@ <!DOCTYPE html> -<html lang="en"> +<html lang="<%= Pleroma.Web.Gettext.language_tag() %>"> <head> <meta charset="utf-8"> <title><%= @email.subject %></title> </head> <body> - <%= render @view_module, @view_template, assigns %> + <%= render Phoenix.Controller.view_module(@conn), Phoenix.Controller.view_template(@conn), assigns %> </body> -</html>
\ No newline at end of file +</html> diff --git a/lib/pleroma/web/templates/layout/embed.html.eex b/lib/pleroma/web/templates/layout/embed.html.eex index 8b905f070..1197288e5 100644 --- a/lib/pleroma/web/templates/layout/embed.html.eex +++ b/lib/pleroma/web/templates/layout/embed.html.eex @@ -10,6 +10,6 @@ <base target="_parent"> </head> <body> - <%= render @view_module, @view_template, assigns %> + <%= render Phoenix.Controller.view_module(@conn), Phoenix.Controller.view_template(@conn), assigns %> </body> </html> diff --git a/lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex index 7b476f02d..df090ffcd 100644 --- a/lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex +++ b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex @@ -1 +1 @@ -<h1>UNSUBSCRIBE FAILURE</h1> +<h1><%= Gettext.dpgettext("static_pages", "mailer unsubscribe failed message", "UNSUBSCRIBE FAILURE") %></h1> diff --git a/lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex index 6dfa2c185..cbce495d4 100644 --- a/lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex +++ b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex @@ -1 +1 @@ -<h1>UNSUBSCRIBE SUCCESSFUL</h1> +<h1><%= Gettext.dpgettext("static_pages", "mailer unsubscribe successful message", "UNSUBSCRIBE SUCCESSFUL") %></h1> diff --git a/lib/pleroma/web/templates/masto_fe/index.html.eex b/lib/pleroma/web/templates/masto_fe/index.html.eex deleted file mode 100644 index 6f2b98957..000000000 --- a/lib/pleroma/web/templates/masto_fe/index.html.eex +++ /dev/null @@ -1,35 +0,0 @@ -<!DOCTYPE html> -<html lang='en'> -<head> -<meta charset='utf-8'> -<meta content='width=device-width, initial-scale=1' name='viewport'> -<title> -<%= Config.get([:instance, :name]) %> -</title> -<link rel="icon" type="image/png" href="/favicon.png"/> -<link rel="manifest" type="applicaton/manifest+json" href="<%= Routes.masto_fe_path(Pleroma.Web.Endpoint, :manifest) %>" /> - -<meta name="theme-color" content="<%= Config.get([:manifest, :theme_color]) %>" /> - -<script crossorigin='anonymous' src="/packs/locales.js"></script> -<script crossorigin='anonymous' src="/packs/locales/glitch/en.js"></script> - -<link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/getting_started.js'> -<link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/compose.js'> -<link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/home_timeline.js'> -<link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/notifications.js'> -<script id='initial-state' type='application/json'><%= initial_state(@token, @user, @custom_emojis) %></script> - -<script src="/packs/core/common.js"></script> -<link rel="stylesheet" media="all" href="/packs/core/common.css" /> - -<script src="/packs/flavours/glitch/common.js"></script> -<link rel="stylesheet" media="all" href="/packs/flavours/glitch/common.css" /> - -<script src="/packs/flavours/glitch/home.js"></script> -</head> -<body class='app-body no-reduce-motion system-font'> - <div class='app-holder' data-props='{"locale":"en"}' id='mastodon'> - </div> -</body> -</html> diff --git a/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex b/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex index b9daa8d8b..e45d13bdf 100644 --- a/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex +++ b/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex @@ -5,11 +5,11 @@ <p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p> <% end %> -<h2>Two-factor recovery</h2> +<h2><%= Gettext.dpgettext("static_pages", "mfa recover page title", "Two-factor recovery") %></h2> <%= form_for @conn, Routes.mfa_verify_path(@conn, :verify), [as: "mfa"], fn f -> %> <div class="input"> - <%= label f, :code, "Recovery code" %> + <%= label f, :code, Gettext.dpgettext("static_pages", "mfa recover recovery code prompt", "Recovery code") %> <%= text_input f, :code, [autocomplete: false, autocorrect: "off", autocapitalize: "off", autofocus: true, spellcheck: false] %> <%= hidden_input f, :mfa_token, value: @mfa_token %> <%= hidden_input f, :state, value: @state %> @@ -17,8 +17,8 @@ <%= hidden_input f, :challenge_type, value: "recovery" %> </div> -<%= submit "Verify" %> +<%= submit Gettext.dpgettext("static_pages", "mfa recover verify recovery code button", "Verify") %> <% end %> <a href="<%= Routes.mfa_path(@conn, :show, %{challenge_type: "totp", mfa_token: @mfa_token, state: @state, redirect_uri: @redirect_uri}) %>"> - Enter a two-factor code + <%= Gettext.dpgettext("static_pages", "mfa recover use 2fa code link", "Enter a two-factor code") %> </a> diff --git a/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex b/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex index 29ea7c5fb..50e6c04b6 100644 --- a/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex +++ b/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex @@ -5,20 +5,20 @@ <p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p> <% end %> -<h2>Two-factor authentication</h2> +<h2><%= Gettext.dpgettext("static_pages", "mfa auth page title", "Two-factor authentication") %></h2> <%= form_for @conn, Routes.mfa_verify_path(@conn, :verify), [as: "mfa"], fn f -> %> <div class="input"> - <%= label f, :code, "Authentication code" %> - <%= text_input f, :code, [autocomplete: false, autocorrect: "off", autocapitalize: "off", autofocus: true, pattern: "[0-9]*", spellcheck: false] %> + <%= label f, :code, Gettext.dpgettext("static_pages", "mfa auth code prompt", "Authentication code") %> + <%= text_input f, :code, [autocomplete: "one-time-code", autocorrect: "off", autocapitalize: "off", autofocus: true, pattern: "[0-9]*", spellcheck: false] %> <%= hidden_input f, :mfa_token, value: @mfa_token %> <%= hidden_input f, :state, value: @state %> <%= hidden_input f, :redirect_uri, value: @redirect_uri %> <%= hidden_input f, :challenge_type, value: "totp" %> </div> -<%= submit "Verify" %> +<%= submit Gettext.dpgettext("static_pages", "mfa auth verify code button", "Verify") %> <% end %> <a href="<%= Routes.mfa_path(@conn, :show, %{challenge_type: "recovery", mfa_token: @mfa_token, state: @state, redirect_uri: @redirect_uri}) %>"> - Enter a two-factor recovery code + <%= Gettext.dpgettext("static_pages", "mfa auth page use recovery code link", "Enter a two-factor recovery code") %> </a> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex index c9ec1ecbf..73115e92a 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex @@ -1,5 +1,5 @@ <div class="scopes-input"> - <%= label @form, :scope, "The following permissions will be granted" %> + <%= label @form, :scope, Gettext.dpgettext("static_pages", "oauth scopes message", "The following permissions will be granted") %> <div class="scopes"> <%= for scope <- @available_scopes do %> <%# Note: using hidden input with `unchecked_value` in order to distinguish user's empty selection from `scope` param being omitted %> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex index dc4521a62..98904ad64 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex @@ -1,8 +1,8 @@ -<h2>Sign in with external provider</h2> +<h2><%= Gettext.dpgettext("static_pages", "oauth external provider page title", "Sign in with external provider") %></h2> <%= form_for @conn, Routes.o_auth_path(@conn, :prepare_request), [as: "authorization", method: "get"], fn f -> %> <div style="display: none"> - <%= render @view_module, "_scopes.html", Map.merge(assigns, %{form: f}) %> + <%= render Phoenix.Controller.view_module(@conn), "_scopes.html", Map.merge(assigns, %{form: f}) %> </div> <%= hidden_input f, :client_id, value: @client_id %> @@ -10,6 +10,6 @@ <%= hidden_input f, :state, value: @state %> <%= for strategy <- Pleroma.Config.oauth_consumer_strategies() do %> - <%= submit "Sign in with #{String.capitalize(strategy)}", name: "provider", value: strategy %> + <%= submit Gettext.dpgettext("static_pages", "oauth external provider sign in button", "Sign in with %{strategy}", strategy: String.capitalize(strategy)), name: "provider", value: strategy %> <% end %> <% end %> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex index ffabe29a6..76ed3fda5 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex @@ -1,2 +1,2 @@ -<h1>Successfully authorized</h1> -<h2>Token code is <br><%= @auth.token %></h2> +<h1><%= Gettext.dpgettext("static_pages", "oauth authorized page title", "Successfully authorized") %></h1> +<h2><%= raw Gettext.dpgettext("static_pages", "oauth token code message", "Token code is <br>%{token}", token: safe_to_string(html_escape(@auth.token))) %></h2> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex index 82785c4b9..754bf2eb0 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex @@ -1,2 +1,2 @@ -<h1>Authorization exists</h1> -<h2>Access token is <br><%= @token.token %></h2> +<h1><%= Gettext.dpgettext("static_pages", "oauth authorization exists page title", "Authorization exists") %></h1> +<h2><%= raw Gettext.dpgettext("static_pages", "oauth token code message", "Token code is <br>%{token}", token: safe_to_string(html_escape(@token.token))) %></h2> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex index 99f900fb7..1f661efb2 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex @@ -5,34 +5,34 @@ <p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p> <% end %> -<h2>Registration Details</h2> +<h2><%= Gettext.dpgettext("static_pages", "oauth register page title", "Registration Details") %></h2> -<p>If you'd like to register a new account, please provide the details below.</p> +<p><%= Gettext.dpgettext("static_pages", "oauth register page fill form prompt", "If you'd like to register a new account, please provide the details below.") %></p> <%= form_for @conn, Routes.o_auth_path(@conn, :register), [as: "authorization"], fn f -> %> <div class="input"> - <%= label f, :nickname, "Nickname" %> - <%= text_input f, :nickname, value: @nickname %> + <%= label f, :nickname, Gettext.dpgettext("static_pages", "oauth register page nickname prompt", "Nickname") %> + <%= text_input f, :nickname, value: @nickname, autocomplete: "username" %> </div> <div class="input"> - <%= label f, :email, "Email" %> - <%= text_input f, :email, value: @email %> + <%= label f, :email, Gettext.dpgettext("static_pages", "oauth register page email prompt", "Email") %> + <%= text_input f, :email, value: @email, autocomplete: "email" %> </div> -<%= submit "Proceed as new user", name: "op", value: "register" %> +<%= submit Gettext.dpgettext("static_pages", "oauth register page register button", "Proceed as new user"), name: "op", value: "register" %> -<p>Alternatively, sign in to connect to existing account.</p> +<p><%= Gettext.dpgettext("static_pages", "oauth register page login prompt", "Alternatively, sign in to connect to existing account.") %></p> <div class="input"> - <%= label f, :name, "Name or email" %> - <%= text_input f, :name %> + <%= label f, :name, Gettext.dpgettext("static_pages", "oauth register page login username prompt", "Name or email") %> + <%= text_input f, :name, autocomplete: "username" %> </div> <div class="input"> - <%= label f, :password, "Password" %> - <%= password_input f, :password %> + <%= label f, :password, Gettext.dpgettext("static_pages", "oauth register page login password prompt", "Password") %> + <%= password_input f, :password, autocomplete: "password" %> </div> -<%= submit "Proceed as existing user", name: "op", value: "connect" %> +<%= submit Gettext.dpgettext("static_pages", "oauth register page login button", "Proceed as existing user"), name: "op", value: "connect" %> <%= hidden_input f, :client_id, value: @client_id %> <%= hidden_input f, :redirect_uri, value: @redirect_uri %> diff --git a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex index 181a9519a..b3654f3eb 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex @@ -20,36 +20,38 @@ <div class="container__content"> <%= if @app do %> - <p>Application <strong><%= @app.client_name %></strong> is requesting access to your account.</p> - <%= render @view_module, "_scopes.html", Map.merge(assigns, %{form: f}) %> + <p><%= raw Gettext.dpgettext("static_pages", "oauth authorize message", "Application <strong>%{client_name}</strong> is requesting access to your account.", client_name: safe_to_string(html_escape(@app.client_name))) %></p> + <%= render Phoenix.Controller.view_module(@conn), "_scopes.html", Map.merge(assigns, %{form: f}) %> <% end %> <%= if @user do %> <div class="actions"> - <a class="button button--cancel" href="/">Cancel</a> - <%= submit "Approve", class: "button--approve" %> + <a class="button button--cancel" href="/"> + <%= Gettext.dpgettext("static_pages", "oauth authorize cancel button", "Cancel") %> + </a> + <%= submit Gettext.dpgettext("static_pages", "oauth authorize approve button", "Approve"), class: "button--approve" %> </div> <% else %> <%= if @params["registration"] in ["true", true] do %> - <h3>This is the first time you visit! Please enter your Pleroma handle.</h3> - <p>Choose carefully! You won't be able to change this later. You will be able to change your display name, though.</p> + <h3><%= Gettext.dpgettext("static_pages", "oauth register page title", "This is the first time you visit! Please enter your Pleroma handle.") %></h3> + <p><%= Gettext.dpgettext("static_pages", "oauth register nickname unchangeable warning", "Choose carefully! You won't be able to change this later. You will be able to change your display name, though.") %></p> <div class="input"> - <%= label f, :nickname, "Pleroma Handle" %> - <%= text_input f, :nickname, placeholder: "lain" %> + <%= label f, :nickname, Gettext.dpgettext("static_pages", "oauth register nickname prompt", "Pleroma Handle") %> + <%= text_input f, :nickname, placeholder: "lain", autocomplete: "username" %> </div> <%= hidden_input f, :name, value: @params["name"] %> <%= hidden_input f, :password, value: @params["password"] %> <br> <% else %> <div class="input"> - <%= label f, :name, "Username" %> + <%= label f, :name, Gettext.dpgettext("static_pages", "oauth login username prompt", "Username") %> <%= text_input f, :name %> </div> <div class="input"> - <%= label f, :password, "Password" %> + <%= label f, :password, Gettext.dpgettext("static_pages", "oauth login password prompt", "Password") %> <%= password_input f, :password %> </div> - <%= submit "Log In" %> + <%= submit Gettext.dpgettext("static_pages", "oauth login button", "Log In") %> <% end %> <% end %> </div> @@ -61,5 +63,5 @@ <% end %> <%= if Pleroma.Config.oauth_consumer_enabled?() do %> - <%= render @view_module, Pleroma.Web.Auth.WrapperAuthenticator.oauth_consumer_template(), assigns %> + <%= render Phoenix.Controller.view_module(@conn), Pleroma.Web.Auth.WrapperAuthenticator.oauth_consumer_template(), assigns %> <% end %> diff --git a/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex b/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex index 3191bf450..a14ca305e 100644 --- a/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex +++ b/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex @@ -5,7 +5,7 @@ <form class="pull-right collapse" method="POST" action="<%= Helpers.util_path(@conn, :remote_subscribe) %>"> <input type="hidden" name="nickname" value="<%= @user.nickname %>"> <input type="hidden" name="profile" value=""> - <button type="submit" class="collapse">Remote follow</button> + <button type="submit" class="collapse"><%= Gettext.dpgettext("static_pages", "static fe profile page remote follow button", "Remote follow") %></button> </form> <%= raw Formatter.emojify(@user.name, @user.emoji) %> | <%= link "@#{@user.nickname}@#{Endpoint.host()}", to: (@user.uri || @user.ap_id) %> diff --git a/lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex b/lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex index ee84750c7..5ac0aa4e0 100644 --- a/lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex +++ b/lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex @@ -1 +1 @@ -<h2>Invalid Token</h2> +<h2><%= Gettext.dpgettext("static_pages", "password reset invalid token message", "Invalid Token") %></h2> diff --git a/lib/pleroma/web/templates/twitter_api/password/reset.html.eex b/lib/pleroma/web/templates/twitter_api/password/reset.html.eex index fbcacdc14..6a544af51 100644 --- a/lib/pleroma/web/templates/twitter_api/password/reset.html.eex +++ b/lib/pleroma/web/templates/twitter_api/password/reset.html.eex @@ -1,13 +1,13 @@ <h2>Password Reset for <%= @user.nickname %></h2> <%= form_for @conn, Routes.reset_password_path(@conn, :do_reset), [as: "data"], fn f -> %> <div class="form-row"> - <%= label f, :password, "Password" %> + <%= label f, :password, Gettext.dpgettext("static_pages", "password reset form password prompt", "Password") %> <%= password_input f, :password %> </div> <div class="form-row"> - <%= label f, :password_confirmation, "Confirmation" %> + <%= label f, :password_confirmation, Gettext.dpgettext("static_pages", "password reset form confirm password prompt", "Confirmation") %> <%= password_input f, :password_confirmation %> </div> <%= hidden_input f, :token, value: @token.token %> - <%= submit "Reset" %> + <%= submit Gettext.dpgettext("static_pages", "password reset button", "Reset") %> <% end %> diff --git a/lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex b/lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex index 4ed4ac8bc..774e3462a 100644 --- a/lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex +++ b/lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex @@ -1,2 +1,6 @@ -<h2>Password reset failed</h2> -<h3><a href="<%= Pleroma.Web.Endpoint.url() %>">Homepage</a></h3> +<h2><%= Gettext.dpgettext("static_pages", "password reset failed message", "Password reset failed") %></h2> +<h3> + <a href="<%= Pleroma.Web.Endpoint.url() %>"> + <%= Gettext.dpgettext("static_pages", "password reset failed homepage link", "Homepage") %> + </a> +</h3> diff --git a/lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex b/lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex index 086d4e08b..40f6bb3fc 100644 --- a/lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex +++ b/lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex @@ -1,2 +1,2 @@ -<h2>Password changed!</h2> -<h3><a href="<%= Pleroma.Web.Endpoint.url() %>">Homepage</a></h3> +<h2><%= Gettext.dpgettext("static_pages", "password reset successful message", "Password changed!") %></h2> +<h3><a href="<%= Pleroma.Web.Endpoint.url() %>"><%= Gettext.dpgettext("static_pages", "password reset successful homepage link", "Homepage") %></a></h3> diff --git a/lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex b/lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex index a7be53091..e2d251fac 100644 --- a/lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex +++ b/lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex @@ -1,11 +1,11 @@ <%= if @error == :error do %> - <h2>Error fetching user</h2> + <h2><%= Gettext.dpgettext("static_pages", "remote follow error", "Error fetching user") %></h2> <% else %> - <h2>Remote follow</h2> + <h2><%= Gettext.dpgettext("static_pages", "remote follow header", "Remote follow") %></h2> <img height="128" width="128" src="<%= avatar_url(@followee) %>"> <p><%= @followee.nickname %></p> <%= form_for @conn, Routes.remote_follow_path(@conn, :do_follow), [as: "user"], fn f -> %> <%= hidden_input f, :id, value: @followee.id %> - <%= submit "Authorize" %> + <%= submit Gettext.dpgettext("static_pages", "remote follow authorization button", "Authorize") %> <% end %> <% end %> diff --git a/lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex b/lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex index a8026fa9d..26340a906 100644 --- a/lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex +++ b/lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex @@ -1,14 +1,14 @@ <%= if @error do %> <h2><%= @error %></h2> <% end %> -<h2>Log in to follow</h2> +<h2><%= Gettext.dpgettext("static_pages", "remote follow header, need login", "Log in to follow") %></h2> <p><%= @followee.nickname %></p> <img height="128" width="128" src="<%= avatar_url(@followee) %>"> <%= form_for @conn, Routes.remote_follow_path(@conn, :do_follow), [as: "authorization"], fn f -> %> -<%= text_input f, :name, placeholder: "Username", required: true %> +<%= text_input f, :name, placeholder: Gettext.dpgettext("static_pages", "placeholder text for username entry", "Username"), required: true, autocomplete: "username" %> <br> -<%= password_input f, :password, placeholder: "Password", required: true %> +<%= password_input f, :password, placeholder: Gettext.dpgettext("static_pages", "placeholder text for password entry", "Password"), required: true, autocomplete: "password" %> <br> <%= hidden_input f, :id, value: @followee.id %> -<%= submit "Authorize" %> +<%= submit Gettext.dpgettext("static_pages", "remote follow authorization button for login", "Authorize") %> <% end %> diff --git a/lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex b/lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex index a54ed83b5..638212c1e 100644 --- a/lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex +++ b/lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex @@ -1,13 +1,13 @@ <%= if @error do %> <h2><%= @error %></h2> <% end %> -<h2>Two-factor authentication</h2> +<h2><%= Gettext.dpgettext("static_pages", "remote follow mfa header", "Two-factor authentication") %></h2> <p><%= @followee.nickname %></p> <img height="128" width="128" src="<%= avatar_url(@followee) %>"> <%= form_for @conn, Routes.remote_follow_path(@conn, :do_follow), [as: "mfa"], fn f -> %> -<%= text_input f, :code, placeholder: "Authentication code", required: true %> +<%= text_input f, :code, placeholder: Gettext.dpgettext("static_pages", "placeholder text for auth code entry", "Authentication code"), required: true %> <br> <%= hidden_input f, :id, value: @followee.id %> <%= hidden_input f, :token, value: @mfa_token %> -<%= submit "Authorize" %> +<%= submit Gettext.dpgettext("static_pages", "remote follow authorization button for mfa", "Authorize") %> <% end %> diff --git a/lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex b/lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex index da473d502..2fb4cc5d3 100644 --- a/lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex +++ b/lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex @@ -1,6 +1,5 @@ <%= if @error do %> -<p>Error following account</p> +<p><%= Gettext.dpgettext("static_pages", "remote follow error", "Error following account") %></p> <% else %> -<h2>Account followed!</h2> +<h2><%= Gettext.dpgettext("static_pages", "remote follow success", "Account followed!") %></h2> <% end %> - diff --git a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex new file mode 100644 index 000000000..d77174967 --- /dev/null +++ b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex @@ -0,0 +1,10 @@ +<%= if @error do %> + <h2><%= Gettext.dpgettext("static_pages", "status interact error", "Error: %{error}", error: @error) %></h2> +<% else %> + <h2><%= raw Gettext.dpgettext("static_pages", "status interact header", "Interacting with %{nickname}'s %{status_link}", nickname: safe_to_string(html_escape(@nickname)), status_link: safe_to_string(link(Gettext.dpgettext("static_pages", "status interact header - status link text", "status"), to: @status_link))) %></h2> + <%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "status"], fn f -> %> + <%= hidden_input f, :status_id, value: @status_id %> + <%= text_input f, :profile, placeholder: Gettext.dpgettext("static_pages", "placeholder text for account id", "Your account ID, e.g. lain@quitter.se") %> + <%= submit Gettext.dpgettext("static_pages", "status interact authorization button", "Interact") %> + <% end %> +<% end %> diff --git a/lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex b/lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex index a6b313d8a..848660f26 100644 --- a/lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex +++ b/lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex @@ -1,10 +1,10 @@ <%= if @error do %> - <h2>Error: <%= @error %></h2> + <h2><%= Gettext.dpgettext("static_pages", "remote follow error", "Error: %{error}", error: @error) %></h2> <% else %> - <h2>Remotely follow <%= @nickname %></h2> + <h2><%= Gettext.dpgettext("static_pages", "remote follow header", "Remotely follow %{nickname}", nickname: @nickname) %></h2> <%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "user"], fn f -> %> <%= hidden_input f, :nickname, value: @nickname %> - <%= text_input f, :profile, placeholder: "Your account ID, e.g. lain@quitter.se" %> - <%= submit "Follow" %> + <%= text_input f, :profile, placeholder: Gettext.dpgettext("static_pages", "placeholder text for account id", "Your account ID, e.g. lain@quitter.se") %> + <%= submit Gettext.dpgettext("static_pages", "remote follow authorization button for following with a remote account", "Follow") %> <% end %> <% end %> diff --git a/lib/pleroma/web/translation_helpers.ex b/lib/pleroma/web/translation_helpers.ex index 0fe31d189..e9638b00a 100644 --- a/lib/pleroma/web/translation_helpers.ex +++ b/lib/pleroma/web/translation_helpers.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TranslationHelpers do diff --git a/lib/pleroma/web/twitter_api/controller.ex b/lib/pleroma/web/twitter_api/controller.ex index 1e78ff2c1..6db3d6067 100644 --- a/lib/pleroma/web/twitter_api/controller.ex +++ b/lib/pleroma/web/twitter_api/controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.Controller do diff --git a/lib/pleroma/web/twitter_api/controllers/password_controller.ex b/lib/pleroma/web/twitter_api/controllers/password_controller.ex index bc04a4d49..31b7dd728 100644 --- a/lib/pleroma/web/twitter_api/controllers/password_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/password_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.PasswordController do @@ -11,9 +11,23 @@ defmodule Pleroma.Web.TwitterAPI.PasswordController do require Logger + import Pleroma.Web.ControllerHelper, only: [json_response: 3] + alias Pleroma.PasswordResetToken alias Pleroma.Repo alias Pleroma.User + alias Pleroma.Web.TwitterAPI.TwitterAPI + + plug(Pleroma.Web.Plugs.RateLimiter, [name: :request] when action == :request) + + @doc "POST /auth/password" + def request(conn, params) do + nickname_or_email = params["email"] || params["nickname"] + + TwitterAPI.password_reset(nickname_or_email) + + json_response(conn, :no_content, "") + end def reset(conn, %{"token" => token}) do with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}), diff --git a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex index 42d7601ed..6229d5d05 100644 --- a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index ef43f7682..d5a24ae6c 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.UtilController do @@ -7,16 +7,26 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do require Logger + alias Pleroma.Activity alias Pleroma.Config alias Pleroma.Emoji alias Pleroma.Healthcheck alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.CommonAPI alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.WebFinger - plug(Pleroma.Web.ApiSpec.CastAndValidate when action != :remote_subscribe) - plug(Pleroma.Web.Plugs.FederatingPlug when action == :remote_subscribe) + plug( + Pleroma.Web.ApiSpec.CastAndValidate + when action != :remote_subscribe and action != :show_subscribe_form + ) + + plug( + Pleroma.Web.Plugs.FederatingPlug + when action == :remote_subscribe + when action == :show_subscribe_form + ) plug( OAuthScopesPlug, @@ -26,13 +36,24 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do :change_password, :delete_account, :update_notificaton_settings, - :disable_account + :disable_account, + :move_account, + :add_alias, + :delete_alias + ] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["read:accounts"]} + when action in [ + :list_aliases ] ) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TwitterUtilOperation - def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do + def show_subscribe_form(conn, %{"nickname" => nick}) do with %User{} = user <- User.get_cached_by_nickname(nick), avatar = User.avatar_url(user) do conn @@ -42,11 +63,52 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do render(conn, "subscribe.html", %{ nickname: nick, avatar: nil, - error: "Could not find user" + error: + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "remote follow error message - user not found", + "Could not find user" + ) + }) + end + end + + def show_subscribe_form(conn, %{"status_id" => id}) do + with %Activity{} = activity <- Activity.get_by_id(id), + {:ok, ap_id} <- get_ap_id(activity), + %User{} = user <- User.get_cached_by_ap_id(activity.actor), + avatar = User.avatar_url(user) do + conn + |> render("status_interact.html", %{ + status_link: ap_id, + status_id: id, + nickname: user.nickname, + avatar: avatar, + error: false + }) + else + _e -> + render(conn, "status_interact.html", %{ + status_id: id, + avatar: nil, + error: + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "status interact error message - status not found", + "Could not find status" + ) }) end end + def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do + show_subscribe_form(conn, %{"nickname" => nick}) + end + + def remote_subscribe(conn, %{"status_id" => id, "profile" => _}) do + show_subscribe_form(conn, %{"status_id" => id}) + end + def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile), %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do @@ -57,11 +119,55 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do render(conn, "subscribe.html", %{ nickname: nick, avatar: nil, - error: "Something went wrong." + error: + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "remote follow error message - unknown error", + "Something went wrong." + ) + }) + end + end + + def remote_subscribe(conn, %{"status" => %{"status_id" => id, "profile" => profile}}) do + with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile), + %Activity{} = activity <- Activity.get_by_id(id), + {:ok, ap_id} <- get_ap_id(activity) do + conn + |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id)) + else + _e -> + render(conn, "status_interact.html", %{ + status_id: id, + avatar: nil, + error: + Pleroma.Web.Gettext.dpgettext( + "static_pages", + "status interact error message - unknown error", + "Something went wrong." + ) }) end end + def remote_interaction(%{body_params: %{ap_id: ap_id, profile: profile}} = conn, _params) do + with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile) do + conn + |> json(%{url: String.replace(template, "{uri}", ap_id)}) + else + _e -> json(conn, %{error: "Couldn't find user"}) + end + end + + defp get_ap_id(activity) do + object = Pleroma.Object.normalize(activity, fetch: false) + + case object do + %{data: %{"id" => ap_id}} -> {:ok, ap_id} + _ -> {:no_ap_id, nil} + end + end + def frontend_configurations(conn, _params) do render(conn, "frontend_configurations.json") end @@ -123,8 +229,10 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end end - def delete_account(%{assigns: %{user: user}} = conn, params) do - password = params[:password] || "" + def delete_account(%{assigns: %{user: user}, body_params: body_params} = conn, params) do + # This endpoint can accept a query param or JSON body for backwards-compatibility. + # Submitting a JSON body is recommended, so passwords don't end up in server logs. + password = body_params[:password] || params[:password] || "" case CommonAPI.Utils.confirm_current_password(user, password) do {:ok, user} -> @@ -147,6 +255,91 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end end + def move_account(%{assigns: %{user: user}, body_params: body_params} = conn, %{}) do + case CommonAPI.Utils.confirm_current_password(user, body_params.password) do + {:ok, user} -> + with {:ok, target_user} <- find_or_fetch_user_by_nickname(body_params.target_account), + {:ok, _user} <- ActivityPub.move(user, target_user) do + json(conn, %{status: "success"}) + else + {:not_found, _} -> + conn + |> put_status(404) + |> json(%{error: "Target account not found."}) + + {:error, error} -> + json(conn, %{error: error}) + end + + {:error, msg} -> + json(conn, %{error: msg}) + end + end + + def add_alias(%{assigns: %{user: user}, body_params: body_params} = conn, _) do + with {:ok, alias_user} <- find_user_by_nickname(body_params.alias), + {:ok, _user} <- user |> User.add_alias(alias_user) do + json(conn, %{status: "success"}) + else + {:not_found, _} -> + conn + |> put_status(404) + |> json(%{error: "Target account does not exist."}) + + {:error, error} -> + json(conn, %{error: error}) + end + end + + def delete_alias(%{assigns: %{user: user}, body_params: body_params} = conn, _) do + with {:ok, alias_user} <- find_user_by_nickname(body_params.alias), + {:ok, _user} <- user |> User.delete_alias(alias_user) do + json(conn, %{status: "success"}) + else + {:error, :no_such_alias} -> + conn + |> put_status(404) + |> json(%{error: "Account has no such alias."}) + + {:error, error} -> + json(conn, %{error: error}) + end + end + + def list_aliases(%{assigns: %{user: user}} = conn, %{}) do + alias_nicks = + user + |> User.alias_users() + |> Enum.map(&User.full_nickname/1) + + json(conn, %{aliases: alias_nicks}) + end + + defp find_user_by_nickname(nickname) do + user = User.get_cached_by_nickname(nickname) + + if user == nil do + {:not_found, nil} + else + {:ok, user} + end + end + + defp find_or_fetch_user_by_nickname(nickname) do + user = User.get_by_nickname(nickname) + + if user != nil and user.local do + {:ok, user} + else + with {:ok, user} <- User.fetch_by_nickname(nickname) do + {:ok, user} + else + _ -> + {:not_found, nil} + end + end + end + def captcha(conn, _params) do json(conn, Pleroma.Captcha.new()) end diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index 76ca82d20..ef2eb75f4 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.TwitterAPI do @@ -12,6 +12,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do alias Pleroma.UserInviteToken def register_user(params, opts \\ []) do + fallback_language = Gettext.get_locale() + params = params |> Map.take([:email, :token, :password]) @@ -20,6 +22,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do |> Map.put(:name, Map.get(params, :fullname, params[:username])) |> Map.put(:password_confirmation, params[:password]) |> Map.put(:registration_reason, params[:reason]) + |> Map.put(:birthday, params[:birthday]) + |> Map.put( + :language, + Pleroma.Web.Gettext.normalize_locale(params[:language]) || fallback_language + ) if Pleroma.Config.get([:instance, :registrations_open]) do create_user(params, opts) diff --git a/lib/pleroma/web/twitter_api/views/password_view.ex b/lib/pleroma/web/twitter_api/views/password_view.ex index a9bb95a2c..55790941f 100644 --- a/lib/pleroma/web/twitter_api/views/password_view.ex +++ b/lib/pleroma/web/twitter_api/views/password_view.ex @@ -1,8 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.PasswordView do use Pleroma.Web, :view import Phoenix.HTML.Form + alias Pleroma.Web.Gettext end diff --git a/lib/pleroma/web/twitter_api/views/remote_follow_view.ex b/lib/pleroma/web/twitter_api/views/remote_follow_view.ex index ac3f15eec..8902261b0 100644 --- a/lib/pleroma/web/twitter_api/views/remote_follow_view.ex +++ b/lib/pleroma/web/twitter_api/views/remote_follow_view.ex @@ -1,10 +1,15 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.RemoteFollowView do use Pleroma.Web, :view import Phoenix.HTML.Form + alias Pleroma.Web.Gettext - defdelegate avatar_url(user), to: Pleroma.User + def avatar_url(user) do + user + |> Pleroma.User.avatar_url() + |> Pleroma.Web.MediaProxy.url() + end end diff --git a/lib/pleroma/web/twitter_api/views/token_view.ex b/lib/pleroma/web/twitter_api/views/token_view.ex index 99884e714..2e492c13f 100644 --- a/lib/pleroma/web/twitter_api/views/token_view.ex +++ b/lib/pleroma/web/twitter_api/views/token_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.TokenView do diff --git a/lib/pleroma/web/twitter_api/views/util_view.ex b/lib/pleroma/web/twitter_api/views/util_view.ex index 87cb79dd7..31b7c0c0c 100644 --- a/lib/pleroma/web/twitter_api/views/util_view.ex +++ b/lib/pleroma/web/twitter_api/views/util_view.ex @@ -1,12 +1,15 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.UtilView do use Pleroma.Web, :view + import Phoenix.HTML import Phoenix.HTML.Form + import Phoenix.HTML.Link alias Pleroma.Config alias Pleroma.Web.Endpoint + alias Pleroma.Web.Gettext def status_net_config(instance) do """ diff --git a/lib/pleroma/web/uploader_controller.ex b/lib/pleroma/web/uploader_controller.ex index 0d42c7ec3..d5c804932 100644 --- a/lib/pleroma/web/uploader_controller.ex +++ b/lib/pleroma/web/uploader_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.UploaderController do diff --git a/lib/pleroma/web/utils/guards.ex b/lib/pleroma/web/utils/guards.ex index aea7b6314..8a61421fa 100644 --- a/lib/pleroma/web/utils/guards.ex +++ b/lib/pleroma/web/utils/guards.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Utils.Guards do diff --git a/lib/pleroma/web/utils/params.ex b/lib/pleroma/web/utils/params.ex index 6e0900341..636e3de33 100644 --- a/lib/pleroma/web/utils/params.ex +++ b/lib/pleroma/web/utils/params.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Utils.Params do diff --git a/lib/pleroma/web/views/email_view.ex b/lib/pleroma/web/views/email_view.ex index f7659b994..9ab708212 100644 --- a/lib/pleroma/web/views/email_view.ex +++ b/lib/pleroma/web/views/email_view.ex @@ -1,11 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.EmailView do use Pleroma.Web, :view import Phoenix.HTML import Phoenix.HTML.Link + alias Pleroma.Web.Gettext def avatar_url(user) do Pleroma.User.avatar_url(user) diff --git a/lib/pleroma/web/views/embed_view.ex b/lib/pleroma/web/views/embed_view.ex index 81e196730..1bfd8c523 100644 --- a/lib/pleroma/web/views/embed_view.ex +++ b/lib/pleroma/web/views/embed_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.EmbedView do diff --git a/lib/pleroma/web/views/error_helpers.ex b/lib/pleroma/web/views/error_helpers.ex index d282c04b7..fd85f7c15 100644 --- a/lib/pleroma/web/views/error_helpers.ex +++ b/lib/pleroma/web/views/error_helpers.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ErrorHelpers do diff --git a/lib/pleroma/web/views/error_view.ex b/lib/pleroma/web/views/error_view.ex index c9715dc4b..9ab16323d 100644 --- a/lib/pleroma/web/views/error_view.ex +++ b/lib/pleroma/web/views/error_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ErrorView do diff --git a/lib/pleroma/web/views/layout_view.ex b/lib/pleroma/web/views/layout_view.ex index c2da10f04..3161bb1ae 100644 --- a/lib/pleroma/web/views/layout_view.ex +++ b/lib/pleroma/web/views/layout_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.LayoutView do diff --git a/lib/pleroma/web/views/mailer/subscription_view.ex b/lib/pleroma/web/views/mailer/subscription_view.ex index 1dc80987b..037fb6578 100644 --- a/lib/pleroma/web/views/mailer/subscription_view.ex +++ b/lib/pleroma/web/views/mailer/subscription_view.ex @@ -1,7 +1,8 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Mailer.SubscriptionView do use Pleroma.Web, :view + alias Pleroma.Web.Gettext end diff --git a/lib/pleroma/web/views/manifest_view.ex b/lib/pleroma/web/views/manifest_view.ex new file mode 100644 index 000000000..2ae82191e --- /dev/null +++ b/lib/pleroma/web/views/manifest_view.ex @@ -0,0 +1,28 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ManifestView do + use Pleroma.Web, :view + alias Pleroma.Config + alias Pleroma.Web.Endpoint + + def render("manifest.json", _params) do + %{ + name: Config.get([:instance, :name]), + description: Config.get([:instance, :description]), + icons: Config.get([:manifest, :icons]), + theme_color: Config.get([:manifest, :theme_color]), + background_color: Config.get([:manifest, :background_color]), + display: "standalone", + scope: Endpoint.url(), + start_url: "/", + categories: [ + "social" + ], + serviceworker: %{ + src: "/sw.js" + } + } + end +end diff --git a/lib/pleroma/web/views/masto_fe_view.ex b/lib/pleroma/web/views/masto_fe_view.ex deleted file mode 100644 index 63a9c8179..000000000 --- a/lib/pleroma/web/views/masto_fe_view.ex +++ /dev/null @@ -1,91 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.MastoFEView do - use Pleroma.Web, :view - alias Pleroma.Config - alias Pleroma.User - alias Pleroma.Web.MastodonAPI.AccountView - alias Pleroma.Web.MastodonAPI.CustomEmojiView - - def initial_state(token, user, custom_emojis) do - limit = Config.get([:instance, :limit]) - - %{ - meta: %{ - streaming_api_base_url: Pleroma.Web.Endpoint.websocket_url(), - access_token: token, - locale: "en", - domain: Pleroma.Web.Endpoint.host(), - admin: "1", - me: "#{user.id}", - unfollow_modal: false, - boost_modal: false, - delete_modal: true, - auto_play_gif: false, - display_sensitive_media: false, - reduce_motion: false, - max_toot_chars: limit, - mascot: User.get_mascot(user)["url"] - }, - poll_limits: Config.get([:instance, :poll_limits]), - rights: %{ - delete_others_notice: present?(user.is_moderator), - admin: present?(user.is_admin) - }, - compose: %{ - me: "#{user.id}", - default_privacy: user.default_scope, - default_sensitive: false, - allow_content_types: Config.get([:instance, :allowed_post_formats]) - }, - media_attachments: %{ - accept_content_types: [ - ".jpg", - ".jpeg", - ".png", - ".gif", - ".webm", - ".mp4", - ".m4v", - "image\/jpeg", - "image\/png", - "image\/gif", - "video\/webm", - "video\/mp4" - ] - }, - settings: user.mastofe_settings || %{}, - push_subscription: nil, - accounts: %{user.id => render(AccountView, "show.json", user: user, for: user)}, - custom_emojis: render(CustomEmojiView, "index.json", custom_emojis: custom_emojis), - char_limit: limit - } - |> Jason.encode!() - |> Phoenix.HTML.raw() - end - - defp present?(nil), do: false - defp present?(false), do: false - defp present?(_), do: true - - def render("manifest.json", _params) do - %{ - name: Config.get([:instance, :name]), - description: Config.get([:instance, :description]), - icons: Config.get([:manifest, :icons]), - theme_color: Config.get([:manifest, :theme_color]), - background_color: Config.get([:manifest, :background_color]), - display: "standalone", - scope: Pleroma.Web.Endpoint.url(), - start_url: Routes.masto_fe_path(Pleroma.Web.Endpoint, :index, ["getting-started"]), - categories: [ - "social" - ], - serviceworker: %{ - src: "/sw.js" - } - } - end -end diff --git a/lib/pleroma/web/views/streamer_view.ex b/lib/pleroma/web/views/streamer_view.ex index 7706035e9..6a55242b0 100644 --- a/lib/pleroma/web/views/streamer_view.ex +++ b/lib/pleroma/web/views/streamer_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.StreamerView do @@ -25,6 +25,20 @@ defmodule Pleroma.Web.StreamerView do |> Jason.encode!() end + def render("status_update.json", %Activity{} = activity, %User{} = user) do + %{ + event: "status.update", + payload: + Pleroma.Web.MastodonAPI.StatusView.render( + "show.json", + activity: activity, + for: user + ) + |> Jason.encode!() + } + |> Jason.encode!() + end + def render("notification.json", %Notification{} = notify, %User{} = user) do %{ event: "notification", @@ -51,6 +65,19 @@ defmodule Pleroma.Web.StreamerView do |> Jason.encode!() end + def render("status_update.json", %Activity{} = activity) do + %{ + event: "status.update", + payload: + Pleroma.Web.MastodonAPI.StatusView.render( + "show.json", + activity: activity + ) + |> Jason.encode!() + } + |> Jason.encode!() + end + def render("chat_update.json", %{chat_message_reference: cm_ref}) do # Explicitly giving the cmr for the object here, so we don't accidentally # send a later 'last_message' that was inserted between inserting this and diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex index 938fc09e3..f95dc2458 100644 --- a/lib/pleroma/web/web_finger.ex +++ b/lib/pleroma/web/web_finger.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.WebFinger do @@ -32,7 +32,13 @@ defmodule Pleroma.Web.WebFinger do def webfinger(resource, fmt) when fmt in ["XML", "JSON"] do host = Pleroma.Web.Endpoint.host() - regex = ~r/(acct:)?(?<username>[a-z0-9A-Z_\.-]+)@#{host}/ + + regex = + if webfinger_domain = Pleroma.Config.get([__MODULE__, :domain]) do + ~r/(acct:)?(?<username>[a-z0-9A-Z_\.-]+)@(#{host}|#{webfinger_domain})/ + else + ~r/(acct:)?(?<username>[a-z0-9A-Z_\.-]+)@#{host}/ + end with %{"username" => username} <- Regex.named_captures(regex, resource), %User{} = user <- User.get_cached_by_nickname(username) do @@ -63,18 +69,14 @@ defmodule Pleroma.Web.WebFinger do end def represent_user(user, "JSON") do - {:ok, user} = User.ensure_keys_present(user) - %{ - "subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}", + "subject" => "acct:#{user.nickname}@#{domain()}", "aliases" => gather_aliases(user), "links" => gather_links(user) } end def represent_user(user, "XML") do - {:ok, user} = User.ensure_keys_present(user) - aliases = user |> gather_aliases() @@ -88,12 +90,16 @@ defmodule Pleroma.Web.WebFinger do :XRD, %{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"}, [ - {:Subject, "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}"} + {:Subject, "acct:#{user.nickname}@#{domain()}"} ] ++ aliases ++ links } |> XmlBuilder.to_doc() end + defp domain do + Pleroma.Config.get([__MODULE__, :domain]) || Pleroma.Web.Endpoint.host() + end + defp webfinger_from_xml(body) do with {:ok, doc} <- XML.parse_document(body) do subject = XML.string_from_xpath("//Subject", doc) @@ -150,17 +156,15 @@ defmodule Pleroma.Web.WebFinger do end def find_lrdd_template(domain) do - with {:ok, %{status: status, body: body}} when status in 200..299 <- - HTTP.get("http://#{domain}/.well-known/host-meta") do + # WebFinger is restricted to HTTPS - https://tools.ietf.org/html/rfc7033#section-9.1 + meta_url = "https://#{domain}/.well-known/host-meta" + + with {:ok, %{status: status, body: body}} when status in 200..299 <- HTTP.get(meta_url) do get_template_from_xml(body) else - _ -> - with {:ok, %{body: body, status: status}} when status in 200..299 <- - HTTP.get("https://#{domain}/.well-known/host-meta") do - get_template_from_xml(body) - else - e -> {:error, "Can't find LRDD template: #{inspect(e)}"} - end + error -> + Logger.warn("Can't find LRDD template in #{inspect(meta_url)}: #{inspect(error)}") + {:error, :lrdd_not_found} end end @@ -174,7 +178,7 @@ defmodule Pleroma.Web.WebFinger do end end - defp get_address_from_domain(_, _), do: nil + defp get_address_from_domain(_, _), do: {:error, :webfinger_no_domain} @spec finger(String.t()) :: {:ok, map()} | {:error, any()} def finger(account) do @@ -191,13 +195,11 @@ defmodule Pleroma.Web.WebFinger do encoded_account = URI.encode("acct:#{account}") with address when is_binary(address) <- get_address_from_domain(domain, encoded_account), - response <- + {:ok, %{status: status, body: body, headers: headers}} when status in 200..299 <- HTTP.get( address, [{"accept", "application/xrd+xml,application/jrd+json"}] - ), - {:ok, %{status: status, body: body, headers: headers}} when status in 200..299 <- - response do + ) do case List.keyfind(headers, "content-type", 0) do {_, content_type} -> case Plug.Conn.Utils.media_type(content_type) do @@ -215,10 +217,9 @@ defmodule Pleroma.Web.WebFinger do {:error, {:content_type, nil}} end else - e -> - Logger.debug(fn -> "Couldn't finger #{account}" end) - Logger.debug(fn -> inspect(e) end) - {:error, e} + error -> + Logger.debug("Couldn't finger #{account}: #{inspect(error)}") + error end end end diff --git a/lib/pleroma/web/web_finger/web_finger_controller.ex b/lib/pleroma/web/web_finger/web_finger_controller.ex index 7944c50ad..9e5efb77f 100644 --- a/lib/pleroma/web/web_finger/web_finger_controller.ex +++ b/lib/pleroma/web/web_finger/web_finger_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.WebFinger.WebFingerController do diff --git a/lib/pleroma/web/xml.ex b/lib/pleroma/web/xml.ex index 0ab6e9d32..b699446b0 100644 --- a/lib/pleroma/web/xml.ex +++ b/lib/pleroma/web/xml.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.XML do diff --git a/lib/pleroma/workers/attachments_cleanup_worker.ex b/lib/pleroma/workers/attachments_cleanup_worker.ex index f5090dae7..4c1764053 100644 --- a/lib/pleroma/workers/attachments_cleanup_worker.ex +++ b/lib/pleroma/workers/attachments_cleanup_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.AttachmentsCleanupWorker do @@ -31,6 +31,9 @@ defmodule Pleroma.Workers.AttachmentsCleanupWorker do def perform(%Job{args: %{"op" => "cleanup_attachments", "object" => _object}}), do: {:ok, :skip} + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(900) + defp do_clean({object_ids, attachment_urls}) do uploader = Pleroma.Config.get([Pleroma.Upload, :uploader]) diff --git a/lib/pleroma/workers/background_worker.ex b/lib/pleroma/workers/background_worker.ex index 1e28384cb..3805293bc 100644 --- a/lib/pleroma/workers/background_worker.ex +++ b/lib/pleroma/workers/background_worker.ex @@ -1,8 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.BackgroundWorker do + alias Pleroma.Instances.Instance alias Pleroma.User use Pleroma.Workers.WorkerHelper, queue: "background" @@ -38,4 +39,11 @@ defmodule Pleroma.Workers.BackgroundWorker do Pleroma.FollowingRelationship.move_following(origin, target) end + + def perform(%Job{args: %{"op" => "delete_instance", "host" => host}}) do + Instance.perform(:delete_instance, host) + end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/backup_worker.ex b/lib/pleroma/workers/backup_worker.ex index 9b763b04b..12ee70f00 100644 --- a/lib/pleroma/workers/backup_worker.ex +++ b/lib/pleroma/workers/backup_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.BackupWorker do @@ -30,6 +30,7 @@ defmodule Pleroma.Workers.BackupWorker do |> Oban.insert() end + @impl Oban.Worker def perform(%Job{ args: %{"op" => "process", "backup_id" => backup_id, "admin_user_id" => admin_user_id} }) do @@ -37,10 +38,7 @@ defmodule Pleroma.Workers.BackupWorker do backup_id |> Backup.get() |> Backup.process(), {:ok, _job} <- schedule_deletion(backup), :ok <- Backup.remove_outdated(backup), - {:ok, _} <- - backup - |> Pleroma.Emails.UserEmail.backup_is_ready_email(admin_user_id) - |> Pleroma.Emails.Mailer.deliver() do + :ok <- maybe_deliver_email(backup, admin_user_id) do {:ok, backup} end end @@ -51,4 +49,26 @@ defmodule Pleroma.Workers.BackupWorker do nil -> :ok end end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(900) + + defp has_email?(user) do + not is_nil(user.email) and user.email != "" + end + + defp maybe_deliver_email(backup, admin_user_id) do + has_mailer = Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled]) + backup = backup |> Pleroma.Repo.preload(:user) + + if has_email?(backup.user) and has_mailer do + backup + |> Pleroma.Emails.UserEmail.backup_is_ready_email(admin_user_id) + |> Pleroma.Emails.Mailer.deliver() + + :ok + else + :ok + end + end end diff --git a/lib/pleroma/workers/cron/digest_emails_worker.ex b/lib/pleroma/workers/cron/digest_emails_worker.ex index 83dc75d60..1540c1605 100644 --- a/lib/pleroma/workers/cron/digest_emails_worker.ex +++ b/lib/pleroma/workers/cron/digest_emails_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.Cron.DigestEmailsWorker do diff --git a/lib/pleroma/workers/cron/new_users_digest_worker.ex b/lib/pleroma/workers/cron/new_users_digest_worker.ex index 9dfd92228..267fe2837 100644 --- a/lib/pleroma/workers/cron/new_users_digest_worker.ex +++ b/lib/pleroma/workers/cron/new_users_digest_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.Cron.NewUsersDigestWorker do diff --git a/lib/pleroma/workers/mailer_worker.ex b/lib/pleroma/workers/mailer_worker.ex index 592230e7a..940716558 100644 --- a/lib/pleroma/workers/mailer_worker.ex +++ b/lib/pleroma/workers/mailer_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.MailerWorker do @@ -12,4 +12,7 @@ defmodule Pleroma.Workers.MailerWorker do |> :erlang.binary_to_term() |> Pleroma.Emails.Mailer.deliver(config) end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/mute_expire_worker.ex b/lib/pleroma/workers/mute_expire_worker.ex index 8da903e76..8ce458d48 100644 --- a/lib/pleroma/workers/mute_expire_worker.ex +++ b/lib/pleroma/workers/mute_expire_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.MuteExpireWorker do @@ -17,4 +17,7 @@ defmodule Pleroma.Workers.MuteExpireWorker do Pleroma.Web.CommonAPI.remove_mute(user_id, activity_id) :ok end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex new file mode 100644 index 000000000..022d026f8 --- /dev/null +++ b/lib/pleroma/workers/poll_worker.ex @@ -0,0 +1,48 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.PollWorker do + @moduledoc """ + Generates notifications when a poll ends. + """ + use Pleroma.Workers.WorkerHelper, queue: "poll_notifications" + + alias Pleroma.Activity + alias Pleroma.Notification + alias Pleroma.Object + + @impl Oban.Worker + def perform(%Job{args: %{"op" => "poll_end", "activity_id" => activity_id}}) do + with %Activity{} = activity <- find_poll_activity(activity_id) do + Notification.create_poll_notifications(activity) + end + end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) + + defp find_poll_activity(activity_id) do + with nil <- Activity.get_by_id(activity_id) do + {:error, :poll_activity_not_found} + end + end + + def schedule_poll_end(%Activity{data: %{"type" => "Create"}, id: activity_id} = activity) do + with %Object{data: %{"type" => "Question", "closed" => closed}} when is_binary(closed) <- + Object.normalize(activity), + {:ok, end_time} <- NaiveDateTime.from_iso8601(closed), + :gt <- NaiveDateTime.compare(end_time, NaiveDateTime.utc_now()) do + %{ + op: "poll_end", + activity_id: activity_id + } + |> new(scheduled_at: end_time) + |> Oban.insert() + else + _ -> {:error, activity} + end + end + + def schedule_poll_end(activity), do: {:error, activity} +end diff --git a/lib/pleroma/workers/publisher_worker.ex b/lib/pleroma/workers/publisher_worker.ex index 6209715b3..598ae3779 100644 --- a/lib/pleroma/workers/publisher_worker.ex +++ b/lib/pleroma/workers/publisher_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.PublisherWorker do @@ -22,4 +22,7 @@ defmodule Pleroma.Workers.PublisherWorker do params = Map.new(params, fn {k, v} -> {String.to_atom(k), v} end) Federator.perform(:publish_one, String.to_atom(module_name), params) end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(10) end diff --git a/lib/pleroma/workers/purge_expired_activity.ex b/lib/pleroma/workers/purge_expired_activity.ex index 027171c1e..e554684fe 100644 --- a/lib/pleroma/workers/purge_expired_activity.ex +++ b/lib/pleroma/workers/purge_expired_activity.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.PurgeExpiredActivity do @@ -35,6 +35,9 @@ defmodule Pleroma.Workers.PurgeExpiredActivity do end end + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) + defp enabled? do with false <- Pleroma.Config.get([__MODULE__, :enabled], false) do {:error, :expired_activities_disabled} diff --git a/lib/pleroma/workers/purge_expired_filter.ex b/lib/pleroma/workers/purge_expired_filter.ex index 4740d52e9..9114aeb7f 100644 --- a/lib/pleroma/workers/purge_expired_filter.ex +++ b/lib/pleroma/workers/purge_expired_filter.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.PurgeExpiredFilter do @@ -31,6 +31,9 @@ defmodule Pleroma.Workers.PurgeExpiredFilter do |> Repo.delete() end + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) + @spec get_expiration(pos_integer()) :: Job.t() | nil def get_expiration(id) do from(j in Job, diff --git a/lib/pleroma/workers/purge_expired_token.ex b/lib/pleroma/workers/purge_expired_token.ex index cfdf5c6dc..2ccd9e80b 100644 --- a/lib/pleroma/workers/purge_expired_token.ex +++ b/lib/pleroma/workers/purge_expired_token.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.PurgeExpiredToken do @@ -26,4 +26,7 @@ defmodule Pleroma.Workers.PurgeExpiredToken do |> Pleroma.Repo.get(id) |> Pleroma.Repo.delete() end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex index 69125dcd0..4f513b907 100644 --- a/lib/pleroma/workers/receiver_worker.ex +++ b/lib/pleroma/workers/receiver_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.ReceiverWorker do @@ -9,6 +9,15 @@ defmodule Pleroma.Workers.ReceiverWorker do @impl Oban.Worker def perform(%Job{args: %{"op" => "incoming_ap_doc", "params" => params}}) do - Federator.perform(:incoming_ap_doc, params) + with {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do + {:ok, res} + else + {:error, :origin_containment_failed} -> {:cancel, :origin_containment_failed} + {:error, {:reject, reason}} -> {:cancel, reason} + e -> e + end end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/remote_fetcher_worker.ex b/lib/pleroma/workers/remote_fetcher_worker.ex index ad4d785a1..d2a77aa17 100644 --- a/lib/pleroma/workers/remote_fetcher_worker.ex +++ b/lib/pleroma/workers/remote_fetcher_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.RemoteFetcherWorker do @@ -11,4 +11,7 @@ defmodule Pleroma.Workers.RemoteFetcherWorker do def perform(%Job{args: %{"op" => "fetch_remote", "id" => id} = args}) do {:ok, _object} = Fetcher.fetch_object_from_id(id, depth: args["depth"]) end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(10) end diff --git a/lib/pleroma/workers/scheduled_activity_worker.ex b/lib/pleroma/workers/scheduled_activity_worker.ex index a4ab9928d..4df84d00f 100644 --- a/lib/pleroma/workers/scheduled_activity_worker.ex +++ b/lib/pleroma/workers/scheduled_activity_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.ScheduledActivityWorker do @@ -37,6 +37,9 @@ defmodule Pleroma.Workers.ScheduledActivityWorker do end end + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) + defp find_scheduled_activity(id) do with nil <- Repo.get(ScheduledActivity, id) do {:error, :scheduled_activity_not_found} diff --git a/lib/pleroma/workers/transmogrifier_worker.ex b/lib/pleroma/workers/transmogrifier_worker.ex index b39c1ea62..1f3f5385e 100644 --- a/lib/pleroma/workers/transmogrifier_worker.ex +++ b/lib/pleroma/workers/transmogrifier_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.TransmogrifierWorker do @@ -12,4 +12,7 @@ defmodule Pleroma.Workers.TransmogrifierWorker do user = User.get_cached_by_id(user_id) Pleroma.Web.ActivityPub.Transmogrifier.perform(:user_upgrade, user) end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/web_pusher_worker.ex b/lib/pleroma/workers/web_pusher_worker.ex index 8fc2aff26..67e84b0c9 100644 --- a/lib/pleroma/workers/web_pusher_worker.ex +++ b/lib/pleroma/workers/web_pusher_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.WebPusherWorker do @@ -17,4 +17,7 @@ defmodule Pleroma.Workers.WebPusherWorker do Pleroma.Web.Push.Impl.perform(notification) end + + @impl Oban.Worker + def timeout(_job), do: :timer.seconds(5) end diff --git a/lib/pleroma/workers/worker_helper.ex b/lib/pleroma/workers/worker_helper.ex index 4befbeb3b..1d20cbd89 100644 --- a/lib/pleroma/workers/worker_helper.ex +++ b/lib/pleroma/workers/worker_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.WorkerHelper do diff --git a/lib/pleroma/xml_builder.ex b/lib/pleroma/xml_builder.ex index 922d3f6ee..cd74cfbec 100644 --- a/lib/pleroma/xml_builder.ex +++ b/lib/pleroma/xml_builder.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.XmlBuilder do |