diff options
author | lain <lain@soykaf.club> | 2020-09-17 14:09:01 +0000 |
---|---|---|
committer | lain <lain@soykaf.club> | 2020-09-17 14:09:01 +0000 |
commit | 7c60789ba540b8346c1a642b49c29df213217655 (patch) | |
tree | 9df5bb75df8f1344768ebe4d26a14d1043820667 /lib | |
parent | f58262c673616661baf9750c216a07d239ae99c3 (diff) | |
parent | 5426eb59976044f88247486da86a91b8f9eb35b5 (diff) | |
download | pleroma-7c60789ba540b8346c1a642b49c29df213217655.tar.gz |
Merge branch 'develop' into 'nondiscoverable-user-metadata'
# Conflicts:
# CHANGELOG.md
Diffstat (limited to 'lib')
33 files changed, 567 insertions, 272 deletions
diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex index 7f1108dcf..a01c36ece 100644 --- a/lib/mix/tasks/pleroma/database.ex +++ b/lib/mix/tasks/pleroma/database.ex @@ -99,7 +99,7 @@ defmodule Mix.Tasks.Pleroma.Database do where: fragment("(?)->>'likes' is not null", object.data), select: %{id: object.id, likes: fragment("(?)->>'likes'", object.data)} ) - |> Pleroma.RepoStreamer.chunk_stream(100) + |> Pleroma.Repo.chunk_stream(100, :batches) |> Stream.each(fn objects -> ids = objects @@ -145,7 +145,7 @@ defmodule Mix.Tasks.Pleroma.Database do |> where(local: true) |> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data)) |> where([_a, o], fragment("?->>'type' = 'Note'", o.data)) - |> Pleroma.RepoStreamer.chunk_stream(100) + |> Pleroma.Repo.chunk_stream(100, :batches) |> Stream.each(fn activities -> Enum.each(activities, fn activity -> expires_at = diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 01824aa18..b20c49d89 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -179,7 +179,7 @@ defmodule Mix.Tasks.Pleroma.User do start_pleroma() Pleroma.User.Query.build(%{nickname: "@#{instance}"}) - |> Pleroma.RepoStreamer.chunk_stream(500) + |> Pleroma.Repo.chunk_stream(500, :batches) |> Stream.each(fn users -> users |> Enum.each(fn user -> @@ -370,7 +370,7 @@ defmodule Mix.Tasks.Pleroma.User do start_pleroma() Pleroma.User.Query.build(%{local: true}) - |> Pleroma.RepoStreamer.chunk_stream(500) + |> Pleroma.Repo.chunk_stream(500, :batches) |> Stream.each(fn users -> users |> Enum.each(fn user -> diff --git a/lib/pleroma/chat.ex b/lib/pleroma/chat.ex index 24a86371e..84f8806a0 100644 --- a/lib/pleroma/chat.ex +++ b/lib/pleroma/chat.ex @@ -6,7 +6,9 @@ defmodule Pleroma.Chat do use Ecto.Schema import Ecto.Changeset + import Ecto.Query + alias Pleroma.Chat alias Pleroma.Repo alias Pleroma.User @@ -69,4 +71,12 @@ defmodule Pleroma.Chat do conflict_target: [:user_id, :recipient] ) end + + @spec for_user_query(FlakeId.Ecto.CompatType.t()) :: Ecto.Query.t() + def for_user_query(user_id) do + from(c in Chat, + where: c.user_id == ^user_id, + order_by: [desc: c.updated_at] + ) + end end diff --git a/lib/pleroma/gun/connection_pool/worker.ex b/lib/pleroma/gun/connection_pool/worker.ex index 49d41e4c7..bf57e9e5f 100644 --- a/lib/pleroma/gun/connection_pool/worker.ex +++ b/lib/pleroma/gun/connection_pool/worker.ex @@ -93,25 +93,18 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do end) {ref, state} = pop_in(state.client_monitors[client_pid]) - # DOWN message can receive right after `remove_client` call and cause worker to terminate - state = - if is_nil(ref) do - state - else - Process.demonitor(ref) - timer = - if used_by == [] do - max_idle = Pleroma.Config.get([:connections_pool, :max_idle_time], 30_000) - Process.send_after(self(), :idle_close, max_idle) - else - nil - end + Process.demonitor(ref, [:flush]) - %{state | timer: timer} + timer = + if used_by == [] do + max_idle = Pleroma.Config.get([:connections_pool, :max_idle_time], 30_000) + Process.send_after(self(), :idle_close, max_idle) + else + nil end - {:reply, :ok, state, :hibernate} + {:reply, :ok, %{state | timer: timer}, :hibernate} end @impl true diff --git a/lib/pleroma/migration_helper/notification_backfill.ex b/lib/pleroma/migration_helper/notification_backfill.ex index d260e62ca..24f4733fe 100644 --- a/lib/pleroma/migration_helper/notification_backfill.ex +++ b/lib/pleroma/migration_helper/notification_backfill.ex @@ -19,13 +19,13 @@ defmodule Pleroma.MigrationHelper.NotificationBackfill do query |> Repo.chunk_stream(100) |> Enum.each(fn notification -> - type = - notification.activity - |> type_from_activity() + if notification.activity do + type = type_from_activity(notification.activity) - notification - |> Ecto.Changeset.change(%{type: type}) - |> Repo.update() + notification + |> Ecto.Changeset.change(%{type: type}) + |> Repo.update() + end end) end @@ -72,8 +72,7 @@ defmodule Pleroma.MigrationHelper.NotificationBackfill do "pleroma:emoji_reaction" "Create" -> - activity - |> type_from_activity_object() + type_from_activity_object(activity) t -> raise "No notification type for activity type #{t}" diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex index 31c9afe2a..47036a6f6 100644 --- a/lib/pleroma/moderation_log.ex +++ b/lib/pleroma/moderation_log.ex @@ -320,6 +320,19 @@ defmodule Pleroma.ModerationLog do |> insert_log_entry_with_message() end + @spec insert_log(%{actor: User, action: String.t(), subject_id: String.t()}) :: + {:ok, ModerationLog} | {:error, any} + def insert_log(%{actor: %User{} = actor, action: "chat_message_delete", subject_id: subject_id}) do + %ModerationLog{ + data: %{ + "actor" => %{"nickname" => actor.nickname}, + "action" => "chat_message_delete", + "subject_id" => subject_id + } + } + |> insert_log_entry_with_message() + end + @spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any} defp insert_log_entry_with_message(entry) do entry.data["message"] @@ -627,6 +640,17 @@ defmodule Pleroma.ModerationLog do "@#{actor_nickname} updated users: #{users_to_nicknames_string(subjects)}" end + @spec get_log_entry_message(ModerationLog) :: String.t() + def get_log_entry_message(%ModerationLog{ + data: %{ + "actor" => %{"nickname" => actor_nickname}, + "action" => "chat_message_delete", + "subject_id" => subject_id + } + }) do + "@#{actor_nickname} deleted chat message ##{subject_id}" + end + defp nicknames_to_string(nicknames) do nicknames |> Enum.map(&"@#{&1}") diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index 1de2ce6c3..24dc7cb95 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -98,8 +98,8 @@ defmodule Pleroma.Object.Fetcher do {:containment, _} -> {:error, "Object containment failed."} - {:transmogrifier, {:error, {:reject, nil}}} -> - {:reject, nil} + {:transmogrifier, {:error, {:reject, e}}} -> + {:reject, e} {:transmogrifier, _} = e -> {:error, e} diff --git a/lib/pleroma/repo.ex b/lib/pleroma/repo.ex index f317e4d58..4524bd5e2 100644 --- a/lib/pleroma/repo.ex +++ b/lib/pleroma/repo.ex @@ -49,7 +49,21 @@ defmodule Pleroma.Repo do end end - def chunk_stream(query, chunk_size) do + @doc """ + Returns a lazy enumerable that emits all entries from the data store matching the given query. + + `returns_as` use to group records. use the `batches` option to fetch records in bulk. + + ## Examples + + # fetch records one-by-one + iex> Pleroma.Repo.chunk_stream(Pleroma.Activity.Queries.by_actor(ap_id), 500) + + # fetch records in bulk + iex> Pleroma.Repo.chunk_stream(Pleroma.Activity.Queries.by_actor(ap_id), 500, :batches) + """ + @spec chunk_stream(Ecto.Query.t(), integer(), atom()) :: Enumerable.t() + def chunk_stream(query, chunk_size, returns_as \\ :one) do # We don't actually need start and end funcitons of resource streaming, # but it seems to be the only way to not fetch records one-by-one and # have individual records be the elements of the stream, instead of @@ -69,7 +83,12 @@ defmodule Pleroma.Repo do records -> last_id = List.last(records).id - {records, last_id} + + if returns_as == :one do + {records, last_id} + else + {[records], last_id} + end end end, fn _ -> :ok end diff --git a/lib/pleroma/repo_streamer.ex b/lib/pleroma/repo_streamer.ex deleted file mode 100644 index cb4d7bb7a..000000000 --- a/lib/pleroma/repo_streamer.ex +++ /dev/null @@ -1,34 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.RepoStreamer do - alias Pleroma.Repo - import Ecto.Query - - def chunk_stream(query, chunk_size) do - Stream.unfold(0, fn - :halt -> - {[], :halt} - - last_id -> - query - |> order_by(asc: :id) - |> where([r], r.id > ^last_id) - |> limit(^chunk_size) - |> Repo.all() - |> case do - [] -> - {[], :halt} - - records -> - last_id = List.last(records).id - {records, last_id} - end - end) - |> Stream.take_while(fn - [] -> false - _ -> true - end) - end -end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index e73d19964..1ffe60dfc 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -25,7 +25,6 @@ defmodule Pleroma.User do alias Pleroma.Object alias Pleroma.Registration alias Pleroma.Repo - alias Pleroma.RepoStreamer alias Pleroma.User alias Pleroma.UserRelationship alias Pleroma.Web @@ -276,9 +275,9 @@ defmodule Pleroma.User do @spec account_status(User.t()) :: account_status() def account_status(%User{deactivated: true}), do: :deactivated def account_status(%User{password_reset_pending: true}), do: :password_reset_pending - def account_status(%User{approval_pending: true}), do: :approval_pending + def account_status(%User{local: true, approval_pending: true}), do: :approval_pending - def account_status(%User{confirmation_pending: true}) do + def account_status(%User{local: true, confirmation_pending: true}) do if Config.get([:instance, :account_activation_required]) do :confirmation_pending else @@ -1775,7 +1774,7 @@ defmodule Pleroma.User do def delete_user_activities(%User{ap_id: ap_id} = user) do ap_id |> Activity.Queries.by_actor() - |> RepoStreamer.chunk_stream(50) + |> Repo.chunk_stream(50, :batches) |> Stream.each(fn activities -> Enum.each(activities, fn activity -> delete_activity(activity, user) end) end) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 66a9f78a3..92fc1e422 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -84,7 +84,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp increase_replies_count_if_reply(_create_data), do: :noop - @object_types ~w[ChatMessage Question Answer Audio Event] + @object_types ~w[ChatMessage Question Answer Audio Video Event Article] @spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()} def persist(%{"type" => type} = object, meta) when type in @object_types do with {:ok, object} <- Object.create(object) do @@ -154,8 +154,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {:remote_limit_pass, _} -> {:error, :remote_limit} - {:reject, reason} -> - {:error, reason} + {:reject, _} = e -> + {:error, e} end end @@ -767,7 +767,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end defp restrict_replies(query, %{ - reply_filtering_user: user, + reply_filtering_user: %User{} = user, reply_visibility: "self" }) do from( @@ -783,7 +783,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end defp restrict_replies(query, %{ - reply_filtering_user: user, + reply_filtering_user: %User{} = user, reply_visibility: "following" }) do from( diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index bb193475a..161177727 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -66,7 +66,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do "type" => "Create", "object" => child_object } = object - ) do + ) + when is_map(child_object) do media_nsfw = Config.get([:mrf_simple, :media_nsfw]) |> MRF.subdomains_regex() diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index b77c06395..bd0a2a8dc 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -12,11 +12,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do alias Pleroma.Activity alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Object + alias Pleroma.Object.Containment alias Pleroma.User alias Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator - alias Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator @@ -149,10 +151,20 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do end end - def validate(%{"type" => "Audio"} = object, meta) do + def validate(%{"type" => type} = object, meta) when type in ~w[Audio Video] do with {:ok, object} <- object - |> AudioValidator.cast_and_validate() + |> AudioVideoValidator.cast_and_validate() + |> Ecto.Changeset.apply_action(:insert) do + object = stringify_keys(object) + {:ok, object, meta} + end + end + + def validate(%{"type" => "Article"} = object, meta) do + with {:ok, object} <- + object + |> ArticleNoteValidator.cast_and_validate() |> Ecto.Changeset.apply_action(:insert) do object = stringify_keys(object) {:ok, object, meta} @@ -198,7 +210,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do %{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity, meta ) - when objtype in ~w[Question Answer Audio Event] do + when objtype in ~w[Question Answer Audio Video Event Article] do with {:ok, object_data} <- cast_and_apply(object), meta = Keyword.put(meta, :object_data, object_data |> stringify_keys), {:ok, create_activity} <- @@ -232,14 +244,18 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do AnswerValidator.cast_and_apply(object) end - def cast_and_apply(%{"type" => "Audio"} = object) do - AudioValidator.cast_and_apply(object) + def cast_and_apply(%{"type" => type} = object) when type in ~w[Audio Video] do + AudioVideoValidator.cast_and_apply(object) end def cast_and_apply(%{"type" => "Event"} = object) do EventValidator.cast_and_apply(object) end + def cast_and_apply(%{"type" => "Article"} = object) do + ArticleNoteValidator.cast_and_apply(object) + end + def cast_and_apply(o), do: {:error, {:validator_not_set, o}} # is_struct/1 isn't present in Elixir 1.8.x @@ -262,7 +278,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do def stringify_keys(object), do: object def fetch_actor(object) do - with {:ok, actor} <- ObjectValidators.ObjectID.cast(object["actor"]) do + with actor <- Containment.get_actor(object), + {:ok, actor} <- ObjectValidators.ObjectID.cast(actor) do User.get_or_fetch_by_ap_id(actor) end end diff --git a/lib/pleroma/web/activity_pub/object_validators/audio_validator.ex b/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex index 1a97c504a..5b7dad517 100644 --- a/lib/pleroma/web/activity_pub/object_validators/audio_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator do +defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator do use Ecto.Schema alias Pleroma.EctoType.ActivityPub.ObjectValidators @@ -25,14 +25,19 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator do # TODO: Write type field(:tag, {:array, :map}, default: []) 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(:summary, :string) field(:published, ObjectValidators.DateTime) field(:emoji, ObjectValidators.Emoji, default: %{}) field(:sensitive, :boolean, default: false) @@ -40,13 +45,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator do field(:replies_count, :integer, default: 0) field(:like_count, :integer, default: 0) field(:announcement_count, :integer, default: 0) - field(:inReplyTo, :string) + field(:inReplyTo, ObjectValidators.ObjectID) field(:url, ObjectValidators.Uri) - # short identifier for PleromaFE to group statuses by context - field(:context_id, :integer) - field(:likes, {:array, :string}, default: []) - field(:announcements, {:array, :string}, default: []) + field(:likes, {:array, ObjectValidators.ObjectID}, default: []) + field(:announcements, {:array, ObjectValidators.ObjectID}, default: []) end def cast_and_apply(data) do @@ -62,19 +65,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator do end def cast_data(data) do + data = fix(data) + %__MODULE__{} |> changeset(data) end - defp fix_url(%{"url" => url} = data) when is_list(url) do - attachment = - Enum.find(url, fn x -> is_map(x) and String.starts_with?(x["mimeType"], "audio/") end) - - link_element = Enum.find(url, fn x -> is_map(x) and x["mimeType"] == "text/html" end) - - data - |> Map.put("attachment", [attachment]) - |> Map.put("url", link_element["href"]) + defp fix_url(%{"url" => url} = data) when is_map(url) do + Map.put(data, "url", url["href"]) end defp fix_url(data), do: data @@ -83,8 +81,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator do data |> CommonFixes.fix_defaults() |> CommonFixes.fix_attribution() - |> Transmogrifier.fix_emoji() + |> CommonFixes.fix_actor() |> fix_url() + |> Transmogrifier.fix_emoji() end def changeset(struct, data) do @@ -97,8 +96,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator do def validate_data(data_cng) do data_cng - |> validate_inclusion(:type, ["Audio"]) - |> validate_required([:id, :actor, :attributedTo, :type, :context, :attachment]) + |> validate_inclusion(:type, ["Article", "Note"]) + |> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id]) |> 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 c8b148280..df102a134 100644 --- a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do use Ecto.Schema + alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Web.ActivityPub.ObjectValidators.UrlObjectValidator import Ecto.Changeset @@ -15,7 +16,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do field(:mediaType, :string, default: "application/octet-stream") field(:name, :string) - embeds_many(:url, UrlObjectValidator) + embeds_many :url, UrlObjectValidator, primary_key: false do + field(:type, :string) + field(:href, ObjectValidators.Uri) + field(:mediaType, :string, default: "application/octet-stream") + end end def cast_and_validate(data) do @@ -37,7 +42,18 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do struct |> cast(data, [:type, :mediaType, :name]) - |> cast_embed(:url, required: true) + |> cast_embed(:url, with: &url_changeset/2) + |> validate_inclusion(:type, ~w[Link Document Audio Image Video]) + |> validate_required([:type, :mediaType, :url]) + end + + def url_changeset(struct, data) do + data = fix_media_type(data) + + struct + |> cast(data, [:type, :href, :mediaType]) + |> validate_inclusion(:type, ["Link"]) + |> validate_required([:type, :href, :mediaType]) end def fix_media_type(data) do @@ -75,6 +91,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do def validate_data(cng) do cng + |> validate_inclusion(:type, ~w[Document Audio Image Video]) |> validate_required([:mediaType, :url, :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 new file mode 100644 index 000000000..16973e5db --- /dev/null +++ b/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex @@ -0,0 +1,134 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do + use Ecto.Schema + + alias Pleroma.EarmarkRenderer + 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.Transmogrifier + + import Ecto.Changeset + + @primary_key false + @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: []) + # TODO: Write type + field(:tag, {:array, :map}, default: []) + 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: []) + end + + def cast_and_apply(data) do + data + |> cast_data + |> apply_action(:insert) + end + + def cast_and_validate(data) do + data + |> cast_data() + |> validate_data() + end + + def cast_data(data) do + %__MODULE__{} + |> changeset(data) + end + + defp fix_url(%{"url" => url} = data) when is_list(url) do + attachment = + Enum.find(url, fn x -> + mime_type = x["mimeType"] || x["mediaType"] || "" + + is_map(x) and String.starts_with?(mime_type, ["video/", "audio/"]) + end) + + link_element = + Enum.find(url, fn x -> + mime_type = x["mimeType"] || x["mediaType"] || "" + + is_map(x) and mime_type == "text/html" + end) + + data + |> Map.put("attachment", [attachment]) + |> Map.put("url", link_element["href"]) + end + + defp fix_url(data), do: data + + defp fix_content(%{"mediaType" => "text/markdown", "content" => content} = data) + when is_binary(content) do + content = + content + |> Earmark.as_html!(%Earmark.Options{renderer: EarmarkRenderer}) + |> Pleroma.HTML.filter_tags() + + Map.put(data, "content", content) + end + + defp fix_content(data), do: data + + defp fix(data) do + data + |> CommonFixes.fix_defaults() + |> CommonFixes.fix_attribution() + |> CommonFixes.fix_actor() + |> Transmogrifier.fix_emoji() + |> fix_url() + |> fix_content() + end + + def changeset(struct, data) do + data = fix(data) + + struct + |> cast(data, __schema__(:fields) -- [:attachment]) + |> cast_embed(:attachment) + end + + def validate_data(data_cng) do + data_cng + |> validate_inclusion(:type, ["Audio", "Video"]) + |> validate_required([:id, :actor, :attributedTo, :type, :context, :attachment]) + |> CommonValidations.validate_any_presence([:cc, :to]) + |> CommonValidations.validate_fields_match([:actor, :attributedTo]) + |> CommonValidations.validate_actor_presence() + |> CommonValidations.validate_host_match() + 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 720213d73..b3638cfc7 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -3,6 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do + alias Pleroma.Object.Containment alias Pleroma.Web.ActivityPub.Utils # based on Pleroma.Web.ActivityPub.Utils.lazy_put_objects_defaults @@ -19,4 +20,12 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do data |> Map.put_new("actor", data["attributedTo"]) end + + def fix_actor(data) do + actor = Containment.get_actor(data) + + data + |> Map.put("actor", actor) + |> Map.put("attributedTo", actor) + end end 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 b3dbeea57..422ee07be 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 @@ -10,9 +10,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Object + alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes + alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations import Ecto.Changeset - import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations @primary_key false @@ -75,14 +76,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do data |> fix_context(meta) |> fix_addressing(meta) + |> CommonFixes.fix_actor() end def validate_data(cng, meta \\ []) do cng |> validate_required([:actor, :type, :object]) |> validate_inclusion(:type, ["Create"]) - |> validate_actor_presence() - |> validate_any_presence([:to, :cc]) + |> CommonValidations.validate_actor_presence() + |> CommonValidations.validate_any_presence([:to, :cc]) |> validate_actors_match(meta) |> validate_context_match(meta) |> validate_object_nonexistence() diff --git a/lib/pleroma/web/activity_pub/object_validators/note_validator.ex b/lib/pleroma/web/activity_pub/object_validators/note_validator.ex deleted file mode 100644 index ab4469a59..000000000 --- a/lib/pleroma/web/activity_pub/object_validators/note_validator.ex +++ /dev/null @@ -1,73 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do - use Ecto.Schema - - alias Pleroma.EctoType.ActivityPub.ObjectValidators - alias Pleroma.Web.ActivityPub.Transmogrifier - - import Ecto.Changeset - - @primary_key false - - 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: []) - # TODO: Write type - field(:tag, {:array, :map}, default: []) - 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) - - field(:actor, ObjectValidators.ObjectID) - field(:attributedTo, ObjectValidators.ObjectID) - field(:published, ObjectValidators.DateTime) - field(:emoji, ObjectValidators.Emoji, default: %{}) - field(:sensitive, :boolean, default: false) - # TODO: Write type - field(:attachment, {:array, :map}, default: []) - 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, :string}, default: []) - field(:announcements, {:array, :string}, default: []) - end - - def cast_and_validate(data) do - data - |> cast_data() - |> validate_data() - end - - defp fix(data) do - data - |> Transmogrifier.fix_emoji() - end - - def cast_data(data) do - data = fix(data) - - %__MODULE__{} - |> cast(data, __schema__(:fields)) - end - - def validate_data(data_cng) do - data_cng - |> validate_inclusion(:type, ["Note"]) - |> validate_required([:id, :actor, :to, :cc, :type, :content, :context]) - end -end 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 934d3c1ea..9310485dc 100644 --- a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex @@ -47,8 +47,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do # short identifier for PleromaFE to group statuses by context field(:context_id, :integer) - field(:likes, {:array, :string}, default: []) - field(:announcements, {:array, :string}, default: []) + field(:likes, {:array, ObjectValidators.ObjectID}, default: []) + field(:announcements, {:array, ObjectValidators.ObjectID}, default: []) field(:closed, ObjectValidators.DateTime) field(:voters, {:array, ObjectValidators.ObjectID}, default: []) diff --git a/lib/pleroma/web/activity_pub/object_validators/url_object_validator.ex b/lib/pleroma/web/activity_pub/object_validators/url_object_validator.ex deleted file mode 100644 index 881030f38..000000000 --- a/lib/pleroma/web/activity_pub/object_validators/url_object_validator.ex +++ /dev/null @@ -1,24 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.UrlObjectValidator do - use Ecto.Schema - - alias Pleroma.EctoType.ActivityPub.ObjectValidators - - import Ecto.Changeset - @primary_key false - - embedded_schema do - field(:type, :string) - field(:href, ObjectValidators.Uri) - field(:mediaType, :string, default: "application/octet-stream") - end - - def changeset(struct, data) do - struct - |> cast(data, __schema__(:fields)) - |> validate_required([:type, :href, :mediaType]) - end -end diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 46a8be767..b9a83a544 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -336,7 +336,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do end def handle_object_creation(%{"type" => objtype} = object, meta) - when objtype in ~w[Audio Question Event] do + when objtype in ~w[Audio Video Question Event Article] do with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do {:ok, object, meta} end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index af4384213..fcca014f0 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -7,7 +7,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do A module to handle coding from internal to wire ActivityPub and back. """ alias Pleroma.Activity - alias Pleroma.EarmarkRenderer alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Maps alias Pleroma.Object @@ -45,7 +44,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> fix_addressing |> fix_summary |> fix_type(options) - |> fix_content end def fix_summary(%{"summary" => nil} = object) do @@ -274,24 +272,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do Map.put(object, "url", url["href"]) end - def fix_url(%{"type" => "Video", "url" => url} = object) when is_list(url) do - attachment = - Enum.find(url, fn x -> - media_type = x["mediaType"] || x["mimeType"] || "" - - is_map(x) and String.starts_with?(media_type, "video/") - end) - - link_element = - Enum.find(url, fn x -> is_map(x) and (x["mediaType"] || x["mimeType"]) == "text/html" end) - - object - |> Map.put("attachment", [attachment]) - |> Map.put("url", link_element["href"]) - end - - def fix_url(%{"type" => object_type, "url" => url} = object) - when object_type != "Video" and is_list(url) do + def fix_url(%{"url" => url} = object) when is_list(url) do first_element = Enum.at(url, 0) url_string = @@ -309,7 +290,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def fix_emoji(%{"tag" => tags} = object) when is_list(tags) do emoji = tags - |> Enum.filter(fn data -> data["type"] == "Emoji" and data["icon"] end) + |> Enum.filter(fn data -> is_map(data) and data["type"] == "Emoji" and data["icon"] end) |> Enum.reduce(%{}, fn data, mapping -> name = String.trim(data["name"], ":") @@ -371,18 +352,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def fix_type(object, _), do: object - defp fix_content(%{"mediaType" => "text/markdown", "content" => content} = object) - when is_binary(content) do - html_content = - content - |> Earmark.as_html!(%Earmark.Options{renderer: EarmarkRenderer}) - |> Pleroma.HTML.filter_tags() - - Map.merge(object, %{"content" => html_content, "mediaType" => "text/html"}) - end - - defp fix_content(object), do: object - # Reduce the object list to find the reported user. defp get_reported(objects) do Enum.reduce_while(objects, nil, fn ap_id, _ -> @@ -455,7 +424,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do %{"type" => "Create", "object" => %{"type" => objtype} = object} = data, options ) - when objtype in ~w{Article Note Video Page} do + when objtype in ~w{Note Page} do actor = Containment.get_actor(data) with nil <- Activity.get_create_by_object_ap_id(object["id"]), @@ -549,7 +518,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do %{"type" => "Create", "object" => %{"type" => objtype}} = data, _options ) - when objtype in ~w{Question Answer ChatMessage Audio Event} do + when objtype in ~w{Question Answer ChatMessage Audio Video Event Article} do + data = Map.put(data, "object", strip_internal_fields(data["object"])) + with {:ok, %User{}} <- ObjectValidator.fetch_actor(data), {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do {:ok, activity} 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 f5e4d49f9..d5713c3dd 100644 --- a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex @@ -23,8 +23,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do alias Pleroma.Web.Endpoint alias Pleroma.Web.Router - require Logger - @users_page_size 50 plug( @@ -70,6 +68,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, + %{scopes: ["read:chats"], admin: true} + when action in [:list_user_chats] + ) + + plug( + OAuthScopesPlug, %{scopes: ["read"], admin: true} when action in [ :list_log, @@ -256,6 +260,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end end + def list_user_chats(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = _params) do + with %User{id: user_id} <- User.get_cached_by_nickname_or_id(nickname, for: admin) do + chats = + Pleroma.Chat.for_user_query(user_id) + |> Pleroma.Repo.all() + + conn + |> put_view(AdminAPI.ChatView) + |> render("index.json", chats: chats) + else + _ -> {:error, :not_found} + end + end + def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do user = User.get_cached_by_nickname(nickname) diff --git a/lib/pleroma/web/admin_api/controllers/chat_controller.ex b/lib/pleroma/web/admin_api/controllers/chat_controller.ex new file mode 100644 index 000000000..967600d69 --- /dev/null +++ b/lib/pleroma/web/admin_api/controllers/chat_controller.ex @@ -0,0 +1,85 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.ChatController do + use Pleroma.Web, :controller + + alias Pleroma.Activity + alias Pleroma.Chat + alias Pleroma.Chat.MessageReference + alias Pleroma.ModerationLog + alias Pleroma.Pagination + alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Web.AdminAPI + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView + + require Logger + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + plug( + OAuthScopesPlug, + %{scopes: ["read:chats"], admin: true} when action in [:show, :messages] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:chats"], admin: true} when action in [:delete_message] + ) + + action_fallback(Pleroma.Web.AdminAPI.FallbackController) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ChatOperation + + def delete_message(%{assigns: %{user: user}} = conn, %{ + message_id: message_id, + id: chat_id + }) do + with %MessageReference{object: %{data: %{"id" => object_ap_id}}} = cm_ref <- + MessageReference.get_by_id(message_id), + ^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) + else + _e -> + {:error, :could_not_delete} + end + end + + def messages(conn, %{id: id} = params) do + with %Chat{} = chat <- Chat.get_by_id(id) do + cm_refs = + chat + |> MessageReference.for_chat_query() + |> Pagination.fetch_paginated(params) + + conn + |> put_view(MessageReferenceView) + |> render("index.json", chat_message_references: cm_refs) + else + _ -> + conn + |> put_status(:not_found) + |> json(%{error: "not found"}) + end + end + + def show(conn, %{id: id}) do + with %Chat{} = chat <- Chat.get_by_id(id) do + conn + |> put_view(AdminAPI.ChatView) + |> render("show.json", chat: chat) + end + end +end diff --git a/lib/pleroma/web/admin_api/views/chat_view.ex b/lib/pleroma/web/admin_api/views/chat_view.ex new file mode 100644 index 000000000..847df1423 --- /dev/null +++ b/lib/pleroma/web/admin_api/views/chat_view.ex @@ -0,0 +1,30 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.ChatView do + use Pleroma.Web, :view + + alias Pleroma.Chat + alias Pleroma.User + alias Pleroma.Web.MastodonAPI + alias Pleroma.Web.PleromaAPI + + def render("index.json", %{chats: chats} = opts) do + render_many(chats, __MODULE__, "show.json", Map.delete(opts, :chats)) + end + + def render("show.json", %{chat: %Chat{user_id: user_id}} = opts) do + user = User.get_by_id(user_id) + sender = MastodonAPI.AccountView.render("show.json", user: user, skip_visibility_check: true) + + serialized_chat = PleromaAPI.ChatView.render("show.json", opts) + + serialized_chat + |> Map.put(:sender, sender) + |> Map.put(:receiver, serialized_chat[:account]) + |> Map.delete(:account) + end + + def render(view, opts), do: PleromaAPI.ChatView.render(view, opts) +end diff --git a/lib/pleroma/web/admin_api/views/status_view.ex b/lib/pleroma/web/admin_api/views/status_view.ex index 500800be2..6042a22b6 100644 --- a/lib/pleroma/web/admin_api/views/status_view.ex +++ b/lib/pleroma/web/admin_api/views/status_view.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Web.AdminAPI.StatusView do require Pleroma.Constants alias Pleroma.Web.AdminAPI + alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI defdelegate merge_account_views(user), to: AdminAPI.AccountView @@ -17,7 +18,7 @@ defmodule Pleroma.Web.AdminAPI.StatusView do end def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do - user = MastodonAPI.StatusView.get_user(activity.data["actor"]) + user = CommonAPI.get_user(activity.data["actor"]) MastodonAPI.StatusView.render("show.json", opts) |> Map.merge(%{account: merge_account_views(user)}) diff --git a/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex b/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex new file mode 100644 index 000000000..d3e5dfc1c --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex @@ -0,0 +1,96 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Admin.ChatOperation do + alias OpenApiSpex.Operation + alias Pleroma.Web.ApiSpec.Schemas.Chat + alias Pleroma.Web.ApiSpec.Schemas.ChatMessage + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def delete_message_operation do + %Operation{ + tags: ["admin", "chat"], + summary: "Delete an individual chat message", + operationId: "AdminAPI.ChatController.delete_message", + parameters: [ + Operation.parameter(:id, :path, :string, "The ID of the Chat"), + Operation.parameter(:message_id, :path, :string, "The ID of the message") + ], + responses: %{ + 200 => + Operation.response( + "The deleted ChatMessage", + "application/json", + ChatMessage + ) + }, + security: [ + %{ + "oAuth" => ["write:chats"] + } + ] + } + end + + def messages_operation do + %Operation{ + tags: ["admin", "chat"], + summary: "Get the most recent messages of the chat", + operationId: "AdminAPI.ChatController.messages", + parameters: + [Operation.parameter(:id, :path, :string, "The ID of the Chat")] ++ + pagination_params(), + responses: %{ + 200 => + Operation.response( + "The messages in the chat", + "application/json", + Pleroma.Web.ApiSpec.ChatOperation.chat_messages_response() + ) + }, + security: [ + %{ + "oAuth" => ["read:chats"] + } + ] + } + end + + def show_operation do + %Operation{ + tags: ["chat"], + summary: "Create a chat", + operationId: "AdminAPI.ChatController.show", + parameters: [ + Operation.parameter( + :id, + :path, + :string, + "The id of the chat", + required: true, + example: "1234" + ) + ], + responses: %{ + 200 => + Operation.response( + "The existing chat", + "application/json", + Chat + ) + }, + security: [ + %{ + "oAuth" => ["read"] + } + ] + } + end +end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 500c3883e..a8c83bc8f 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -550,4 +550,21 @@ defmodule Pleroma.Web.CommonAPI do def show_reblogs(%User{} = user, %User{} = target) do UserRelationship.delete_reblog_mute(user, target) end + + def get_user(ap_id, fake_record_fallback \\ true) do + cond do + user = User.get_cached_by_ap_id(ap_id) -> + user + + user = User.get_by_guessed_nickname(ap_id) -> + user + + fake_record_fallback -> + # TODO: refactor (fake records is never a good idea) + User.error_user(ap_id) + + true -> + nil + end + end end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index ca42917fc..94b8dc8c6 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -55,23 +55,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do end) end - def get_user(ap_id, fake_record_fallback \\ true) do - cond do - user = User.get_cached_by_ap_id(ap_id) -> - user - - user = User.get_by_guessed_nickname(ap_id) -> - user - - fake_record_fallback -> - # TODO: refactor (fake records is never a good idea) - User.error_user(ap_id) - - true -> - nil - end - end - defp get_context_id(%{data: %{"context_id" => context_id}}) when not is_nil(context_id), do: context_id @@ -119,7 +102,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do # Note: unresolved users are filtered out actors = (activities ++ parent_activities) - |> Enum.map(&get_user(&1.data["actor"], false)) + |> Enum.map(&CommonAPI.get_user(&1.data["actor"], false)) |> Enum.filter(& &1) UserRelationship.view_relationships_option(reading_user, actors, subset: :source_mutes) @@ -138,7 +121,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do "show.json", %{activity: %{data: %{"type" => "Announce", "object" => _object}} = activity} = opts ) do - user = get_user(activity.data["actor"]) + user = CommonAPI.get_user(activity.data["actor"]) created_at = Utils.to_masto_date(activity.data["published"]) activity_object = Object.normalize(activity) @@ -211,7 +194,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do object = Object.normalize(activity) - user = get_user(activity.data["actor"]) + user = CommonAPI.get_user(activity.data["actor"]) user_follower_address = user.follower_address like_count = object.data["like_count"] || 0 @@ -265,7 +248,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do reply_to = get_reply_to(activity, opts) - reply_to_user = reply_to && get_user(reply_to.data["actor"]) + reply_to_user = reply_to && CommonAPI.get_user(reply_to.data["actor"]) content = object diff --git a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex index e8a1746d4..27c9a2e0f 100644 --- a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex @@ -146,11 +146,8 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do blocked_ap_ids = User.blocked_users_ap_ids(user) chats = - from(c in Chat, - where: c.user_id == ^user_id, - where: c.recipient not in ^blocked_ap_ids, - order_by: [desc: c.updated_at] - ) + Chat.for_user_query(user_id) + |> where([c], c.recipient not in ^blocked_ap_ids) |> Repo.all() conn diff --git a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex index bbff93abe..95bd4c368 100644 --- a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex +++ b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex @@ -10,14 +10,14 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleView do alias Pleroma.Activity alias Pleroma.HTML alias Pleroma.Object + alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.MastodonAPI.AccountView - alias Pleroma.Web.MastodonAPI.StatusView def render("show.json", %{activity: %Activity{data: %{"type" => "Listen"}} = activity} = opts) do object = Object.normalize(activity) - user = StatusView.get_user(activity.data["actor"]) + user = CommonAPI.get_user(activity.data["actor"]) created_at = Utils.to_masto_date(activity.data["published"]) %{ diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index c6433cc53..e4440d442 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -178,6 +178,7 @@ defmodule Pleroma.Web.Router do get("/users", AdminAPIController, :list_users) get("/users/:nickname", AdminAPIController, :user_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) @@ -214,6 +215,10 @@ defmodule Pleroma.Web.Router do get("/media_proxy_caches", MediaProxyCacheController, :index) post("/media_proxy_caches/delete", MediaProxyCacheController, :delete) post("/media_proxy_caches/purge", MediaProxyCacheController, :purge) + + get("/chats/:id", ChatController, :show) + get("/chats/:id/messages", ChatController, :messages) + delete("/chats/:id/messages/:message_id", ChatController, :delete_message) end scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do |